oversip_p 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,84 @@
|
|
1
|
+
module OverSIP::SIP
|
2
|
+
|
3
|
+
class UacRequest
|
4
|
+
|
5
|
+
DEFAULT_MAX_FORWARDS = "20"
|
6
|
+
DEFAULT_FROM = "\"OverSIP #{::OverSIP::VERSION}\" <sip:uac@oversip.net>"
|
7
|
+
|
8
|
+
attr_reader :sip_method, :ruri, :from, :from_tag, :to, :body, :call_id, :cseq
|
9
|
+
attr_reader :antiloop_id, :via_branch_id
|
10
|
+
attr_reader :routes # Always nil (needed for OverSIP::SIP::Tags.create_antiloop_id().
|
11
|
+
attr_accessor :tvars # Transaction variables (a hash).
|
12
|
+
|
13
|
+
|
14
|
+
def initialize data, extra_headers=[], body=nil
|
15
|
+
unless (@sip_method = data[:sip_method])
|
16
|
+
raise ::OverSIP::RuntimeError, "no data[:sip_method] given"
|
17
|
+
end
|
18
|
+
unless (ruri = data[:ruri])
|
19
|
+
raise ::OverSIP::RuntimeError, "no data[:ruri] given"
|
20
|
+
end
|
21
|
+
|
22
|
+
case ruri
|
23
|
+
when ::OverSIP::SIP::Uri, ::OverSIP::SIP::NameAddr
|
24
|
+
@ruri = ruri
|
25
|
+
when ::String
|
26
|
+
@ruri = OverSIP::SIP::Uri.parse ruri
|
27
|
+
else
|
28
|
+
raise ::OverSIP::RuntimeError, "invalid URI #{ruri.inspect}"
|
29
|
+
end
|
30
|
+
|
31
|
+
@from = data[:from] || DEFAULT_FROM
|
32
|
+
@from_tag = data[:from_tag] || ::SecureRandom.hex(4)
|
33
|
+
@to = data[:to] || @ruri
|
34
|
+
@call_id = data[:call_id] || ::SecureRandom.hex(8)
|
35
|
+
@cseq = data[:cseq] || rand(1000)
|
36
|
+
@max_forwards = data[:max_forwards] || DEFAULT_MAX_FORWARDS
|
37
|
+
|
38
|
+
@headers = {}
|
39
|
+
@extra_headers = extra_headers
|
40
|
+
|
41
|
+
@body = body
|
42
|
+
|
43
|
+
@antiloop_id = ::OverSIP::SIP::Tags.create_antiloop_id(self)
|
44
|
+
@via_branch_id = ::SecureRandom.hex(4)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def insert_header name, value
|
49
|
+
@headers[name] = value.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def delete_header_top name
|
54
|
+
@headers.delete name
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
msg = "#{@sip_method.to_s} #{@ruri.uri} SIP/2.0\r\n"
|
60
|
+
|
61
|
+
@headers.each do |name, value|
|
62
|
+
msg << name << ": #{value}\r\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
msg << "From: #{@from.to_s};tag=#{@from_tag}\r\n"
|
66
|
+
msg << "To: #{@to.to_s}\r\n"
|
67
|
+
msg << "Call-ID: #{@call_id}\r\n"
|
68
|
+
msg << "CSeq: #{@cseq.to_s} #{@sip_method.to_s}\r\n"
|
69
|
+
msg << "Content-Length: #{@body ? @body.bytesize : "0"}\r\n"
|
70
|
+
msg << "Max-Forwards: #{@max_forwards.to_s}\r\n"
|
71
|
+
msg << HDR_USER_AGENT << CRLF
|
72
|
+
|
73
|
+
@extra_headers.each do |header|
|
74
|
+
msg << header << CRLF
|
75
|
+
end
|
76
|
+
|
77
|
+
msg << CRLF
|
78
|
+
msg << @body if @body
|
79
|
+
msg
|
80
|
+
end
|
81
|
+
|
82
|
+
end # class Request
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module OverSIP::SIP
|
2
|
+
|
3
|
+
class Uri
|
4
|
+
attr_reader :scheme, :user, :host, :host_type, :port, :params, :transport_param, :phone_context_param, :ovid_param, :headers
|
5
|
+
|
6
|
+
|
7
|
+
def self.parse value
|
8
|
+
uri = ::OverSIP::SIP::MessageParser.parse_uri value, false
|
9
|
+
raise ::OverSIP::ParsingError, "invalid URI #{value.inspect}" unless uri.is_a? (::OverSIP::SIP::Uri)
|
10
|
+
uri
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def initialize scheme=:sip, user=nil, host=nil, port=nil
|
15
|
+
@scheme = scheme.to_sym
|
16
|
+
@user = user
|
17
|
+
@host = host
|
18
|
+
@host_type = ::OverSIP::Utils.ip_type(host) || :domain if host
|
19
|
+
@port = port
|
20
|
+
|
21
|
+
@uri_modified = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def sip?
|
25
|
+
@scheme == :sip or @scheme == :sips
|
26
|
+
end
|
27
|
+
|
28
|
+
def tel?
|
29
|
+
@scheme == :tel
|
30
|
+
end
|
31
|
+
|
32
|
+
def scheme= value
|
33
|
+
return nil if unknown_scheme?
|
34
|
+
@scheme = value
|
35
|
+
@uri_modified = true
|
36
|
+
end
|
37
|
+
|
38
|
+
def unknown_scheme?
|
39
|
+
not @scheme.is_a? Symbol
|
40
|
+
end
|
41
|
+
|
42
|
+
def user= value
|
43
|
+
return nil if unknown_scheme?
|
44
|
+
@user = value
|
45
|
+
@uri_modified = true
|
46
|
+
end
|
47
|
+
alias :number :user
|
48
|
+
alias :number= :user=
|
49
|
+
|
50
|
+
def host= value
|
51
|
+
return nil if unknown_scheme?
|
52
|
+
@host = value
|
53
|
+
@host_type = ::OverSIP::Utils.ip_type(value) || :domain
|
54
|
+
@uri_modified = true
|
55
|
+
end
|
56
|
+
alias :domain :host
|
57
|
+
alias :domain= :host=
|
58
|
+
|
59
|
+
def host_type= value
|
60
|
+
return nil if unknown_scheme?
|
61
|
+
@host_type = value
|
62
|
+
end
|
63
|
+
|
64
|
+
def port= value
|
65
|
+
return nil if unknown_scheme?
|
66
|
+
@port = value
|
67
|
+
@uri_modified = true
|
68
|
+
end
|
69
|
+
|
70
|
+
def params
|
71
|
+
@params ||= {}
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_param? k
|
75
|
+
return nil if unknown_scheme?
|
76
|
+
params.include? k.to_s.downcase
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_param k
|
80
|
+
return nil if unknown_scheme?
|
81
|
+
params[k.to_s.downcase]
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_param k, v
|
85
|
+
return nil if unknown_scheme?
|
86
|
+
@params ||= {}
|
87
|
+
@params[k.downcase] = v
|
88
|
+
@uri_modified = true
|
89
|
+
end
|
90
|
+
|
91
|
+
def del_param k
|
92
|
+
return nil if unknown_scheme?
|
93
|
+
return false unless @params
|
94
|
+
if @params.include?(k=k.downcase)
|
95
|
+
@uri_modified = true
|
96
|
+
return @params.delete(k)
|
97
|
+
end
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
def clear_params
|
102
|
+
return nil if unknown_scheme?
|
103
|
+
return false unless @params
|
104
|
+
@params.clear
|
105
|
+
@transport_param = nil
|
106
|
+
@phone_context_param = nil
|
107
|
+
@uri_modified = true
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
def transport_param= value
|
112
|
+
return nil unless @scheme == :sip or @scheme == :sips
|
113
|
+
if value
|
114
|
+
@transport_param = value.to_sym
|
115
|
+
set_param "transport", value.to_s
|
116
|
+
else
|
117
|
+
@transport_param = nil
|
118
|
+
del_param "transport"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def phone_context_param= value
|
123
|
+
return nil unless @scheme == :tel
|
124
|
+
if value
|
125
|
+
@phone_context_param = value.to_sym
|
126
|
+
set_param "phone-context", value.to_s
|
127
|
+
else
|
128
|
+
@phone_context_param = nil
|
129
|
+
del_param "phone-context"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def lr_param?
|
134
|
+
@lr_param ? true : false
|
135
|
+
end
|
136
|
+
|
137
|
+
def ob_param?
|
138
|
+
@ob_param ? true : false
|
139
|
+
end
|
140
|
+
|
141
|
+
def headers= value
|
142
|
+
return nil if unknown_scheme?
|
143
|
+
@headers = value
|
144
|
+
@uri_modified = true
|
145
|
+
end
|
146
|
+
|
147
|
+
def uri
|
148
|
+
return @uri unless @uri_modified
|
149
|
+
|
150
|
+
case @scheme
|
151
|
+
when :sip, :sips
|
152
|
+
@uri = @scheme.to_s << ":"
|
153
|
+
( @uri << ::EscapeUtils.escape_uri(@user) << "@" ) if @user
|
154
|
+
@uri << @host
|
155
|
+
( @uri << ":" << @port.to_s ) if @port
|
156
|
+
|
157
|
+
@params.each do |k,v|
|
158
|
+
@uri << ";" << k
|
159
|
+
( @uri << "=" << v.to_s ) if v
|
160
|
+
end if @params
|
161
|
+
|
162
|
+
@uri << @headers if @headers
|
163
|
+
|
164
|
+
when :tel
|
165
|
+
@uri = "tel:"
|
166
|
+
@uri << @user
|
167
|
+
|
168
|
+
@params.each do |k,v|
|
169
|
+
@uri << ";" << k
|
170
|
+
( @uri << "=" << v.to_s ) if v
|
171
|
+
end if @params
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
@uri_modified = false
|
176
|
+
@uri
|
177
|
+
end
|
178
|
+
alias :to_s :uri
|
179
|
+
alias :inspect :uri
|
180
|
+
|
181
|
+
# Returns a String with the AoR of the URI:
|
182
|
+
# - SIP URI: sip:user@domain.
|
183
|
+
# - TEL URI: tel:number
|
184
|
+
# - Others: nil
|
185
|
+
#
|
186
|
+
def aor
|
187
|
+
case @scheme
|
188
|
+
when :sip, :sips
|
189
|
+
aor = "sip:"
|
190
|
+
( aor << ::EscapeUtils.escape_uri(@user) << "@" ) if @user
|
191
|
+
aor << @host
|
192
|
+
|
193
|
+
when :tel
|
194
|
+
aor = "tel:"
|
195
|
+
aor << @user
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
aor
|
200
|
+
end
|
201
|
+
|
202
|
+
def modified?
|
203
|
+
@uri_modified
|
204
|
+
end
|
205
|
+
|
206
|
+
end # class Uri
|
207
|
+
|
208
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module OverSIP
|
2
|
+
|
3
|
+
module Syslog
|
4
|
+
|
5
|
+
SYSLOG_FACILITY_MAPPING = {
|
6
|
+
"kern" => ::Syslog::LOG_KERN,
|
7
|
+
"user" => ::Syslog::LOG_USER,
|
8
|
+
"daemon" => ::Syslog::LOG_DAEMON,
|
9
|
+
"local0" => ::Syslog::LOG_LOCAL0,
|
10
|
+
"local1" => ::Syslog::LOG_LOCAL1,
|
11
|
+
"local2" => ::Syslog::LOG_LOCAL2,
|
12
|
+
"local3" => ::Syslog::LOG_LOCAL3,
|
13
|
+
"local4" => ::Syslog::LOG_LOCAL4,
|
14
|
+
"local5" => ::Syslog::LOG_LOCAL5,
|
15
|
+
"local6" => ::Syslog::LOG_LOCAL6,
|
16
|
+
"local7" => ::Syslog::LOG_LOCAL7
|
17
|
+
}
|
18
|
+
|
19
|
+
SYSLOG_SEVERITY_MAPPING = {
|
20
|
+
"debug" => 0,
|
21
|
+
"info" => 1,
|
22
|
+
"notice" => 2,
|
23
|
+
"warn" => 3,
|
24
|
+
"error" => 4,
|
25
|
+
"crit" => 5,
|
26
|
+
"alert" => 6,
|
27
|
+
"emerg" => 7
|
28
|
+
}
|
29
|
+
|
30
|
+
def self.log level_value, msg, log_id, user
|
31
|
+
user = user ? " [user] " : " "
|
32
|
+
|
33
|
+
msg = case msg
|
34
|
+
when ::String
|
35
|
+
"<#{log_id}>#{user}#{msg}"
|
36
|
+
when ::Exception
|
37
|
+
"<#{log_id}>#{user}#{msg.message} (#{msg.class })\n#{(msg.backtrace || [])[0..3].join("\n")}"
|
38
|
+
else
|
39
|
+
"<#{log_id}>#{user}#{msg.inspect}"
|
40
|
+
end
|
41
|
+
|
42
|
+
msg = msg.gsub(/%/,"%%").gsub(/\x00/,"")
|
43
|
+
|
44
|
+
case level_value
|
45
|
+
when 0
|
46
|
+
::Syslog.debug sprintf("%7s %s", "DEBUG:", msg)
|
47
|
+
when 1
|
48
|
+
::Syslog.info sprintf("%7s %s", "INFO:", msg)
|
49
|
+
when 2
|
50
|
+
::Syslog.notice sprintf("%7s %s", "NOTICE:", msg)
|
51
|
+
when 3
|
52
|
+
::Syslog.warning sprintf("%7s %s", "WARN:", msg)
|
53
|
+
when 4
|
54
|
+
::Syslog.err sprintf("%7s %s", "ERROR:", msg)
|
55
|
+
when 5
|
56
|
+
::Syslog.crit sprintf("%7s %s", "CRIT:", msg)
|
57
|
+
when 6
|
58
|
+
::Syslog.alert sprintf("%7s %s", "ALERT:", msg)
|
59
|
+
when 7
|
60
|
+
::Syslog.emerg sprintf("%7s %s", "EMERG:", msg)
|
61
|
+
else # Shouldn't occur.
|
62
|
+
::Syslog.err sprintf("%7s %s", "UNKNOWN:", msg)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module OverSIP
|
2
|
+
|
3
|
+
# This module is intended for 3rd party modules that need custom code to be
|
4
|
+
# executed when OverSIP is started, reloaded or terminated.
|
5
|
+
#
|
6
|
+
module SystemCallbacks
|
7
|
+
|
8
|
+
extend ::OverSIP::Logger
|
9
|
+
|
10
|
+
@log_id = "SystemCallbacks"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :on_started_callbacks
|
14
|
+
attr_reader :on_terminated_callbacks
|
15
|
+
attr_reader :on_reload_callbacks
|
16
|
+
end
|
17
|
+
|
18
|
+
@on_started_callbacks = []
|
19
|
+
@on_terminated_callbacks = []
|
20
|
+
@on_reload_callbacks = []
|
21
|
+
|
22
|
+
def self.on_started pr=nil, &bl
|
23
|
+
block = pr || bl
|
24
|
+
raise ::ArgumentError, "no block given" unless block.is_a? ::Proc
|
25
|
+
|
26
|
+
@on_started_callbacks << block
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.on_terminated pr=nil, &bl
|
30
|
+
block = pr || bl
|
31
|
+
raise ::ArgumentError, "no block given" unless block.is_a? ::Proc
|
32
|
+
|
33
|
+
@on_terminated_callbacks << block
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.on_reload pr=nil, &bl
|
37
|
+
block = pr || bl
|
38
|
+
raise ::ArgumentError, "no block given" unless block.is_a? ::Proc
|
39
|
+
|
40
|
+
@on_reload_callbacks << block
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/oversip/tls.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module OverSIP
|
2
|
+
|
3
|
+
module TLS
|
4
|
+
|
5
|
+
extend ::OverSIP::Logger
|
6
|
+
|
7
|
+
TLS_PEM_CHAIN_REGEXP = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
|
8
|
+
|
9
|
+
@log_id = "TLS"
|
10
|
+
|
11
|
+
|
12
|
+
def self.module_init
|
13
|
+
configuration = ::OverSIP.configuration
|
14
|
+
if configuration[:tls][:public_cert] and configuration[:tls][:private_cert]
|
15
|
+
log_system_info "TLS enabled"
|
16
|
+
::OverSIP.tls_public_cert = configuration[:tls][:public_cert]
|
17
|
+
::OverSIP.tls_private_cert = configuration[:tls][:private_cert]
|
18
|
+
else
|
19
|
+
log_system_info "TLS disabled"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
if (ca_dir = configuration[:tls][:ca_dir])
|
24
|
+
@store = ::OpenSSL::X509::Store.new
|
25
|
+
num_certs_added = 0
|
26
|
+
|
27
|
+
::Dir.chdir ca_dir
|
28
|
+
ca_files = ::Dir["*"]
|
29
|
+
ca_files.select! { |ca_file| ::File.file?(ca_file) and ::File.readable?(ca_file) }
|
30
|
+
ca_files.each do |ca_file|
|
31
|
+
log_system_info "inspecting CA file '#{ca_file}'..."
|
32
|
+
|
33
|
+
ca_file_content = ::File.read(ca_file)
|
34
|
+
unless ca_file_content.valid_encoding?
|
35
|
+
log_system_error "ignoring '#{ca_file}', invalid symbols found"
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
pems = ca_file_content.scan(TLS_PEM_CHAIN_REGEXP).flatten
|
40
|
+
num_pems = pems.size
|
41
|
+
|
42
|
+
if num_pems == 0
|
43
|
+
log_system_warn "'#{ca_file}': no public certificates found"
|
44
|
+
next
|
45
|
+
end
|
46
|
+
log_system_info "'#{ca_file}': #{num_pems} public certificates found"
|
47
|
+
|
48
|
+
now = ::Time.now
|
49
|
+
certs = []
|
50
|
+
pems.each do |pem|
|
51
|
+
begin
|
52
|
+
certs << ::OpenSSL::X509::Certificate.new(pem)
|
53
|
+
rescue => e
|
54
|
+
log_system_error "ignoring invalid X509 certificate: #{e.message} (#{e.class})"
|
55
|
+
num_pems -= 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
certs.reject! { |cert| cert.not_after < now }
|
60
|
+
if certs.size != num_pems
|
61
|
+
log_system_info "'#{ca_file}': ignoring #{num_pems - certs.size} expired certificates"
|
62
|
+
end
|
63
|
+
|
64
|
+
certs.each do |cert|
|
65
|
+
begin
|
66
|
+
@store.add_cert cert
|
67
|
+
num_certs_added += 1
|
68
|
+
# This occurs when a certificate is repeated.
|
69
|
+
rescue ::OpenSSL::X509::StoreError => e
|
70
|
+
log_system_warn "'#{ca_file}': ignoring certificate: #{e.message} (#{e.class})"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if num_certs_added == 0
|
76
|
+
log_system_notice "zero public certificates found in '#{ca_dir}' directory, disabling TLS validation"
|
77
|
+
@store = nil
|
78
|
+
end
|
79
|
+
log_system_info "#{num_certs_added} public certificates available for TLS validation"
|
80
|
+
end
|
81
|
+
|
82
|
+
end # def self.module_init
|
83
|
+
|
84
|
+
|
85
|
+
# Return an array with the result of the TLS certificate validation as follows:
|
86
|
+
# cert, validated, tls_error, tls_error_string
|
87
|
+
# where:
|
88
|
+
# - cert: the ::OpenSSL::X509::Certificate instance of the first PEM provided by
|
89
|
+
# the peer, nil otherwise.
|
90
|
+
# - validated: true if the given certificate(s) have been validated, false otherwise
|
91
|
+
# and nil if no certificate is provided by peer or no CA's were configured
|
92
|
+
# for TLS validation.
|
93
|
+
# - tls_error: OpenSSL validation error code (Fixnum) in case of validation error.
|
94
|
+
# - tls_error_string: OpenSSL validation error string in case of validation error.
|
95
|
+
def self.validate pems
|
96
|
+
return nil, nil, nil, "no CAs provided, validation disabled" unless @store
|
97
|
+
return nil, false, nil, "no certificate provided by peer" unless pems.any?
|
98
|
+
|
99
|
+
pem = pems.pop
|
100
|
+
intermediate_pems = pems
|
101
|
+
|
102
|
+
begin
|
103
|
+
cert = ::OpenSSL::X509::Certificate.new pem
|
104
|
+
|
105
|
+
if intermediate_pems and intermediate_pems.any?
|
106
|
+
intermediate_certs = []
|
107
|
+
intermediate_pems.each do |pem|
|
108
|
+
intermediate_certs << ::OpenSSL::X509::Certificate.new(pem)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
intermediate_certs = nil
|
112
|
+
end
|
113
|
+
|
114
|
+
if @store.verify cert, intermediate_certs
|
115
|
+
return cert, true
|
116
|
+
else
|
117
|
+
return cert, false, @store.error, @store.error_string
|
118
|
+
end
|
119
|
+
|
120
|
+
rescue => e
|
121
|
+
log_system_error "exception validating a certificate: #{e.class}: #{e.message}"
|
122
|
+
return nil, false, e.class, e.message
|
123
|
+
end
|
124
|
+
end # def self.validate
|
125
|
+
|
126
|
+
|
127
|
+
def self.get_sip_identities cert
|
128
|
+
return [] unless cert
|
129
|
+
|
130
|
+
verify_subjectAltName_DNS = true
|
131
|
+
verify_CN = true
|
132
|
+
subjectAltName_URI_sip_entries = []
|
133
|
+
subjectAltName_DNS_entries = []
|
134
|
+
sip_identities = {}
|
135
|
+
|
136
|
+
cert.extensions.each do |ext|
|
137
|
+
next if ext.oid != "subjectAltName"
|
138
|
+
verify_CN = false
|
139
|
+
|
140
|
+
ext.value.split(/,\s+/).each do |name|
|
141
|
+
if /^URI:sip:([^@]*)/i =~ name
|
142
|
+
verify_subjectAltName_DNS = false
|
143
|
+
subjectAltName_URI_sip_entries << $1.downcase
|
144
|
+
elsif verify_subjectAltName_DNS && /^DNS:(.*)/i =~ name
|
145
|
+
subjectAltName_DNS_entries << $1.downcase
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
unless verify_CN
|
151
|
+
unless verify_subjectAltName_DNS
|
152
|
+
subjectAltName_URI_sip_entries.each {|domain| sip_identities[domain] = true}
|
153
|
+
else
|
154
|
+
subjectAltName_DNS_entries.each {|domain| sip_identities[domain] = true}
|
155
|
+
end
|
156
|
+
|
157
|
+
else
|
158
|
+
cert.subject.to_a.each do |oid, value|
|
159
|
+
if oid == "CN"
|
160
|
+
sip_identities[value.downcase] = true
|
161
|
+
break
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Return an array with the SIP identities (domains) in the certificate.
|
167
|
+
return sip_identities.keys
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module OverSIP
|
2
|
+
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
# It ensures that two identical byte secuences are matched regardless
|
6
|
+
# they have different encoding.
|
7
|
+
# For example in Ruby the following returns false:
|
8
|
+
# "iñaki".force_encoding(::Encoding::BINARY) == "iñaki"
|
9
|
+
def self.string_compare string1, string2
|
10
|
+
string1.to_s.force_encoding(::Encoding::BINARY) == string2.to_s.force_encoding(::Encoding::BINARY)
|
11
|
+
end
|
12
|
+
|
13
|
+
# This avoid "invalid byte sequence in UTF-8" when the directly doing:
|
14
|
+
# string =~ /EXPRESSION/
|
15
|
+
# and string has invalid UTF-8 bytes secuence.
|
16
|
+
# Also avoids "incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string)"
|
17
|
+
# NOTE: expression argument must be a String or a Regexp.
|
18
|
+
def self.regexp_compare string, expression
|
19
|
+
string = string.to_s.force_encoding(::Encoding::BINARY)
|
20
|
+
if expression.is_a? ::Regexp
|
21
|
+
expression = /#{expression.source.force_encoding(::Encoding::BINARY)}/
|
22
|
+
else
|
23
|
+
expression = /#{expression.to_s.force_encoding(::Encoding::BINARY)}/
|
24
|
+
end
|
25
|
+
string =~ expression
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module OverSIP
|
4
|
+
|
5
|
+
module Version
|
6
|
+
MAJOR = 2
|
7
|
+
MINOR = 0
|
8
|
+
TINY = 4
|
9
|
+
DEVEL = nil # Set to nil for stable releases.
|
10
|
+
end
|
11
|
+
|
12
|
+
PROGRAM_NAME = "OverSIP"
|
13
|
+
VERSION = [Version::MAJOR, Version::MINOR, Version::TINY].join(".")
|
14
|
+
VERSION << ".#{Version::DEVEL}" if Version::DEVEL
|
15
|
+
AUTHOR = "Inaki Baz Castillo"
|
16
|
+
AUTHOR_EMAIL = "ibc@aliax.net"
|
17
|
+
HOMEPAGE = "http://oversip.net"
|
18
|
+
year = "2012-2014"
|
19
|
+
DESCRIPTION = "#{PROGRAM_NAME} #{VERSION}\n#{HOMEPAGE}\n#{year}, #{AUTHOR} <#{AUTHOR_EMAIL}>"
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OverSIP::WebSocket
|
2
|
+
|
3
|
+
CRLF = "\r\n"
|
4
|
+
|
5
|
+
REASON_PHRASE = {
|
6
|
+
100 => "Continue",
|
7
|
+
101 => "Switching Protocols",
|
8
|
+
200 => "OK",
|
9
|
+
201 => "Created",
|
10
|
+
202 => "Accepted",
|
11
|
+
203 => "Non-Authoritative Information",
|
12
|
+
204 => "No Content",
|
13
|
+
205 => "Reset Content",
|
14
|
+
206 => "Partial Content",
|
15
|
+
300 => "Multiple Choices",
|
16
|
+
301 => "Moved Permanently",
|
17
|
+
302 => "Found",
|
18
|
+
303 => "See Other",
|
19
|
+
304 => "Not Modified",
|
20
|
+
305 => "Use Proxy",
|
21
|
+
307 => "Temporary Redirect",
|
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
|
+
409 => "Conflict",
|
32
|
+
410 => "Gone",
|
33
|
+
411 => "Length Required",
|
34
|
+
412 => "Precondition Failed",
|
35
|
+
413 => "Request Entity Too Large",
|
36
|
+
414 => "Request-URI Too Long",
|
37
|
+
415 => "Unsupported Media Type",
|
38
|
+
416 => "Requested Range Not Satisfiable",
|
39
|
+
417 => "Expectation Failed",
|
40
|
+
426 => "Upgrade Required", # RFC 2817
|
41
|
+
500 => "Server Internal Error",
|
42
|
+
501 => "Not Implemented",
|
43
|
+
502 => "Bad Gateway",
|
44
|
+
503 => "Service Unavailable",
|
45
|
+
504 => "Gateway Time-out",
|
46
|
+
505 => "HTTP Version Not Supported"
|
47
|
+
}
|
48
|
+
|
49
|
+
REASON_PHRASE_NOT_SET = "Reason Phrase Not Set"
|
50
|
+
|
51
|
+
HDR_SERVER = "Server: #{::OverSIP::PROGRAM_NAME}/#{::OverSIP::VERSION}".freeze
|
52
|
+
|
53
|
+
WS_SIP_PROTOCOL = "sip"
|
54
|
+
|
55
|
+
end
|