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.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +22 -0
  3. data/LICENSE +25 -0
  4. data/README.md +43 -0
  5. data/Rakefile +54 -0
  6. data/bin/oversip +184 -0
  7. data/etc/oversip.conf +274 -0
  8. data/etc/proxies.conf +145 -0
  9. data/etc/server.rb +315 -0
  10. data/etc/tls/ca/cacert.pem +3894 -0
  11. data/etc/tls/demo-tls.oversip.net.crt +17 -0
  12. data/etc/tls/demo-tls.oversip.net.key +15 -0
  13. data/etc/tls/upgrade-cacert.sh +12 -0
  14. data/etc/tls/utils/create-cert.rb +162 -0
  15. data/etc/tls/utils/get-sip-identities.rb +95 -0
  16. data/ext/common/c_util.h +74 -0
  17. data/ext/common/ruby_c_util.h +88 -0
  18. data/ext/sip_parser/common_headers.h +210 -0
  19. data/ext/sip_parser/ext_help.h +18 -0
  20. data/ext/sip_parser/extconf.rb +3 -0
  21. data/ext/sip_parser/sip_message_parser.c +29741 -0
  22. data/ext/sip_parser/sip_parser.h +250 -0
  23. data/ext/sip_parser/sip_parser_ruby.c +1370 -0
  24. data/ext/sip_parser/sip_uri_parser.c +39699 -0
  25. data/ext/stud/extconf.rb +43 -0
  26. data/ext/stun/ext_help.h +16 -0
  27. data/ext/stun/extconf.rb +3 -0
  28. data/ext/stun/stun_ruby.c +394 -0
  29. data/ext/utils/ext_help.h +14 -0
  30. data/ext/utils/extconf.rb +3 -0
  31. data/ext/utils/haproxy_protocol.c +6163 -0
  32. data/ext/utils/haproxy_protocol.h +27 -0
  33. data/ext/utils/ip_utils.c +5952 -0
  34. data/ext/utils/ip_utils.h +64 -0
  35. data/ext/utils/outbound_utils.c +3227 -0
  36. data/ext/utils/outbound_utils.h +27 -0
  37. data/ext/utils/utils_ruby.c +392 -0
  38. data/ext/utils/utils_ruby.h +76 -0
  39. data/ext/websocket_framing_utils/ext_help.h +18 -0
  40. data/ext/websocket_framing_utils/extconf.rb +3 -0
  41. data/ext/websocket_framing_utils/ws_framing_utils.h +47 -0
  42. data/ext/websocket_framing_utils/ws_framing_utils_ruby.c +135 -0
  43. data/ext/websocket_http_parser/ext_help.h +18 -0
  44. data/ext/websocket_http_parser/extconf.rb +3 -0
  45. data/ext/websocket_http_parser/ws_http_parser.c +1635 -0
  46. data/ext/websocket_http_parser/ws_http_parser.h +87 -0
  47. data/ext/websocket_http_parser/ws_http_parser_ruby.c +630 -0
  48. data/lib/oversip/config.rb +597 -0
  49. data/lib/oversip/config_validators.rb +126 -0
  50. data/lib/oversip/default_server.rb +52 -0
  51. data/lib/oversip/errors.rb +10 -0
  52. data/lib/oversip/fiber_pool.rb +56 -0
  53. data/lib/oversip/launcher.rb +635 -0
  54. data/lib/oversip/logger.rb +84 -0
  55. data/lib/oversip/modules/outbound_mangling.rb +56 -0
  56. data/lib/oversip/modules/user_assertion.rb +73 -0
  57. data/lib/oversip/proxies_config.rb +189 -0
  58. data/lib/oversip/ruby_ext/eventmachine.rb +38 -0
  59. data/lib/oversip/sip/client.rb +428 -0
  60. data/lib/oversip/sip/client_transaction.rb +586 -0
  61. data/lib/oversip/sip/constants.rb +88 -0
  62. data/lib/oversip/sip/core.rb +217 -0
  63. data/lib/oversip/sip/launcher.rb +221 -0
  64. data/lib/oversip/sip/listeners/connection.rb +54 -0
  65. data/lib/oversip/sip/listeners/ipv4_tcp_client.rb +21 -0
  66. data/lib/oversip/sip/listeners/ipv4_tcp_server.rb +22 -0
  67. data/lib/oversip/sip/listeners/ipv4_tls_client.rb +21 -0
  68. data/lib/oversip/sip/listeners/ipv4_tls_server.rb +22 -0
  69. data/lib/oversip/sip/listeners/ipv4_tls_tunnel_server.rb +22 -0
  70. data/lib/oversip/sip/listeners/ipv4_udp_server.rb +21 -0
  71. data/lib/oversip/sip/listeners/ipv6_tcp_client.rb +21 -0
  72. data/lib/oversip/sip/listeners/ipv6_tcp_server.rb +22 -0
  73. data/lib/oversip/sip/listeners/ipv6_tls_client.rb +21 -0
  74. data/lib/oversip/sip/listeners/ipv6_tls_server.rb +22 -0
  75. data/lib/oversip/sip/listeners/ipv6_tls_tunnel_server.rb +22 -0
  76. data/lib/oversip/sip/listeners/ipv6_udp_server.rb +21 -0
  77. data/lib/oversip/sip/listeners/tcp_client.rb +97 -0
  78. data/lib/oversip/sip/listeners/tcp_connection.rb +202 -0
  79. data/lib/oversip/sip/listeners/tcp_server.rb +71 -0
  80. data/lib/oversip/sip/listeners/tls_client.rb +125 -0
  81. data/lib/oversip/sip/listeners/tls_server.rb +88 -0
  82. data/lib/oversip/sip/listeners/tls_tunnel_connection.rb +89 -0
  83. data/lib/oversip/sip/listeners/tls_tunnel_server.rb +61 -0
  84. data/lib/oversip/sip/listeners/udp_connection.rb +214 -0
  85. data/lib/oversip/sip/listeners.rb +24 -0
  86. data/lib/oversip/sip/message.rb +177 -0
  87. data/lib/oversip/sip/message_processor.rb +213 -0
  88. data/lib/oversip/sip/name_addr.rb +51 -0
  89. data/lib/oversip/sip/proxy.rb +324 -0
  90. data/lib/oversip/sip/request.rb +179 -0
  91. data/lib/oversip/sip/response.rb +37 -0
  92. data/lib/oversip/sip/rfc3263.rb +643 -0
  93. data/lib/oversip/sip/server_transaction.rb +295 -0
  94. data/lib/oversip/sip/sip.rb +76 -0
  95. data/lib/oversip/sip/tags.rb +39 -0
  96. data/lib/oversip/sip/timers.rb +55 -0
  97. data/lib/oversip/sip/transport_manager.rb +130 -0
  98. data/lib/oversip/sip/uac.rb +89 -0
  99. data/lib/oversip/sip/uac_request.rb +84 -0
  100. data/lib/oversip/sip/uri.rb +208 -0
  101. data/lib/oversip/syslog.rb +68 -0
  102. data/lib/oversip/system_callbacks.rb +45 -0
  103. data/lib/oversip/tls.rb +172 -0
  104. data/lib/oversip/utils.rb +30 -0
  105. data/lib/oversip/version.rb +21 -0
  106. data/lib/oversip/websocket/constants.rb +55 -0
  107. data/lib/oversip/websocket/http_request.rb +59 -0
  108. data/lib/oversip/websocket/launcher.rb +183 -0
  109. data/lib/oversip/websocket/listeners/connection.rb +51 -0
  110. data/lib/oversip/websocket/listeners/ipv4_ws_server.rb +22 -0
  111. data/lib/oversip/websocket/listeners/ipv4_wss_server.rb +22 -0
  112. data/lib/oversip/websocket/listeners/ipv4_wss_tunnel_server.rb +22 -0
  113. data/lib/oversip/websocket/listeners/ipv6_ws_server.rb +22 -0
  114. data/lib/oversip/websocket/listeners/ipv6_wss_server.rb +22 -0
  115. data/lib/oversip/websocket/listeners/ipv6_wss_tunnel_server.rb +22 -0
  116. data/lib/oversip/websocket/listeners/ws_server.rb +331 -0
  117. data/lib/oversip/websocket/listeners/wss_server.rb +88 -0
  118. data/lib/oversip/websocket/listeners/wss_tunnel_server.rb +133 -0
  119. data/lib/oversip/websocket/listeners.rb +13 -0
  120. data/lib/oversip/websocket/websocket.rb +13 -0
  121. data/lib/oversip/websocket/ws_framing.rb +545 -0
  122. data/lib/oversip/websocket/ws_sip_app.rb +120 -0
  123. data/lib/oversip.rb +127 -0
  124. data/test/oversip_test_helper.rb +19 -0
  125. data/test/test_http_parser.rb +73 -0
  126. data/test/test_name_addr.rb +27 -0
  127. data/test/test_name_addr_parser.rb +24 -0
  128. data/test/test_sip_message_parser.rb +168 -0
  129. data/test/test_sip_uri_parser.rb +56 -0
  130. data/test/test_uri.rb +68 -0
  131. data/thirdparty/stud/stud.tar.gz +0 -0
  132. 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
@@ -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