quaff 0.7.3 → 0.8.0pre1
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.
- checksums.yaml +4 -4
- data/lib/quaff.rb +2 -2
- data/lib/{auth.rb → quaff/auth.rb} +5 -16
- data/lib/quaff/call.rb +385 -0
- data/lib/{endpoint.rb → quaff/endpoint.rb} +58 -56
- data/lib/{message.rb → quaff/message.rb} +0 -0
- data/lib/quaff/sip_dialog.rb +43 -0
- data/lib/{sip_parser.rb → quaff/sip_parser.rb} +2 -7
- data/lib/{sources.rb → quaff/sources.rb} +0 -0
- data/lib/{utils.rb → quaff/utils.rb} +0 -4
- metadata +13 -13
- data/lib/call.rb +0 -339
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c2cf0abc7f41e6977f8bec2c36a4c7f614cf319
|
4
|
+
data.tar.gz: 58d82abdf2ea57652f5fa14d81ce1fbbbaf770be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e44bb218bf436fc72fefb1c1113c3eb52ddea5694706fdfccf7e2d9c5a7bf7d7c12940b08ca2ee4252f0cdad87415bade59eea460cad27cecdbca8a34cd8d6
|
7
|
+
data.tar.gz: 2f696985ca1e300e8a61eda9ea89c7a239e05688db511cfbd7b03c8bcac634ca22889621d5f174c19e73fbe605f2fe3358415e303d63ac0726a5b1254ff7d1db
|
data/lib/quaff.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
require_relative './call.rb'
|
2
|
-
require_relative './endpoint.rb'
|
1
|
+
require_relative './quaff/call.rb'
|
2
|
+
require_relative './quaff/endpoint.rb'
|
3
3
|
|
@@ -2,18 +2,14 @@ require 'base64'
|
|
2
2
|
|
3
3
|
module Quaff
|
4
4
|
module Auth #:nodoc:
|
5
|
-
def Auth.gen_nonce auth_pairs, username, passwd, method, sip_uri, ha1=""
|
5
|
+
def Auth.gen_nonce auth_pairs, username, passwd, method, sip_uri, ha1=""
|
6
6
|
if ha1.empty?
|
7
7
|
a1 = username + ":" + auth_pairs["realm"] + ":" + passwd
|
8
8
|
ha1 = Digest::MD5::hexdigest(a1)
|
9
9
|
end
|
10
10
|
a2 = method + ":" + sip_uri
|
11
11
|
ha2 = Digest::MD5::hexdigest(a2)
|
12
|
-
|
13
|
-
digest = Digest::MD5.hexdigest(ha1 + ":" + auth_pairs["nonce"] + ":" + nc.to_s(16).rjust(8, "0") + ":" + cnonce + ":" + qop + ":" + ha2)
|
14
|
-
else
|
15
|
-
digest = Digest::MD5.hexdigest(ha1 + ":" + auth_pairs["nonce"] + ":" + ha2)
|
16
|
-
end
|
12
|
+
digest = Digest::MD5.hexdigest(ha1 + ":" + auth_pairs["nonce"] + ":" + ha2)
|
17
13
|
return digest
|
18
14
|
end
|
19
15
|
|
@@ -35,22 +31,15 @@ module Quaff
|
|
35
31
|
# the SIP URI
|
36
32
|
end
|
37
33
|
|
38
|
-
def Auth.gen_auth_header auth_line, username, passwd, method, sip_uri, ha1=""
|
34
|
+
def Auth.gen_auth_header auth_line, username, passwd, method, sip_uri, ha1=""
|
39
35
|
# Split auth line on commas
|
40
36
|
auth_pairs = {}
|
41
37
|
auth_line.sub("Digest ", "").split(",") .each do |pair|
|
42
38
|
key, value = pair.split "="
|
43
39
|
auth_pairs[key.gsub(" ", "")] = value.gsub("\"", "").gsub(" ", "")
|
44
40
|
end
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
digest = gen_nonce auth_pairs, username, passwd, method, sip_uri, ha1, qop, nc, cnonce
|
49
|
-
if !qop.empty?
|
50
|
-
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']}",qop="#{qop}",nc="#{nc.to_s(16).rjust(8, "0")}",cnonce="#{cnonce}"!
|
51
|
-
else
|
52
|
-
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']}"!
|
53
|
-
end
|
41
|
+
digest = gen_nonce auth_pairs, username, passwd, method, sip_uri, ha1
|
42
|
+
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']}"!
|
54
43
|
# Return Authorization header with fields username, realm, nonce, uri, nc, cnonce, response, opaque
|
55
44
|
end
|
56
45
|
end
|
data/lib/quaff/call.rb
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
# -*- coding: us-ascii -*-
|
2
|
+
require 'securerandom'
|
3
|
+
require 'timeout'
|
4
|
+
require_relative './utils.rb'
|
5
|
+
require_relative './sources.rb'
|
6
|
+
require_relative './auth.rb'
|
7
|
+
require_relative './message.rb'
|
8
|
+
require_relative './sip_dialog.rb'
|
9
|
+
|
10
|
+
module Quaff
|
11
|
+
class CSeq # :nodoc:
|
12
|
+
attr_reader :num
|
13
|
+
def initialize cseq_str
|
14
|
+
@num, @method = cseq_str.split
|
15
|
+
@num = @num.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def increment
|
19
|
+
@num = @num + 1
|
20
|
+
to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"#{@num.to_s} #{@method}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Call
|
29
|
+
attr_reader :cid, :dialog
|
30
|
+
|
31
|
+
def initialize(cxn,
|
32
|
+
cid,
|
33
|
+
my_uri,
|
34
|
+
target_uri,
|
35
|
+
destination=nil,
|
36
|
+
instance_id=nil)
|
37
|
+
@cxn = cxn
|
38
|
+
setdest(destination, recv_from_this: true) if destination
|
39
|
+
@current_retrans = nil
|
40
|
+
@retrans_keys = {}
|
41
|
+
@t1, @t2 = 0.5, 32
|
42
|
+
@instance_id = instance_id
|
43
|
+
@dialog = SipDialog.new cid, my_uri, target_uri
|
44
|
+
update_branch
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_callee uri
|
48
|
+
if /<(.*?)>/ =~ uri
|
49
|
+
uri = $1
|
50
|
+
end
|
51
|
+
|
52
|
+
@dialog.target = uri unless uri.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :set_dialog_target, :set_callee
|
56
|
+
|
57
|
+
# Sets the Source where messages in this call should be sent to by
|
58
|
+
# default.
|
59
|
+
#
|
60
|
+
# Options:
|
61
|
+
# :recv_from_this - if true, also listens for any incoming
|
62
|
+
# messages over this source's connection. (This is only
|
63
|
+
# meaningful for connection-oriented transports.)
|
64
|
+
def setdest source, options={}
|
65
|
+
@src = source
|
66
|
+
if options[:recv_from_this] and source.sock
|
67
|
+
@cxn.add_sock source.sock
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Receives a SIP request.
|
72
|
+
#
|
73
|
+
# Options:
|
74
|
+
# :dialog_creating - whether the dialog state (peer tags, etc.)
|
75
|
+
# should be updated with information from this request. Defaults to true.
|
76
|
+
def recv_request(method, options={})
|
77
|
+
dialog_creating = options[:dialog_creating] || true
|
78
|
+
begin
|
79
|
+
msg = recv_something
|
80
|
+
rescue Timeout::Error
|
81
|
+
raise "#{ @uri } timed out waiting for #{ method }"
|
82
|
+
end
|
83
|
+
|
84
|
+
unless msg.type == :request \
|
85
|
+
and Regexp.new(method) =~ msg.method
|
86
|
+
raise((msg.to_s || "Message is nil!"))
|
87
|
+
end
|
88
|
+
|
89
|
+
@dialog.cseq = CSeq.new(msg.header("CSeq")).num
|
90
|
+
|
91
|
+
if dialog_creating
|
92
|
+
create_dialog_from_request msg
|
93
|
+
end
|
94
|
+
msg
|
95
|
+
end
|
96
|
+
|
97
|
+
# Waits until the next message comes in, and handles it if it is one
|
98
|
+
# of possible_messages.
|
99
|
+
#
|
100
|
+
# possible_messages is a list of things that can be received.
|
101
|
+
# Elements can be:
|
102
|
+
# * a string representing the SIP method, e.g. "INVITE"
|
103
|
+
# * a number representing the SIP status code, e.g. 200
|
104
|
+
# * a two-item list, containing one of the above and a boolean
|
105
|
+
# value, which indicates whether this message is dialog-creating. by
|
106
|
+
# default, requests are assumed to be dialog-creating and responses
|
107
|
+
# are not.
|
108
|
+
#
|
109
|
+
# For example, ["INVITE", 301, ["ACK", false], [200, true]] is a
|
110
|
+
# valid value for possible_messages.
|
111
|
+
def recv_any_of(possible_messages)
|
112
|
+
begin
|
113
|
+
msg = recv_something
|
114
|
+
rescue Timeout::Error
|
115
|
+
raise "#{ @uri } timed out waiting for one of these: #{possible_messages}"
|
116
|
+
end
|
117
|
+
|
118
|
+
found_match = false
|
119
|
+
dialog_creating = nil
|
120
|
+
|
121
|
+
possible_messages.each do
|
122
|
+
| what, this_dialog_creating |
|
123
|
+
type = if (what.class == String) then :request else :response end
|
124
|
+
if this_dialog_creating.nil?
|
125
|
+
this_dialog_creating = (type == :request)
|
126
|
+
end
|
127
|
+
|
128
|
+
found_match =
|
129
|
+
if type == :request
|
130
|
+
msg.type == :request and what == msg.method
|
131
|
+
else
|
132
|
+
msg.type == :response and what.to_s == msg.status_code
|
133
|
+
end
|
134
|
+
|
135
|
+
if found_match
|
136
|
+
dialog_creating = this_dialog_creating
|
137
|
+
break
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
unless found_match
|
142
|
+
raise((msg.to_s || "Message is nil!"))
|
143
|
+
end
|
144
|
+
|
145
|
+
if dialog_creating
|
146
|
+
create_dialog msg
|
147
|
+
end
|
148
|
+
msg
|
149
|
+
end
|
150
|
+
|
151
|
+
# Receives a SIP response.
|
152
|
+
#
|
153
|
+
# Options:
|
154
|
+
# :dialog_creating - whether the dialog state (peer tags, etc.)
|
155
|
+
# should be updated with information from this response. Defaults
|
156
|
+
# to false.
|
157
|
+
# :ignore_responses - a list of status codes to ignore (e.g.
|
158
|
+
# [100] will mean that 100 Tryings are ignored rather than
|
159
|
+
# treated as unexpected).
|
160
|
+
def recv_response(code, options={})
|
161
|
+
dialog_creating = options[:dialog_creating] || false
|
162
|
+
ignore_responses = options[:ignore_responses] || []
|
163
|
+
begin
|
164
|
+
msg = recv_something
|
165
|
+
rescue Timeout::Error
|
166
|
+
raise "#{ @uri } timed out waiting for #{ code }"
|
167
|
+
end
|
168
|
+
|
169
|
+
if msg.type != :response
|
170
|
+
raise "Expected #{code}, got #{msg}"
|
171
|
+
elsif ignore_responses.include? msg.status_code
|
172
|
+
return recv_response(code, options)
|
173
|
+
else
|
174
|
+
unless Regexp.new(code) =~ msg.status_code
|
175
|
+
raise "Expected #{code}, got #{msg.status_code}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if dialog_creating
|
180
|
+
create_dialog_from_response msg
|
181
|
+
end
|
182
|
+
|
183
|
+
msg
|
184
|
+
end
|
185
|
+
|
186
|
+
# Sends a SIP response with the given status code and reason phrase.
|
187
|
+
#
|
188
|
+
# Options:
|
189
|
+
# :body - the SIP body to use.
|
190
|
+
# :sdp_body - as :body, but an appropriate Content-Type header is
|
191
|
+
# automatically added.
|
192
|
+
# :response_to - a message to use the branch and CSeq from.
|
193
|
+
# Useful for responding to an INVITE after handling a CANCEL
|
194
|
+
# transaction.
|
195
|
+
# :retrans - whether or not to retransmit this periodically until
|
196
|
+
# the next message is received. Defaults to false.
|
197
|
+
# :headers - a map of headers to use in this message
|
198
|
+
|
199
|
+
def send_response(code, phrase, options={})
|
200
|
+
body = options[:body] || ""
|
201
|
+
retrans = options[:retrans] || false
|
202
|
+
headers = options[:headers] || {}
|
203
|
+
if options[:sdp_body]
|
204
|
+
body = options[:sdp_body]
|
205
|
+
headers['Content-Type'] = "application/sdp"
|
206
|
+
end
|
207
|
+
|
208
|
+
if options[:response_to]
|
209
|
+
assoc_with_msg(options[:response_to])
|
210
|
+
headers['CSeq'] ||= CSeq.new(options[:response_to].header("CSeq"))
|
211
|
+
end
|
212
|
+
|
213
|
+
method = nil
|
214
|
+
msg = build_message headers, body, :response, method, code, phrase
|
215
|
+
send_something(msg, retrans)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Sends a SIP request with the given method.
|
219
|
+
#
|
220
|
+
# Options:
|
221
|
+
# :body - the SIP body to use.
|
222
|
+
# :sdp_body - as :body, but an appropriate Content-Type header is
|
223
|
+
# automatically added.
|
224
|
+
# :new_tsx - whether to generate a new branch ID. Defaults to true.
|
225
|
+
# :same_tsx_as - a message to use the branch and CSeq from.
|
226
|
+
# Useful for ACKing to an INVITE after handling a PRACK
|
227
|
+
# transaction.
|
228
|
+
# :retrans - whether or not to retransmit this periodically until
|
229
|
+
# the next message is received. Defaults to true unless the
|
230
|
+
# method is ACK.
|
231
|
+
# :headers - a map of headers to use in this message
|
232
|
+
def send_request(method, options={})
|
233
|
+
body = options[:body] || ""
|
234
|
+
headers = options[:headers] || {}
|
235
|
+
new_tsx = options[:new_tsx].nil? ? true : options[:new_tsx]
|
236
|
+
retrans =
|
237
|
+
if options[:retrans].nil?
|
238
|
+
if method == "ACK"
|
239
|
+
false
|
240
|
+
else
|
241
|
+
true
|
242
|
+
end
|
243
|
+
else
|
244
|
+
options[:retrans]
|
245
|
+
end
|
246
|
+
|
247
|
+
if options[:sdp_body]
|
248
|
+
body = options[:sdp_body]
|
249
|
+
headers['Content-Type'] = "application/sdp"
|
250
|
+
end
|
251
|
+
|
252
|
+
if options[:same_tsx_as]
|
253
|
+
assoc_with_msg(options[:same_tsx_as])
|
254
|
+
end
|
255
|
+
|
256
|
+
if new_tsx
|
257
|
+
update_branch
|
258
|
+
end
|
259
|
+
|
260
|
+
msg = build_message headers, body, :request, method
|
261
|
+
send_something(msg, retrans)
|
262
|
+
end
|
263
|
+
|
264
|
+
def end_call
|
265
|
+
@cxn.mark_call_dead @dialog.call_id
|
266
|
+
end
|
267
|
+
|
268
|
+
def get_next_hop header
|
269
|
+
/<sip:(.+@)?(.+):(\d+);(.*)>/ =~ header
|
270
|
+
sock = TCPSocket.new $2, $3
|
271
|
+
return TCPSource.new sock
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
def assoc_with_msg(msg)
|
276
|
+
@last_Via = msg.all_headers("Via")
|
277
|
+
end
|
278
|
+
|
279
|
+
# Changes the branch parameter if the Via header, creating a new transaction
|
280
|
+
def update_branch via_hdr=nil
|
281
|
+
via_hdr ||= get_new_via_hdr
|
282
|
+
@last_Via = via_hdr
|
283
|
+
end
|
284
|
+
|
285
|
+
alias_method :new_transaction, :update_branch
|
286
|
+
|
287
|
+
def get_new_via_hdr
|
288
|
+
"SIP/2.0/#{@cxn.transport} #{Quaff::Utils.local_ip}:#{@cxn.local_port};rport;branch=#{Quaff::Utils::new_branch}"
|
289
|
+
end
|
290
|
+
|
291
|
+
def recv_something
|
292
|
+
msg = @cxn.get_new_message @dialog.call_id
|
293
|
+
@retrans_keys.delete @current_retrans
|
294
|
+
@src = msg.source
|
295
|
+
@last_Via = msg.headers["Via"]
|
296
|
+
@last_CSeq = CSeq.new(msg.header("CSeq"))
|
297
|
+
msg
|
298
|
+
end
|
299
|
+
|
300
|
+
def calculate_cseq type, method
|
301
|
+
if (type == :response)
|
302
|
+
@last_CSeq.to_s
|
303
|
+
else
|
304
|
+
if (method != "ACK") and (method != "CANCEL")
|
305
|
+
@dialog.cseq += 1
|
306
|
+
end
|
307
|
+
"#{@dialog.cseq} #{method}"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def build_message headers, body, type, method=nil, code=nil, phrase=nil
|
312
|
+
is_request = code.nil?
|
313
|
+
|
314
|
+
defaults = {
|
315
|
+
"Call-ID" => @dialog.call_id,
|
316
|
+
"CSeq" => calculate_cseq(type, method),
|
317
|
+
"Via" => @last_Via,
|
318
|
+
"Max-Forwards" => "70",
|
319
|
+
"Content-Length" => "0",
|
320
|
+
"User-Agent" => "Quaff SIP Scripting Engine",
|
321
|
+
"Contact" => @cxn.contact_header
|
322
|
+
}
|
323
|
+
|
324
|
+
if is_request
|
325
|
+
defaults['From'] = @dialog.local_fromto
|
326
|
+
defaults['To'] = @dialog.peer_fromto
|
327
|
+
defaults['Route'] = @dialog.routeset
|
328
|
+
else
|
329
|
+
defaults['To'] = @dialog.local_fromto
|
330
|
+
defaults['From'] = @dialog.peer_fromto
|
331
|
+
defaults['Record-Route'] = @dialog.routeset
|
332
|
+
end
|
333
|
+
|
334
|
+
defaults.merge! headers
|
335
|
+
|
336
|
+
SipMessage.new(method, code, phrase, @dialog.target, body, defaults.merge!(headers)).to_s
|
337
|
+
end
|
338
|
+
|
339
|
+
def send_something(msg, retrans)
|
340
|
+
@cxn.send_msg(msg, @src)
|
341
|
+
if retrans and (@cxn.transport == "UDP") then
|
342
|
+
key = SecureRandom::hex
|
343
|
+
@current_retrans = key
|
344
|
+
@retrans_keys[key] = true
|
345
|
+
Thread.new do
|
346
|
+
timer = @t1
|
347
|
+
sleep timer
|
348
|
+
while @retrans_keys[key] do
|
349
|
+
#puts "Retransmitting #{ msg } on call #{ @dialog.call_id }"
|
350
|
+
@cxn.send_msg(msg, @src)
|
351
|
+
timer *=2
|
352
|
+
if timer > @t2 then
|
353
|
+
raise "Too many retransmits!"
|
354
|
+
end
|
355
|
+
sleep timer
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def create_dialog_from_request msg
|
362
|
+
@dialog.established = true
|
363
|
+
|
364
|
+
set_dialog_target msg.first_header("Contact")
|
365
|
+
|
366
|
+
unless msg.all_headers("Record-Route").nil?
|
367
|
+
@dialog.routeset = msg.all_headers("Record-Route")
|
368
|
+
end
|
369
|
+
|
370
|
+
@dialog.get_peer_info msg.header("From")
|
371
|
+
end
|
372
|
+
|
373
|
+
def create_dialog_from_response msg
|
374
|
+
@dialog.established = true
|
375
|
+
|
376
|
+
set_dialog_target msg.first_header("Contact")
|
377
|
+
|
378
|
+
unless msg.all_headers("Record-Route").nil?
|
379
|
+
@dialog.routeset = msg.all_headers("Record-Route").reverse
|
380
|
+
end
|
381
|
+
|
382
|
+
@dialog.get_peer_info msg.header("To")
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
@@ -14,6 +14,58 @@ module Quaff
|
|
14
14
|
attr_accessor :msg_trace, :uri, :sdp_port, :sdp_socket, :local_hostname
|
15
15
|
attr_reader :msg_log, :local_port, :instance_id
|
16
16
|
|
17
|
+
# Constructs a new endpoint
|
18
|
+
# Params:
|
19
|
+
# +uri+:: The SIP URI of this endpoint
|
20
|
+
# +username+:: The authentication username of this endpoint
|
21
|
+
# +password+:: The authentication password of this endpoint
|
22
|
+
# +local_port+:: The port this endpoint should bind to. Use
|
23
|
+
# ':anyport' to bind to an ephemeral port.
|
24
|
+
# +outbound_proxy+:: The outbound proxy where all requests should
|
25
|
+
# be directed. Optional, but it only makes sense to omit it when
|
26
|
+
# Quaff is emulating a server rather than a client.
|
27
|
+
# +outbound_port+:: The port of the outbound proxy
|
28
|
+
def initialize(uri, username, password, local_port, outbound_proxy=nil, outbound_port=5060)
|
29
|
+
@msg_log = Array.new
|
30
|
+
@uri = uri
|
31
|
+
@resolver = Resolv::DNS.new
|
32
|
+
@username = username
|
33
|
+
@password = password
|
34
|
+
@local_hostname = Utils::local_ip
|
35
|
+
@local_port = local_port
|
36
|
+
initialize_connection
|
37
|
+
if outbound_proxy
|
38
|
+
@outbound_connection = new_connection(outbound_proxy, outbound_port)
|
39
|
+
end
|
40
|
+
@hashes = []
|
41
|
+
@contact_params = {}
|
42
|
+
@contact_uri_params = {"transport" => transport, "ob" => true}
|
43
|
+
@terminated = false
|
44
|
+
@last_sent_msg = nil
|
45
|
+
initialize_queues
|
46
|
+
start
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieves the next unhandled call for this endpoint and returns
|
50
|
+
# a +Call+ object representing it
|
51
|
+
def incoming_call
|
52
|
+
begin
|
53
|
+
call_id = get_new_call_id
|
54
|
+
rescue Timeout::Error
|
55
|
+
raise "#{ @uri } timed out waiting for new incoming call"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Call-Id for endpoint on #{@local_port} is #{call_id}" if @msg_trace
|
59
|
+
Call.new(self, call_id, @uri, nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates a +Call+ object representing a new outbound call
|
63
|
+
def outgoing_call to_uri
|
64
|
+
call_id = generate_call_id
|
65
|
+
puts "Call-Id for endpoint on #{@local_port} is #{call_id}" if @msg_trace
|
66
|
+
Call.new(self, call_id, @uri, to_uri, @outbound_connection, @instance_id)
|
67
|
+
end
|
68
|
+
|
17
69
|
# Creates an SDP socket bound to an ephemeral port
|
18
70
|
def setup_sdp
|
19
71
|
@sdp_socket = UDPSocket.new
|
@@ -56,26 +108,6 @@ module Quaff
|
|
56
108
|
add_contact_param "+sip.instance", "\"<urn:uuid:#{id}>\""
|
57
109
|
end
|
58
110
|
|
59
|
-
# Retrieves the next unhandled call for this endpoint and returns
|
60
|
-
# a +Call+ object representing it
|
61
|
-
def incoming_call
|
62
|
-
begin
|
63
|
-
call_id = get_new_call_id
|
64
|
-
rescue Timeout::Error
|
65
|
-
raise "#{ @uri } timed out waiting for new incoming call"
|
66
|
-
end
|
67
|
-
|
68
|
-
puts "Call-Id for endpoint on #{@local_port} is #{call_id}" if @msg_trace
|
69
|
-
Call.new(self, call_id, @instance_id, @uri)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Creates a +Call+ object representing a new outbound call
|
73
|
-
def outgoing_call to_uri
|
74
|
-
call_id = generate_call_id
|
75
|
-
puts "Call-Id for endpoint on #{@local_port} is #{call_id}" if @msg_trace
|
76
|
-
Call.new(self, call_id, @instance_id, @uri, @outbound_connection, to_uri)
|
77
|
-
end
|
78
|
-
|
79
111
|
# Not yet ready for use
|
80
112
|
def create_client(uri, username, password, outbound_proxy, outbound_port=5060) # :nodoc:
|
81
113
|
end
|
@@ -88,38 +120,6 @@ module Quaff
|
|
88
120
|
def create_aka_client(uri, username, key, op, outbound_proxy, outbound_port=5060) # :nodoc:
|
89
121
|
end
|
90
122
|
|
91
|
-
# Constructs a new endpoint
|
92
|
-
# Params:
|
93
|
-
# +uri+:: The SIP URI of this endpoint
|
94
|
-
# +username+:: The authentication username of this endpoint
|
95
|
-
# +password+:: The authentication password of this endpoint
|
96
|
-
# +local_port+:: The port this endpoint should bind to. Use
|
97
|
-
# ':anyport' to bind to an ephemeral port.
|
98
|
-
# +outbound_proxy+:: The outbound proxy where all requests should
|
99
|
-
# be directed. Optional, but it only makes sense to omit it when
|
100
|
-
# Quaff is emulating a server rather than a client.
|
101
|
-
# +outbound_port+:: The port of the outbound proxy
|
102
|
-
def initialize(uri, username, password, local_port, outbound_proxy=nil, outbound_port=5060)
|
103
|
-
@msg_log = Array.new
|
104
|
-
@uri = uri
|
105
|
-
@resolver = Resolv::DNS.new
|
106
|
-
@username = username
|
107
|
-
@password = password
|
108
|
-
@local_host = Utils::local_ip
|
109
|
-
@local_port = local_port
|
110
|
-
initialize_connection
|
111
|
-
if outbound_proxy
|
112
|
-
@outbound_connection = new_connection(outbound_proxy, outbound_port)
|
113
|
-
end
|
114
|
-
@hashes = []
|
115
|
-
@contact_params = {}
|
116
|
-
@contact_uri_params = {"transport" => transport, "ob" => true}
|
117
|
-
@terminated = false
|
118
|
-
@local_hostname = Utils::local_ip
|
119
|
-
initialize_queues
|
120
|
-
start
|
121
|
-
end
|
122
|
-
|
123
123
|
def contact_header
|
124
124
|
param_str = Utils.paramhash_to_str(@contact_params)
|
125
125
|
uri_param_str = Utils.paramhash_to_str(@contact_uri_params)
|
@@ -129,7 +129,8 @@ module Quaff
|
|
129
129
|
def send_msg(data, source) # :nodoc:
|
130
130
|
@msg_log.push "Endpoint on #{@local_port} sending:\n\n#{data.strip}\n\nto #{source.inspect}"
|
131
131
|
puts "Endpoint on #{@local_port} sending #{data} to #{source.inspect}" if @msg_trace
|
132
|
-
|
132
|
+
source.send_msg(@cxn, data)
|
133
|
+
@last_sent_msg = data
|
133
134
|
end
|
134
135
|
|
135
136
|
# Not yet ready for use
|
@@ -150,8 +151,8 @@ module Quaff
|
|
150
151
|
@reg_call ||= outgoing_call(@uri)
|
151
152
|
auth_hdr = Quaff::Auth.gen_empty_auth_header @username
|
152
153
|
@reg_call.update_branch
|
153
|
-
@reg_call.send_request("REGISTER",
|
154
|
-
response_data = @reg_call.
|
154
|
+
@reg_call.send_request("REGISTER", retrans: true, headers: {"Authorization" => auth_hdr, "Expires" => expires.to_s})
|
155
|
+
response_data = @reg_call.recv_response_and_create_dialog("401|200")
|
155
156
|
if response_data.status_code == "401"
|
156
157
|
if aka
|
157
158
|
rand = Quaff::Auth.extract_rand response_data.header("WWW-Authenticate")
|
@@ -161,7 +162,7 @@ module Quaff
|
|
161
162
|
end
|
162
163
|
auth_hdr = Quaff::Auth.gen_auth_header response_data.header("WWW-Authenticate"), @username, @password, "REGISTER", @uri
|
163
164
|
@reg_call.update_branch
|
164
|
-
@reg_call.send_request("REGISTER",
|
165
|
+
@reg_call.send_request("REGISTER", retrans: true, headers: {"Authorization" => auth_hdr, "Expires" => expires.to_s})
|
165
166
|
response_data = @reg_call.recv_response("200")
|
166
167
|
end
|
167
168
|
return response_data # always the 200 OK
|
@@ -234,6 +235,7 @@ module Quaff
|
|
234
235
|
if is_retransmission? msg
|
235
236
|
@msg_log.push "Endpoint on #{@local_port} received retransmission"
|
236
237
|
puts "Endpoint on #{@local_port} received retransmission" if @msg_trace
|
238
|
+
source.send_msg(@cxn, @last_sent_msg) if @last_sent_msg
|
237
239
|
return
|
238
240
|
end
|
239
241
|
|
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: us-ascii -*-
|
2
|
+
require 'securerandom'
|
3
|
+
require_relative './sip_parser.rb'
|
4
|
+
|
5
|
+
module Quaff
|
6
|
+
class SipDialog
|
7
|
+
attr_accessor :local_uri, :local_tag, :peer_uri, :peer_tag, :target, :call_id, :cseq, :routeset, :established
|
8
|
+
|
9
|
+
def initialize call_id, local_uri, peer_uri
|
10
|
+
@cseq = 1
|
11
|
+
@call_id = call_id
|
12
|
+
@established = false
|
13
|
+
@routeset = []
|
14
|
+
@local_tag = SecureRandom::hex
|
15
|
+
@peer_uri = peer_uri
|
16
|
+
@target = peer_uri
|
17
|
+
@local_uri = local_uri
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_peer_info fromto_hdr
|
21
|
+
tospec = ToSpec.new
|
22
|
+
tospec.parse(fromto_hdr)
|
23
|
+
@peer_tag = tospec.params['tag']
|
24
|
+
@peer_uri = tospec.uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def local_fromto
|
28
|
+
if @local_tag
|
29
|
+
"<#{@local_uri}>;tag=#{@local_tag}"
|
30
|
+
else
|
31
|
+
"<#{@local_uri}>"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def peer_fromto
|
36
|
+
if @peer_tag
|
37
|
+
"<#{@peer_uri}>;tag=#{@peer_tag}"
|
38
|
+
else
|
39
|
+
"<#{@peer_uri}>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -35,7 +35,7 @@ module Quaff
|
|
35
35
|
elsif udp and msg.header("Content-Length").nil?
|
36
36
|
add_body io.read(1500)
|
37
37
|
end
|
38
|
-
|
38
|
+
msg
|
39
39
|
end
|
40
40
|
|
41
41
|
def parse_start
|
@@ -195,13 +195,8 @@ module Quaff
|
|
195
195
|
uri_parameters)
|
196
196
|
end
|
197
197
|
|
198
|
-
def tel_uri
|
199
|
-
Concat.new(Literal.new("tel:"),
|
200
|
-
Repetition.new([:at_least, 1], Digit.new))
|
201
|
-
end
|
202
|
-
|
203
198
|
def addr_spec
|
204
|
-
|
199
|
+
sip_uri
|
205
200
|
end
|
206
201
|
|
207
202
|
def wsp
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quaff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Day
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: system-getifaddrs
|
@@ -58,14 +58,15 @@ executables: []
|
|
58
58
|
extensions: []
|
59
59
|
extra_rdoc_files: []
|
60
60
|
files:
|
61
|
-
- lib/auth.rb
|
62
|
-
- lib/call.rb
|
63
|
-
- lib/endpoint.rb
|
64
|
-
- lib/message.rb
|
65
61
|
- lib/quaff.rb
|
66
|
-
- lib/
|
67
|
-
- lib/
|
68
|
-
- lib/
|
62
|
+
- lib/quaff/auth.rb
|
63
|
+
- lib/quaff/call.rb
|
64
|
+
- lib/quaff/endpoint.rb
|
65
|
+
- lib/quaff/message.rb
|
66
|
+
- lib/quaff/sip_dialog.rb
|
67
|
+
- lib/quaff/sip_parser.rb
|
68
|
+
- lib/quaff/sources.rb
|
69
|
+
- lib/quaff/utils.rb
|
69
70
|
homepage: http://github.com/rkday/quaff
|
70
71
|
licenses:
|
71
72
|
- GPL3
|
@@ -82,14 +83,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
83
|
version: '0'
|
83
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
85
|
requirements:
|
85
|
-
- - "
|
86
|
+
- - ">"
|
86
87
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
88
|
+
version: 1.3.1
|
88
89
|
requirements: []
|
89
90
|
rubyforge_project:
|
90
|
-
rubygems_version: 2.4.
|
91
|
+
rubygems_version: 2.4.3
|
91
92
|
signing_key:
|
92
93
|
specification_version: 4
|
93
94
|
summary: Quaff
|
94
95
|
test_files: []
|
95
|
-
has_rdoc:
|
data/lib/call.rb
DELETED
@@ -1,339 +0,0 @@
|
|
1
|
-
# -*- coding: us-ascii -*-
|
2
|
-
require 'securerandom'
|
3
|
-
require 'timeout'
|
4
|
-
require_relative './utils.rb'
|
5
|
-
require_relative './sources.rb'
|
6
|
-
require_relative './auth.rb'
|
7
|
-
require_relative './message.rb'
|
8
|
-
|
9
|
-
module Quaff
|
10
|
-
class CSeq # :nodoc:
|
11
|
-
attr_reader :num
|
12
|
-
def initialize cseq_str
|
13
|
-
@num, @method = cseq_str.split
|
14
|
-
@num = @num.to_i
|
15
|
-
end
|
16
|
-
|
17
|
-
def increment
|
18
|
-
@num = @num + 1
|
19
|
-
to_s
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_s
|
23
|
-
"#{@num.to_s} #{@method}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Call
|
28
|
-
attr_reader :cid, :last_To, :last_From, :sip_destination
|
29
|
-
|
30
|
-
def initialize(cxn,
|
31
|
-
cid,
|
32
|
-
instance_id=nil,
|
33
|
-
uri=nil,
|
34
|
-
destination=nil,
|
35
|
-
target_uri=nil)
|
36
|
-
@cxn = cxn
|
37
|
-
setdest(destination, recv_from_this: true) if destination
|
38
|
-
@retrans = nil
|
39
|
-
@t1, @t2 = 0.5, 32
|
40
|
-
@instance_id = instance_id
|
41
|
-
@cid = cid
|
42
|
-
set_default_headers cid, uri, target_uri
|
43
|
-
end
|
44
|
-
|
45
|
-
# Changes the branch parameter if the Via header, creating a new transaction
|
46
|
-
def update_branch via_hdr=nil
|
47
|
-
via_hdr ||= get_new_via_hdr
|
48
|
-
@last_Via = via_hdr
|
49
|
-
end
|
50
|
-
|
51
|
-
alias_method :new_transaction, :update_branch
|
52
|
-
|
53
|
-
def get_new_via_hdr
|
54
|
-
"SIP/2.0/#{@cxn.transport} #{Quaff::Utils.local_ip}:#{@cxn.local_port};rport;branch=#{Quaff::Utils::new_branch}"
|
55
|
-
end
|
56
|
-
|
57
|
-
def set_callee uri
|
58
|
-
if /<(.*?)>/ =~ uri
|
59
|
-
uri = $1
|
60
|
-
end
|
61
|
-
|
62
|
-
@sip_destination = "#{uri}"
|
63
|
-
end
|
64
|
-
|
65
|
-
def create_dialog msg
|
66
|
-
if @in_dialog
|
67
|
-
return
|
68
|
-
end
|
69
|
-
|
70
|
-
@in_dialog = true
|
71
|
-
|
72
|
-
uri = msg.first_header("Contact")
|
73
|
-
|
74
|
-
if /<(.*?)>/ =~ uri
|
75
|
-
uri = $1
|
76
|
-
end
|
77
|
-
|
78
|
-
@sip_destination = uri
|
79
|
-
|
80
|
-
unless msg.all_headers("Record-Route").nil?
|
81
|
-
if msg.type == :request
|
82
|
-
@routeset = msg.all_headers("Record-Route")
|
83
|
-
else
|
84
|
-
@routeset = msg.all_headers("Record-Route").reverse
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
# Sets the Source where messages in this call should be sent to by
|
91
|
-
# default.
|
92
|
-
#
|
93
|
-
# Options:
|
94
|
-
# :recv_from_this - if true, also listens for any incoming
|
95
|
-
# messages over this source's connection. (This is only
|
96
|
-
# meaningful for connection-oriented transports.)
|
97
|
-
def setdest source, options={}
|
98
|
-
@src = source
|
99
|
-
if options[:recv_from_this] and source.sock
|
100
|
-
@cxn.add_sock source.sock
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def recv_request(method, dialog_creating=true)
|
105
|
-
begin
|
106
|
-
msg = recv_something
|
107
|
-
rescue Timeout::Error
|
108
|
-
raise "#{ @uri } timed out waiting for #{ method }"
|
109
|
-
end
|
110
|
-
|
111
|
-
unless msg.type == :request \
|
112
|
-
and Regexp.new(method) =~ msg.method
|
113
|
-
raise((msg.to_s || "Message is nil!"))
|
114
|
-
end
|
115
|
-
|
116
|
-
unless @has_To_tag
|
117
|
-
@has_To_tag = true
|
118
|
-
tospec = ToSpec.new
|
119
|
-
tospec.parse(msg.header("To"))
|
120
|
-
tospec.params['tag'] = generate_random_tag
|
121
|
-
@last_To = tospec.to_s
|
122
|
-
@last_From = msg.header("From")
|
123
|
-
end
|
124
|
-
|
125
|
-
if dialog_creating
|
126
|
-
create_dialog msg
|
127
|
-
end
|
128
|
-
msg
|
129
|
-
end
|
130
|
-
|
131
|
-
# Waits until the next message comes in, and handles it if it is one
|
132
|
-
# of possible_messages.
|
133
|
-
#
|
134
|
-
# possible_messages is a list of things that can be received.
|
135
|
-
# Elements can be:
|
136
|
-
# * a string representing the SIP method, e.g. "INVITE"
|
137
|
-
# * a number representing the SIP status code, e.g. 200
|
138
|
-
# * a two-item list, containing one of the above and a boolean
|
139
|
-
# value, which indicates whether this message is dialog-creating. by
|
140
|
-
# default, requests are assumed to be dialog-creating and responses
|
141
|
-
# are not.
|
142
|
-
#
|
143
|
-
# For example, ["INVITE", 301, ["ACK", false], [200, true]] is a
|
144
|
-
# valid value for possible_messages.
|
145
|
-
def recv_any_of(possible_messages)
|
146
|
-
begin
|
147
|
-
msg = recv_something
|
148
|
-
rescue Timeout::Error
|
149
|
-
raise "#{ @uri } timed out waiting for one of these: #{possible_messages}"
|
150
|
-
end
|
151
|
-
|
152
|
-
found_match = false
|
153
|
-
dialog_creating = nil
|
154
|
-
|
155
|
-
possible_messages.each do
|
156
|
-
| what, this_dialog_creating |
|
157
|
-
type = if (what.class == String) then :request else :response end
|
158
|
-
if this_dialog_creating.nil?
|
159
|
-
this_dialog_creating = (type == :request)
|
160
|
-
end
|
161
|
-
|
162
|
-
found_match =
|
163
|
-
if type == :request
|
164
|
-
msg.type == :request and what == msg.method
|
165
|
-
else
|
166
|
-
msg.type == :response and what.to_s == msg.status_code
|
167
|
-
end
|
168
|
-
|
169
|
-
if found_match
|
170
|
-
dialog_creating = this_dialog_creating
|
171
|
-
break
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
unless found_match
|
176
|
-
raise((msg.to_s || "Message is nil!"))
|
177
|
-
end
|
178
|
-
|
179
|
-
if msg.type == :request
|
180
|
-
unless @has_To_tag
|
181
|
-
@has_To_tag = true
|
182
|
-
tospec = ToSpec.new
|
183
|
-
tospec.parse(msg.header("To"))
|
184
|
-
tospec.params['tag'] = generate_random_tag
|
185
|
-
@last_To = tospec.to_s
|
186
|
-
@last_From = msg.header("From")
|
187
|
-
end
|
188
|
-
else
|
189
|
-
if @in_dialog
|
190
|
-
@has_To_tag = true
|
191
|
-
@last_To = msg.header("To")
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
if dialog_creating
|
196
|
-
create_dialog msg
|
197
|
-
end
|
198
|
-
msg
|
199
|
-
end
|
200
|
-
|
201
|
-
def recv_response(code, dialog_creating=false)
|
202
|
-
begin
|
203
|
-
msg = recv_something
|
204
|
-
rescue Timeout::Error
|
205
|
-
raise "#{ @uri } timed out waiting for #{ code }"
|
206
|
-
end
|
207
|
-
unless msg.type == :response \
|
208
|
-
and Regexp.new(code) =~ msg.status_code
|
209
|
-
raise "Expected #{ code}, got #{msg.status_code || msg}"
|
210
|
-
end
|
211
|
-
|
212
|
-
if dialog_creating
|
213
|
-
create_dialog msg
|
214
|
-
end
|
215
|
-
|
216
|
-
if @in_dialog
|
217
|
-
@has_To_tag = true
|
218
|
-
@last_To = msg.header("To")
|
219
|
-
end
|
220
|
-
|
221
|
-
msg
|
222
|
-
end
|
223
|
-
|
224
|
-
def recv_response_and_create_dialog(code)
|
225
|
-
recv_response code, true
|
226
|
-
end
|
227
|
-
|
228
|
-
def send_response(code, phrase, body="", retrans=nil, headers={})
|
229
|
-
method = nil
|
230
|
-
msg = build_message headers, body, :response, method, code, phrase
|
231
|
-
send_something(msg, retrans)
|
232
|
-
end
|
233
|
-
|
234
|
-
def send_request(method, body="", headers={})
|
235
|
-
msg = build_message headers, body, :request, method
|
236
|
-
send_something(msg, nil)
|
237
|
-
end
|
238
|
-
|
239
|
-
def end_call
|
240
|
-
@cxn.mark_call_dead @cid
|
241
|
-
end
|
242
|
-
|
243
|
-
def assoc_with_msg(msg)
|
244
|
-
@last_Via = msg.all_headers("Via")
|
245
|
-
@last_CSeq = CSeq.new(msg.header("CSeq"))
|
246
|
-
end
|
247
|
-
|
248
|
-
def get_next_hop header
|
249
|
-
/<sip:(.+@)?(.+):(\d+);(.*)>/ =~ header
|
250
|
-
sock = TCPSocket.new $2, $3
|
251
|
-
return TCPSource.new sock
|
252
|
-
end
|
253
|
-
|
254
|
-
private
|
255
|
-
def recv_something
|
256
|
-
msg = @cxn.get_new_message @cid
|
257
|
-
@retrans = nil
|
258
|
-
@src = msg.source
|
259
|
-
set_callee msg.header("From")
|
260
|
-
@last_Via = msg.headers["Via"]
|
261
|
-
@last_CSeq = CSeq.new(msg.header("CSeq"))
|
262
|
-
msg
|
263
|
-
end
|
264
|
-
|
265
|
-
def calculate_cseq type, method
|
266
|
-
if (type == :response)
|
267
|
-
@last_CSeq.to_s
|
268
|
-
elsif (method == "ACK")
|
269
|
-
"#{@last_CSeq.num} ACK"
|
270
|
-
else
|
271
|
-
@cseq_number = @cseq_number + 1
|
272
|
-
"#{@cseq_number} #{method}"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def build_message headers, body, type, method=nil, code=nil, phrase=nil
|
277
|
-
defaults = {
|
278
|
-
"From" => @last_From,
|
279
|
-
"To" => @last_To,
|
280
|
-
"Call-ID" => @cid,
|
281
|
-
"CSeq" => calculate_cseq(type, method),
|
282
|
-
"Via" => @last_Via,
|
283
|
-
"Max-Forwards" => "70",
|
284
|
-
"Content-Length" => "0",
|
285
|
-
"User-Agent" => "Quaff SIP Scripting Engine",
|
286
|
-
"Contact" => @cxn.contact_header
|
287
|
-
}
|
288
|
-
|
289
|
-
is_request = code.nil?
|
290
|
-
if is_request
|
291
|
-
defaults['Route'] = @routeset
|
292
|
-
else
|
293
|
-
defaults['Record-Route'] = @routeset
|
294
|
-
end
|
295
|
-
|
296
|
-
defaults.merge! headers
|
297
|
-
|
298
|
-
SipMessage.new(method, code, phrase, @sip_destination, body, defaults.merge!(headers)).to_s
|
299
|
-
|
300
|
-
end
|
301
|
-
|
302
|
-
def send_something(msg, retrans)
|
303
|
-
@cxn.send_msg(msg, @src)
|
304
|
-
if retrans and (@transport == "UDP") then
|
305
|
-
@retrans = true
|
306
|
-
Thread.new do
|
307
|
-
timer = @t1
|
308
|
-
sleep timer
|
309
|
-
while @retrans do
|
310
|
-
#puts "Retransmitting on call #{ @cid }"
|
311
|
-
@cxn.send(msg, @src)
|
312
|
-
timer *=2
|
313
|
-
if timer < @t2 then
|
314
|
-
raise "Too many retransmits!"
|
315
|
-
end
|
316
|
-
sleep timer
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
def set_default_headers cid, uri, target_uri
|
323
|
-
@cseq_number = 1
|
324
|
-
@uri = uri
|
325
|
-
@last_From = "<#{uri}>;tag=" + generate_random_tag
|
326
|
-
@in_dialog = false
|
327
|
-
@has_To_tag = false
|
328
|
-
update_branch
|
329
|
-
@last_To = "<#{target_uri}>"
|
330
|
-
@sip_destination = target_uri
|
331
|
-
@routeset = []
|
332
|
-
end
|
333
|
-
|
334
|
-
def generate_random_tag
|
335
|
-
SecureRandom::hex
|
336
|
-
end
|
337
|
-
|
338
|
-
end
|
339
|
-
end
|