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,185 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
2
|
+
|
|
3
|
+
class TcpReactor < Reactor
|
|
4
|
+
|
|
5
|
+
# Max size (bytes) of the buffered data when receiving message headers
|
|
6
|
+
# (avoid DoS attacks).
|
|
7
|
+
HEADERS_MAX_SIZE = 16384
|
|
8
|
+
|
|
9
|
+
def receive_data data
|
|
10
|
+
@state == :ignore and return
|
|
11
|
+
@buffer << data
|
|
12
|
+
|
|
13
|
+
while (case @state
|
|
14
|
+
when :init
|
|
15
|
+
@parser.reset
|
|
16
|
+
@parser_nbytes = 0
|
|
17
|
+
@state = :headers
|
|
18
|
+
|
|
19
|
+
when :headers
|
|
20
|
+
parse_headers
|
|
21
|
+
# TODO: Add a timer for the case in which an attacker sends us slow headers that never end:
|
|
22
|
+
# http://ha.ckers.org/slowloris/.
|
|
23
|
+
|
|
24
|
+
when :body
|
|
25
|
+
get_body
|
|
26
|
+
|
|
27
|
+
when :finished
|
|
28
|
+
# Invoke the custom logic for requests.
|
|
29
|
+
if @msg.request?
|
|
30
|
+
process_request
|
|
31
|
+
else
|
|
32
|
+
process_response
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Set state to :init.
|
|
36
|
+
@state = :init
|
|
37
|
+
# Return true to continue processing possible remaining data.
|
|
38
|
+
true
|
|
39
|
+
|
|
40
|
+
when :ignore
|
|
41
|
+
false
|
|
42
|
+
end)
|
|
43
|
+
end # while
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse_headers
|
|
48
|
+
return false if @buffer.empty?
|
|
49
|
+
|
|
50
|
+
# Parse the currently buffered data. If parsing fails @parser_nbytes gets nil value.
|
|
51
|
+
unless @parser_nbytes = @parser.execute(@buffer.to_str, @parser_nbytes)
|
|
52
|
+
# The parsed data is invalid, however some data could be parsed so @parsed.parsed
|
|
53
|
+
# can be:
|
|
54
|
+
# - SIP::Request
|
|
55
|
+
# - SIP::Response
|
|
56
|
+
# - nil (the message is so wrong that cannot be neither a request or response).
|
|
57
|
+
if wrong_message = @parser.parsed
|
|
58
|
+
log_system_warn "parsing error for #{MSG_TYPE[wrong_message.class]}: \"#{@parser.error}\""
|
|
59
|
+
else
|
|
60
|
+
log_system_warn "parsing error: \"#{@parser.error}\""
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
close_connection_after_writing
|
|
64
|
+
@state = :ignore
|
|
65
|
+
return false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Avoid flood attacks in TCP (very long headers).
|
|
69
|
+
if @parser_nbytes > HEADERS_MAX_SIZE
|
|
70
|
+
log_system_warn "DoS attack detected: headers size exceedes #{HEADERS_MAX_SIZE} bytes, closing connection with #{remote_desc}"
|
|
71
|
+
close_connection
|
|
72
|
+
# After closing client connection some data can still arrrive to "receive_data()"
|
|
73
|
+
# (explained in EM documentation). By setting @state = :ignore we ensure such
|
|
74
|
+
# remaining data is not processed.
|
|
75
|
+
@state = :ignore
|
|
76
|
+
return false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# If the parsing has not finished, it is correct in TCP so return false and wait for more data under :headers state.
|
|
80
|
+
return false unless @parser.finished?
|
|
81
|
+
|
|
82
|
+
# At this point we've got a SIP::Request, SIP::Response or :outbound_keepalive symbol.
|
|
83
|
+
@msg = @parser.parsed
|
|
84
|
+
|
|
85
|
+
# Clear parsed data from the buffer.
|
|
86
|
+
@buffer.read(@parser_nbytes)
|
|
87
|
+
|
|
88
|
+
# Received data is a Outbound keealive.
|
|
89
|
+
if @msg == :outbound_keepalive
|
|
90
|
+
log_system_debug "Outbound keepalive received, replying single CRLF" if $oversip_debug
|
|
91
|
+
# Reply a single CRLF over the same connection.
|
|
92
|
+
send_data CRLF
|
|
93
|
+
# If TCP then go back to :init state so possible remaining data would be processed.
|
|
94
|
+
@state = :init
|
|
95
|
+
return true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@parser.post_parsing
|
|
99
|
+
|
|
100
|
+
# Here we have received the entire headers of a SIP request or response. Fill some
|
|
101
|
+
# attributes.
|
|
102
|
+
@msg.connection = self
|
|
103
|
+
@msg.transport = self.class.transport
|
|
104
|
+
@msg.source_ip = @remote_ip
|
|
105
|
+
@msg.source_port = @remote_port
|
|
106
|
+
@msg.source_ip_type = @remote_ip_type || self.class.ip_type
|
|
107
|
+
|
|
108
|
+
unless valid_message?
|
|
109
|
+
close_connection_after_writing
|
|
110
|
+
@state = :ignore
|
|
111
|
+
return false
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
add_via_received_rport if @msg.request?
|
|
115
|
+
|
|
116
|
+
unless check_via_branch
|
|
117
|
+
close_connection_after_writing
|
|
118
|
+
@state = :ignore
|
|
119
|
+
return false
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Examine Content-Length header.
|
|
123
|
+
# In SIP over TCP Content-Length header is mandatory.
|
|
124
|
+
if (@body_length = @msg.content_length)
|
|
125
|
+
# There is body (or should be).
|
|
126
|
+
if @body_length > 0
|
|
127
|
+
@state = :body
|
|
128
|
+
# Return true to continue in get_body() method.
|
|
129
|
+
return true
|
|
130
|
+
# No body.
|
|
131
|
+
else
|
|
132
|
+
# Set :finished state and return true to process the parsed message.
|
|
133
|
+
@state = :finished
|
|
134
|
+
return true
|
|
135
|
+
end
|
|
136
|
+
# No Content-Length, invalid message!
|
|
137
|
+
else
|
|
138
|
+
# Log it and reply a 400 Bad Request (if it's a request).
|
|
139
|
+
# Close the connection, set :ignore state and return false to leave
|
|
140
|
+
# receive_data().
|
|
141
|
+
if @msg.request?
|
|
142
|
+
unless @msg.sip_method == :ACK
|
|
143
|
+
log_system_warn "request body size doesn't match Content-Length => 400"
|
|
144
|
+
@msg.reply 400, "Body size doesn't match Content-Length"
|
|
145
|
+
else
|
|
146
|
+
log_system_warn "ACK body size doesn't match Content-Length, ignoring it"
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
log_system_warn "response has not Content-Length header, ignoring it"
|
|
150
|
+
end
|
|
151
|
+
close_connection_after_writing
|
|
152
|
+
@state = :ignore
|
|
153
|
+
return false
|
|
154
|
+
end
|
|
155
|
+
end # parse_headers
|
|
156
|
+
|
|
157
|
+
def get_body
|
|
158
|
+
# Return false until the buffer gets all the body.
|
|
159
|
+
return false if @buffer.size < @body_length
|
|
160
|
+
|
|
161
|
+
### TODO: Creo que es mejor forzarlo a BINARY y no a UTF-8. Aunque IOBuffer ya lo saca siempre en BINARY.
|
|
162
|
+
# ¿Por qué lo forcé a UTF-8?
|
|
163
|
+
# RESPUESTA: Si no lo hago y resulta que el body no es UTF-8 válido, al añadir el body a los headers (que
|
|
164
|
+
# se generan como un string en UTF-8 (aunque contengan símbolos no UTF-8) fallaría. O todo UTF-8 (aunque
|
|
165
|
+
# tenga símbolos inválidos) o todo BINARY.
|
|
166
|
+
@msg.body = @buffer.read(@body_length).force_encoding(::Encoding::UTF_8)
|
|
167
|
+
@state = :finished
|
|
168
|
+
return true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Parameters ip and port are just included because they are needed in UDP, so the API remains equal.
|
|
173
|
+
def send_sip_msg msg, ip=nil, port=nil
|
|
174
|
+
if self.error?
|
|
175
|
+
log_system_notice "SIP message could not be sent, connection is closed"
|
|
176
|
+
return false
|
|
177
|
+
end
|
|
178
|
+
send_data msg
|
|
179
|
+
true
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
2
|
+
|
|
3
|
+
class TcpServer < TcpReactor
|
|
4
|
+
|
|
5
|
+
attr_reader :outbound_flow_token
|
|
6
|
+
|
|
7
|
+
def post_connection
|
|
8
|
+
begin
|
|
9
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
10
|
+
rescue => e
|
|
11
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
12
|
+
close_connection
|
|
13
|
+
@state = :ignore
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
log_system_info "connection opened from " << remote_desc
|
|
18
|
+
|
|
19
|
+
@connection_id = ::OverSIP::SIP::TransportManager.add_connection self, self.class, self.class.ip_type, @remote_ip, @remote_port
|
|
20
|
+
|
|
21
|
+
# Create an Outbound (RFC 5626) flow token for this connection.
|
|
22
|
+
@outbound_flow_token = ::OverSIP::SIP::TransportManager.add_outbound_connection self
|
|
23
|
+
|
|
24
|
+
### Testing TCP keepalive.
|
|
25
|
+
# https://github.com/bklang/eventmachine/commit/74c65a56c733bc1b6f092e32a9f0d722501ade46
|
|
26
|
+
# http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
|
|
27
|
+
if ::OverSIP::SIP.tcp_keepalive_interval
|
|
28
|
+
set_sock_opt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true
|
|
29
|
+
set_sock_opt Socket::SOL_TCP, Socket::TCP_KEEPIDLE, ::OverSIP::SIP.tcp_keepalive_interval # First TCP ping.
|
|
30
|
+
set_sock_opt Socket::SOL_TCP, Socket::TCP_KEEPINTVL, ::OverSIP::SIP.tcp_keepalive_interval # Interval between TCP pings.
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def remote_desc force=nil
|
|
35
|
+
if force
|
|
36
|
+
@remote_desc = case @remote_ip_type
|
|
37
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
38
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
@remote_desc ||= case self.class.ip_type
|
|
42
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
43
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def unbind cause=nil
|
|
50
|
+
@state = :ignore
|
|
51
|
+
|
|
52
|
+
# Remove the connection.
|
|
53
|
+
self.class.connections.delete @connection_id
|
|
54
|
+
|
|
55
|
+
# Remove the Outbound token flow.
|
|
56
|
+
::OverSIP::SIP::TransportManager.delete_outbound_connection @outbound_flow_token
|
|
57
|
+
|
|
58
|
+
@local_closed = true if cause == ::Errno::ETIMEDOUT
|
|
59
|
+
|
|
60
|
+
if $oversip_debug
|
|
61
|
+
log_msg = "connection from #{remote_desc} "
|
|
62
|
+
log_msg << ( @local_closed ? "locally closed" : "remotely closed" )
|
|
63
|
+
log_msg << " (cause: #{cause.inspect})" if cause
|
|
64
|
+
log_system_debug log_msg
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
2
|
+
|
|
3
|
+
class TlsClient < TcpClient
|
|
4
|
+
|
|
5
|
+
TLS_HANDSHAKE_MAX_TIME = 4
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
attr_writer :tls_validation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def initialize ip, port
|
|
12
|
+
super
|
|
13
|
+
@pending_messages = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def connection_completed
|
|
18
|
+
@server_pems = []
|
|
19
|
+
@server_last_pem = false
|
|
20
|
+
|
|
21
|
+
start_tls({
|
|
22
|
+
:verify_peer => @tls_validation,
|
|
23
|
+
:cert_chain_file => ::OverSIP.tls_public_cert,
|
|
24
|
+
:private_key_file => ::OverSIP.tls_private_cert
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
# If the remote server does never send us a TLS certificate
|
|
28
|
+
# after the TCP connection we would leak by storing more and
|
|
29
|
+
# more messages in @pending_messages array.
|
|
30
|
+
@timer_tls_handshake = ::EM::Timer.new(TLS_HANDSHAKE_MAX_TIME) do
|
|
31
|
+
unless @connected
|
|
32
|
+
log_system_notice "TLS handshake not performed within #{TLS_HANDSHAKE_MAX_TIME} seconds, closing the connection"
|
|
33
|
+
close_connection
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Called for every certificate provided by the peer.
|
|
40
|
+
# This is just called in case @tls_validation is true.
|
|
41
|
+
def ssl_verify_peer pem
|
|
42
|
+
# TODO: Dirty workaround for bug https://github.com/eventmachine/eventmachine/issues/194.
|
|
43
|
+
return true if @server_last_pem == pem
|
|
44
|
+
|
|
45
|
+
@server_last_pem = pem
|
|
46
|
+
@server_pems << pem
|
|
47
|
+
|
|
48
|
+
log_system_debug "received certificate num #{@server_pems.size} from server" if $oversip_debug
|
|
49
|
+
|
|
50
|
+
# Validation must be done in ssl_handshake_completed after receiving all the certs, so return true.
|
|
51
|
+
return true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# This is called after all the calls to ssl_verify_peer().
|
|
56
|
+
def ssl_handshake_completed
|
|
57
|
+
log_system_info "TLS connection established to " << remote_desc
|
|
58
|
+
|
|
59
|
+
# @connected in TlsClient means "TLS connection" rather than
|
|
60
|
+
# just "TCP connection".
|
|
61
|
+
@connected = true
|
|
62
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
63
|
+
|
|
64
|
+
if @tls_validation
|
|
65
|
+
validated, cert, tls_error, tls_error_string = ::OverSIP::TLS.validate @server_pems.pop, @server_pems
|
|
66
|
+
if validated
|
|
67
|
+
log_system_info "server provides a valid TLS certificate"
|
|
68
|
+
sip_identities = ::OverSIP::TLS.get_sip_identities(cert)
|
|
69
|
+
log_system_debug "SIP identities in peer cert: #{sip_identities.keys}" if $oversip_debug
|
|
70
|
+
else
|
|
71
|
+
log_system_notice "server's TLS certificate validation failed (TLS error: #{tls_error.inspect}, description: #{tls_error_string.inspect})"
|
|
72
|
+
@pending_client_transactions.each do |client_transaction|
|
|
73
|
+
client_transaction.tls_validation_failed
|
|
74
|
+
end
|
|
75
|
+
@pending_client_transactions.clear
|
|
76
|
+
@pending_messages.clear
|
|
77
|
+
close_connection
|
|
78
|
+
@state = :ignore
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
@pending_client_transactions.clear
|
|
84
|
+
@pending_messages.each do |msg|
|
|
85
|
+
send_data msg
|
|
86
|
+
end
|
|
87
|
+
@pending_messages.clear
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def unbind cause=nil
|
|
91
|
+
super
|
|
92
|
+
@timer_tls_handshake.cancel if @timer_tls_handshake
|
|
93
|
+
@pending_messages.clear
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# In TLS client, we must wait until ssl_handshake_completed is
|
|
97
|
+
# completed before sending data. If not, data will be sent in
|
|
98
|
+
# plain TCP.
|
|
99
|
+
# http://dev.sipdoc.net/issues/457
|
|
100
|
+
def send_sip_msg msg, ip=nil, port=nil
|
|
101
|
+
if self.error?
|
|
102
|
+
log_system_notice "SIP message could not be sent, connection is closed"
|
|
103
|
+
return false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if @connected
|
|
107
|
+
send_data msg
|
|
108
|
+
else
|
|
109
|
+
log_system_debug "TLS handshake not completed yet, waiting before sending the message" if $oversip_debug
|
|
110
|
+
@pending_messages << msg
|
|
111
|
+
end
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
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
|
|
70
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
2
|
+
|
|
3
|
+
class TlsTunnelReactor < TcpReactor
|
|
4
|
+
|
|
5
|
+
# Max size (bytes) of the buffered data when receiving message headers
|
|
6
|
+
# (avoid DoS attacks).
|
|
7
|
+
HEADERS_MAX_SIZE = 16384
|
|
8
|
+
|
|
9
|
+
def receive_data data
|
|
10
|
+
@state == :ignore and return
|
|
11
|
+
@buffer << data
|
|
12
|
+
|
|
13
|
+
while (case @state
|
|
14
|
+
when :init
|
|
15
|
+
@parser.reset
|
|
16
|
+
@parser_nbytes = 0
|
|
17
|
+
# If it's a TCP connection from the TLS tunnel then parse the HAProxy Protocol line
|
|
18
|
+
# if it's not yet done.
|
|
19
|
+
unless @haproxy_protocol_parsed
|
|
20
|
+
@state = :haproxy_protocol
|
|
21
|
+
else
|
|
22
|
+
@state = :headers
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
when :haproxy_protocol
|
|
26
|
+
parse_haproxy_protocol
|
|
27
|
+
|
|
28
|
+
when :client_pems
|
|
29
|
+
parse_client_pems
|
|
30
|
+
|
|
31
|
+
when :headers
|
|
32
|
+
parse_headers
|
|
33
|
+
|
|
34
|
+
when :body
|
|
35
|
+
get_body
|
|
36
|
+
|
|
37
|
+
when :finished
|
|
38
|
+
# Invoke the custom logic for requests.
|
|
39
|
+
if @msg.request?
|
|
40
|
+
process_request
|
|
41
|
+
else
|
|
42
|
+
process_response
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Set state to :init.
|
|
46
|
+
@state = :init
|
|
47
|
+
# Return true to continue processing possible remaining data.
|
|
48
|
+
true
|
|
49
|
+
|
|
50
|
+
when :ignore
|
|
51
|
+
false
|
|
52
|
+
end)
|
|
53
|
+
end # while
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parse_haproxy_protocol
|
|
58
|
+
if (haproxy_protocol_data = ::OverSIP::Utils.parse_haproxy_protocol(@buffer.to_str))
|
|
59
|
+
@haproxy_protocol_parsed = true
|
|
60
|
+
|
|
61
|
+
# Update connection information.
|
|
62
|
+
@remote_ip_type = haproxy_protocol_data[1]
|
|
63
|
+
@remote_ip = haproxy_protocol_data[2]
|
|
64
|
+
@remote_port = haproxy_protocol_data[3]
|
|
65
|
+
|
|
66
|
+
# Add the connection with the client's source data. Note that we pass a TlsServer as class, but
|
|
67
|
+
# the server instance is a TcpServer.
|
|
68
|
+
@connection_id = case @remote_ip_type
|
|
69
|
+
when :ipv4
|
|
70
|
+
::OverSIP::SIP::TransportManager.add_connection self, ::OverSIP::SIP::IPv4TlsServer, :ipv4, @remote_ip, @remote_port
|
|
71
|
+
when :ipv6
|
|
72
|
+
::OverSIP::SIP::TransportManager.add_connection self, ::OverSIP::SIP::IPv6TlsServer, :ipv6, @remote_ip, @remote_port
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Update log information.
|
|
76
|
+
remote_desc true
|
|
77
|
+
|
|
78
|
+
# Remove the HAProxy Protocol line from the received data.
|
|
79
|
+
@buffer.read haproxy_protocol_data[0]
|
|
80
|
+
|
|
81
|
+
@state = :headers
|
|
82
|
+
|
|
83
|
+
else
|
|
84
|
+
log_system_error "HAProxy Protocol parsing error, closing connection"
|
|
85
|
+
close_connection_after_writing
|
|
86
|
+
@state = :ignore
|
|
87
|
+
return false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# TODO: Not terminated yet.
|
|
92
|
+
def parse_client_pems
|
|
93
|
+
# TODO: Wrong, it could occur that here the last PEMs byte arries.
|
|
94
|
+
return false if @buffer.size < 3 # 3 bytes = 0\r\n (minimum data).
|
|
95
|
+
|
|
96
|
+
@pems_str ||= ""
|
|
97
|
+
@pems_str << @buffer.read(2)
|
|
98
|
+
|
|
99
|
+
# No PEMS.
|
|
100
|
+
if @pems_str == "\r\n"
|
|
101
|
+
@state = :headers
|
|
102
|
+
return true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#@pem_size_str =
|
|
106
|
+
|
|
107
|
+
@state = :headers
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module OverSIP::SIP
|
|
2
|
+
|
|
3
|
+
class TlsTunnelServer < TlsTunnelReactor
|
|
4
|
+
|
|
5
|
+
attr_reader :outbound_flow_token
|
|
6
|
+
|
|
7
|
+
def post_connection
|
|
8
|
+
begin
|
|
9
|
+
# Temporal @remote_ip and @remote_port until the HAProxy protocol line is parsed.
|
|
10
|
+
@remote_port, @remote_ip = ::Socket.unpack_sockaddr_in(get_peername)
|
|
11
|
+
rescue => e
|
|
12
|
+
log_system_error "error obtaining remote IP/port (#{e.class}: #{e.message}), closing connection"
|
|
13
|
+
close_connection
|
|
14
|
+
@state = :ignore
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
log_system_debug "connection from the TLS tunnel " << remote_desc
|
|
19
|
+
|
|
20
|
+
# Create an Outbound (RFC 5626) flow token for this connection.
|
|
21
|
+
@outbound_flow_token = ::OverSIP::SIP::TransportManager.add_outbound_connection self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def remote_desc force=nil
|
|
25
|
+
if force
|
|
26
|
+
@remote_desc = case @remote_ip_type
|
|
27
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
28
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
@remote_desc ||= case self.class.ip_type
|
|
32
|
+
when :ipv4 ; "#{@remote_ip}:#{@remote_port.to_s}"
|
|
33
|
+
when :ipv6 ; "[#{@remote_ip}]:#{@remote_port.to_s}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def unbind cause=nil
|
|
40
|
+
@state = :ignore
|
|
41
|
+
|
|
42
|
+
# Remove the connection.
|
|
43
|
+
self.class.connections.delete @connection_id if @connection_id
|
|
44
|
+
|
|
45
|
+
# Remove the Outbound token flow.
|
|
46
|
+
::OverSIP::SIP::TransportManager.delete_outbound_connection @outbound_flow_token
|
|
47
|
+
|
|
48
|
+
@local_closed = true if cause == ::Errno::ETIMEDOUT
|
|
49
|
+
|
|
50
|
+
if $oversip_debug
|
|
51
|
+
log_msg = "connection from the TLS tunnel #{remote_desc} "
|
|
52
|
+
log_msg << ( @local_closed ? "locally closed" : "remotely closed" )
|
|
53
|
+
log_msg << " (cause: #{cause.inspect})" if cause
|
|
54
|
+
log_system_debug log_msg
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|