quaff 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ module Quaff
2
+ module Auth
3
+ def Auth.gen_nonce auth_pairs, username, passwd, method, sip_uri
4
+ a1 = username + ":" + auth_pairs["realm"] + ":" + passwd
5
+ a2 = method + ":" + sip_uri
6
+ ha1 = Digest::MD5::hexdigest(a1)
7
+ ha2 = Digest::MD5::hexdigest(a2)
8
+ digest = Digest::MD5.hexdigest(ha1 + ":" + auth_pairs["nonce"] + ":" + ha2)
9
+ return digest
10
+ end
11
+
12
+ def Auth.gen_auth_header auth_line, username, passwd, method, sip_uri
13
+ # Split auth line on commas
14
+ auth_pairs = {}
15
+ auth_line.sub("Digest ", "").split(",") .each do |pair|
16
+ key, value = pair.split "="
17
+ auth_pairs[key.gsub(" ", "")] = value.gsub("\"", "").gsub(" ", "")
18
+ end
19
+ digest = gen_nonce auth_pairs, username, passwd, method, sip_uri
20
+ return %Q!Digest username="#{username}",realm="#{auth_pairs['realm']}",nonce="#{auth_pairs['nonce']}",uri="#{sip_uri}",response="#{digest}",algorithm="#{auth_pairs['algorithm']}",opaque="#{auth_pairs['opaque']}"!
21
+ # Return Authorization header with fields username, realm, nonce, uri, nc, cnonce, response, opaque
22
+ end
23
+ end
24
+ end
@@ -1,22 +1,25 @@
1
1
  # -*- coding: us-ascii -*-
2
2
  require_relative './utils.rb'
3
3
  require_relative './sources.rb'
4
+ require_relative './auth.rb'
5
+ require_relative './message.rb'
4
6
 
5
- class CSeq
7
+ module Quaff
8
+ class CSeq # :nodoc:
6
9
  def initialize cseq_str
7
10
  @num, @method = cseq_str.split
8
11
  @num = @num.to_i
9
12
  end
10
13
 
11
14
  def increment
12
- @num = @num +1
15
+ @num = @num + 1
13
16
  to_s
14
17
  end
15
18
 
16
19
  def to_s
17
20
  "#{@num.to_s} #{@method}"
18
21
  end
19
-
22
+
20
23
  end
21
24
 
22
25
  class Call
@@ -24,7 +27,7 @@ class Call
24
27
 
25
28
  def initialize(cxn,
26
29
  cid,
27
- uri="sip:5557777888@#{QuaffUtils.local_ip}",
30
+ uri="sip:5557777888@#{Utils::local_ip}",
28
31
  destination=nil,
29
32
  target_uri=nil)
30
33
  @cxn = cxn
@@ -45,9 +48,9 @@ class Call
45
48
  end
46
49
 
47
50
  def update_branch
48
- @last_Via = "SIP/2.0/#{@cxn.transport} #{QuaffUtils.local_ip}:#{@cxn.local_port};rport;branch=#{QuaffUtils.new_branch}"
51
+ @last_Via = "SIP/2.0/#{@cxn.transport} #{Quaff::Utils.local_ip}:#{@cxn.local_port};rport;branch=#{Quaff::Utils::new_branch}"
49
52
  end
50
-
53
+
51
54
  def create_dialog msg
52
55
  set_callee msg.first_header("Contact")
53
56
  @routeset = msg.all_headers("Record-Route")
@@ -56,23 +59,11 @@ class Call
56
59
  end
57
60
  end
58
61
 
59
- def recv_something
60
- data = @cxn.get_new_message @cid
61
- @retrans = nil
62
- @src = data['source']
63
- @last_To = data["message"].header("To")
64
- @last_From = data["message"].header("From")
65
- @sip_destination ||= data["message"].header("From")
66
- @last_Via = data["message"].headers["Via"]
67
- @last_CSeq = CSeq.new(data["message"].header("CSeq"))
68
- data
69
- end
70
-
71
62
  def set_callee uri
72
63
  if /<(.*)>/ =~ uri
73
64
  uri = $1
74
65
  end
75
-
66
+
76
67
  @sip_destination = "#{uri}"
77
68
  @last_To = "<#{uri}>"
78
69
  end
@@ -90,8 +81,9 @@ class Call
90
81
  rescue
91
82
  raise "#{ @uri } timed out waiting for #{ method }"
92
83
  end
93
- unless data["message"].type == :request and Regexp.new(method) =~ data["message"].method
94
- raise (data['message'].to_s || "Message is nil!")
84
+ unless data["message"].type == :request \
85
+ and Regexp.new(method) =~ data["message"].method
86
+ raise((data['message'].to_s || "Message is nil!"))
95
87
  end
96
88
  data
97
89
  end
@@ -102,44 +94,94 @@ class Call
102
94
  rescue
103
95
  raise "#{ @uri } timed out waiting for #{ code }"
104
96
  end
105
- unless data["message"].type == :response and Regexp.new(code) =~ data["message"].status_code
97
+ unless data["message"].type == :response \
98
+ and Regexp.new(code) =~ data["message"].status_code
106
99
  raise "Expected #{ code}, got #{data["message"].status_code || data['message']}"
107
100
  end
108
101
  data
109
102
  end
110
103
 
111
- def send_response(code, retrans=nil, headers={})
112
- msg = build_message headers, :response, code
104
+ def send_response(code, phrase, body="", retrans=nil, headers={})
105
+ method = nil
106
+ msg = build_message headers, body, :response, method, code, phrase
113
107
  send_something(msg, retrans)
114
108
  end
115
109
 
116
- def send_request(method, sdp=true, retrans=nil, headers={})
117
- sdp="v=0
118
- o=user1 53655765 2353687637 IN IP4 #{QuaffUtils.local_ip}
119
- s=-
120
- c=IN IP4 #{QuaffUtils.local_ip}
121
- t=0 0
122
- m=audio 7000 RTP/AVP 0
123
- a=rtpmap:0 PCMU/8000"
110
+ def send_request(method, body="", headers={})
111
+ msg = build_message headers, body, :request, method
112
+ send_something(msg, nil)
113
+ end
124
114
 
125
- msg = build_message headers, :request, method
126
- send_something(msg, retrans)
115
+ def end_call
116
+ @cxn.mark_call_dead @cid
117
+ end
118
+
119
+
120
+ def clear_tag str
121
+ str
122
+ end
123
+
124
+ def clone_details other_message
125
+ @headers['To'] = [clear_tag(other_message.header("To"))]
126
+ @headers['From'] = [clear_tag(other_message.header("From"))]
127
+ @headers['Route'] = [other_message.header("Route")]
128
+ end
129
+
130
+ def get_next_hop header
131
+ /<sip:(.+@)?(.+):(\d+);(.*)>/ =~ header
132
+ sock = TCPSocket.new $2, $3
133
+ return TCPSource.new sock
134
+ end
135
+
136
+ def register username=@username, password=@password, expires="3600"
137
+ @username, @password = username, password
138
+ set_callee(@uri)
139
+ send_request("REGISTER", "", { "Expires" => expires.to_s })
140
+ response_data = recv_response("401|200")
141
+ if response_data['message'].status_code == "401"
142
+ send_request("ACK")
143
+ auth_hdr = Quaff::Auth.gen_auth_header response_data['message'].header("WWW-Authenticate"), username, password, "REGISTER", @uri
144
+ update_branch
145
+ send_request("REGISTER", "", {"Authorization" => auth_hdr, "Expires" => expires.to_s, "CSeq" => "2 REGISTER"})
146
+ recv_response("200")
147
+ end
148
+ return true
149
+ end
150
+
151
+ def unregister
152
+ register @username, @password, 0
153
+ end
154
+
155
+ private
156
+ def recv_something
157
+ data = @cxn.get_new_message @cid
158
+ @retrans = nil
159
+ @src = data['source']
160
+ @last_To = data["message"].header("To")
161
+ @last_From = data["message"].header("From")
162
+ @sip_destination ||= data["message"].header("From")
163
+ @last_Via = data["message"].headers["Via"]
164
+ @last_CSeq = CSeq.new(data["message"].header("CSeq"))
165
+ data
127
166
  end
128
167
 
129
- def build_message headers, type, method_or_code
168
+
169
+
170
+ def build_message headers, body, type, method=nil, code=nil, phrase=nil
130
171
  defaults = {
131
172
  "From" => @last_From,
132
173
  "To" => @last_To,
133
174
  "Call-ID" => @cid,
134
- "CSeq" => (/\d+/ =~ method_or_code) ? @last_CSeq.to_s : (method_or_code == "ACK") ? @last_CSeq.increment : "1 #{method_or_code}",
175
+ "CSeq" => (type == :response) ? @last_CSeq.to_s : (method == "ACK") ? @last_CSeq.increment : "1 #{method}",
135
176
  "Via" => @last_Via,
136
177
  "Max-Forwards" => "70",
137
178
  "Content-Length" => "0",
138
179
  "User-Agent" => "Quaff SIP Scripting Engine",
139
- "Contact" => "<sip:quaff@#{QuaffUtils.local_ip}:#{@cxn.local_port};transport=#{@cxn.transport};ob>",
180
+ "Contact" => "<sip:quaff@#{Utils::local_ip}:#{@cxn.local_port};transport=#{@cxn.transport};ob>",
140
181
  }
141
182
 
142
- if type == :request
183
+ is_request = method.nil?
184
+ if is_request
143
185
  defaults['Route'] = @routeset
144
186
  else
145
187
  defaults['Record-Route'] = @routeset
@@ -147,25 +189,8 @@ class Call
147
189
 
148
190
  defaults.merge! headers
149
191
 
192
+ SipMessage.new(method, code, phrase, @sip_destination, body, defaults.merge!(headers)).to_s
150
193
 
151
- if type == :request
152
- msg = "#{method_or_code} #{@sip_destination} SIP/2.0\r\n"
153
- else
154
- msg = "SIP/2.0 #{ method_or_code }\r\n"
155
- end
156
-
157
- defaults.each do |key, value|
158
- if value.nil?
159
- elsif not value.kind_of? Array
160
- msg += "#{key}: #{value}\r\n"
161
- else value.each do |subvalue|
162
- msg += "#{key}: #{subvalue}\r\n"
163
- end
164
- end
165
- end
166
- msg += "\r\n"
167
-
168
- msg
169
194
  end
170
195
 
171
196
  def send_something(msg, retrans)
@@ -188,43 +213,5 @@ class Call
188
213
  end
189
214
  end
190
215
 
191
- def end_call
192
- @cxn.mark_call_dead @cid
193
- end
194
-
195
- def clear_tag str
196
- str
197
- end
198
-
199
- def clone_details other_message
200
- @headers['To'] = [clear_tag(other_message.header("To"))]
201
- @headers['From'] = [clear_tag(other_message.header("From"))]
202
- @headers['Route'] = [other_message.header("Route")]
203
- end
204
-
205
- def get_next_hop header
206
- /<sip:(.+@)?(.+):(\d+);(.*)>/ =~ header
207
- sock = TCPSocket.new $2, $3
208
- return TCPSource.new sock
209
- end
210
-
211
- def register username=@username, password=@password, expires="3600"
212
- @username, @password = username, password
213
- set_callee(@uri)
214
- send_request("REGISTER", nil, nil, { "Expires" => expires.to_s })
215
- response_data = recv_response("401|200")
216
- if response_data['message'].status_code == "401"
217
- send_request("ACK")
218
- auth_hdr = gen_auth_header response_data['message'].header("WWW-Authenticate"), username, password, "REGISTER", @uri
219
- update_branch
220
- send_request("REGISTER", nil, nil, {"Authorization" => auth_hdr, "Expires" => expires.to_s})
221
- recv_response("200")
222
- end
223
- return true
224
- end
225
-
226
- def unregister
227
- register @username, @password, 0
228
- end
229
-
216
+ end
230
217
  end
@@ -5,6 +5,7 @@ require 'timeout'
5
5
  require_relative './sip_parser.rb'
6
6
  require_relative './sources.rb'
7
7
 
8
+ module Quaff
8
9
  class BaseEndpoint
9
10
  attr_accessor :msg_trace
10
11
 
@@ -31,6 +32,31 @@ class BaseEndpoint
31
32
  @lport
32
33
  end
33
34
 
35
+ def add_call_id cid
36
+ @messages[cid] ||= Queue.new
37
+ end
38
+
39
+ def get_new_call_id time_limit=5
40
+ Timeout::timeout(time_limit) { @call_ids.deq }
41
+ end
42
+
43
+ def get_new_message(cid, time_limit=5)
44
+ Timeout::timeout(time_limit) { @messages[cid].deq }
45
+ end
46
+
47
+ def mark_call_dead(cid)
48
+ @messages.delete cid
49
+ now = Time.now
50
+ @dead_calls[cid] = now + 30
51
+ @dead_calls = @dead_calls.keep_if {|k, v| v > now}
52
+ end
53
+
54
+ def send(data, source)
55
+ puts "Endpoint on #{@lport} sending #{data} to #{source.inspect}" if @msg_trace
56
+ source.send(@cxn, data)
57
+ end
58
+
59
+ private
34
60
  def initialize_queues
35
61
  @messages = {}
36
62
  @call_ids = Queue.new
@@ -59,30 +85,6 @@ class BaseEndpoint
59
85
  end
60
86
  end
61
87
 
62
- def add_call_id cid
63
- @messages[cid] ||= Queue.new
64
- end
65
-
66
- def get_new_call_id time_limit=5
67
- Timeout::timeout(time_limit) { @call_ids.deq }
68
- end
69
-
70
- def get_new_message(cid, time_limit=5)
71
- Timeout::timeout(time_limit) { @messages[cid].deq }
72
- end
73
-
74
- def mark_call_dead(cid)
75
- @messages.delete cid
76
- now = Time.now
77
- @dead_calls[cid] = now + 30
78
- @dead_calls = @dead_calls.keep_if {|k, v| v > now}
79
- end
80
-
81
- def send(data, source)
82
- puts "Endpoint on #{@lport} sending #{data} to #{source.inspect}" if @msg_trace
83
- source.send(@cxn, data)
84
- end
85
-
86
88
  end
87
89
 
88
90
  class TCPSIPEndpoint < BaseEndpoint
@@ -102,8 +104,22 @@ class TCPSIPEndpoint < BaseEndpoint
102
104
  return TCPSource.new ip, port
103
105
  end
104
106
 
105
- alias_method :new_connection, :new_source
107
+ def add_sock sock
108
+ @sockets.push sock
109
+ end
110
+
111
+ def terminate
112
+ oldsockets = @sockets.dup
113
+ @sockets = []
114
+ oldsockets.each do |s| s.close unless s.closed? end
115
+ mycxn = @cxn
116
+ @cxn = nil
117
+ mycxn.close
118
+ end
119
+
106
120
 
121
+ alias_method :new_connection, :new_source
122
+ private
107
123
  def recv_msg
108
124
  select_response = IO.select(@sockets, [], [], 0) || [[]]
109
125
  readable = select_response[0]
@@ -130,40 +146,21 @@ class TCPSIPEndpoint < BaseEndpoint
130
146
  queue_msg msg, TCPSourceFromSocket.new(sock)
131
147
  end
132
148
 
133
- def add_sock sock
134
- @sockets.push sock
135
- end
136
-
137
- def terminate
138
- oldsockets = @sockets.dup
139
- @sockets = []
140
- oldsockets.each do |s| s.close unless s.closed? end
141
- mycxn = @cxn
142
- @cxn = nil
143
- mycxn.close
144
- end
145
-
146
149
  end
147
150
 
148
151
  class UDPSIPEndpoint < BaseEndpoint
149
152
 
150
- def recv_msg
151
- data, addrinfo = @cxn.recvfrom(65535)
152
- @parser.parse_start
153
- msg = @parser.parse_partial(data)
154
- queue_msg msg, UDPSourceFromAddrinfo.new(addrinfo) unless msg.nil?
155
- end
156
-
157
153
  def transport
158
154
  "UDP"
159
155
  end
160
-
156
+
161
157
  def new_source ip, port
162
158
  return UDPSource.new ip, port
163
159
  end
164
-
160
+
165
161
  alias_method :new_connection, :new_source
166
-
162
+
163
+ private
167
164
  def initialize_connection(lport)
168
165
  @cxn = UDPSocket.new
169
166
  @cxn.bind('0.0.0.0', lport)
@@ -171,5 +168,14 @@ class UDPSIPEndpoint < BaseEndpoint
171
168
  @parser = SipParser.new
172
169
  end
173
170
 
171
+ def recv_msg
172
+ data, addrinfo = @cxn.recvfrom(65535)
173
+ @parser.parse_start
174
+ msg = @parser.parse_partial(data)
175
+ queue_msg msg, UDPSourceFromAddrinfo.new(addrinfo) unless msg.nil?
176
+ end
177
+
178
+
174
179
  end
175
180
 
181
+ end
@@ -0,0 +1,68 @@
1
+ module Quaff
2
+ class SipMessage
3
+ attr_accessor :method, :requri, :reason, :status_code, :headers
4
+ attr_reader :body
5
+
6
+ def initialize method=nil, status_code=nil, reason=nil,
7
+ req_uri=nil, body="", headers={}
8
+ @headers = headers
9
+ @method, @status_code, @reason, @req_uri = method, status_code, reason, req_uri
10
+ @body = body
11
+ @headers['Content-Length'] = [body.length]
12
+ end
13
+
14
+ def type
15
+ if @method
16
+ :request
17
+ elsif @status_code
18
+ :response
19
+ else
20
+ :nil
21
+ end
22
+ end
23
+
24
+ def all_headers hdr
25
+ return @headers[hdr]
26
+ end
27
+
28
+ def header hdr
29
+ return @headers[hdr][0] unless @headers[hdr].nil?
30
+ end
31
+
32
+ alias_method :first_header, :header
33
+
34
+ def body= body
35
+ @body = body
36
+ @headers['Content-Length'] = [body.length]
37
+ end
38
+
39
+ def to_s
40
+ msg = ""
41
+ if type == :request
42
+ msg << "#{@method} #{@req_uri} SIP/2.0\r\n"
43
+ else
44
+ msg << "SIP/2.0 #{@status_code} #{@reason}\r\n"
45
+ end
46
+
47
+ @headers.each do |key, value|
48
+ if value.nil?
49
+ elsif not value.kind_of? Array
50
+ msg << "#{key}: #{value}\r\n"
51
+ else value.each do |subvalue|
52
+ msg << "#{key}: #{subvalue}\r\n"
53
+ end
54
+ end
55
+ end
56
+ if body and body != ""
57
+ msg << "\r\n"
58
+ msg << body
59
+ else
60
+ msg << "\r\n"
61
+ end
62
+
63
+
64
+ msg
65
+ end
66
+
67
+ end
68
+ end
@@ -1,2 +1,3 @@
1
1
  require_relative './call.rb'
2
2
  require_relative './endpoint.rb'
3
+
@@ -1,133 +1,104 @@
1
1
  # -*- coding: us-ascii -*-
2
2
  require 'digest/md5'
3
+ require_relative './message.rb'
3
4
 
4
- class SipMessage
5
- attr_accessor :type, :method, :requri, :reason, :status_code, :headers, :body
6
-
7
- def initialize
8
- @headers = {}
9
- @method, @status_code, @reason, @req_uri = nil
10
- @body = ""
11
- end
12
-
13
- def all_headers hdr
14
- return @headers[hdr]
15
- end
16
-
17
- def header hdr
18
- return @headers[hdr][0] unless @headers[hdr].nil?
19
- end
20
-
21
- alias_method :first_header, :header
22
-
23
- def to_s
24
- "#{@method} #{@status_code} #{@headers}"
25
- end
26
-
27
- end
28
-
5
+ module Quaff
29
6
  class SipParser
30
7
  attr_reader :state
31
8
  def parse_start
32
- @buf = ""
33
- @msg = SipMessage.new
34
- @state = :blank
9
+ @buf = ""
10
+ @msg = SipMessage.new
11
+ @state = :blank
35
12
  end
36
13
 
37
14
  def parse_partial(data)
38
- return nil if data.nil?
39
- data.lines.each do |line|
40
- if @state == :blank
41
- parse_line_blank line
42
- elsif @state == :parsing_body
43
- parse_line_body line
44
- else
45
- parse_line_first_line_parsed line
46
- end
15
+ return nil if data.nil?
16
+ data.lines.each do |line|
17
+ line.rstrip!
18
+ if @state == :blank
19
+ parse_line_blank line
20
+ elsif @state == :parsing_body
21
+ parse_line_body line
22
+ else
23
+ parse_line_first_line_parsed line
47
24
  end
48
- if @state == :done
49
- return @msg
50
- else
51
- return nil
52
- end
25
+ end
26
+ if @state == :done
27
+ return @msg
28
+ else
29
+ return nil
30
+ end
53
31
  end
54
32
 
55
33
  def message_identifier(msg)
56
- msg.header("Call-ID")
34
+ msg.header("Call-ID")
57
35
  end
58
36
 
59
37
  def parse_line_blank line
60
- if line =~ %r!^([A-Z]+) (.+) SIP/2.0\r$!
61
- @msg.type = :request
62
- @msg.method = $1
63
- @msg.requri = $2
64
- @state = :first_line_parsed
65
- elsif line =~ %r!^SIP/2.0 (\d+) (.+)\r$!
66
- @msg.type = :response
67
- @msg.status_code = $1
68
- @msg.reason = $3 || ""
69
- @state = :first_line_parsed
70
- elsif line == "\r" or line == "\r\n"
71
- # skip empty lines
38
+ if line == ""
39
+ # skip empty lines
40
+ return
41
+ else
42
+ parts = line.split " ", 3
43
+ if parts[2] == "SIP/2.0"
44
+ # Looks like a SIP request
45
+ @msg.method = parts[0]
46
+ @msg.requri = parts[1]
47
+ @state = :first_line_parsed
48
+ return
49
+ elsif parts[0] == "SIP/2.0"
50
+ # Looks like a SIP response
51
+ @msg.status_code = parts[1]
52
+ @msg.reason = parts[2] || ""
53
+ @state = :first_line_parsed
54
+ return
72
55
  else
73
- raise line.inspect
56
+ raise parts.inspect
74
57
  end
58
+ end
59
+ # We haven't returned, so looks like a malformed line
60
+ raise line.inspect
75
61
  end
76
62
 
77
63
  def parse_line_first_line_parsed line
78
- if line =~ /^\s+(.+)\r/
79
- @msg.headers[@cur_hdr][-1] += " "
80
- @msg.headers[@cur_hdr][-1] += $1
81
- if $1 == "Content-Length"
82
- @state = :got_content_length
83
- else
84
- @state = :middle_of_headers
85
- end
86
- elsif line =~ /^([-\w]+)\s*:\s*(.+)\r/
87
- @msg.headers[$1] ||= []
88
- @msg.headers[$1].push $2
89
- @cur_hdr = $1
90
- if $1 == "Content-Length"
91
- @state = :got_content_length
92
- else
93
- @state = :middle_of_headers
94
- end
95
- elsif line == "\r" or line == "\r\n"
96
- if @state == :got_content_length and @msg.header("Content-Length").to_i > 0
97
- @state = :parsing_body
98
- else
99
- @state = :done
100
- end
101
- else raise line.inspect
64
+ if line.start_with? " "
65
+ @msg.headers[@cur_hdr][-1] += " "
66
+ @msg.headers[@cur_hdr][-1] += line.lstrip
67
+ if $1 == "Content-Length"
68
+ @state = :got_content_length
69
+ else
70
+ @state = :middle_of_headers
71
+ end
72
+ elsif line.include? ":"
73
+ parts = line.split ":", 2
74
+ header_name, header_value = parts[0].rstrip, parts[1].lstrip
75
+ @msg.headers[header_name] ||= []
76
+ @msg.headers[header_name].push header_value
77
+ @cur_hdr = header_name
78
+ if header_name == "Content-Length"
79
+ @state = :got_content_length
80
+ @msg.headers[header_name] = [header_value]
81
+ else
82
+ @state = :middle_of_headers
83
+ end
84
+ elsif line == ""
85
+ if @state == :got_content_length and @msg.header("Content-Length").to_i > 0
86
+ @state = :parsing_body
87
+ else
88
+ @state = :done
102
89
  end
90
+ else raise line.inspect
91
+ end
103
92
  end
104
93
 
105
94
  def parse_line_body line
106
- @msg.body << line
107
- if line == "\r" or @msg.body.length >= @msg.header("Content-Length").to_i
108
- @state = :done
109
- end
95
+ @msg.body << line
96
+ @msg.body << "\r\n"
97
+ if line == "" or @msg.body.length >= @msg.header("Content-Length").to_i
98
+ @state = :done
99
+ end
110
100
  end
111
101
 
112
- end
113
-
114
- def gen_nonce auth_pairs, username, passwd, method, sip_uri
115
- a1 = username + ":" + auth_pairs["realm"] + ":" + passwd
116
- a2 = method + ":" + sip_uri
117
- ha1 = Digest::MD5::hexdigest(a1)
118
- ha2 = Digest::MD5::hexdigest(a2)
119
- digest = Digest::MD5.hexdigest(ha1 + ":" + auth_pairs["nonce"] + ":" + ha2)
120
- return digest
121
102
  end
122
103
 
123
- def gen_auth_header auth_line, username, passwd, method, sip_uri
124
- # Split auth line on commas
125
- auth_pairs = {}
126
- auth_line.sub("Digest ", "").split(",") .each do |pair|
127
- key, value = pair.split "="
128
- auth_pairs[key.gsub(" ", "")] = value.gsub("\"", "").gsub(" ", "")
129
- end
130
- digest = gen_nonce auth_pairs, username, passwd, method, sip_uri
131
- return %Q!Digest username="#{username}",realm="#{auth_pairs['realm']}",nonce="#{auth_pairs['nonce']}",uri="#{sip_uri}",response="#{digest}",algorithm="#{auth_pairs['algorithm']}",opaque="#{auth_pairs['opaque']}"!
132
- # Return Authorization header with fields username, realm, nonce, uri, nc, cnonce, response, opaque
133
- end
104
+ end
@@ -1,5 +1,5 @@
1
1
  require 'socket'
2
-
2
+ module Quaff
3
3
  class Source
4
4
  def remote_ip
5
5
  @ip
@@ -38,12 +38,12 @@ class TCPSource < Source
38
38
  attr_reader :sock
39
39
 
40
40
  def initialize ip, port
41
- @sock = TCPSocket.new ip, port
41
+ @sock = TCPSocket.new ip, port
42
42
  @port, @ip = port, ip
43
43
  end
44
44
 
45
45
  def send _, data
46
- @sock.puts data
46
+ @sock.sendmsg data
47
47
  end
48
48
 
49
49
  def close cxn
@@ -58,3 +58,4 @@ class TCPSourceFromSocket < TCPSource
58
58
  @port, @ip = Socket.unpack_sockaddr_in(@sock.getpeername)
59
59
  end
60
60
  end
61
+ end
@@ -1,23 +1,23 @@
1
1
  require 'socket'
2
+ require 'facter'
2
3
 
3
- module QuaffUtils
4
- def QuaffUtils.local_ip
5
- Socket.ip_address_list.select {|i| !(i.ipv6? || i.ipv4_loopback?)}[0].ip_address
6
- end
4
+ module Quaff
7
5
 
8
- def local_ipv6
9
- Socket.ip_address_list.select {|i| !(i.ipv4? || i.ipv6_loopback?)}[0].ip_address
6
+ module Utils
7
+ def Utils.local_ip
8
+ Facter.value("ipaddress")
10
9
  end
11
10
 
12
- def pid
11
+ def Utils.pid
13
12
  Process.pid
14
13
  end
15
14
 
16
- def new_call_id
15
+ def Utils.new_call_id
17
16
  "#{pid}_#{Time.new.to_i}@#{local_ipv4}"
18
17
  end
19
18
 
20
- def QuaffUtils.new_branch
19
+ def Utils.new_branch
21
20
  "z9hG4bK#{Time.new.to_f}"
22
21
  end
23
22
  end
23
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quaff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-24 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-10-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: facter
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.7.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.7.3
14
30
  description: A Ruby library for writing SIP test scenarios
15
31
  email: rkd@rkd.me.uk
16
32
  executables: []
@@ -18,12 +34,14 @@ extensions: []
18
34
  extra_rdoc_files: []
19
35
  files:
20
36
  - lib/quaff.rb
37
+ - lib/sip_parser.rb
38
+ - lib/message.rb
21
39
  - lib/utils.rb
22
40
  - lib/call.rb
23
- - lib/sources.rb
24
- - lib/sip_parser.rb
25
41
  - lib/endpoint.rb
26
- homepage: http://github.com/rkd91/quaff
42
+ - lib/auth.rb
43
+ - lib/sources.rb
44
+ homepage: http://github.com/rkday/quaff
27
45
  licenses:
28
46
  - GPL3
29
47
  post_install_message: