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,265 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class TcpServer < ::EM::Connection
|
|
4
|
+
|
|
5
|
+
include ::OverSIP::Logger
|
|
6
|
+
include ::OverSIP::WebSocket::DefaultPolicy
|
|
7
|
+
include ::OverSIP::WebSocket::Policy rescue nil # As it could not exist (i.e. the user deleted the file).
|
|
8
|
+
|
|
9
|
+
# Max size (bytes) of the buffered data when receiving message headers
|
|
10
|
+
# (avoid DoS attacks).
|
|
11
|
+
HEADERS_MAX_SIZE = 2048
|
|
12
|
+
|
|
13
|
+
WS_MAGIC_GUID_04 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".freeze
|
|
14
|
+
WS_VERSIONS = { 7=>true, 8=>true, 13=>true }
|
|
15
|
+
HDR_SUPPORTED_WEBSOCKET_VERSIONS = [ "X-Supported-WebSocket-Versions: #{WS_VERSIONS.keys.join(", ")}" ]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
attr_accessor :ip_type, :transport
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_accessor :ws_protocol, :ws_app_klass
|
|
23
|
+
attr_reader :connection_log_id, :remote_ip_type, :remote_ip, :remote_port
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
@http_parser = ::OverSIP::WebSocket::HttpRequestParser.new
|
|
28
|
+
@buffer = ::IO::Buffer.new
|
|
29
|
+
@state = :init
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def post_connection
|
|
34
|
+
begin
|
|
35
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
36
|
+
rescue => e
|
|
37
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
38
|
+
close_connection
|
|
39
|
+
@state = :ignore
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
@connection_log_id = ::SecureRandom.hex(4)
|
|
43
|
+
|
|
44
|
+
log_system_info "connection opened from " << remote_desc
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def remote_desc force=nil
|
|
49
|
+
if force
|
|
50
|
+
@remote_desc = case @remote_ip_type
|
|
51
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
52
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
@remote_desc ||= case self.class.ip_type
|
|
56
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
57
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def unbind cause=nil
|
|
64
|
+
@local_closed = true if cause == ::Errno::ETIMEDOUT
|
|
65
|
+
|
|
66
|
+
log_msg = "connection from #{remote_desc} "
|
|
67
|
+
log_msg << ( @local_closed ? "locally closed" : "remotely closed" )
|
|
68
|
+
log_msg << " (cause: #{cause.inspect})" if cause
|
|
69
|
+
log_system_debug log_msg if $oversip_debug
|
|
70
|
+
|
|
71
|
+
@ws_framing.tcp_closed if @ws_framing
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def receive_data data
|
|
76
|
+
@state == :ignore and return
|
|
77
|
+
@buffer << data
|
|
78
|
+
|
|
79
|
+
while (case @state
|
|
80
|
+
when :init
|
|
81
|
+
@http_request = ::OverSIP::WebSocket::HttpRequest.new
|
|
82
|
+
@http_parser.reset
|
|
83
|
+
@http_parser_nbytes = 0
|
|
84
|
+
@bytes_remaining = 0
|
|
85
|
+
@state = :http_headers
|
|
86
|
+
|
|
87
|
+
when :http_headers
|
|
88
|
+
parse_http_headers
|
|
89
|
+
|
|
90
|
+
when :check_http_request
|
|
91
|
+
check_http_request
|
|
92
|
+
|
|
93
|
+
when :accept_ws_handshake
|
|
94
|
+
accept_ws_handshake
|
|
95
|
+
|
|
96
|
+
when :websocket_frames
|
|
97
|
+
return false if @buffer.size.zero?
|
|
98
|
+
|
|
99
|
+
@ws_framing.receive_data
|
|
100
|
+
false
|
|
101
|
+
|
|
102
|
+
when :ignore
|
|
103
|
+
false
|
|
104
|
+
end)
|
|
105
|
+
end # while
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def parse_http_headers
|
|
111
|
+
return false if @buffer.empty?
|
|
112
|
+
|
|
113
|
+
# Parse the currently buffered data. If parsing fails @http_parser_nbytes gets nil value.
|
|
114
|
+
unless @http_parser_nbytes = @http_parser.execute(@http_request, @buffer.to_str, @http_parser_nbytes)
|
|
115
|
+
log_system_warn "parsing error: \"#{@http_parser.error}\""
|
|
116
|
+
close_connection_after_writing
|
|
117
|
+
@state = :ignore
|
|
118
|
+
return false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Avoid flood attacks in TCP (very long headers).
|
|
122
|
+
if @http_parser_nbytes > HEADERS_MAX_SIZE
|
|
123
|
+
log_system_warn "DoS attack detected: headers size exceedes #{HEADERS_MAX_SIZE} bytes, closing connection with #{remote_desc}"
|
|
124
|
+
close_connection
|
|
125
|
+
@state = :ignore
|
|
126
|
+
return false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
return false unless @http_parser.finished?
|
|
130
|
+
|
|
131
|
+
# Clear parsed data from the buffer.
|
|
132
|
+
@buffer.read(@http_parser_nbytes)
|
|
133
|
+
|
|
134
|
+
@http_request.connection = self
|
|
135
|
+
@http_request.transport = self.class.transport
|
|
136
|
+
@http_request.source_ip = @remote_ip
|
|
137
|
+
@http_request.source_port = @remote_port
|
|
138
|
+
@http_request.source_ip_type = @remote_ip_type ||= self.class.ip_type
|
|
139
|
+
|
|
140
|
+
@state = :check_http_request
|
|
141
|
+
true
|
|
142
|
+
end # parse_headers
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def check_http_request
|
|
146
|
+
|
|
147
|
+
# HTTP method must be GET.
|
|
148
|
+
if @http_request.http_method != :GET
|
|
149
|
+
log_system_notice "rejecting HTTP #{@http_request.http_method} request => 405"
|
|
150
|
+
http_reject 405
|
|
151
|
+
return false
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# "Sec-WebSocket-Version" must be 8.
|
|
155
|
+
unless WS_VERSIONS[@http_request.hdr_sec_websocket_version]
|
|
156
|
+
if @http_request.hdr_sec_websocket_version
|
|
157
|
+
log_system_notice "WebSocket version #{@http_request.hdr_sec_websocket_version} not implemented => 426"
|
|
158
|
+
else
|
|
159
|
+
log_system_notice "WebSocket version header not present => 426"
|
|
160
|
+
end
|
|
161
|
+
http_reject 426, nil, HDR_SUPPORTED_WEBSOCKET_VERSIONS
|
|
162
|
+
return false
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Connection header must include "upgrade".
|
|
166
|
+
unless @http_request.hdr_connection and @http_request.hdr_connection.include? "upgrade"
|
|
167
|
+
log_system_notice "Connection header must include \"upgrade\" => 400"
|
|
168
|
+
http_reject 400, "Connection header must include \"upgrade\""
|
|
169
|
+
return false
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# "Upgrade: websocket" is required.
|
|
173
|
+
unless @http_request.hdr_upgrade == "websocket"
|
|
174
|
+
log_system_notice "Upgrade header must be \"websocket\" => 400"
|
|
175
|
+
http_reject 400, "Upgrade header must be \"websocket\""
|
|
176
|
+
return false
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Sec-WebSocket-Key is required.
|
|
180
|
+
unless @http_request.hdr_sec_websocket_key
|
|
181
|
+
log_system_notice "Sec-WebSocket-Key header not present => 400"
|
|
182
|
+
http_reject 400, "Sec-WebSocket-Key header not present"
|
|
183
|
+
return false
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Check Sec-WebSocket-Protocol.
|
|
187
|
+
if @http_request.hdr_sec_websocket_protocol
|
|
188
|
+
if @http_request.hdr_sec_websocket_protocol.include? @ws_protocol
|
|
189
|
+
@websocket_protocol_negotiated = true
|
|
190
|
+
else
|
|
191
|
+
log_system_notice "Sec-WebSocket-Protocol does not contain a supported protocol but #{@http_request.hdr_sec_websocket_protocol} => 501"
|
|
192
|
+
http_reject 501, "No Suitable WebSocket Protocol"
|
|
193
|
+
return false
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Check WebSocket policy.
|
|
198
|
+
|
|
199
|
+
unless check_hostport(@http_request.host, @http_request.port)
|
|
200
|
+
log_system_notice "host/port policy not satisfied (host=#{@http_request.host.inspect}, port=#{@http_request.port.inspect}) => 403"
|
|
201
|
+
http_reject 403, "request host/port not satisfied"
|
|
202
|
+
return false
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
unless check_origin(@http_request.hdr_origin)
|
|
206
|
+
log_system_notice "'Origin' policy not satisfied (origin=#{@http_request.hdr_origin.inspect}) => 403"
|
|
207
|
+
http_reject 403, "request 'Origin' not satisfied"
|
|
208
|
+
return false
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
unless check_request_uri(@http_request.uri_path, @http_request.uri_query)
|
|
212
|
+
log_system_notice "request URI path/query not satisfied (path=#{@http_request.uri_path.inspect}, query=#{@http_request.uri_query.inspect}) => 403"
|
|
213
|
+
http_reject 403, "request URI path/query not satisfied"
|
|
214
|
+
return false
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
@state = :accept_ws_handshake
|
|
218
|
+
true
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def accept_ws_handshake
|
|
223
|
+
sec_websocket_accept = Digest::SHA1::base64digest @http_request.hdr_sec_websocket_key + WS_MAGIC_GUID_04
|
|
224
|
+
|
|
225
|
+
extra_headers = [
|
|
226
|
+
"Upgrade: websocket",
|
|
227
|
+
"Connection: Upgrade",
|
|
228
|
+
"Sec-WebSocket-Accept: #{sec_websocket_accept}"
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
if @websocket_protocol_negotiated
|
|
232
|
+
extra_headers << "Sec-WebSocket-Protocol: #{@ws_protocol}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
if @websocket_extensions
|
|
236
|
+
extra_headers << "Sec-WebSocket-Extensions: #{@websocket_extensions.to_s}"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
@http_request.reply 101, nil, extra_headers
|
|
240
|
+
|
|
241
|
+
# Set the WS framming layer and WS application layer.
|
|
242
|
+
@ws_framing = ::OverSIP::WebSocket::WsFraming.new(self, @buffer)
|
|
243
|
+
@ws_framing.ws_app = @ws_app_klass.new(self, @ws_framing)
|
|
244
|
+
|
|
245
|
+
@state = :websocket_frames
|
|
246
|
+
true
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def http_reject status_code, reason_phrase=nil, extra_headers=nil
|
|
251
|
+
@http_request.reply(status_code, reason_phrase, extra_headers)
|
|
252
|
+
close_connection_after_writing
|
|
253
|
+
@state = :ignore
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def ignore_incoming_data
|
|
258
|
+
@state = :ignore # The WS application needs to set the connection in :ignore state
|
|
259
|
+
# after sending a close frame to the client.
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end # class TcpServer
|
|
263
|
+
|
|
264
|
+
end
|
|
265
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class TlsServer < TcpServer
|
|
4
|
+
|
|
5
|
+
TLS_HANDSHAKE_MAX_TIME = 8
|
|
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
|
+
})
|
|
17
|
+
|
|
18
|
+
# If the remote client does never send us a TLS certificate
|
|
19
|
+
# after the TCP connection we would leak by storing more and
|
|
20
|
+
# more messages in @pending_messages array.
|
|
21
|
+
@timer_tls_handshake = ::EM::Timer.new(TLS_HANDSHAKE_MAX_TIME) do
|
|
22
|
+
unless @connected
|
|
23
|
+
log_system_notice "TLS handshake not performed within #{TLS_HANDSHAKE_MAX_TIME} seconds, closing the connection"
|
|
24
|
+
close_connection
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ssl_verify_peer pem
|
|
31
|
+
# TODO: Dirty workaround for bug https://github.com/eventmachine/eventmachine/issues/194.
|
|
32
|
+
return true if @client_last_pem == pem
|
|
33
|
+
|
|
34
|
+
@client_last_pem = pem
|
|
35
|
+
@client_pems << pem
|
|
36
|
+
|
|
37
|
+
log_system_debug "received certificate num #{@client_pems.size} from client" if $oversip_debug
|
|
38
|
+
|
|
39
|
+
# Validation must be done in ssl_handshake_completed after receiving all the certs, so return true.
|
|
40
|
+
return true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def ssl_handshake_completed
|
|
45
|
+
log_system_info "TLS connection established from " << remote_desc
|
|
46
|
+
|
|
47
|
+
# TODO: What to do it falidation fails? always do validation?
|
|
48
|
+
|
|
49
|
+
validated, cert, tls_error, tls_error_string = ::OverSIP::TLS.validate @client_pems.pop, @client_pems
|
|
50
|
+
if validated
|
|
51
|
+
log_system_info "client provides a valid TLS certificate"
|
|
52
|
+
else
|
|
53
|
+
log_system_notice "client's TLS certificate validation failed (TLS error: #{tls_error.inspect}, description: #{tls_error_string.inspect})"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @connected in TlsServer means "TLS connection" rather than
|
|
57
|
+
# just "TCP connection".
|
|
58
|
+
@connected = true
|
|
59
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def unbind cause=nil
|
|
64
|
+
super
|
|
65
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class TlsTunnelServer < TcpServer
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@http_parser = ::OverSIP::WebSocket::HttpRequestParser.new
|
|
7
|
+
@buffer = ::IO::Buffer.new
|
|
8
|
+
@state = :init
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def post_connection
|
|
13
|
+
begin
|
|
14
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
15
|
+
rescue => e
|
|
16
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
17
|
+
close_connection
|
|
18
|
+
@state = :ignore
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
@connection_log_id = ::SecureRandom.hex(4)
|
|
22
|
+
|
|
23
|
+
log_system_info "connection from the TLS tunnel " << remote_desc
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def receive_data data
|
|
28
|
+
@state == :ignore and return
|
|
29
|
+
@buffer << data
|
|
30
|
+
|
|
31
|
+
while (case @state
|
|
32
|
+
when :init
|
|
33
|
+
@http_request = ::OverSIP::WebSocket::HttpRequest.new
|
|
34
|
+
@http_parser.reset
|
|
35
|
+
@http_parser_nbytes = 0
|
|
36
|
+
@bytes_remaining = 0
|
|
37
|
+
# If it's a TCP connection from the TLS proxy then parse the HAProxy Protocol line
|
|
38
|
+
# if it's not yet done.
|
|
39
|
+
unless @haproxy_protocol_parsed
|
|
40
|
+
@state = :haproxy_protocol
|
|
41
|
+
else
|
|
42
|
+
@state = :http_headers
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
when :haproxy_protocol
|
|
46
|
+
parse_haproxy_protocol
|
|
47
|
+
|
|
48
|
+
when :http_headers
|
|
49
|
+
parse_http_headers
|
|
50
|
+
|
|
51
|
+
when :check_http_request
|
|
52
|
+
check_http_request
|
|
53
|
+
|
|
54
|
+
when :accept_ws_handshake
|
|
55
|
+
accept_ws_handshake
|
|
56
|
+
|
|
57
|
+
when :websocket_frames
|
|
58
|
+
return false if @buffer.size.zero?
|
|
59
|
+
|
|
60
|
+
@ws_framing.receive_data
|
|
61
|
+
false
|
|
62
|
+
|
|
63
|
+
when :ignore
|
|
64
|
+
false
|
|
65
|
+
end)
|
|
66
|
+
end # while
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def parse_haproxy_protocol
|
|
72
|
+
if (haproxy_protocol_data = ::OverSIP::Utils.parse_haproxy_protocol(@buffer.to_str))
|
|
73
|
+
@haproxy_protocol_parsed = true
|
|
74
|
+
|
|
75
|
+
# Update connection information.
|
|
76
|
+
@remote_ip_type = haproxy_protocol_data[1]
|
|
77
|
+
@remote_ip = haproxy_protocol_data[2]
|
|
78
|
+
@remote_port = haproxy_protocol_data[3]
|
|
79
|
+
|
|
80
|
+
# Update log information.
|
|
81
|
+
remote_desc true
|
|
82
|
+
|
|
83
|
+
# Remove the HAProxy Protocol line from the received data.
|
|
84
|
+
@buffer.read haproxy_protocol_data[0]
|
|
85
|
+
|
|
86
|
+
@state = :http_headers
|
|
87
|
+
|
|
88
|
+
# If parsing fails then the TLS proxy has sent us a wrong HAProxy Protocol line ¿?
|
|
89
|
+
else
|
|
90
|
+
log_system_error "HAProxy Protocol parsing error, closing connection"
|
|
91
|
+
close_connection_after_writing
|
|
92
|
+
@state = :ignore
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# OverSIP files
|
|
2
|
+
|
|
3
|
+
require "oversip/websocket/listeners/tcp_server"
|
|
4
|
+
require "oversip/websocket/listeners/tls_server"
|
|
5
|
+
require "oversip/websocket/listeners/tls_tunnel_server"
|
|
6
|
+
|
|
7
|
+
require "oversip/websocket/listeners/ipv4_tcp_server"
|
|
8
|
+
require "oversip/websocket/listeners/ipv6_tcp_server"
|
|
9
|
+
require "oversip/websocket/listeners/ipv4_tls_server"
|
|
10
|
+
require "oversip/websocket/listeners/ipv6_tls_server"
|
|
11
|
+
require "oversip/websocket/listeners/ipv4_tls_tunnel_server"
|
|
12
|
+
require "oversip/websocket/listeners/ipv6_tls_tunnel_server"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class WsApp
|
|
4
|
+
|
|
5
|
+
include ::OverSIP::Logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def self.class_init
|
|
9
|
+
@@max_message_size = ::OverSIP.configuration[:websocket][:max_ws_message_size]
|
|
10
|
+
@@ws_keepalive_interval = ::OverSIP.configuration[:websocket][:ws_keepalive_interval]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def initialize connection, ws_framing
|
|
15
|
+
@connection = connection
|
|
16
|
+
@ws_framing = ws_framing
|
|
17
|
+
@ws_message = ::IO::Buffer.new
|
|
18
|
+
|
|
19
|
+
# Mantain WebSocket keepalive.
|
|
20
|
+
@ws_framing.do_keep_alive @@ws_keepalive_interval if @@ws_keepalive_interval
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def close_connection status=nil, reason=nil
|
|
25
|
+
@ws_framing.send_close_frame status, reason
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def receive_payload_data payload_data
|
|
30
|
+
# payload_data is always Encoding::BINARY so also @ws_message.to_str.
|
|
31
|
+
@ws_message << payload_data
|
|
32
|
+
|
|
33
|
+
# Check max message size.
|
|
34
|
+
if @ws_message.size > @@max_message_size
|
|
35
|
+
close_connection 1009, "message too big"
|
|
36
|
+
return false
|
|
37
|
+
end
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def message_done type
|
|
43
|
+
log_system_debug "received WS message: length=#{@ws_message.size}" if $oversip_debug
|
|
44
|
+
|
|
45
|
+
case type
|
|
46
|
+
|
|
47
|
+
when :text
|
|
48
|
+
ws_message = @ws_message.to_str.force_encoding ::Encoding::UTF_8
|
|
49
|
+
process_text_message ws_message
|
|
50
|
+
|
|
51
|
+
when :binary
|
|
52
|
+
process_binary_message @ws_message.to_str # As IO::Buffer#to_str always generates Encoding::BINARY.
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@ws_message.clear
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def tcp_closed
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Methods to be overriden by child classes.
|
|
66
|
+
def process_text_message ws_message
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def process_binary_message ws_message
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
end # WsApplication
|
|
74
|
+
|
|
75
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class IPv4WsSipApp < WsSipApp
|
|
4
|
+
|
|
5
|
+
@ip_type = :ipv4
|
|
6
|
+
@transport = :ws
|
|
7
|
+
@connections = {}
|
|
8
|
+
@invite_server_transactions = {}
|
|
9
|
+
@non_invite_server_transactions = {}
|
|
10
|
+
@invite_client_transactions = {}
|
|
11
|
+
@non_invite_client_transactions = {}
|
|
12
|
+
@is_reliable_transport_listener = true
|
|
13
|
+
|
|
14
|
+
LOG_ID = "WS IPv4 SIP app"
|
|
15
|
+
def log_id
|
|
16
|
+
LOG_ID
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class IPv4WssSipApp < WsSipApp
|
|
4
|
+
|
|
5
|
+
@ip_type = :ipv4
|
|
6
|
+
@transport = :wss
|
|
7
|
+
@connections = {}
|
|
8
|
+
@invite_server_transactions = {}
|
|
9
|
+
@non_invite_server_transactions = {}
|
|
10
|
+
@invite_client_transactions = {}
|
|
11
|
+
@non_invite_client_transactions = {}
|
|
12
|
+
@is_reliable_transport_listener = true
|
|
13
|
+
|
|
14
|
+
LOG_ID = "WSS IPv4 SIP app"
|
|
15
|
+
def log_id
|
|
16
|
+
LOG_ID
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module OverSIP::WebSocket
|
|
2
|
+
|
|
3
|
+
class IPv6WsSipApp < WsSipApp
|
|
4
|
+
|
|
5
|
+
@ip_type = :ipv6
|
|
6
|
+
@transport = :ws
|
|
7
|
+
@connections = {}
|
|
8
|
+
@invite_server_transactions = {}
|
|
9
|
+
@non_invite_server_transactions = {}
|
|
10
|
+
@invite_client_transactions = {}
|
|
11
|
+
@non_invite_client_transactions = {}
|
|
12
|
+
@is_reliable_transport_listener = true
|
|
13
|
+
|
|
14
|
+
LOG_ID = "WS IPv6 SIP app"
|
|
15
|
+
def log_id
|
|
16
|
+
LOG_ID
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|