oversip 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/AUTHORS.txt +11 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +16 -0
  4. data/Rakefile +55 -0
  5. data/bin/oversip +182 -0
  6. data/ext/common/c_util.h +74 -0
  7. data/ext/common/ruby_c_util.h +88 -0
  8. data/ext/sip_parser/common_headers.h +209 -0
  9. data/ext/sip_parser/ext_help.h +18 -0
  10. data/ext/sip_parser/extconf.rb +3 -0
  11. data/ext/sip_parser/sip_parser.c +29649 -0
  12. data/ext/sip_parser/sip_parser.h +227 -0
  13. data/ext/sip_parser/sip_parser_ruby.c +1292 -0
  14. data/ext/stud/extconf.rb +27 -0
  15. data/ext/stud/stud.tar.gz +0 -0
  16. data/ext/stun/ext_help.h +16 -0
  17. data/ext/stun/extconf.rb +3 -0
  18. data/ext/stun/stun_ruby.c +391 -0
  19. data/ext/utils/ext_help.h +14 -0
  20. data/ext/utils/extconf.rb +3 -0
  21. data/ext/utils/haproxy_protocol.c +6163 -0
  22. data/ext/utils/haproxy_protocol.h +27 -0
  23. data/ext/utils/ip_utils.c +5952 -0
  24. data/ext/utils/ip_utils.h +61 -0
  25. data/ext/utils/outbound_utils.c +3227 -0
  26. data/ext/utils/outbound_utils.h +27 -0
  27. data/ext/utils/utils_ruby.c +384 -0
  28. data/ext/utils/utils_ruby.h +75 -0
  29. data/ext/websocket_framing_utils/ext_help.h +18 -0
  30. data/ext/websocket_framing_utils/extconf.rb +3 -0
  31. data/ext/websocket_framing_utils/ws_framing_utils.h +46 -0
  32. data/ext/websocket_framing_utils/ws_framing_utils_ruby.c +135 -0
  33. data/ext/websocket_http_parser/ext_help.h +18 -0
  34. data/ext/websocket_http_parser/extconf.rb +3 -0
  35. data/ext/websocket_http_parser/ws_http_parser.c +2598 -0
  36. data/ext/websocket_http_parser/ws_http_parser.h +86 -0
  37. data/ext/websocket_http_parser/ws_http_parser_ruby.c +630 -0
  38. data/lib/oversip/config.rb +541 -0
  39. data/lib/oversip/config_validators.rb +126 -0
  40. data/lib/oversip/errors.rb +7 -0
  41. data/lib/oversip/fiber_pool.rb +56 -0
  42. data/lib/oversip/launcher.rb +507 -0
  43. data/lib/oversip/logger.rb +170 -0
  44. data/lib/oversip/master_process.rb +67 -0
  45. data/lib/oversip/posix_mq.rb +121 -0
  46. data/lib/oversip/proxies_config.rb +169 -0
  47. data/lib/oversip/ruby_ext/eventmachine.rb +38 -0
  48. data/lib/oversip/sip/client_transaction.rb +587 -0
  49. data/lib/oversip/sip/constants.rb +87 -0
  50. data/lib/oversip/sip/grammar/name_addr.rb +27 -0
  51. data/lib/oversip/sip/grammar/uri.rb +116 -0
  52. data/lib/oversip/sip/launcher.rb +180 -0
  53. data/lib/oversip/sip/listeners/ipv4_tcp_client.rb +21 -0
  54. data/lib/oversip/sip/listeners/ipv4_tcp_server.rb +21 -0
  55. data/lib/oversip/sip/listeners/ipv4_tls_client.rb +21 -0
  56. data/lib/oversip/sip/listeners/ipv4_tls_server.rb +21 -0
  57. data/lib/oversip/sip/listeners/ipv4_tls_tunnel_server.rb +21 -0
  58. data/lib/oversip/sip/listeners/ipv4_udp_server.rb +20 -0
  59. data/lib/oversip/sip/listeners/ipv6_tcp_client.rb +21 -0
  60. data/lib/oversip/sip/listeners/ipv6_tcp_server.rb +21 -0
  61. data/lib/oversip/sip/listeners/ipv6_tls_client.rb +21 -0
  62. data/lib/oversip/sip/listeners/ipv6_tls_server.rb +21 -0
  63. data/lib/oversip/sip/listeners/ipv6_tls_tunnel_server.rb +21 -0
  64. data/lib/oversip/sip/listeners/ipv6_udp_server.rb +20 -0
  65. data/lib/oversip/sip/listeners/reactor.rb +39 -0
  66. data/lib/oversip/sip/listeners/tcp_client.rb +73 -0
  67. data/lib/oversip/sip/listeners/tcp_reactor.rb +185 -0
  68. data/lib/oversip/sip/listeners/tcp_server.rb +71 -0
  69. data/lib/oversip/sip/listeners/tls_client.rb +117 -0
  70. data/lib/oversip/sip/listeners/tls_server.rb +70 -0
  71. data/lib/oversip/sip/listeners/tls_tunnel_reactor.rb +113 -0
  72. data/lib/oversip/sip/listeners/tls_tunnel_server.rb +61 -0
  73. data/lib/oversip/sip/listeners/udp_reactor.rb +213 -0
  74. data/lib/oversip/sip/listeners.rb +28 -0
  75. data/lib/oversip/sip/logic.rb +14 -0
  76. data/lib/oversip/sip/message.rb +168 -0
  77. data/lib/oversip/sip/message_processor.rb +202 -0
  78. data/lib/oversip/sip/modules/core.rb +200 -0
  79. data/lib/oversip/sip/modules/registrar_without_path.rb +75 -0
  80. data/lib/oversip/sip/modules/user_assertion.rb +123 -0
  81. data/lib/oversip/sip/proxy.rb +460 -0
  82. data/lib/oversip/sip/request.rb +128 -0
  83. data/lib/oversip/sip/response.rb +30 -0
  84. data/lib/oversip/sip/rfc3263.rb +646 -0
  85. data/lib/oversip/sip/server_transaction.rb +295 -0
  86. data/lib/oversip/sip/sip.rb +74 -0
  87. data/lib/oversip/sip/tags.rb +39 -0
  88. data/lib/oversip/sip/timers.rb +55 -0
  89. data/lib/oversip/sip/transport_manager.rb +129 -0
  90. data/lib/oversip/syslogger_process.rb +119 -0
  91. data/lib/oversip/tls.rb +179 -0
  92. data/lib/oversip/utils.rb +25 -0
  93. data/lib/oversip/version.rb +23 -0
  94. data/lib/oversip/websocket/constants.rb +56 -0
  95. data/lib/oversip/websocket/default_policy.rb +19 -0
  96. data/lib/oversip/websocket/http_request.rb +63 -0
  97. data/lib/oversip/websocket/launcher.rb +207 -0
  98. data/lib/oversip/websocket/listeners/ipv4_tcp_server.rb +15 -0
  99. data/lib/oversip/websocket/listeners/ipv4_tls_server.rb +15 -0
  100. data/lib/oversip/websocket/listeners/ipv4_tls_tunnel_server.rb +15 -0
  101. data/lib/oversip/websocket/listeners/ipv6_tcp_server.rb +15 -0
  102. data/lib/oversip/websocket/listeners/ipv6_tls_server.rb +15 -0
  103. data/lib/oversip/websocket/listeners/ipv6_tls_tunnel_server.rb +15 -0
  104. data/lib/oversip/websocket/listeners/tcp_server.rb +265 -0
  105. data/lib/oversip/websocket/listeners/tls_server.rb +69 -0
  106. data/lib/oversip/websocket/listeners/tls_tunnel_server.rb +100 -0
  107. data/lib/oversip/websocket/listeners.rb +12 -0
  108. data/lib/oversip/websocket/ws_app.rb +75 -0
  109. data/lib/oversip/websocket/ws_apps/ipv4_ws_sip_app.rb +21 -0
  110. data/lib/oversip/websocket/ws_apps/ipv4_wss_sip_app.rb +21 -0
  111. data/lib/oversip/websocket/ws_apps/ipv6_ws_sip_app.rb +21 -0
  112. data/lib/oversip/websocket/ws_apps/ipv6_wss_sip_app.rb +22 -0
  113. data/lib/oversip/websocket/ws_apps/ws_autobahn_app.rb +23 -0
  114. data/lib/oversip/websocket/ws_apps/ws_sip_app.rb +156 -0
  115. data/lib/oversip/websocket/ws_apps.rb +9 -0
  116. data/lib/oversip/websocket/ws_framing.rb +597 -0
  117. data/lib/oversip.rb +59 -0
  118. data/test/oversip_test_helper.rb +20 -0
  119. data/test/test_http_parser.rb +73 -0
  120. data/test/test_sip_parser.rb +139 -0
  121. metadata +256 -0
@@ -0,0 +1,587 @@
1
+ module OverSIP::SIP
2
+
3
+ class ClientTransaction
4
+
5
+ include ::OverSIP::Logger
6
+
7
+ def self.get_class request
8
+ case request.sip_method
9
+ when :INVITE ; ::OverSIP::SIP::InviteClientTransaction
10
+ when :ACK ; ::OverSIP::SIP::Ack2xxForwarder
11
+ else ; ::OverSIP::SIP::NonInviteClientTransaction
12
+ end
13
+ end
14
+
15
+ attr_reader :core, :request, :state, :connection
16
+
17
+ # In case _transport_ is a String, it's an Outbound flow token.
18
+ def initialize core, request, transaction_conf, transport, ip=nil, ip_type=nil, port=nil
19
+ @core = core
20
+ @request = request
21
+ @transaction_conf = transaction_conf || {}
22
+ @transaction_id = ::SecureRandom.hex(4) << @request.antiloop_id
23
+
24
+ # A client transaction for using an existing Outbound connection.
25
+ if transport.is_a? String
26
+ @connection, @ip, @port = ::OverSIP::SIP::TransportManager.get_outbound_connection transport
27
+ if @connection
28
+ @server_klass = @connection.class
29
+ @transport = @server_klass.transport
30
+ end
31
+
32
+ # A client transaction based on procedures of RFC 3263. The connection could exist (so reuse it)
33
+ # or not (so try to create it).
34
+ else
35
+ @transport = transport
36
+ @ip = ip
37
+ @ip_type = ip_type
38
+ @port = port
39
+
40
+ @server_klass = case @transport
41
+ when :udp
42
+ case @ip_type
43
+ when :ipv4 ; ::OverSIP::SIP::IPv4UdpServer
44
+ when :ipv6 ; ::OverSIP::SIP::IPv6UdpServer
45
+ end
46
+ when :tcp
47
+ case @ip_type
48
+ when :ipv4 ; ::OverSIP::SIP::IPv4TcpServer
49
+ when :ipv6 ; ::OverSIP::SIP::IPv6TcpServer
50
+ end
51
+ when :tls
52
+ case @ip_type
53
+ when :ipv4 ; ::OverSIP::SIP::IPv4TlsServer
54
+ when :ipv6 ; ::OverSIP::SIP::IPv6TlsServer
55
+ end
56
+ end
57
+
58
+ @connection = ::OverSIP::SIP::TransportManager.get_connection @server_klass, @ip, @port, self, transaction_conf[:tls_validation]
59
+ end
60
+
61
+ # Ensure the request has Content-Length. Add it otherwise.
62
+ if @request.body
63
+ @request.headers["Content-Length"] = [ @request.body.bytesize.to_s ]
64
+ else
65
+ @request.headers["Content-Length"] = HDR_ARRAY_CONTENT_LENGTH_0
66
+ end
67
+
68
+ end # def initialize
69
+
70
+ end # class ClientTransaction
71
+
72
+
73
+ class InviteClientTransaction < ClientTransaction
74
+
75
+ def initialize core, request, transaction_conf, transport, ip=nil, ip_type=nil, port=nil
76
+ super
77
+ @log_id = "ICT #{@transaction_id}"
78
+
79
+ # Can be :calling, :proceeding, :completed, :accepted or :terminated.
80
+ @state = :calling
81
+ end
82
+
83
+ def send_request
84
+ @client_transactions = @server_klass.invite_client_transactions
85
+ # Store the new client transaction.
86
+ @client_transactions[@transaction_id] = self
87
+
88
+ @top_via = "#{@server_klass.via_core};branch=z9hG4bK#{@transaction_id};rport"
89
+ @request.insert_header "Via", @top_via
90
+
91
+ case @request.in_rr
92
+ # Add a second Record-Route just in case there is transport change.
93
+ when :rr
94
+ unless @request.connection.is_a?(@server_klass)
95
+ @out_rr = :rr
96
+ @request.insert_header "Record-Route", @server_klass.record_route
97
+ end
98
+ # When there is outgoing Outbound always add a second Record-Route header.
99
+ when :outgoing_outbound_rr
100
+ @out_rr = :rr
101
+ @request.insert_header "Record-Route", @server_klass.record_route
102
+ # When there is incoming Outbound always add a second Record-Route header containing the flow token.
103
+ when :incoming_outbound_rr
104
+ @out_rr = :rr
105
+ @request.insert_header "Record-Route", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_record_route_fragment
106
+ # When there is both incoming and outgoing Outbound always add a second Record-Route header containing the flow token.
107
+ when :both_outbound_rr
108
+ @out_rr = :rr
109
+ @request.insert_header "Record-Route", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_record_route_fragment
110
+ end
111
+
112
+ @request_leg_b = @request.to_s
113
+
114
+ # NOTE: This cannot return false as the connection has been retrieved from the corresponding hash,
115
+ # and when a connection is terminated its value is automatically deleted from such hash.
116
+ @connection.send_sip_msg @request_leg_b, @ip, @port
117
+
118
+ @request.delete_header_top "Via"
119
+ if @out_rr == :rr
120
+ @request.delete_header_top "Record-Route"
121
+ end
122
+
123
+ start_timer_A if @transport == :udp
124
+ start_timer_B
125
+ start_timer_C
126
+
127
+ true
128
+ end
129
+
130
+ def start_timer_A
131
+ @timer_A_interval = TIMER_A
132
+ @timer_A = ::EM::PeriodicTimer.new(@timer_A_interval) do
133
+ log_system_debug "timer A expires, retransmitting request" if $oversip_debug
134
+ retransmit_request
135
+ @timer_A_interval = @timer_A.interval = 2*@timer_A_interval
136
+ end
137
+ end
138
+
139
+ def start_timer_B
140
+ @timer_B = ::EM::Timer.new(@transaction_conf[:timer_B] || TIMER_B) do
141
+ log_system_debug "timer B expires, transaction timeout" if $oversip_debug
142
+ @timer_A.cancel if @timer_A
143
+ @timer_C.cancel
144
+ terminate_transaction
145
+ @core.client_timeout
146
+ end
147
+ end
148
+
149
+ def start_timer_C
150
+ @timer_C = ::EM::Timer.new(@transaction_conf[:timer_C] || TIMER_C) do
151
+ log_system_debug "timer C expires, transaction timeout" if $oversip_debug
152
+ @timer_A.cancel if @timer_A
153
+ @timer_B.cancel
154
+ do_cancel
155
+ @core.invite_timeout
156
+ end
157
+ end
158
+
159
+ def start_timer_D
160
+ ::EM.add_timer(TIMER_D_UDP) do
161
+ log_system_debug "timer D expires, transaction terminated" if $oversip_debug
162
+ terminate_transaction
163
+ end
164
+ end
165
+
166
+ def start_timer_M
167
+ ::EM.add_timer(TIMER_M) do
168
+ log_system_debug "timer M expires, transaction terminated" if $oversip_debug
169
+ terminate_transaction
170
+ end
171
+ end
172
+
173
+ # Terminate current transaction and delete from the list of transactions.
174
+ def terminate_transaction
175
+ @state = :terminated
176
+ @client_transactions.delete(@transaction_id)
177
+ end
178
+
179
+ def retransmit_request
180
+ @connection.send_sip_msg @request_leg_b, @ip, @port
181
+ end
182
+
183
+ def receive_response response
184
+ # Set the request attribute to the response so we can access the related outgoing request.
185
+ response.request = @request
186
+
187
+ # Set server transaction variables to the response.
188
+ response.tvars = @request.tvars
189
+
190
+ # Provisional response
191
+ if response.status_code < 200
192
+ case @state
193
+ when :calling
194
+ @state = :proceeding
195
+ @timer_A.cancel if @timer_A
196
+ @timer_B.cancel
197
+ @core.receive_response(response) unless response.status_code == 100
198
+ # RFC 3261 - 9.1 states that a CANCEL must be sent after receiving a 1XX response.
199
+ send_cancel if @cancel
200
+ return true
201
+ when :proceeding
202
+ @core.receive_response(response) unless response.status_code == 100
203
+ return true
204
+ else
205
+ log_system_notice "received a provisional response #{response.status_code} while in #{@state} state"
206
+ return false
207
+ end
208
+
209
+ # [3456]XX final response.
210
+ elsif response.status_code >= 300
211
+ case @state
212
+ when :calling, :proceeding
213
+ @state = :completed
214
+ @timer_A.cancel if @timer_A
215
+ @timer_B.cancel
216
+ @timer_C.cancel
217
+ if @transport == :udp
218
+ start_timer_D
219
+ else
220
+ terminate_transaction
221
+ end
222
+ send_ack(response)
223
+ @core.receive_response(response)
224
+ return true
225
+ when :completed
226
+ send_ack(response)
227
+ return false
228
+ when :accepted
229
+ log_system_notice "received a [3456]XX response while in accepted state, ignoring it"
230
+ return false
231
+ end
232
+
233
+ # 2XX final response.
234
+ else
235
+ case @state
236
+ when :calling, :proceeding
237
+ @state = :accepted
238
+ @timer_A.cancel if @timer_A
239
+ @timer_B.cancel
240
+ @timer_C.cancel
241
+ start_timer_M
242
+ @core.receive_response(response)
243
+ return true
244
+ when :accepted
245
+ @core.receive_response(response)
246
+ return true
247
+ when :completed
248
+ ### NOTE: It could be accepted and bypassed to the UAC, but makes no sense.
249
+ log_system_notice "received 2XX response while in completed state, ignoring it"
250
+ return false
251
+ end
252
+
253
+ end
254
+ end
255
+
256
+ def connection_failed
257
+ # This avoid the case in which the TCP connection timeout raises after the transaction timeout.
258
+ # Neither we react if the transaction has been canceled and the CANCEL cannot be sent due to
259
+ # TCP disconnection.
260
+ return unless @state == :calling or not @cancel
261
+
262
+ @timer_A.cancel if @timer_A
263
+ @timer_B.cancel
264
+ @timer_C.cancel
265
+ terminate_transaction
266
+
267
+ @core.connection_failed
268
+ end
269
+
270
+ def tls_validation_failed
271
+ return unless @state == :calling or not @cancel
272
+
273
+ @timer_A.cancel if @timer_A
274
+ @timer_B.cancel
275
+ @timer_C.cancel
276
+ terminate_transaction
277
+
278
+ @core.tls_validation_failed
279
+ end
280
+
281
+ def send_ack response
282
+ unless @ack
283
+ @ack = "ACK #{@request.ruri} SIP/2.0\r\n"
284
+ @ack << "Via: #{@top_via}\r\n"
285
+
286
+ @request.hdr_route.each do |route|
287
+ @ack << "Route: " << route << "\r\n"
288
+ end if @request.hdr_route
289
+
290
+ @ack << "From: " << @request.hdr_from << "\r\n"
291
+ @ack << "To: " << @request.hdr_to
292
+ unless @request.to_tag
293
+ @ack << ";tag=#{response.to_tag}" if response.to_tag
294
+ end
295
+ @ack << "\r\n"
296
+
297
+ @ack << "Call-ID: " << @request.call_id << "\r\n"
298
+ @ack << "CSeq: " << @request.cseq.to_s << " ACK\r\n"
299
+ @ack << "Content-Length: 0\r\n"
300
+ @ack << HDR_USER_AGENT << "\r\n"
301
+ @ack << "\r\n"
302
+ end
303
+
304
+ log_system_debug "sending ACK for [3456]XX response" if $oversip_debug
305
+ @connection.send_sip_msg @ack, @ip, @port
306
+ end
307
+
308
+ # It receives the received CANCEL request as parameter so it can check the existence of
309
+ # Reason header and act according (RFC 3326).
310
+ # This method is also called (without argument) when Timer C expires (INVITE).
311
+ def do_cancel cancel=nil
312
+ return if @cancel
313
+
314
+ @cancel = "CANCEL #{@request.ruri} SIP/2.0\r\n"
315
+ @cancel << "Via: #{@top_via}\r\n"
316
+
317
+ @request.hdr_route.each do |route|
318
+ @cancel << "Route: " << route << "\r\n"
319
+ end if @request.hdr_route
320
+
321
+ # RFC 3326. Copy Reason headers if present in the received CANCEL.
322
+ cancel.header_all("Reason").each do |reason|
323
+ @cancel << "Reason: " << reason << "\r\n"
324
+ end if cancel
325
+
326
+ @cancel << "From: " << @request.hdr_from << "\r\n"
327
+ @cancel << "To: " << @request.hdr_to << "\r\n"
328
+ @cancel << "Call-ID: " << @request.call_id << "\r\n"
329
+ @cancel << "CSeq: " << @request.cseq.to_s << " CANCEL\r\n"
330
+ @cancel << "Content-Length: 0\r\n"
331
+ @cancel << HDR_USER_AGENT << "\r\n"
332
+ @cancel << "\r\n"
333
+
334
+ # Just send the ACK inmediately if the branch has replied a 1XX response.
335
+ send_cancel if @state == :proceeding
336
+ end
337
+
338
+ def send_cancel
339
+ log_system_debug "sending CANCEL" if $oversip_debug
340
+
341
+ @connection.send_sip_msg @cancel, @ip, @port
342
+
343
+ start_timer_E_cancel if @transport == :udp
344
+ start_timer_F_cancel
345
+ end
346
+
347
+ def start_timer_E_cancel
348
+ @timer_E_cancel_interval = TIMER_E
349
+ @timer_E_cancel = ::EM::PeriodicTimer.new(@timer_E_cancel_interval) do
350
+ log_system_debug "timer E expires, retransmitting CANCEL" if $oversip_debug
351
+ retransmit_cancel
352
+ @timer_E_cancel_interval = @timer_E_cancel.interval = [2*@timer_E_cancel_interval, T2].min
353
+ end
354
+ end
355
+
356
+ def start_timer_F_cancel
357
+ @timer_F_cancel = ::EM::Timer.new(@transaction_conf[:timer_F] || TIMER_F) do
358
+ unless @state == :terminated
359
+ log_system_debug "timer F expires, CANCEL timeout, transaction terminated" if $oversip_debug
360
+ @timer_E_cancel.cancel if @timer_E_cancel
361
+ terminate_transaction
362
+ end
363
+ end
364
+ end
365
+
366
+ def retransmit_cancel
367
+ @connection.send_sip_msg @cancel, @ip, @port
368
+ end
369
+
370
+ def receive_response_to_cancel(response)
371
+ unless @state == :terminated
372
+ log_system_debug "our CANCEL got a #{response.status_code} response, transaction terminated" if $oversip_debug
373
+
374
+ @timer_E_cancel.cancel if @timer_E_cancel
375
+ @timer_F_cancel.cancel
376
+ # We MUST ensure that we end the client transaction, so after sending a CANCEL and get a response
377
+ # for it, ensure the transaction is terminated after a while.
378
+ ::EM.add_timer(4) { terminate_transaction }
379
+ end
380
+ end
381
+
382
+ end # class InviteClientTransaction
383
+
384
+
385
+ class NonInviteClientTransaction < ClientTransaction
386
+
387
+ def initialize core, request, transaction_conf, transport, ip=nil, ip_type=nil, port=nil
388
+ super
389
+ @log_id = "NICT #{@transaction_id}"
390
+
391
+ # Can be :trying, :proceeding, :completed or :terminated.
392
+ @state = :trying
393
+ end
394
+
395
+ def send_request
396
+ @client_transactions = @server_klass.non_invite_client_transactions
397
+ # Store the new client transaction.
398
+ @client_transactions[@transaction_id] = self
399
+
400
+ @top_via = "#{@server_klass.via_core};branch=z9hG4bK#{@transaction_id};rport"
401
+ @request.insert_header "Via", @top_via
402
+
403
+ case @request.in_rr
404
+ # Add a second Record-Route just in case there is transport change.
405
+ when :rr
406
+ unless @request.connection.is_a?(@server_klass)
407
+ @out_rr = :rr
408
+ @request.insert_header "Record-Route", @server_klass.record_route
409
+ end
410
+ # When there is outgoing Outbound always add a second Record-Route header.
411
+ when :outgoing_outbound_rr
412
+ @out_rr = :rr
413
+ @request.insert_header "Record-Route", @server_klass.record_route
414
+ # When there is incoming Outbound always add a second Record-Route header containing the flow token.
415
+ when :incoming_outbound_rr
416
+ @out_rr = :rr
417
+ @request.insert_header "Record-Route", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_record_route_fragment
418
+ # When there is both outgoing/incoming Outbound always add a second Record-Route header containing the flow token.
419
+ when :both_outbound_rr
420
+ @out_rr = :rr
421
+ @request.insert_header "Record-Route", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_record_route_fragment
422
+ # Add a second Path just in case there is transport change.
423
+ when :path
424
+ unless @request.connection.is_a?(@server_klass)
425
+ @out_rr = :path
426
+ @request.insert_header "Path", @server_klass.record_route
427
+ end
428
+ # When there is outgoing Outbound always add a second Path header.
429
+ when :outgoing_outbound_path
430
+ @out_rr = :path
431
+ @request.insert_header "Path", @server_klass.record_route
432
+ # When there is incoming Outbound always add a second Path header containing the flow token.
433
+ when :incoming_outbound_path
434
+ @out_rr = :path
435
+ @request.insert_header "Path", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_path_fragment
436
+ # When there is both outgoing/incoming Outbound always add a second Path header containing the flow token.
437
+ when :both_outbound_path
438
+ @out_rr = :rr
439
+ @request.insert_header "Path", "<sip:" << @request.route_outbound_flow_token << @server_klass.outbound_path_fragment
440
+ end
441
+
442
+ @request_leg_b = @request.to_s
443
+
444
+ @connection.send_sip_msg @request_leg_b, @ip, @port
445
+
446
+ @request.delete_header_top "Via"
447
+ case @out_rr
448
+ when :rr
449
+ @request.delete_header_top "Record-Route"
450
+ when :path
451
+ @request.delete_header_top "Path"
452
+ end
453
+
454
+ start_timer_E if @transport == :udp
455
+ start_timer_F
456
+
457
+ true
458
+ end
459
+
460
+ def start_timer_E
461
+ @timer_E_interval = TIMER_E
462
+ @timer_E = ::EM::PeriodicTimer.new(@timer_E_interval) do
463
+ log_system_debug "timer E expires, retransmitting request" if $oversip_debug
464
+ retransmit_request
465
+ if @state == :trying
466
+ @timer_E_interval = @timer_E.interval = [2*@timer_E_interval, T2].min
467
+ else
468
+ @timer_E_interval = @timer_E.interval = T2
469
+ end
470
+ end
471
+ end
472
+
473
+ def start_timer_F
474
+ @timer_F = ::EM::Timer.new(@transaction_conf[:timer_F] || TIMER_F) do
475
+ log_system_debug "timer F expires, transaction timeout" if $oversip_debug
476
+ @timer_E.cancel if @timer_E
477
+ terminate_transaction
478
+ @core.client_timeout
479
+ end
480
+ end
481
+
482
+ def start_timer_K
483
+ ::EM.add_timer(TIMER_K_UDP) do
484
+ log_system_debug "timer K expires, transaction terminated" if $oversip_debug
485
+ terminate_transaction
486
+ end
487
+ end
488
+
489
+ # Terminate current transaction and delete from the list of transactions.
490
+ def terminate_transaction
491
+ @state = :terminated
492
+ @client_transactions.delete(@transaction_id)
493
+ end
494
+
495
+ def retransmit_request
496
+ @connection.send_sip_msg @request_leg_b, @ip, @port
497
+ end
498
+
499
+ def receive_response response
500
+ # Set the request attribute to the response so we can access the related outgoing request.
501
+ response.request = @request
502
+
503
+ # Set server transaction variables to the response.
504
+ response.tvars = @request.tvars
505
+
506
+ # Provisional response
507
+ if response.status_code < 200
508
+ case @state
509
+ when :trying
510
+ @state = :proceeding
511
+ @core.receive_response(response) unless response.status_code == 100
512
+ return true
513
+ when :proceeding
514
+ @core.receive_response(response) unless response.status_code == 100
515
+ return true
516
+ else
517
+ log_system_notice "received a provisional response #{response.status_code} while in #{@state} state"
518
+ return false
519
+ end
520
+
521
+ # [23456]XX final response.
522
+ elsif response.status_code >= 200
523
+ case @state
524
+ when :trying, :proceeding
525
+ @state = :completed
526
+ @timer_F.cancel
527
+ @timer_E.cancel if @timer_E
528
+ if @transport == :udp
529
+ start_timer_K
530
+ else
531
+ terminate_transaction
532
+ end
533
+ @core.receive_response(response)
534
+ return true
535
+ else
536
+ log_system_notice "received a final response #{response.status_code} while in #{@state} state"
537
+ return false
538
+ end
539
+
540
+ end
541
+ end
542
+
543
+ def connection_failed
544
+ @timer_F.cancel
545
+ @timer_E.cancel if @timer_E
546
+ terminate_transaction
547
+
548
+ @core.connection_failed
549
+ end
550
+
551
+ def tls_validation_failed
552
+ @timer_F.cancel
553
+ @timer_E.cancel if @timer_E
554
+ terminate_transaction
555
+
556
+ @core.tls_validation_failed
557
+ end
558
+
559
+ end # class NonInviteClientTransaction
560
+
561
+
562
+ class Ack2xxForwarder < ClientTransaction
563
+
564
+ def initialize core, request, transaction_conf, transport, ip=nil, ip_type=nil, port=nil
565
+ super
566
+ @log_id = "ICT #{@transaction_id}"
567
+ end
568
+
569
+ def send_request
570
+ @request.insert_header "Via", "#{@server_klass.via_core};branch=z9hG4bK#{@transaction_id}"
571
+
572
+ @connection.send_sip_msg @request.to_s, @ip, @port
573
+
574
+ true
575
+ end
576
+
577
+ def connection_failed
578
+ # Do nothing.
579
+ end
580
+
581
+ def tls_validation_failed
582
+ # Do nothing.
583
+ end
584
+
585
+ end # class Ack2xxForwarder
586
+
587
+ end
@@ -0,0 +1,87 @@
1
+ module OverSIP::SIP
2
+
3
+ CRLF = "\r\n"
4
+ DOUBLE_CRLF = "\r\n\r\n"
5
+
6
+ # DOC: http://www.iana.org/assignments/sip-parameters
7
+ REASON_PHARSE = {
8
+ 100 => "Trying",
9
+ 180 => "Ringing",
10
+ 181 => "Call Is Being Forwarded",
11
+ 182 => "Queued",
12
+ 183 => "Session Progress",
13
+ 199 => "Early Dialog Terminated", # draft-ietf-sipcore-199
14
+ 200 => "OK",
15
+ 202 => "Accepted", # RFC 3265
16
+ 204 => "No Notification", #RFC 5839
17
+ 300 => "Multiple Choices",
18
+ 301 => "Moved Permanently",
19
+ 302 => "Moved Temporarily",
20
+ 305 => "Use Proxy",
21
+ 380 => "Alternative Service",
22
+ 400 => "Bad Request",
23
+ 401 => "Unauthorized",
24
+ 402 => "Payment Required",
25
+ 403 => "Forbidden",
26
+ 404 => "Not Found",
27
+ 405 => "Method Not Allowed",
28
+ 406 => "Not Acceptable",
29
+ 407 => "Proxy Authentication Required",
30
+ 408 => "Request Timeout",
31
+ 410 => "Gone",
32
+ 412 => "Conditional Request Failed", # RFC 3903
33
+ 413 => "Request Entity Too Large",
34
+ 414 => "Request-URI Too Long",
35
+ 415 => "Unsupported Media Type",
36
+ 416 => "Unsupported URI Scheme",
37
+ 417 => "Unknown Resource-Priority", # RFC 4412
38
+ 420 => "Bad Extension",
39
+ 421 => "Extension Required",
40
+ 422 => "Session Interval Too Small", # RFC 4028
41
+ 423 => "Interval Too Brief",
42
+ 428 => "Use Identity Header", # RFC 4474
43
+ 429 => "Provide Referrer Identity", # RFC 3892
44
+ 430 => "Flow Failed", # RFC 5626
45
+ 433 => "Anonymity Disallowed", # RFC 5079
46
+ 436 => "Bad Identity-Info", # RFC 4474
47
+ 437 => "Unsupported Certificate", # RFC 4744
48
+ 438 => "Invalid Identity Header", # RFC 4744
49
+ 439 => "First Hop Lacks Outbound Support", # RFC 5626
50
+ 440 => "Max-Breadth Exceeded", # RFC 5393
51
+ 469 => "Bad Info Package", # draft-ietf-sipcore-info-events
52
+ 470 => "Consent Needed", # RF C5360
53
+ 478 => "Unresolvable Destination", # Custom code copied from Kamailio.
54
+ 480 => "Temporarily Unavailable",
55
+ 481 => "Call/Transaction Does Not Exist",
56
+ 482 => "Loop Detected",
57
+ 483 => "Too Many Hops",
58
+ 484 => "Address Incomplete",
59
+ 485 => "Ambiguous",
60
+ 486 => "Busy Here",
61
+ 487 => "Request Terminated",
62
+ 488 => "Not Acceptable Here",
63
+ 489 => "Bad Event", # RFC 3265
64
+ 491 => "Request Pending",
65
+ 493 => "Undecipherable",
66
+ 494 => "Security Agreement Required", # RFC 3329
67
+ 500 => "Server Internal Error",
68
+ 501 => "Not Implemented",
69
+ 502 => "Bad Gateway",
70
+ 503 => "Service Unavailable",
71
+ 504 => "Server Time-out",
72
+ 505 => "Version Not Supported",
73
+ 513 => "Message Too Large",
74
+ 580 => "Precondition Failure", # RFC 3312
75
+ 600 => "Busy Everywhere",
76
+ 603 => "Decline",
77
+ 604 => "Does Not Exist Anywhere",
78
+ 606 => "Not Acceptable"
79
+ }
80
+
81
+ REASON_PHARSE_NOT_SET = "Reason Phrase Not Set"
82
+
83
+ HDR_SERVER = "Server: #{::OverSIP::PROGRAM_NAME}/#{::OverSIP::VERSION}".freeze
84
+ HDR_USER_AGENT = "User-Agent: #{::OverSIP::PROGRAM_NAME}/#{::OverSIP::VERSION}".freeze
85
+ HDR_ARRAY_CONTENT_LENGTH_0 = [ "0" ].freeze
86
+
87
+ end
@@ -0,0 +1,27 @@
1
+ module OverSIP::SIP
2
+
3
+ class NameAddr < OverSIP::SIP::Uri
4
+
5
+ attr_reader :display_name
6
+
7
+ def display_name= value
8
+ @display_name = value
9
+ @name_addr_modified = true
10
+ end
11
+
12
+ def to_s
13
+ return @name_addr if @name_addr and not @name_addr_modified and not @uri_modified
14
+
15
+ @name_addr = ""
16
+ ( @name_addr << '"' << @display_name << '" ' ) if @display_name
17
+ @name_addr << "<" << uri << ">"
18
+
19
+ @name_addr_modified = false
20
+ @name_addr
21
+
22
+ end
23
+ alias :inspect :to_s
24
+
25
+ end
26
+
27
+ end