oversip 0.9.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.
- data/AUTHORS.txt +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +16 -0
- data/Rakefile +55 -0
- data/bin/oversip +182 -0
- data/ext/common/c_util.h +74 -0
- data/ext/common/ruby_c_util.h +88 -0
- data/ext/sip_parser/common_headers.h +209 -0
- data/ext/sip_parser/ext_help.h +18 -0
- data/ext/sip_parser/extconf.rb +3 -0
- data/ext/sip_parser/sip_parser.c +29649 -0
- data/ext/sip_parser/sip_parser.h +227 -0
- data/ext/sip_parser/sip_parser_ruby.c +1292 -0
- data/ext/stud/extconf.rb +27 -0
- data/ext/stud/stud.tar.gz +0 -0
- data/ext/stun/ext_help.h +16 -0
- data/ext/stun/extconf.rb +3 -0
- data/ext/stun/stun_ruby.c +391 -0
- data/ext/utils/ext_help.h +14 -0
- data/ext/utils/extconf.rb +3 -0
- data/ext/utils/haproxy_protocol.c +6163 -0
- data/ext/utils/haproxy_protocol.h +27 -0
- data/ext/utils/ip_utils.c +5952 -0
- data/ext/utils/ip_utils.h +61 -0
- data/ext/utils/outbound_utils.c +3227 -0
- data/ext/utils/outbound_utils.h +27 -0
- data/ext/utils/utils_ruby.c +384 -0
- data/ext/utils/utils_ruby.h +75 -0
- data/ext/websocket_framing_utils/ext_help.h +18 -0
- data/ext/websocket_framing_utils/extconf.rb +3 -0
- data/ext/websocket_framing_utils/ws_framing_utils.h +46 -0
- data/ext/websocket_framing_utils/ws_framing_utils_ruby.c +135 -0
- data/ext/websocket_http_parser/ext_help.h +18 -0
- data/ext/websocket_http_parser/extconf.rb +3 -0
- data/ext/websocket_http_parser/ws_http_parser.c +2598 -0
- data/ext/websocket_http_parser/ws_http_parser.h +86 -0
- data/ext/websocket_http_parser/ws_http_parser_ruby.c +630 -0
- data/lib/oversip/config.rb +541 -0
- data/lib/oversip/config_validators.rb +126 -0
- data/lib/oversip/errors.rb +7 -0
- data/lib/oversip/fiber_pool.rb +56 -0
- data/lib/oversip/launcher.rb +507 -0
- data/lib/oversip/logger.rb +170 -0
- data/lib/oversip/master_process.rb +67 -0
- data/lib/oversip/posix_mq.rb +121 -0
- data/lib/oversip/proxies_config.rb +169 -0
- data/lib/oversip/ruby_ext/eventmachine.rb +38 -0
- data/lib/oversip/sip/client_transaction.rb +587 -0
- data/lib/oversip/sip/constants.rb +87 -0
- data/lib/oversip/sip/grammar/name_addr.rb +27 -0
- data/lib/oversip/sip/grammar/uri.rb +116 -0
- data/lib/oversip/sip/launcher.rb +180 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_tunnel_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_udp_server.rb +20 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_tunnel_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_udp_server.rb +20 -0
- data/lib/oversip/sip/listeners/reactor.rb +39 -0
- data/lib/oversip/sip/listeners/tcp_client.rb +73 -0
- data/lib/oversip/sip/listeners/tcp_reactor.rb +185 -0
- data/lib/oversip/sip/listeners/tcp_server.rb +71 -0
- data/lib/oversip/sip/listeners/tls_client.rb +117 -0
- data/lib/oversip/sip/listeners/tls_server.rb +70 -0
- data/lib/oversip/sip/listeners/tls_tunnel_reactor.rb +113 -0
- data/lib/oversip/sip/listeners/tls_tunnel_server.rb +61 -0
- data/lib/oversip/sip/listeners/udp_reactor.rb +213 -0
- data/lib/oversip/sip/listeners.rb +28 -0
- data/lib/oversip/sip/logic.rb +14 -0
- data/lib/oversip/sip/message.rb +168 -0
- data/lib/oversip/sip/message_processor.rb +202 -0
- data/lib/oversip/sip/modules/core.rb +200 -0
- data/lib/oversip/sip/modules/registrar_without_path.rb +75 -0
- data/lib/oversip/sip/modules/user_assertion.rb +123 -0
- data/lib/oversip/sip/proxy.rb +460 -0
- data/lib/oversip/sip/request.rb +128 -0
- data/lib/oversip/sip/response.rb +30 -0
- data/lib/oversip/sip/rfc3263.rb +646 -0
- data/lib/oversip/sip/server_transaction.rb +295 -0
- data/lib/oversip/sip/sip.rb +74 -0
- data/lib/oversip/sip/tags.rb +39 -0
- data/lib/oversip/sip/timers.rb +55 -0
- data/lib/oversip/sip/transport_manager.rb +129 -0
- data/lib/oversip/syslogger_process.rb +119 -0
- data/lib/oversip/tls.rb +179 -0
- data/lib/oversip/utils.rb +25 -0
- data/lib/oversip/version.rb +23 -0
- data/lib/oversip/websocket/constants.rb +56 -0
- data/lib/oversip/websocket/default_policy.rb +19 -0
- data/lib/oversip/websocket/http_request.rb +63 -0
- data/lib/oversip/websocket/launcher.rb +207 -0
- data/lib/oversip/websocket/listeners/ipv4_tcp_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv4_tls_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv4_tls_tunnel_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tcp_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tls_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tls_tunnel_server.rb +15 -0
- data/lib/oversip/websocket/listeners/tcp_server.rb +265 -0
- data/lib/oversip/websocket/listeners/tls_server.rb +69 -0
- data/lib/oversip/websocket/listeners/tls_tunnel_server.rb +100 -0
- data/lib/oversip/websocket/listeners.rb +12 -0
- data/lib/oversip/websocket/ws_app.rb +75 -0
- data/lib/oversip/websocket/ws_apps/ipv4_ws_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv4_wss_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv6_ws_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv6_wss_sip_app.rb +22 -0
- data/lib/oversip/websocket/ws_apps/ws_autobahn_app.rb +23 -0
- data/lib/oversip/websocket/ws_apps/ws_sip_app.rb +156 -0
- data/lib/oversip/websocket/ws_apps.rb +9 -0
- data/lib/oversip/websocket/ws_framing.rb +597 -0
- data/lib/oversip.rb +59 -0
- data/test/oversip_test_helper.rb +20 -0
- data/test/test_http_parser.rb +73 -0
- data/test/test_sip_parser.rb +139 -0
- 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
|