oversip_p 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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