oversip_p 1.0.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.
- checksums.yaml +7 -0
- data/AUTHORS +22 -0
- data/LICENSE +25 -0
- data/README.md +43 -0
- data/Rakefile +54 -0
- data/bin/oversip +184 -0
- data/etc/oversip.conf +274 -0
- data/etc/proxies.conf +145 -0
- data/etc/server.rb +315 -0
- data/etc/tls/ca/cacert.pem +3894 -0
- data/etc/tls/demo-tls.oversip.net.crt +17 -0
- data/etc/tls/demo-tls.oversip.net.key +15 -0
- data/etc/tls/upgrade-cacert.sh +12 -0
- data/etc/tls/utils/create-cert.rb +162 -0
- data/etc/tls/utils/get-sip-identities.rb +95 -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 +210 -0
- data/ext/sip_parser/ext_help.h +18 -0
- data/ext/sip_parser/extconf.rb +3 -0
- data/ext/sip_parser/sip_message_parser.c +29741 -0
- data/ext/sip_parser/sip_parser.h +250 -0
- data/ext/sip_parser/sip_parser_ruby.c +1370 -0
- data/ext/sip_parser/sip_uri_parser.c +39699 -0
- data/ext/stud/extconf.rb +43 -0
- data/ext/stun/ext_help.h +16 -0
- data/ext/stun/extconf.rb +3 -0
- data/ext/stun/stun_ruby.c +394 -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 +64 -0
- data/ext/utils/outbound_utils.c +3227 -0
- data/ext/utils/outbound_utils.h +27 -0
- data/ext/utils/utils_ruby.c +392 -0
- data/ext/utils/utils_ruby.h +76 -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 +47 -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 +1635 -0
- data/ext/websocket_http_parser/ws_http_parser.h +87 -0
- data/ext/websocket_http_parser/ws_http_parser_ruby.c +630 -0
- data/lib/oversip/config.rb +597 -0
- data/lib/oversip/config_validators.rb +126 -0
- data/lib/oversip/default_server.rb +52 -0
- data/lib/oversip/errors.rb +10 -0
- data/lib/oversip/fiber_pool.rb +56 -0
- data/lib/oversip/launcher.rb +635 -0
- data/lib/oversip/logger.rb +84 -0
- data/lib/oversip/modules/outbound_mangling.rb +56 -0
- data/lib/oversip/modules/user_assertion.rb +73 -0
- data/lib/oversip/proxies_config.rb +189 -0
- data/lib/oversip/ruby_ext/eventmachine.rb +38 -0
- data/lib/oversip/sip/client.rb +428 -0
- data/lib/oversip/sip/client_transaction.rb +586 -0
- data/lib/oversip/sip/constants.rb +88 -0
- data/lib/oversip/sip/core.rb +217 -0
- data/lib/oversip/sip/launcher.rb +221 -0
- data/lib/oversip/sip/listeners/connection.rb +54 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv4_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv4_tls_tunnel_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv4_udp_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv6_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv6_tls_tunnel_server.rb +22 -0
- data/lib/oversip/sip/listeners/ipv6_udp_server.rb +21 -0
- data/lib/oversip/sip/listeners/tcp_client.rb +97 -0
- data/lib/oversip/sip/listeners/tcp_connection.rb +202 -0
- data/lib/oversip/sip/listeners/tcp_server.rb +71 -0
- data/lib/oversip/sip/listeners/tls_client.rb +125 -0
- data/lib/oversip/sip/listeners/tls_server.rb +88 -0
- data/lib/oversip/sip/listeners/tls_tunnel_connection.rb +89 -0
- data/lib/oversip/sip/listeners/tls_tunnel_server.rb +61 -0
- data/lib/oversip/sip/listeners/udp_connection.rb +214 -0
- data/lib/oversip/sip/listeners.rb +24 -0
- data/lib/oversip/sip/message.rb +177 -0
- data/lib/oversip/sip/message_processor.rb +213 -0
- data/lib/oversip/sip/name_addr.rb +51 -0
- data/lib/oversip/sip/proxy.rb +324 -0
- data/lib/oversip/sip/request.rb +179 -0
- data/lib/oversip/sip/response.rb +37 -0
- data/lib/oversip/sip/rfc3263.rb +643 -0
- data/lib/oversip/sip/server_transaction.rb +295 -0
- data/lib/oversip/sip/sip.rb +76 -0
- data/lib/oversip/sip/tags.rb +39 -0
- data/lib/oversip/sip/timers.rb +55 -0
- data/lib/oversip/sip/transport_manager.rb +130 -0
- data/lib/oversip/sip/uac.rb +89 -0
- data/lib/oversip/sip/uac_request.rb +84 -0
- data/lib/oversip/sip/uri.rb +208 -0
- data/lib/oversip/syslog.rb +68 -0
- data/lib/oversip/system_callbacks.rb +45 -0
- data/lib/oversip/tls.rb +172 -0
- data/lib/oversip/utils.rb +30 -0
- data/lib/oversip/version.rb +21 -0
- data/lib/oversip/websocket/constants.rb +55 -0
- data/lib/oversip/websocket/http_request.rb +59 -0
- data/lib/oversip/websocket/launcher.rb +183 -0
- data/lib/oversip/websocket/listeners/connection.rb +51 -0
- data/lib/oversip/websocket/listeners/ipv4_ws_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ipv4_wss_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ipv4_wss_tunnel_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ipv6_ws_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ipv6_wss_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ipv6_wss_tunnel_server.rb +22 -0
- data/lib/oversip/websocket/listeners/ws_server.rb +331 -0
- data/lib/oversip/websocket/listeners/wss_server.rb +88 -0
- data/lib/oversip/websocket/listeners/wss_tunnel_server.rb +133 -0
- data/lib/oversip/websocket/listeners.rb +13 -0
- data/lib/oversip/websocket/websocket.rb +13 -0
- data/lib/oversip/websocket/ws_framing.rb +545 -0
- data/lib/oversip/websocket/ws_sip_app.rb +120 -0
- data/lib/oversip.rb +127 -0
- data/test/oversip_test_helper.rb +19 -0
- data/test/test_http_parser.rb +73 -0
- data/test/test_name_addr.rb +27 -0
- data/test/test_name_addr_parser.rb +24 -0
- data/test/test_sip_message_parser.rb +168 -0
- data/test/test_sip_uri_parser.rb +56 -0
- data/test/test_uri.rb +68 -0
- data/thirdparty/stud/stud.tar.gz +0 -0
- metadata +334 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class WsServer < Connection
|
|
4
|
+
|
|
5
|
+
# Max size (bytes) of the buffered data when receiving HTTP headers
|
|
6
|
+
# (avoid DoS attacks).
|
|
7
|
+
HEADERS_MAX_SIZE = 2048
|
|
8
|
+
|
|
9
|
+
WS_MAGIC_GUID_04 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".freeze
|
|
10
|
+
WS_VERSIONS = { 7=>true, 8=>true, 13=>true }
|
|
11
|
+
HDR_SUPPORTED_WEBSOCKET_VERSIONS = [ "X-Supported-WebSocket-Versions: #{WS_VERSIONS.keys.join(", ")}" ]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
attr_reader :outbound_flow_token
|
|
15
|
+
attr_writer :ws_established, :client_closed
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def remote_ip_type
|
|
19
|
+
@remote_ip_type || self.class.ip_type
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def remote_ip
|
|
23
|
+
@remote_ip
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def remote_port
|
|
27
|
+
@remote_port
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def transport
|
|
31
|
+
self.class.transport
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def post_connection
|
|
35
|
+
begin
|
|
36
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
37
|
+
rescue => e
|
|
38
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
39
|
+
close_connection
|
|
40
|
+
@state = :ignore
|
|
41
|
+
return
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@connection_id = ::OverSIP::SIP::TransportManager.add_connection self, self.class, self.class.ip_type, @remote_ip, @remote_port
|
|
45
|
+
|
|
46
|
+
# Create an Outbound (RFC 5626) flow token for this connection.
|
|
47
|
+
@outbound_flow_token = ::OverSIP::SIP::TransportManager.add_outbound_connection self
|
|
48
|
+
|
|
49
|
+
log_system_debug("connection opened from " << remote_desc) if $oversip_debug
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def remote_desc force=nil
|
|
54
|
+
if force
|
|
55
|
+
@remote_desc = case @remote_ip_type
|
|
56
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
57
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
@remote_desc ||= case self.class.ip_type
|
|
61
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
62
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def unbind cause=nil
|
|
69
|
+
@state = :ignore
|
|
70
|
+
|
|
71
|
+
# Remove the connection.
|
|
72
|
+
self.class.connections.delete @connection_id
|
|
73
|
+
|
|
74
|
+
# Remove the Outbound token flow.
|
|
75
|
+
::OverSIP::SIP::TransportManager.delete_outbound_connection @outbound_flow_token
|
|
76
|
+
|
|
77
|
+
@local_closed = true if cause == ::Errno::ETIMEDOUT
|
|
78
|
+
@local_closed = false if @client_closed
|
|
79
|
+
|
|
80
|
+
if $oversip_debug
|
|
81
|
+
log_msg = "connection from #{remote_desc} "
|
|
82
|
+
log_msg << ( @local_closed ? "locally closed" : "remotely closed" )
|
|
83
|
+
log_msg << " (cause: #{cause.inspect})" if cause
|
|
84
|
+
log_system_debug log_msg
|
|
85
|
+
end unless $!
|
|
86
|
+
|
|
87
|
+
if @ws_established
|
|
88
|
+
# Run OverSIP::WebSocketEvents.on_disconnection
|
|
89
|
+
::Fiber.new do
|
|
90
|
+
begin
|
|
91
|
+
::OverSIP::WebSocketEvents.on_disconnection self, !@local_closed
|
|
92
|
+
rescue ::Exception => e
|
|
93
|
+
log_system_error "error calling OverSIP::WebSocketEvents.on_disconnection():"
|
|
94
|
+
log_system_error e
|
|
95
|
+
end
|
|
96
|
+
end.resume
|
|
97
|
+
end unless $!
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def receive_data data
|
|
102
|
+
@state == :ignore and return
|
|
103
|
+
@buffer << data
|
|
104
|
+
@state == :waiting_for_on_client_tls_handshake and return
|
|
105
|
+
@state == :waiting_for_on_connection and return
|
|
106
|
+
|
|
107
|
+
process_received_data
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def process_received_data
|
|
111
|
+
@state == :ignore and return
|
|
112
|
+
|
|
113
|
+
while (case @state
|
|
114
|
+
when :init
|
|
115
|
+
@http_parser = ::OverSIP::WebSocket::HttpRequestParser.new
|
|
116
|
+
@http_request = ::OverSIP::WebSocket::HttpRequest.new
|
|
117
|
+
@http_parser_nbytes = 0
|
|
118
|
+
@bytes_remaining = 0
|
|
119
|
+
@state = :http_headers
|
|
120
|
+
|
|
121
|
+
when :http_headers
|
|
122
|
+
parse_http_headers
|
|
123
|
+
|
|
124
|
+
when :check_http_request
|
|
125
|
+
check_http_request
|
|
126
|
+
|
|
127
|
+
when :on_connection_callback
|
|
128
|
+
do_on_connection_callback
|
|
129
|
+
false
|
|
130
|
+
|
|
131
|
+
when :accept_ws_handshake
|
|
132
|
+
accept_ws_handshake
|
|
133
|
+
|
|
134
|
+
when :websocket
|
|
135
|
+
@ws_established = true
|
|
136
|
+
@ws_framing.receive_data
|
|
137
|
+
false
|
|
138
|
+
|
|
139
|
+
when :ignore
|
|
140
|
+
false
|
|
141
|
+
end)
|
|
142
|
+
end # while
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def parse_http_headers
|
|
148
|
+
return false if @buffer.empty?
|
|
149
|
+
|
|
150
|
+
# Parse the currently buffered data. If parsing fails @http_parser_nbytes gets nil value.
|
|
151
|
+
unless @http_parser_nbytes = @http_parser.execute(@http_request, @buffer.to_str, @http_parser_nbytes)
|
|
152
|
+
log_system_warn "parsing error: \"#{@http_parser.error}\""
|
|
153
|
+
close_connection_after_writing
|
|
154
|
+
@state = :ignore
|
|
155
|
+
return false
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Avoid flood attacks in TCP (very long headers).
|
|
159
|
+
if @http_parser_nbytes > HEADERS_MAX_SIZE
|
|
160
|
+
log_system_warn "DoS attack detected: headers size exceedes #{HEADERS_MAX_SIZE} bytes, closing connection with #{remote_desc}"
|
|
161
|
+
close_connection
|
|
162
|
+
@state = :ignore
|
|
163
|
+
return false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
return false unless @http_parser.finished?
|
|
167
|
+
|
|
168
|
+
# Clear parsed data from the buffer.
|
|
169
|
+
@buffer.read(@http_parser_nbytes)
|
|
170
|
+
|
|
171
|
+
@http_request.connection = self
|
|
172
|
+
|
|
173
|
+
@state = :check_http_request
|
|
174
|
+
true
|
|
175
|
+
end # parse_headers
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def check_http_request
|
|
179
|
+
# Check OverSIP status.
|
|
180
|
+
unless ::OverSIP.status == :running
|
|
181
|
+
case ::OverSIP.status
|
|
182
|
+
when :loading
|
|
183
|
+
http_reject 500, "Server Still Loading", [ "Retry-After: 5" ]
|
|
184
|
+
when :terminating
|
|
185
|
+
http_reject 500, "Server is Being Stopped"
|
|
186
|
+
end
|
|
187
|
+
return false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# HTTP method must be GET.
|
|
191
|
+
if @http_request.http_method != :GET
|
|
192
|
+
log_system_notice "rejecting HTTP #{@http_request.http_method} request => 405"
|
|
193
|
+
http_reject 405
|
|
194
|
+
return false
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# "Sec-WebSocket-Version" must be 8.
|
|
198
|
+
unless WS_VERSIONS[@http_request.hdr_sec_websocket_version]
|
|
199
|
+
if @http_request.hdr_sec_websocket_version
|
|
200
|
+
log_system_notice "WebSocket version #{@http_request.hdr_sec_websocket_version} not implemented => 426"
|
|
201
|
+
else
|
|
202
|
+
log_system_notice "WebSocket version header not present => 426"
|
|
203
|
+
end
|
|
204
|
+
http_reject 426, nil, HDR_SUPPORTED_WEBSOCKET_VERSIONS
|
|
205
|
+
return false
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Connection header must include "upgrade".
|
|
209
|
+
unless @http_request.hdr_connection and @http_request.hdr_connection.include? "upgrade"
|
|
210
|
+
log_system_notice "Connection header must include \"upgrade\" => 400"
|
|
211
|
+
http_reject 400, "Connection header must include \"upgrade\""
|
|
212
|
+
return false
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# "Upgrade: websocket" is required.
|
|
216
|
+
unless @http_request.hdr_upgrade == "websocket"
|
|
217
|
+
log_system_notice "Upgrade header must be \"websocket\" => 400"
|
|
218
|
+
http_reject 400, "Upgrade header must be \"websocket\""
|
|
219
|
+
return false
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Sec-WebSocket-Key is required.
|
|
223
|
+
unless @http_request.hdr_sec_websocket_key
|
|
224
|
+
log_system_notice "Sec-WebSocket-Key header not present => 400"
|
|
225
|
+
http_reject 400, "Sec-WebSocket-Key header not present"
|
|
226
|
+
return false
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Check Sec-WebSocket-Protocol.
|
|
230
|
+
if @http_request.hdr_sec_websocket_protocol
|
|
231
|
+
if @http_request.hdr_sec_websocket_protocol.include? WS_SIP_PROTOCOL
|
|
232
|
+
@websocket_protocol_negotiated = true
|
|
233
|
+
else
|
|
234
|
+
log_system_notice "Sec-WebSocket-Protocol does not contain a supported protocol but #{@http_request.hdr_sec_websocket_protocol} => 501"
|
|
235
|
+
http_reject 501, "No Suitable WebSocket Protocol"
|
|
236
|
+
return false
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
@state = :on_connection_callback
|
|
241
|
+
true
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def do_on_connection_callback
|
|
246
|
+
# Set the state to :waiting_for_on_connection so data received before
|
|
247
|
+
# user callback validation is just stored.
|
|
248
|
+
@state = :waiting_for_on_connection
|
|
249
|
+
|
|
250
|
+
# Run OverSIP::WebSocketEvents.on_connection.
|
|
251
|
+
::Fiber.new do
|
|
252
|
+
begin
|
|
253
|
+
log_system_debug "running OverSIP::WebSocketEvents.on_connection()..." if $oversip_debug
|
|
254
|
+
::OverSIP::WebSocketEvents.on_connection self, @http_request
|
|
255
|
+
# If the user of the peer has not closed the connection then continue.
|
|
256
|
+
unless @local_closed or error?
|
|
257
|
+
@state = :accept_ws_handshake
|
|
258
|
+
# Call process_received_data() to process possible data received in the meanwhile.
|
|
259
|
+
process_received_data
|
|
260
|
+
else
|
|
261
|
+
log_system_debug "connection closed during OverSIP::WebSocketEvents.on_connection(), aborting" if $oversip_debug
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
rescue ::Exception => e
|
|
265
|
+
log_system_error "error calling OverSIP::WebSocketEvents.on_connection() => 500:"
|
|
266
|
+
log_system_error e
|
|
267
|
+
http_reject 500
|
|
268
|
+
end
|
|
269
|
+
end.resume
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def accept_ws_handshake
|
|
274
|
+
sec_websocket_accept = Digest::SHA1::base64digest @http_request.hdr_sec_websocket_key + WS_MAGIC_GUID_04
|
|
275
|
+
|
|
276
|
+
extra_headers = [
|
|
277
|
+
"Upgrade: websocket",
|
|
278
|
+
"Connection: Upgrade",
|
|
279
|
+
"Sec-WebSocket-Accept: #{sec_websocket_accept}"
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
if @websocket_protocol_negotiated
|
|
283
|
+
extra_headers << "Sec-WebSocket-Protocol: #{WS_SIP_PROTOCOL}"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if @websocket_extensions
|
|
287
|
+
extra_headers << "Sec-WebSocket-Extensions: #{@websocket_extensions.to_s}"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
@http_request.reply 101, nil, extra_headers
|
|
291
|
+
|
|
292
|
+
# Set the WS framing layer and WS application layer.
|
|
293
|
+
@ws_framing = ::OverSIP::WebSocket::WsFraming.new self, @buffer
|
|
294
|
+
ws_sip_app = ::OverSIP::WebSocket::WsSipApp.new self, @ws_framing
|
|
295
|
+
@ws_framing.ws_app = ws_sip_app
|
|
296
|
+
|
|
297
|
+
@state = :websocket
|
|
298
|
+
true
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def http_reject status_code=403, reason_phrase=nil, extra_headers=nil
|
|
303
|
+
@http_request.reply(status_code, reason_phrase, extra_headers)
|
|
304
|
+
close_connection_after_writing
|
|
305
|
+
@state = :ignore
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# Parameters ip and port are just included because they are needed in UDP, so the API remains equal.
|
|
310
|
+
def send_sip_msg msg, ip=nil, port=nil
|
|
311
|
+
if self.error?
|
|
312
|
+
log_system_notice "SIP message could not be sent, connection is closed"
|
|
313
|
+
return false
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# If the SIP message is fully valid UTF-8 send a WS text frame.
|
|
317
|
+
if msg.force_encoding(::Encoding::UTF_8).valid_encoding?
|
|
318
|
+
@ws_framing.send_text_frame msg
|
|
319
|
+
|
|
320
|
+
# If not, send a WS binary frame.
|
|
321
|
+
else
|
|
322
|
+
@ws_framing.send_binary_frame msg
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
true
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
end
|
|
331
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class WssServer < WsServer
|
|
4
|
+
|
|
5
|
+
TLS_HANDSHAKE_MAX_TIME = 4
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def post_init
|
|
9
|
+
@client_pems = []
|
|
10
|
+
@client_last_pem = false
|
|
11
|
+
|
|
12
|
+
start_tls({
|
|
13
|
+
:verify_peer => true,
|
|
14
|
+
:cert_chain_file => ::OverSIP.tls_public_cert,
|
|
15
|
+
:private_key_file => ::OverSIP.tls_private_cert,
|
|
16
|
+
:ssl_version => %w(sslv2) # USE SSL instead of TLS. TODO: yes?
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
# If the remote client does never send us a TLS certificate
|
|
20
|
+
# after the TCP connection we would leak by storing more and
|
|
21
|
+
# more messages in @pending_messages array.
|
|
22
|
+
@timer_tls_handshake = ::EM::Timer.new(TLS_HANDSHAKE_MAX_TIME) do
|
|
23
|
+
unless @connected
|
|
24
|
+
log_system_notice "TLS handshake not performed within #{TLS_HANDSHAKE_MAX_TIME} seconds, closing the connection"
|
|
25
|
+
close_connection
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def ssl_verify_peer pem
|
|
32
|
+
# TODO: Dirty workaround for bug https://github.com/eventmachine/eventmachine/issues/194.
|
|
33
|
+
return true if @client_last_pem == pem
|
|
34
|
+
|
|
35
|
+
@client_last_pem = pem
|
|
36
|
+
@client_pems << pem
|
|
37
|
+
|
|
38
|
+
log_system_debug "received certificate num #{@client_pems.size} from client" if $oversip_debug
|
|
39
|
+
|
|
40
|
+
# Validation must be done in ssl_handshake_completed after receiving all the certs, so return true.
|
|
41
|
+
return true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def ssl_handshake_completed
|
|
46
|
+
log_system_debug ("TLS connection established from " << remote_desc) if $oversip_debug
|
|
47
|
+
|
|
48
|
+
# @connected in WssServer means "TLS connection" rather than
|
|
49
|
+
# just "TCP connection".
|
|
50
|
+
@connected = true
|
|
51
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
52
|
+
|
|
53
|
+
if ::OverSIP::WebSocket.callback_on_client_tls_handshake
|
|
54
|
+
# Set the state to :waiting_for_on_client_tls_handshake so data received after TLS handshake but before
|
|
55
|
+
# user callback validation is just stored.
|
|
56
|
+
@state = :waiting_for_on_client_tls_handshake
|
|
57
|
+
|
|
58
|
+
# Run OverSIP::WebSocketEvents.on_client_tls_handshake.
|
|
59
|
+
::Fiber.new do
|
|
60
|
+
begin
|
|
61
|
+
log_system_debug "running OverSIP::SipWebSocketEvents.on_client_tls_handshake()..." if $oversip_debug
|
|
62
|
+
::OverSIP::WebSocketEvents.on_client_tls_handshake self, @client_pems
|
|
63
|
+
# If the user of the peer has not closed the connection then continue.
|
|
64
|
+
unless @local_closed or error?
|
|
65
|
+
@state = :init
|
|
66
|
+
# Call process_received_data() to process possible data received in the meanwhile.
|
|
67
|
+
process_received_data
|
|
68
|
+
else
|
|
69
|
+
log_system_debug "connection closed during OverSIP::SipWebSocketEvents.on_client_tls_handshake(), aborting" if $oversip_debug
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
rescue ::Exception => e
|
|
73
|
+
log_system_error "error calling OverSIP::WebSocketEvents.on_client_tls_handshake():"
|
|
74
|
+
log_system_error e
|
|
75
|
+
close_connection
|
|
76
|
+
end
|
|
77
|
+
end.resume
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def unbind cause=nil
|
|
83
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
84
|
+
super
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class WssTunnelServer < WsServer
|
|
4
|
+
|
|
5
|
+
def post_connection
|
|
6
|
+
begin
|
|
7
|
+
# Temporal @remote_ip and @remote_port until the HAProxy protocol line is parsed.
|
|
8
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
9
|
+
rescue => e
|
|
10
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
11
|
+
close_connection
|
|
12
|
+
@state = :ignore
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Create an Outbound (RFC 5626) flow token for this connection.
|
|
17
|
+
@outbound_flow_token = ::OverSIP::SIP::TransportManager.add_outbound_connection self
|
|
18
|
+
|
|
19
|
+
log_system_debug ("connection from the TLS tunnel " << remote_desc) if $oversip_debug
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def unbind cause=nil
|
|
24
|
+
@state = :ignore
|
|
25
|
+
|
|
26
|
+
# Remove the connection.
|
|
27
|
+
self.class.connections.delete @connection_id if @connection_id
|
|
28
|
+
|
|
29
|
+
# Remove the Outbound token flow.
|
|
30
|
+
::OverSIP::SIP::TransportManager.delete_outbound_connection @outbound_flow_token
|
|
31
|
+
|
|
32
|
+
@local_closed = true if cause == ::Errno::ETIMEDOUT
|
|
33
|
+
@local_closed = false if @client_closed
|
|
34
|
+
|
|
35
|
+
if $oversip_debug
|
|
36
|
+
log_msg = "connection from the TLS tunnel #{remote_desc} "
|
|
37
|
+
log_msg << ( @local_closed ? "locally closed" : "remotely closed" )
|
|
38
|
+
log_msg << " (cause: #{cause.inspect})" if cause
|
|
39
|
+
log_system_debug log_msg
|
|
40
|
+
end unless $!
|
|
41
|
+
|
|
42
|
+
if @ws_established
|
|
43
|
+
# Run OverSIP::WebSocketEvents.on_disconnection
|
|
44
|
+
::Fiber.new do
|
|
45
|
+
begin
|
|
46
|
+
::OverSIP::WebSocketEvents.on_disconnection self, !@local_closed
|
|
47
|
+
rescue ::Exception => e
|
|
48
|
+
log_system_error "error calling OverSIP::WebSocketEvents.on_disconnection():"
|
|
49
|
+
log_system_error e
|
|
50
|
+
end
|
|
51
|
+
end.resume
|
|
52
|
+
end unless $!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def process_received_data
|
|
57
|
+
@state == :ignore and return
|
|
58
|
+
|
|
59
|
+
while (case @state
|
|
60
|
+
when :init
|
|
61
|
+
@http_parser = ::OverSIP::WebSocket::HttpRequestParser.new
|
|
62
|
+
@http_request = ::OverSIP::WebSocket::HttpRequest.new
|
|
63
|
+
@http_parser.reset
|
|
64
|
+
@http_parser_nbytes = 0
|
|
65
|
+
@bytes_remaining = 0
|
|
66
|
+
# If it's a TCP connection from the TLS proxy then parse the HAProxy Protocol line
|
|
67
|
+
# if it's not yet done.
|
|
68
|
+
unless @haproxy_protocol_parsed
|
|
69
|
+
@state = :haproxy_protocol
|
|
70
|
+
else
|
|
71
|
+
@state = :http_headers
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
when :haproxy_protocol
|
|
75
|
+
parse_haproxy_protocol
|
|
76
|
+
|
|
77
|
+
when :http_headers
|
|
78
|
+
parse_http_headers
|
|
79
|
+
|
|
80
|
+
when :check_http_request
|
|
81
|
+
check_http_request
|
|
82
|
+
|
|
83
|
+
when :on_connection_callback
|
|
84
|
+
do_on_connection_callback
|
|
85
|
+
false
|
|
86
|
+
|
|
87
|
+
when :accept_ws_handshake
|
|
88
|
+
accept_ws_handshake
|
|
89
|
+
|
|
90
|
+
when :websocket
|
|
91
|
+
@ws_established = true
|
|
92
|
+
return false if @buffer.size.zero?
|
|
93
|
+
@ws_framing.receive_data
|
|
94
|
+
false
|
|
95
|
+
|
|
96
|
+
when :ignore
|
|
97
|
+
false
|
|
98
|
+
end)
|
|
99
|
+
end # while
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def parse_haproxy_protocol
|
|
105
|
+
if (haproxy_protocol_data = ::OverSIP::Utils.parse_haproxy_protocol(@buffer.to_str))
|
|
106
|
+
@haproxy_protocol_parsed = true
|
|
107
|
+
|
|
108
|
+
# Update connection information.
|
|
109
|
+
@remote_ip_type = haproxy_protocol_data[1]
|
|
110
|
+
@remote_ip = haproxy_protocol_data[2]
|
|
111
|
+
@remote_port = haproxy_protocol_data[3]
|
|
112
|
+
|
|
113
|
+
# Update log information.
|
|
114
|
+
remote_desc true
|
|
115
|
+
|
|
116
|
+
# Remove the HAProxy Protocol line from the received data.
|
|
117
|
+
@buffer.read haproxy_protocol_data[0]
|
|
118
|
+
|
|
119
|
+
@state = :http_headers
|
|
120
|
+
|
|
121
|
+
# If parsing fails then the TLS proxy has sent us a wrong HAProxy Protocol line ¿?
|
|
122
|
+
else
|
|
123
|
+
log_system_error "HAProxy Protocol parsing error, closing connection"
|
|
124
|
+
close_connection_after_writing
|
|
125
|
+
@state = :ignore
|
|
126
|
+
return false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# OverSIP files
|
|
2
|
+
|
|
3
|
+
require "oversip/websocket/listeners/connection"
|
|
4
|
+
require "oversip/websocket/listeners/ws_server"
|
|
5
|
+
require "oversip/websocket/listeners/wss_server"
|
|
6
|
+
require "oversip/websocket/listeners/wss_tunnel_server"
|
|
7
|
+
|
|
8
|
+
require "oversip/websocket/listeners/ipv4_ws_server"
|
|
9
|
+
require "oversip/websocket/listeners/ipv6_ws_server"
|
|
10
|
+
require "oversip/websocket/listeners/ipv4_wss_server"
|
|
11
|
+
require "oversip/websocket/listeners/ipv6_wss_server"
|
|
12
|
+
require "oversip/websocket/listeners/ipv4_wss_tunnel_server"
|
|
13
|
+
require "oversip/websocket/listeners/ipv6_wss_tunnel_server"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
def self.module_init
|
|
4
|
+
conf = ::OverSIP.configuration
|
|
5
|
+
|
|
6
|
+
@callback_on_client_tls_handshake = conf[:websocket][:callback_on_client_tls_handshake]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.callback_on_client_tls_handshake
|
|
10
|
+
@callback_on_client_tls_handshake
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|