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,17 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIICrzCCAhigAwIBAgIET/1hdzANBgkqhkiG9w0BAQUFADBxMR0wGwYDVQQDDBRk
|
|
3
|
+
ZW1vLXRscy5vdmVyc2lwLm5ldDELMAkGA1UEBhMCRVMxEjAQBgNVBAoMCVZlcnNh
|
|
4
|
+
dGljYTEQMA4GA1UECwwHT3ZlclNJUDEdMBsGCgmSJomT8ixkAQMMDWliY0BhbGlh
|
|
5
|
+
eC5uZXQwHhcNMTIwNzEwMTEyMDMyWhcNMTcwNzEwMTEyMDMyWjBxMR0wGwYDVQQD
|
|
6
|
+
DBRkZW1vLXRscy5vdmVyc2lwLm5ldDELMAkGA1UEBhMCRVMxEjAQBgNVBAoMCVZl
|
|
7
|
+
cnNhdGljYTEQMA4GA1UECwwHT3ZlclNJUDEdMBsGCgmSJomT8ixkAQMMDWliY0Bh
|
|
8
|
+
bGlheC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALXVloxokaERx4xL
|
|
9
|
+
0pH4rEe5liijlScKLGFJtpESUiG1pMTtWCxNzNTZ4J6mgdE07umS7567tHAEpRbr
|
|
10
|
+
C+yJ+VzoLNEpOf+x9zm83NTs3xg55SbhfVEL1vQqlnsfr5YG0iTy2znUPM3r3LVS
|
|
11
|
+
rTXz9UsIpnJO9ICvi28wz2a+HgStAgMBAAGjVDBSMAwGA1UdEwQFMAMBAf8wHQYD
|
|
12
|
+
VR0OBBYEFLMUKsF6Xm3uI2UnBTXZihnyOv90MCMGA1UdEQQcMBqGGHNpcDpkZW1v
|
|
13
|
+
LXRscy5vdmVyc2lwLm5ldDANBgkqhkiG9w0BAQUFAAOBgQAPj8XD5/snSPgjJocn
|
|
14
|
+
TpWqbOIbsQBMn11+sRpftf2SsC82wQ4iZy3E1nEDVItO+YzGkxtt2VV+uFoWYNKp
|
|
15
|
+
kzlTJDjvuE2lHwpiWgHIK4qcuC3NBYRKqopfmEtNj2vojQ3DPqyOA5v1YZyGw+SI
|
|
16
|
+
AhSZ8W6FTvfiHCO5ut/d7036VA==
|
|
17
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
2
|
+
MIICWwIBAAKBgQC11ZaMaJGhEceMS9KR+KxHuZYoo5UnCixhSbaRElIhtaTE7Vgs
|
|
3
|
+
TczU2eCepoHRNO7pku+eu7RwBKUW6wvsiflc6CzRKTn/sfc5vNzU7N8YOeUm4X1R
|
|
4
|
+
C9b0KpZ7H6+WBtIk8ts51DzN69y1Uq018/VLCKZyTvSAr4tvMM9mvh4ErQIDAQAB
|
|
5
|
+
AoGAVFzCOmaRmk8ra9YJ3hunoqdiGXy7yJ8ZtBGFGI2NeYJS7eLIU9XMwLxNUI4k
|
|
6
|
+
ELIkXk4Dynt/3bDp/1YR9C6XeFEZkmLcA3jbaX74/mcx6GgeCdAUg1bvrzpSFqyk
|
|
7
|
+
UYUw2ioFTKyfI1Z3VQmtWDxtz9BkHQ7uakOyu/HA8N8m0EECQQDe1velUoiQUTTn
|
|
8
|
+
DlsdrMvwFhBvJHstXzZMA6Rwp7HKwh6kmaqycr4Lv5UZX/6nzpqiM7EO7eCD6l9n
|
|
9
|
+
x7zIoOD5AkEA0OSHbCzJr0Wlxuq8joQe+ZXRz5BS+A3XWLG2ahnw/FdtLuxzNsWi
|
|
10
|
+
PdDiUOO4xdPoVj/9l2xwkPTfDLsxmDRiVQJAWfZ7QBkT3P+L1gQrsM1EAAdIVzZp
|
|
11
|
+
LCYWK5YE2x44Xt0Dtfv7t9Mu+ls7/GSO0HxOXVF1F8vdKiSCo8k1Y+HfMQJAcrLY
|
|
12
|
+
zQP2ph+2+/cOG67eFys1biQP+pYXBWNnBvFBij0y/U3loVB5Wjnk2od/gFhvvVQb
|
|
13
|
+
mVZ4pI9gHex3OdyhlQJAXNLuufGOEjnUC8lsmupiAQApMXscYPP0ahNES+hfX5uS
|
|
14
|
+
ylXyHsIJp7h6tBbK/bv/BW4HYFsWUpLbjl71EC2ymg==
|
|
15
|
+
-----END RSA PRIVATE KEY-----
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# This script downloads the Root CAs list from Mozilla and stores
|
|
4
|
+
# it under ca/ directory for TLS validation.
|
|
5
|
+
# cacert.pem is downloaded from http://curl.haxx.se/docs/caextract.html
|
|
6
|
+
# (the exact link is http://curl.haxx.se/ca/cacert.pem).
|
|
7
|
+
# cacert.pem is just downloaded in case the server version is newer than
|
|
8
|
+
# the local version of the file.
|
|
9
|
+
|
|
10
|
+
cd ca/
|
|
11
|
+
wget -N http://curl.haxx.se/ca/cacert.pem
|
|
12
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "socket"
|
|
5
|
+
require "readline"
|
|
6
|
+
require "term/ansicolor"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module OverSIP
|
|
10
|
+
module Cert
|
|
11
|
+
|
|
12
|
+
class Error < ::StandardError ; end
|
|
13
|
+
|
|
14
|
+
extend Term::ANSIColor
|
|
15
|
+
|
|
16
|
+
def self.create_cert
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
puts
|
|
20
|
+
puts bold(green("OverSIP TLS Certificate Generator"))
|
|
21
|
+
|
|
22
|
+
puts
|
|
23
|
+
puts bold("Certificate informational fields.")
|
|
24
|
+
|
|
25
|
+
ca = OpenSSL::X509::Name.new
|
|
26
|
+
|
|
27
|
+
cert_common_name = Readline.readline("- Common Name (eg, your name or your server's hostname): ").downcase.strip
|
|
28
|
+
cert_common_name = nil if cert_common_name.empty?
|
|
29
|
+
ca.add_entry "CN", cert_common_name if cert_common_name
|
|
30
|
+
|
|
31
|
+
cert_country_code = Readline.readline("- Country Name (2 letter code): ").upcase.strip
|
|
32
|
+
ca.add_entry "C", cert_country_code unless cert_country_code.empty?
|
|
33
|
+
|
|
34
|
+
cert_state = Readline.readline("- State or Province Name (full name): ").strip
|
|
35
|
+
ca.add_entry "ST", cert_state unless cert_state.empty?
|
|
36
|
+
|
|
37
|
+
cert_locality = Readline.readline("- Locality Name (eg, city): ").strip
|
|
38
|
+
ca.add_entry "L", cert_locality unless cert_locality.empty?
|
|
39
|
+
|
|
40
|
+
cert_organization = Readline.readline("- Organization Name (eg, company): ").strip
|
|
41
|
+
ca.add_entry "O", cert_organization unless cert_organization.empty?
|
|
42
|
+
|
|
43
|
+
cert_organization_unit = Readline.readline("- Organizational Unit Name (eg, section): ").strip
|
|
44
|
+
ca.add_entry "OU", cert_organization_unit unless cert_organization_unit.empty?
|
|
45
|
+
|
|
46
|
+
cert_mail = Readline.readline("- Email: ").strip
|
|
47
|
+
ca.add_entry "mail", cert_mail unless cert_mail.empty?
|
|
48
|
+
|
|
49
|
+
puts
|
|
50
|
+
puts bold("SubjectAltName SIP URI domains. ") + "For each given _domain_ an entry \"URI:sip:_domain_\" will be added to the SubjectAltName field."
|
|
51
|
+
cert_sipuri_domains = Readline.readline("- SubjectAltName SIP URI domains (multiple values separated by space): ").downcase.strip.split
|
|
52
|
+
cert_sipuri_domains = nil if cert_sipuri_domains.empty?
|
|
53
|
+
|
|
54
|
+
puts
|
|
55
|
+
puts bold("SubjectAltName DNS domains. ") + "For each given _domain_ an entry \"DNS:_domain_\" will be added to the SubjectAltName field."
|
|
56
|
+
cert_dns_domains = Readline.readline("- SubjectAltName DNS domains (multiple values separated by space): ").downcase.strip.split
|
|
57
|
+
cert_dns_domains = nil if cert_dns_domains.empty?
|
|
58
|
+
|
|
59
|
+
puts
|
|
60
|
+
puts bold("Signing data.")
|
|
61
|
+
|
|
62
|
+
rsa_key_bits = Readline.readline("- RSA key bits (1024/2048/4096) [1024]: ").strip.to_i
|
|
63
|
+
unless rsa_key_bits.zero?
|
|
64
|
+
unless [1024, 2048, 4096].include? rsa_key_bits
|
|
65
|
+
raise OverSIP::Cert::Error, "invalid number of bits (#{rsa_key_bits}) for RSA key"
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
rsa_key_bits = 1024
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
key = OpenSSL::PKey::RSA.generate(rsa_key_bits)
|
|
72
|
+
|
|
73
|
+
cert = OpenSSL::X509::Certificate.new
|
|
74
|
+
cert.version = 2
|
|
75
|
+
cert.subject = ca
|
|
76
|
+
cert.issuer = ca
|
|
77
|
+
cert.serial = Time.now.to_i
|
|
78
|
+
cert.public_key = key.public_key
|
|
79
|
+
|
|
80
|
+
years_to_expire = Readline.readline("- Expiration (in years from now) [1]: ").strip.to_i
|
|
81
|
+
years_to_expire = 1 if years_to_expire.zero?
|
|
82
|
+
cert.not_after = Time.now + (years_to_expire * 365 * 24 * 60 * 60)
|
|
83
|
+
cert.not_before = Time.now - (24 * 60 * 60)
|
|
84
|
+
|
|
85
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
|
86
|
+
factory.subject_certificate = cert
|
|
87
|
+
factory.issuer_certificate = cert
|
|
88
|
+
|
|
89
|
+
subject_alt_name_fields = []
|
|
90
|
+
|
|
91
|
+
cert_sipuri_domains.each do |sipuri_domain|
|
|
92
|
+
subject_alt_name_fields.<< "URI:sip:#{sipuri_domain}"
|
|
93
|
+
end if cert_sipuri_domains
|
|
94
|
+
|
|
95
|
+
cert_dns_domains.each do |dns_domain|
|
|
96
|
+
subject_alt_name_fields.<< "DNS:#{dns_domain}"
|
|
97
|
+
end if cert_dns_domains
|
|
98
|
+
|
|
99
|
+
extensions = {
|
|
100
|
+
"basicConstraints" => "CA:TRUE",
|
|
101
|
+
"subjectKeyIdentifier" => "hash"
|
|
102
|
+
}
|
|
103
|
+
if subject_alt_name_fields.any?
|
|
104
|
+
extensions["subjectAltName"] = subject_alt_name_fields.join(",")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
cert.extensions = extensions.map {|k,v| factory.create_ext(k,v) }
|
|
108
|
+
|
|
109
|
+
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
|
110
|
+
|
|
111
|
+
puts
|
|
112
|
+
puts bold("File name. ") + "For the given _name_ a public certificate _name_.crt and a private key _name_.key will be created. Also a file _name_.key.crt containing both the public certificate and the private key will be created."
|
|
113
|
+
file_name = Readline.readline("- File name [#{cert_common_name}]: ").strip
|
|
114
|
+
file_name = cert_common_name if file_name.empty?
|
|
115
|
+
unless file_name
|
|
116
|
+
raise OverSIP::Cert::Error, "a file name must be set"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
puts
|
|
120
|
+
|
|
121
|
+
# Make two files:
|
|
122
|
+
# - file_name.crt => public certificate.
|
|
123
|
+
# - file_name.key => private key.
|
|
124
|
+
{"key" => key, "crt" => cert}.each_pair do |ext, o|
|
|
125
|
+
name = "#{file_name}.#{ext}"
|
|
126
|
+
File.open(name, "w") {|f| f.write(o.to_pem) }
|
|
127
|
+
File.chmod(0600, name) if ext == "key"
|
|
128
|
+
|
|
129
|
+
case ext
|
|
130
|
+
when "key"
|
|
131
|
+
puts yellow(">> private key generated in file '#{bold("#{name}")}'")
|
|
132
|
+
when "crt"
|
|
133
|
+
puts yellow(">> public certificate generated in file '#{bold("#{name}")}'")
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Make a single file containing both the public certificate and the private key.
|
|
138
|
+
name = "#{file_name}.key.crt"
|
|
139
|
+
File.open(name, "w") do |f|
|
|
140
|
+
f.write(cert.to_pem)
|
|
141
|
+
f.write(key.to_pem)
|
|
142
|
+
end
|
|
143
|
+
File.chmod(0600, name)
|
|
144
|
+
puts yellow(">> public certificate + private key generated in file '#{bold("#{name}")}'")
|
|
145
|
+
|
|
146
|
+
rescue ::Interrupt => e
|
|
147
|
+
puts "\n\n" + red("Interrupted")
|
|
148
|
+
exit
|
|
149
|
+
|
|
150
|
+
rescue ::OverSIP::Cert::Error => e
|
|
151
|
+
puts "\n" + bold(red("ERROR: #{e}"))
|
|
152
|
+
exit 1
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end # def create_cert
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
OverSIP::Cert.create_cert
|
|
162
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Runs as follows:
|
|
4
|
+
#
|
|
5
|
+
# ~$ ruby get-sip-identities.rb PEM_FILE
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
require "openssl"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module TLS
|
|
12
|
+
|
|
13
|
+
# Extracts the SIP identities in a public certificate following
|
|
14
|
+
# the mechanism in http://tools.ietf.org/html/rfc5922#section-7.1
|
|
15
|
+
# and returns an array containing them.
|
|
16
|
+
#
|
|
17
|
+
# Arguments:
|
|
18
|
+
# - _cert_: must be a public X.509 certificate in PEM format.
|
|
19
|
+
#
|
|
20
|
+
def self.get_sip_identities cert
|
|
21
|
+
puts "DEBUG: following rules in RFC 5922 \"Domain Certificates in SIP\" section 7.1 \"Finding SIP Identities in a Certificate\""
|
|
22
|
+
verify_subjectAltName_DNS = true
|
|
23
|
+
verify_CN = true
|
|
24
|
+
subjectAltName_URI_sip_entries = []
|
|
25
|
+
subjectAltName_DNS_entries = []
|
|
26
|
+
sip_identities = {}
|
|
27
|
+
|
|
28
|
+
cert.extensions.each do |ext|
|
|
29
|
+
next if ext.oid != "subjectAltName"
|
|
30
|
+
verify_CN = false
|
|
31
|
+
|
|
32
|
+
ext.value.split(/,\s+/).each do |name|
|
|
33
|
+
if /^URI:sip:([^@]*)/i =~ name
|
|
34
|
+
verify_subjectAltName_DNS = false
|
|
35
|
+
subjectAltName_URI_sip_entries << $1.downcase
|
|
36
|
+
elsif verify_subjectAltName_DNS && /^DNS:(.*)/i =~ name
|
|
37
|
+
subjectAltName_DNS_entries << $1.downcase
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
unless verify_CN
|
|
43
|
+
puts "DEBUG: certificate contains 'subjectAltName' extensions, 'CommonName' ignored"
|
|
44
|
+
unless verify_subjectAltName_DNS
|
|
45
|
+
subjectAltName_URI_sip_entries.each {|domain| sip_identities[domain] = true}
|
|
46
|
+
puts "DEBUG: 'subjectAltName' entries of type \"URI:sip:\" found, 'subjectAltName' entries of type \"DNS\" ignored"
|
|
47
|
+
else
|
|
48
|
+
subjectAltName_DNS_entries.each {|domain| sip_identities[domain] = true}
|
|
49
|
+
puts "DEBUG: 'subjectAltName' entries of type \"URI:sip:\" not found, using 'subjectAltName' entries of type \"DNS\""
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
else
|
|
53
|
+
puts "DEBUG: no 'subjectAltName' extension found, using 'CommonName' value"
|
|
54
|
+
cert.subject.to_a.each do |oid, value|
|
|
55
|
+
if oid == "CN"
|
|
56
|
+
sip_identities[value.downcase] = true
|
|
57
|
+
break
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return sip_identities
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
unless (file = ARGV[0])
|
|
69
|
+
$stderr.puts "ERROR: no file given as argument"
|
|
70
|
+
exit false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
unless ::File.file?(file) and ::File.readable?(file)
|
|
74
|
+
$stderr.puts "ERROR: given file is not a readable file"
|
|
75
|
+
exit false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
begin
|
|
79
|
+
cert = ::OpenSSL::X509::Certificate.new(::File.read(file))
|
|
80
|
+
rescue => e
|
|
81
|
+
$stderr.puts "ERROR: cannot get a PEM certificate in the given file: #{e.message} (#{e.class})"
|
|
82
|
+
exit false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
sip_identities = TLS.get_sip_identities cert
|
|
86
|
+
|
|
87
|
+
puts
|
|
88
|
+
if sip_identities.any?
|
|
89
|
+
puts "SIP identities found in the certificate:"
|
|
90
|
+
puts
|
|
91
|
+
sip_identities.each_key {|name| puts " - #{name}"}
|
|
92
|
+
else
|
|
93
|
+
puts "No SIP identities found in the certificate"
|
|
94
|
+
end
|
|
95
|
+
puts
|
data/ext/common/c_util.h
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Generic C functions and macros go here, there are no dependencies
|
|
3
|
+
* on OverSIP internal structures or the Ruby C API in here.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#ifndef c_util_h
|
|
7
|
+
#define c_util_h
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* str_to_int: Given a pointer to char and length returns an int (but just possitive).
|
|
15
|
+
*/
|
|
16
|
+
static int str_to_int(const char* str, size_t len)
|
|
17
|
+
{
|
|
18
|
+
TRACE();
|
|
19
|
+
int number = 0;
|
|
20
|
+
const char *s = str;
|
|
21
|
+
|
|
22
|
+
while (len--) {
|
|
23
|
+
/* Ignore zeroes at the beginning. */
|
|
24
|
+
if (number || *s != '0')
|
|
25
|
+
number = number*10 + (*s)-'0';
|
|
26
|
+
s++;
|
|
27
|
+
}
|
|
28
|
+
return number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* strnchr: Find the first character in a length limited string.
|
|
34
|
+
* @s: The string to be searched
|
|
35
|
+
* @len: The number of characters to be searched
|
|
36
|
+
* @c: The character to search for
|
|
37
|
+
*/
|
|
38
|
+
static char *strnchr(const char *s, size_t len, size_t c)
|
|
39
|
+
{
|
|
40
|
+
TRACE();
|
|
41
|
+
for (; len--; ++s)
|
|
42
|
+
if (*s == (char)c)
|
|
43
|
+
return (char *)s;
|
|
44
|
+
return NULL;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* str_find_upcase: Returns non zero if the string (*str, len) contains at least
|
|
50
|
+
* an upcase letter.
|
|
51
|
+
*/
|
|
52
|
+
static char *str_find_upcase(const char *s, size_t len)
|
|
53
|
+
{
|
|
54
|
+
TRACE();
|
|
55
|
+
for (; len--; ++s)
|
|
56
|
+
if (*s >= 'A' && *s <= 'Z')
|
|
57
|
+
return (char *)s;
|
|
58
|
+
return NULL;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
* capitalizes all lower-case ASCII characters.
|
|
64
|
+
*/
|
|
65
|
+
static void downcase_char(char *c)
|
|
66
|
+
{
|
|
67
|
+
TRACE();
|
|
68
|
+
if (*c >= 'A' && *c <= 'Z')
|
|
69
|
+
*c += 32;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
#endif
|
|
74
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Generic Ruby C functions and macros go here.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#ifndef ruby_c_util_h
|
|
6
|
+
#define ruby_c_util_h
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
#include <ruby.h>
|
|
10
|
+
#include <ruby/encoding.h> /* Required: http://redmine.ruby-lang.org/issues/show/4272 */
|
|
11
|
+
#include "c_util.h"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#define RB_STR_UTF8_NEW(s, len) (rb_enc_str_new(s, len, rb_utf8_encoding()))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* my_rb_str_hex_unescape: Unescapes hexadecimal encoded symbols (%NN).
|
|
19
|
+
*/
|
|
20
|
+
static VALUE my_rb_str_hex_unescape(const char *str, size_t len)
|
|
21
|
+
{
|
|
22
|
+
TRACE();
|
|
23
|
+
/* Check if hexadecimal unescaping is required. */
|
|
24
|
+
if (strnchr(str, len, '%')) {
|
|
25
|
+
char *new_str;
|
|
26
|
+
VALUE str_unescaped;
|
|
27
|
+
|
|
28
|
+
new_str = ALLOC_N(char, len);
|
|
29
|
+
memcpy(new_str, str, len);
|
|
30
|
+
|
|
31
|
+
char *s, *t;
|
|
32
|
+
char hex[3] = {0, 0, 0};
|
|
33
|
+
int i;
|
|
34
|
+
|
|
35
|
+
for (s = t = new_str, i = 0 ; i < len ; s++, i++) {
|
|
36
|
+
if (*s != '%' || !(*(s+1)) || !(*(s+2)))
|
|
37
|
+
*t++ = *s;
|
|
38
|
+
else {
|
|
39
|
+
hex[0] = *(s+1);
|
|
40
|
+
hex[1] = *(s+2);
|
|
41
|
+
*t++ = (strtol(hex, NULL, 16) & 0xFF);
|
|
42
|
+
s += 2;
|
|
43
|
+
len -= 2;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
str_unescaped = RB_STR_UTF8_NEW(new_str, len);
|
|
48
|
+
xfree(new_str);
|
|
49
|
+
return(str_unescaped);
|
|
50
|
+
}
|
|
51
|
+
/* If unescaping is not required, then create a Ruby string with original pointer and length. */
|
|
52
|
+
else
|
|
53
|
+
return(RB_STR_UTF8_NEW(str, len));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* my_rb_str_downcase: Downcases a string formed by simple symbols (ASCII).
|
|
58
|
+
*/
|
|
59
|
+
static VALUE my_rb_str_downcase(const char *str, size_t len)
|
|
60
|
+
{
|
|
61
|
+
TRACE();
|
|
62
|
+
/* Check if there is at least an upcase char. */
|
|
63
|
+
if (str_find_upcase(str, len)) {
|
|
64
|
+
char *new_str;
|
|
65
|
+
VALUE str_downcased;
|
|
66
|
+
|
|
67
|
+
new_str = ALLOC_N(char, len);
|
|
68
|
+
memcpy(new_str, str, len);
|
|
69
|
+
|
|
70
|
+
char *s;
|
|
71
|
+
int i;
|
|
72
|
+
|
|
73
|
+
for (s = new_str, i = 0 ; i < len ; s++, i++)
|
|
74
|
+
if (*s >= 'A' && *s <= 'Z')
|
|
75
|
+
*s += 32;
|
|
76
|
+
|
|
77
|
+
str_downcased = RB_STR_UTF8_NEW(new_str, len);
|
|
78
|
+
xfree(new_str);
|
|
79
|
+
return(str_downcased);
|
|
80
|
+
}
|
|
81
|
+
/* If not, then create a Ruby string with original pointer and length. */
|
|
82
|
+
else
|
|
83
|
+
return(RB_STR_UTF8_NEW(str, len));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#endif
|
|
88
|
+
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#ifndef common_headers_h
|
|
2
|
+
#define common_headers_h
|
|
3
|
+
|
|
4
|
+
#include "../common/c_util.h"
|
|
5
|
+
#include "ruby.h"
|
|
6
|
+
#include <ctype.h> // toupper()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/* There are 20 headers with sort representation. */
|
|
10
|
+
#define NUM_SHORT_HEADERS 20
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
struct common_header_name {
|
|
14
|
+
const signed long len;
|
|
15
|
+
const char *name;
|
|
16
|
+
VALUE value;
|
|
17
|
+
const char short_name;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
struct short_header {
|
|
22
|
+
char abbr;
|
|
23
|
+
VALUE value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* A list of common SIP headers we expect to receive.
|
|
29
|
+
* This allows us to avoid repeatedly creating identical string
|
|
30
|
+
* objects to be used with rb_hash_aset().
|
|
31
|
+
*/
|
|
32
|
+
static struct common_header_name common_headers[] = {
|
|
33
|
+
#define f(N, S) { (sizeof(N) - 1), N, Qnil, S }
|
|
34
|
+
f("Accept", ' '),
|
|
35
|
+
f("Accept-Contact", 'A'),
|
|
36
|
+
f("Accept-Encoding", ' '),
|
|
37
|
+
f("Accept-Language", ' '),
|
|
38
|
+
f("Alert-Info", ' '),
|
|
39
|
+
f("Allow", ' '),
|
|
40
|
+
f("Allow-Events", 'U'),
|
|
41
|
+
f("Authentication-Info", ' '),
|
|
42
|
+
f("Authorization", ' '),
|
|
43
|
+
f("Call-ID", 'I'),
|
|
44
|
+
f("Call-Info", ' '),
|
|
45
|
+
f("Contact", 'M'),
|
|
46
|
+
f("Content-Disposition", ' '),
|
|
47
|
+
f("Content-Encoding", 'E'),
|
|
48
|
+
f("Content-Language", ' '),
|
|
49
|
+
f("Content-Length", 'L'),
|
|
50
|
+
f("Content-Type", 'C'),
|
|
51
|
+
f("CSeq", ' '),
|
|
52
|
+
f("Date", ' '),
|
|
53
|
+
f("Event", 'O'),
|
|
54
|
+
f("Error-Info", ' '),
|
|
55
|
+
f("Expires", ' '),
|
|
56
|
+
f("From", 'F'),
|
|
57
|
+
f("Identity", 'Y'),
|
|
58
|
+
f("Identity-Info", 'N'),
|
|
59
|
+
f("In-Reply-To", ' '),
|
|
60
|
+
f("Max-Forwards", ' '),
|
|
61
|
+
f("Min-Expires", ' '),
|
|
62
|
+
f("MIME-Version", ' '),
|
|
63
|
+
f("Organization", ' '),
|
|
64
|
+
f("Priority", ' '),
|
|
65
|
+
f("Proxy-Authenticate", ' '),
|
|
66
|
+
f("Proxy-Authorization", ' '),
|
|
67
|
+
f("Proxy-Require", ' '),
|
|
68
|
+
f("Record-Route", ' '),
|
|
69
|
+
f("Refer-To", 'R'),
|
|
70
|
+
f("Referred-By", 'B'),
|
|
71
|
+
f("Reject-Contact", 'J'),
|
|
72
|
+
f("Reply-To", ' '),
|
|
73
|
+
f("Request-Disposition", 'D'),
|
|
74
|
+
f("Require", ' '),
|
|
75
|
+
f("Retry-After", ' '),
|
|
76
|
+
f("Route", ' '),
|
|
77
|
+
f("Server", ' '),
|
|
78
|
+
f("Session-Expires", 'X'),
|
|
79
|
+
f("Subject", 'S'),
|
|
80
|
+
f("Supported", 'K'),
|
|
81
|
+
f("Timestamp", ' '),
|
|
82
|
+
f("To", 'T'),
|
|
83
|
+
f("Unsupported", ' '),
|
|
84
|
+
f("User-Agent", ' '),
|
|
85
|
+
f("Via", 'V'),
|
|
86
|
+
f("Warning", ' '),
|
|
87
|
+
f("WWW-Authenticate", ' ')
|
|
88
|
+
# undef f
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/*
|
|
93
|
+
* The list of short headers. This list is filled by the funcion
|
|
94
|
+
* init_short_header_names.
|
|
95
|
+
*/
|
|
96
|
+
static struct short_header short_headers[NUM_SHORT_HEADERS];
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/* this function is not performance-critical, called only at load time */
|
|
100
|
+
static void init_common_headers(void)
|
|
101
|
+
{
|
|
102
|
+
TRACE();
|
|
103
|
+
int i;
|
|
104
|
+
struct common_header_name *cf = common_headers;
|
|
105
|
+
|
|
106
|
+
for(i = ARRAY_SIZE(common_headers); --i >= 0; cf++) {
|
|
107
|
+
cf->value = rb_str_new(cf->name, cf->len);
|
|
108
|
+
cf->value = rb_obj_freeze(cf->value);
|
|
109
|
+
/* This tell Ruby not to GC global variables which refer to Ruby's objects,
|
|
110
|
+
but are not exported to the Ruby world. */
|
|
111
|
+
rb_global_variable(&cf->value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* this funcion fills the list of short headers taken the data from
|
|
116
|
+
* common_headers array.
|
|
117
|
+
*/
|
|
118
|
+
static void init_short_headers(void)
|
|
119
|
+
{
|
|
120
|
+
TRACE();
|
|
121
|
+
int i, j;
|
|
122
|
+
struct common_header_name *cf = common_headers;
|
|
123
|
+
|
|
124
|
+
for(i = ARRAY_SIZE(common_headers), j=0; --i >= 0; cf++) {
|
|
125
|
+
if (cf->short_name != ' ') {
|
|
126
|
+
short_headers[j].abbr = cf->short_name;
|
|
127
|
+
short_headers[j].value = cf->value;
|
|
128
|
+
j++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* this function is called for every header set */
|
|
134
|
+
static VALUE find_common_header_name(const char *name, size_t len)
|
|
135
|
+
{
|
|
136
|
+
TRACE();
|
|
137
|
+
int i;
|
|
138
|
+
struct common_header_name *cf = common_headers;
|
|
139
|
+
|
|
140
|
+
for(i = ARRAY_SIZE(common_headers); --i >= 0; cf++) {
|
|
141
|
+
if (cf->len == (long)len && !strncasecmp(cf->name, name, len))
|
|
142
|
+
return cf->value;
|
|
143
|
+
}
|
|
144
|
+
return Qnil;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* This function is called for every short header found */
|
|
148
|
+
static VALUE find_short_header_name(char abbr)
|
|
149
|
+
{
|
|
150
|
+
TRACE();
|
|
151
|
+
int i;
|
|
152
|
+
struct short_header *sh = short_headers;
|
|
153
|
+
|
|
154
|
+
for(i = ARRAY_SIZE(short_headers); --i >= 0; sh++) {
|
|
155
|
+
if (sh->abbr == toupper(abbr))
|
|
156
|
+
return sh->value;
|
|
157
|
+
}
|
|
158
|
+
return Qnil;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
/* Tries to lookup the header name in a list of well-known headers. If so,
|
|
163
|
+
* returns the retrieved VALUE. It also works for short headers.
|
|
164
|
+
* In case the header is unknown, it normalizes it (by capitalizing the
|
|
165
|
+
* first letter and each letter under a "-" or "_" symbol).
|
|
166
|
+
*/
|
|
167
|
+
static VALUE headerize(const char* hname, size_t hname_len)
|
|
168
|
+
{
|
|
169
|
+
TRACE();
|
|
170
|
+
VALUE headerized;
|
|
171
|
+
char* str;
|
|
172
|
+
int i;
|
|
173
|
+
|
|
174
|
+
/* Header short name. */
|
|
175
|
+
if (hname_len == 1) {
|
|
176
|
+
headerized = find_short_header_name(hname[0]);
|
|
177
|
+
if (NIL_P(headerized)) {
|
|
178
|
+
headerized = rb_str_new(hname, hname_len);
|
|
179
|
+
/* Downcase the header name. */
|
|
180
|
+
downcase_char(RSTRING_PTR(headerized));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Header long name. */
|
|
185
|
+
else {
|
|
186
|
+
headerized = find_common_header_name(hname, hname_len);
|
|
187
|
+
if (NIL_P(headerized)) {
|
|
188
|
+
headerized = rb_str_new(hname, hname_len);
|
|
189
|
+
str = RSTRING_PTR(headerized);
|
|
190
|
+
if (*str >= 'a' && *str <= 'z')
|
|
191
|
+
*str &= ~0x20;
|
|
192
|
+
|
|
193
|
+
for(i = 1; i < hname_len; i++) {
|
|
194
|
+
if (str[i-1] == '-' || str[i-1] == '_') {
|
|
195
|
+
if (str[i] >= 'a' && str[i] <= 'z')
|
|
196
|
+
str[i] &= ~0x20;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
if (str[i] >= 'A' && str[i] <= 'Z')
|
|
200
|
+
str[i] += 32;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return(headerized);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
#endif
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#ifndef ext_help_h
|
|
2
|
+
#define ext_help_h
|
|
3
|
+
|
|
4
|
+
#define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
|
|
5
|
+
#define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
|
|
6
|
+
#define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/* Uncomment for enabling TRACE() function. */
|
|
10
|
+
/*#define DEBUG */
|
|
11
|
+
|
|
12
|
+
#ifdef DEBUG
|
|
13
|
+
#define TRACE() fprintf(stderr, "TRACE: %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
|
|
14
|
+
#else
|
|
15
|
+
#define TRACE()
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
#endif
|