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,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
|