oversip 0.9.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.
- data/AUTHORS.txt +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +16 -0
- data/Rakefile +55 -0
- data/bin/oversip +182 -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 +209 -0
- data/ext/sip_parser/ext_help.h +18 -0
- data/ext/sip_parser/extconf.rb +3 -0
- data/ext/sip_parser/sip_parser.c +29649 -0
- data/ext/sip_parser/sip_parser.h +227 -0
- data/ext/sip_parser/sip_parser_ruby.c +1292 -0
- data/ext/stud/extconf.rb +27 -0
- data/ext/stud/stud.tar.gz +0 -0
- data/ext/stun/ext_help.h +16 -0
- data/ext/stun/extconf.rb +3 -0
- data/ext/stun/stun_ruby.c +391 -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 +61 -0
- data/ext/utils/outbound_utils.c +3227 -0
- data/ext/utils/outbound_utils.h +27 -0
- data/ext/utils/utils_ruby.c +384 -0
- data/ext/utils/utils_ruby.h +75 -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 +46 -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 +2598 -0
- data/ext/websocket_http_parser/ws_http_parser.h +86 -0
- data/ext/websocket_http_parser/ws_http_parser_ruby.c +630 -0
- data/lib/oversip/config.rb +541 -0
- data/lib/oversip/config_validators.rb +126 -0
- data/lib/oversip/errors.rb +7 -0
- data/lib/oversip/fiber_pool.rb +56 -0
- data/lib/oversip/launcher.rb +507 -0
- data/lib/oversip/logger.rb +170 -0
- data/lib/oversip/master_process.rb +67 -0
- data/lib/oversip/posix_mq.rb +121 -0
- data/lib/oversip/proxies_config.rb +169 -0
- data/lib/oversip/ruby_ext/eventmachine.rb +38 -0
- data/lib/oversip/sip/client_transaction.rb +587 -0
- data/lib/oversip/sip/constants.rb +87 -0
- data/lib/oversip/sip/grammar/name_addr.rb +27 -0
- data/lib/oversip/sip/grammar/uri.rb +116 -0
- data/lib/oversip/sip/launcher.rb +180 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tcp_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_tls_tunnel_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv4_udp_server.rb +20 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tcp_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_client.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_tls_tunnel_server.rb +21 -0
- data/lib/oversip/sip/listeners/ipv6_udp_server.rb +20 -0
- data/lib/oversip/sip/listeners/reactor.rb +39 -0
- data/lib/oversip/sip/listeners/tcp_client.rb +73 -0
- data/lib/oversip/sip/listeners/tcp_reactor.rb +185 -0
- data/lib/oversip/sip/listeners/tcp_server.rb +71 -0
- data/lib/oversip/sip/listeners/tls_client.rb +117 -0
- data/lib/oversip/sip/listeners/tls_server.rb +70 -0
- data/lib/oversip/sip/listeners/tls_tunnel_reactor.rb +113 -0
- data/lib/oversip/sip/listeners/tls_tunnel_server.rb +61 -0
- data/lib/oversip/sip/listeners/udp_reactor.rb +213 -0
- data/lib/oversip/sip/listeners.rb +28 -0
- data/lib/oversip/sip/logic.rb +14 -0
- data/lib/oversip/sip/message.rb +168 -0
- data/lib/oversip/sip/message_processor.rb +202 -0
- data/lib/oversip/sip/modules/core.rb +200 -0
- data/lib/oversip/sip/modules/registrar_without_path.rb +75 -0
- data/lib/oversip/sip/modules/user_assertion.rb +123 -0
- data/lib/oversip/sip/proxy.rb +460 -0
- data/lib/oversip/sip/request.rb +128 -0
- data/lib/oversip/sip/response.rb +30 -0
- data/lib/oversip/sip/rfc3263.rb +646 -0
- data/lib/oversip/sip/server_transaction.rb +295 -0
- data/lib/oversip/sip/sip.rb +74 -0
- data/lib/oversip/sip/tags.rb +39 -0
- data/lib/oversip/sip/timers.rb +55 -0
- data/lib/oversip/sip/transport_manager.rb +129 -0
- data/lib/oversip/syslogger_process.rb +119 -0
- data/lib/oversip/tls.rb +179 -0
- data/lib/oversip/utils.rb +25 -0
- data/lib/oversip/version.rb +23 -0
- data/lib/oversip/websocket/constants.rb +56 -0
- data/lib/oversip/websocket/default_policy.rb +19 -0
- data/lib/oversip/websocket/http_request.rb +63 -0
- data/lib/oversip/websocket/launcher.rb +207 -0
- data/lib/oversip/websocket/listeners/ipv4_tcp_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv4_tls_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv4_tls_tunnel_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tcp_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tls_server.rb +15 -0
- data/lib/oversip/websocket/listeners/ipv6_tls_tunnel_server.rb +15 -0
- data/lib/oversip/websocket/listeners/tcp_server.rb +265 -0
- data/lib/oversip/websocket/listeners/tls_server.rb +69 -0
- data/lib/oversip/websocket/listeners/tls_tunnel_server.rb +100 -0
- data/lib/oversip/websocket/listeners.rb +12 -0
- data/lib/oversip/websocket/ws_app.rb +75 -0
- data/lib/oversip/websocket/ws_apps/ipv4_ws_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv4_wss_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv6_ws_sip_app.rb +21 -0
- data/lib/oversip/websocket/ws_apps/ipv6_wss_sip_app.rb +22 -0
- data/lib/oversip/websocket/ws_apps/ws_autobahn_app.rb +23 -0
- data/lib/oversip/websocket/ws_apps/ws_sip_app.rb +156 -0
- data/lib/oversip/websocket/ws_apps.rb +9 -0
- data/lib/oversip/websocket/ws_framing.rb +597 -0
- data/lib/oversip.rb +59 -0
- data/test/oversip_test_helper.rb +20 -0
- data/test/test_http_parser.rb +73 -0
- data/test/test_sip_parser.rb +139 -0
- metadata +256 -0
data/AUTHORS.txt
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2012 Iñaki Baz Castillo <ibc@aliax.net>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
OverSIP
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
**UNDER CONSTRUCTION:** please wait a bit...
|
|
5
|
+
|
|
6
|
+
OverSIP is an async SIP proxy/server programmable in Ruby language.
|
|
7
|
+
|
|
8
|
+
Some features of OverSIP are:
|
|
9
|
+
* SIP transports: UDP, TCP, TLS and WebSocket.
|
|
10
|
+
* Full IPv4 and IPv6 support.
|
|
11
|
+
* RFC 3263: SIP DNS mechanism (NAPTR, SRV, A, AAAA) for failover and load balancing based on DNS.
|
|
12
|
+
* RFC 5626: OverSIP is a perfect Outbound EDGE proxy, including an integrated STUN server.
|
|
13
|
+
* Fully programmable in Ruby language (make SIP easy).
|
|
14
|
+
* Fast and efficient: OverSIP core is coded in C language.
|
|
15
|
+
|
|
16
|
+
OverSIP is build on top of EventMachine async library which follows the Reactor Pattern design, allowing thousands of concurrent connections and requests in a never-blocking fashion.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require "rake/testtask"
|
|
2
|
+
require "rake/clean"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
OVERSIP_EXTENSIONS = [
|
|
6
|
+
{ :dir => "ext/sip_parser", :so => "sip_parser.so", :dest => "lib/oversip/sip" },
|
|
7
|
+
{ :dir => "ext/stun", :so => "stun.so", :dest => "lib/oversip" },
|
|
8
|
+
{ :dir => "ext/utils", :so => "utils.so", :dest => "lib/oversip" },
|
|
9
|
+
{ :dir => "ext/websocket_framing_utils", :so => "ws_framing_utils.so", :dest => "lib/oversip/websocket" },
|
|
10
|
+
{ :dir => "ext/websocket_http_parser", :so => "ws_http_parser.so", :dest => "lib/oversip/websocket" },
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
OVERSIP_EXTENSIONS.each do |ext|
|
|
14
|
+
file ext[:so] => Dir.glob(["#{ext[:dir]}/*{.c,.h}"]) do
|
|
15
|
+
Dir.chdir(ext[:dir]) do
|
|
16
|
+
ruby "extconf.rb"
|
|
17
|
+
sh "make"
|
|
18
|
+
end
|
|
19
|
+
cp "#{ext[:dir]}/#{ext[:so]}", "#{ext[:dest]}/"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
CLEAN.include("#{ext[:dir]}/*{.o,.log,.so,.a}")
|
|
23
|
+
CLEAN.include("#{ext[:dir]}/Makefile")
|
|
24
|
+
CLEAN.include("#{ext[:dest]}/#{ext[:so]}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Stud stuff.
|
|
28
|
+
directory "tmp"
|
|
29
|
+
file "bin/oversip_stud" => "tmp" do
|
|
30
|
+
Dir.chdir("ext/stud") do
|
|
31
|
+
ruby "extconf.rb"
|
|
32
|
+
end
|
|
33
|
+
FileUtils.remove_dir "tmp"
|
|
34
|
+
end
|
|
35
|
+
CLEAN.include("ext/stud/Makefile")
|
|
36
|
+
CLEAN.include("ext/stud/mkmf.log")
|
|
37
|
+
CLEAN.include("bin/oversip_stud")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
OVERSIP_COMPILE_ITEMS = OVERSIP_EXTENSIONS.map {|e| e[:so]} << "bin/oversip_stud"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
task :default => :compile
|
|
44
|
+
|
|
45
|
+
desc "Compile"
|
|
46
|
+
task :compile => OVERSIP_COMPILE_ITEMS
|
|
47
|
+
|
|
48
|
+
Rake::TestTask.new do |t|
|
|
49
|
+
t.libs << "test"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Make the :test task depend on the shared object, so it will be built automatically
|
|
53
|
+
# before running the tests.
|
|
54
|
+
desc "Run tests"
|
|
55
|
+
task :test => OVERSIP_COMPILE_ITEMS
|
data/bin/oversip
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/ruby1.9
|
|
2
|
+
# -*- encoding: binary -*-
|
|
3
|
+
|
|
4
|
+
unless RUBY_VERSION >= "1.9.2"
|
|
5
|
+
raise LoadError, "OverSIP requires Ruby version >= 1.9.2 (current version is #{RUBY_VERSION})"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# First of all, trap some signals in order to ignore them if they arrive while
|
|
10
|
+
# loading server libraries.
|
|
11
|
+
[:HUP, :INT, :USR1, :USR2].each {|signal| trap(signal) {} }
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
require "optparse"
|
|
15
|
+
require "etc"
|
|
16
|
+
require "oversip"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
module OverSIP
|
|
20
|
+
|
|
21
|
+
class Executable
|
|
22
|
+
|
|
23
|
+
@log_id = "executable"
|
|
24
|
+
|
|
25
|
+
def self.run
|
|
26
|
+
::OverSIP::Logger.load_methods
|
|
27
|
+
extend ::OverSIP::Logger
|
|
28
|
+
|
|
29
|
+
# Options by default.
|
|
30
|
+
options = {
|
|
31
|
+
:colorize => true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
OptionParser.new("", 24, " ") do |opts|
|
|
35
|
+
opts.banner = "#{::OverSIP::DESCRIPTION}" \
|
|
36
|
+
"\n\nUsage: #{File.basename(__FILE__)} " \
|
|
37
|
+
"[#{::OverSIP::PROGRAM_NAME} options] [Ruby options]"
|
|
38
|
+
|
|
39
|
+
opts.separator "\n#{::OverSIP::PROGRAM_NAME} options:"
|
|
40
|
+
|
|
41
|
+
opts.on("-P", "--pid FILE", "Create a PID file (required)") do |value|
|
|
42
|
+
options[:pid_file] = value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
opts.on("-p", "--process_name NAME", "Change the running process name, also affects to syslogger process and Posix Message Queue name (default 'oversip')") do |value|
|
|
46
|
+
options[:process_name] = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts.on("-c", "--dir_config DIR", "Absolute path to the directory with user configuration files (default '/etc/oversip/')") do |value|
|
|
50
|
+
options[:dir_config] = value
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on("-u", "--user USER", "System user to run with") do |value|
|
|
54
|
+
options[:user] = value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
opts.on("-g", "--group GROUP", "System group to run with") do |value|
|
|
58
|
+
options[:group] = value
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on("--no-color", "Don't colorize text printed in stdout") do |value|
|
|
62
|
+
options[:colorize] = false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on("--remove-mqueue MQUEUE", "Destroy the Posix Message Queue with the given name and exit") do |value|
|
|
66
|
+
require "posix_mq"
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
POSIX_MQ.unlink value
|
|
70
|
+
rescue ::Errno::ENOENT
|
|
71
|
+
rescue ::Errno::EACCES => e
|
|
72
|
+
fatal "cannot remove '#{value}' posix message queue due file permissions"
|
|
73
|
+
exit 1
|
|
74
|
+
rescue ::Errno::EINVAL => e
|
|
75
|
+
fatal "cannot remove '#{value}' posix message queue, invalid name"
|
|
76
|
+
exit 1
|
|
77
|
+
ensure
|
|
78
|
+
exit
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
opts.separator "\nRuby options:"
|
|
83
|
+
|
|
84
|
+
opts.on("-d", "--debug", "Set debugging flags ($DEBUG = true)") do
|
|
85
|
+
$DEBUG = true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
opts.on("-w", "--warn", "Turn warnings on ($-w = true)") do
|
|
89
|
+
$-w = true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
opts.on("-I", "--include PATH", "Add PATH to $LOAD_PATH (may be used more than once)") do |path|
|
|
93
|
+
$LOAD_PATH.unshift(*path.split(/:/))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
opts.on("-r", "--require LIBRARY", "Load LIBRARY before running the programm (may be used more than once)") do |library|
|
|
97
|
+
require library
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
opts.separator "\nCommon options:"
|
|
101
|
+
|
|
102
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
103
|
+
puts opts.to_s
|
|
104
|
+
exit
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
opts.on_tail("-v", "--version", "Show version") do
|
|
108
|
+
puts ::OverSIP::DESCRIPTION
|
|
109
|
+
exit
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
begin
|
|
113
|
+
opts.parse! ARGV
|
|
114
|
+
rescue ::OptionParser::InvalidOption => e
|
|
115
|
+
log_system_error e.message
|
|
116
|
+
puts
|
|
117
|
+
puts opts.to_s
|
|
118
|
+
exit! 1
|
|
119
|
+
rescue ::OptionParser::MissingArgument => e
|
|
120
|
+
log_system_error e.message
|
|
121
|
+
puts
|
|
122
|
+
puts opts.to_s
|
|
123
|
+
exit! 1
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
log_system_info "#{::OverSIP::PROGRAM_NAME} #{::OverSIP::VERSION} starting..."
|
|
128
|
+
|
|
129
|
+
# Options checks.
|
|
130
|
+
|
|
131
|
+
# PID file is required.
|
|
132
|
+
unless options[:pid_file]
|
|
133
|
+
fatal "PID file is required (use -P or --pid option)"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Ignore user/group if the launcher is not being running as root.
|
|
137
|
+
unless Process.euid == 0
|
|
138
|
+
log_system_warn "ignoring user/group parameters when not running as root"
|
|
139
|
+
options.delete :user
|
|
140
|
+
options.delete :group
|
|
141
|
+
else
|
|
142
|
+
# Get the uid and gid to run with.
|
|
143
|
+
if options[:user]
|
|
144
|
+
begin
|
|
145
|
+
Etc.getpwnam options[:user]
|
|
146
|
+
rescue ArgumentError
|
|
147
|
+
fatal "user '#{options[:user]}' does not exist in the system"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
if options[:group]
|
|
151
|
+
begin
|
|
152
|
+
Etc.getgrnam options[:group]
|
|
153
|
+
rescue ArgumentError
|
|
154
|
+
fatal "group '#{options[:group]}' does not exist in the system"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Set the command name (as it appears in "ps" output) to given --process_name option (-p)
|
|
160
|
+
# or to the script filename otherwise.
|
|
161
|
+
$0 = options[:process_name] || File.basename(__FILE__)
|
|
162
|
+
|
|
163
|
+
OverSIP.master_name = $0
|
|
164
|
+
log_system_info "master process name: #{OverSIP.master_name}"
|
|
165
|
+
|
|
166
|
+
OverSIP::Config.load options[:dir_config]
|
|
167
|
+
log_system_info "applied configuration:"
|
|
168
|
+
OverSIP::Config.print options[:colorize]
|
|
169
|
+
|
|
170
|
+
log_system_info "creating Posix Message Queue for communicating master and syslogger processes"
|
|
171
|
+
OverSIP::Logger.init_logger_mq options[:group]
|
|
172
|
+
OverSIP::Logger.load_methods
|
|
173
|
+
|
|
174
|
+
OverSIP::Launcher.daemonize!(options)
|
|
175
|
+
OverSIP::Launcher.run(options)
|
|
176
|
+
|
|
177
|
+
end # def run
|
|
178
|
+
end # class Executable
|
|
179
|
+
end # module OverSIP
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
::OverSIP::Executable.run
|
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: Unexcapes 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,209 @@
|
|
|
1
|
+
#ifndef common_headers_h
|
|
2
|
+
#define common_headers_h
|
|
3
|
+
|
|
4
|
+
#include "../common/c_util.h"
|
|
5
|
+
#include "ruby.h"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* There are 20 headers with sort representation. */
|
|
9
|
+
#define NUM_SHORT_HEADERS 20
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
struct common_header_name {
|
|
13
|
+
const signed long len;
|
|
14
|
+
const char *name;
|
|
15
|
+
VALUE value;
|
|
16
|
+
const char short_name;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
struct short_header {
|
|
21
|
+
char abbr;
|
|
22
|
+
VALUE value;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* A list of common SIP headers we expect to receive.
|
|
28
|
+
* This allows us to avoid repeatedly creating identical string
|
|
29
|
+
* objects to be used with rb_hash_aset().
|
|
30
|
+
*/
|
|
31
|
+
static struct common_header_name common_headers[] = {
|
|
32
|
+
#define f(N, S) { (sizeof(N) - 1), N, Qnil, S }
|
|
33
|
+
f("Accept", ' '),
|
|
34
|
+
f("Accept-Contact", 'A'),
|
|
35
|
+
f("Accept-Encoding", ' '),
|
|
36
|
+
f("Accept-Language", ' '),
|
|
37
|
+
f("Alert-Info", ' '),
|
|
38
|
+
f("Allow", ' '),
|
|
39
|
+
f("Allow-Events", 'U'),
|
|
40
|
+
f("Authentication-Info", ' '),
|
|
41
|
+
f("Authorization", ' '),
|
|
42
|
+
f("Call-ID", 'I'),
|
|
43
|
+
f("Call-Info", ' '),
|
|
44
|
+
f("Contact", 'M'),
|
|
45
|
+
f("Content-Disposition", ' '),
|
|
46
|
+
f("Content-Encoding", 'E'),
|
|
47
|
+
f("Content-Language", ' '),
|
|
48
|
+
f("Content-Length", 'L'),
|
|
49
|
+
f("Content-Type", 'C'),
|
|
50
|
+
f("CSeq", ' '),
|
|
51
|
+
f("Date", ' '),
|
|
52
|
+
f("Event", 'O'),
|
|
53
|
+
f("Error-Info", ' '),
|
|
54
|
+
f("Expires", ' '),
|
|
55
|
+
f("From", 'F'),
|
|
56
|
+
f("Identity", 'Y'),
|
|
57
|
+
f("Identity-Info", 'N'),
|
|
58
|
+
f("In-Reply-To", ' '),
|
|
59
|
+
f("Max-Forwards", ' '),
|
|
60
|
+
f("Min-Expires", ' '),
|
|
61
|
+
f("MIME-Version", ' '),
|
|
62
|
+
f("Organization", ' '),
|
|
63
|
+
f("Priority", ' '),
|
|
64
|
+
f("Proxy-Authenticate", ' '),
|
|
65
|
+
f("Proxy-Authorization", ' '),
|
|
66
|
+
f("Proxy-Require", ' '),
|
|
67
|
+
f("Record-Route", ' '),
|
|
68
|
+
f("Refer-To", 'R'),
|
|
69
|
+
f("Referred-By", 'B'),
|
|
70
|
+
f("Reject-Contact", 'J'),
|
|
71
|
+
f("Reply-To", ' '),
|
|
72
|
+
f("Request-Disposition", 'D'),
|
|
73
|
+
f("Require", ' '),
|
|
74
|
+
f("Retry-After", ' '),
|
|
75
|
+
f("Route", ' '),
|
|
76
|
+
f("Server", ' '),
|
|
77
|
+
f("Session-Expires", 'X'),
|
|
78
|
+
f("Subject", 'S'),
|
|
79
|
+
f("Supported", 'K'),
|
|
80
|
+
f("Timestamp", ' '),
|
|
81
|
+
f("To", 'T'),
|
|
82
|
+
f("Unsupported", ' '),
|
|
83
|
+
f("User-Agent", ' '),
|
|
84
|
+
f("Via", 'V'),
|
|
85
|
+
f("Warning", ' '),
|
|
86
|
+
f("WWW-Authenticate", ' ')
|
|
87
|
+
# undef f
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* The list of short headers. This list is filled by the funcion
|
|
93
|
+
* init_short_header_names.
|
|
94
|
+
*/
|
|
95
|
+
static struct short_header short_headers[NUM_SHORT_HEADERS];
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/* this function is not performance-critical, called only at load time */
|
|
99
|
+
static void init_common_headers(void)
|
|
100
|
+
{
|
|
101
|
+
TRACE();
|
|
102
|
+
int i;
|
|
103
|
+
struct common_header_name *cf = common_headers;
|
|
104
|
+
|
|
105
|
+
for(i = ARRAY_SIZE(common_headers); --i >= 0; cf++) {
|
|
106
|
+
cf->value = rb_str_new(cf->name, cf->len);
|
|
107
|
+
cf->value = rb_obj_freeze(cf->value);
|
|
108
|
+
/* This tell Ruby not to GC global variables which refer to Ruby's objects,
|
|
109
|
+
but are not exported to the Ruby world. */
|
|
110
|
+
rb_global_variable(&cf->value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* this funcion fills the list of short headers taken the data from
|
|
115
|
+
* common_headers array.
|
|
116
|
+
*/
|
|
117
|
+
static void init_short_headers(void)
|
|
118
|
+
{
|
|
119
|
+
TRACE();
|
|
120
|
+
int i, j;
|
|
121
|
+
struct common_header_name *cf = common_headers;
|
|
122
|
+
|
|
123
|
+
for(i = ARRAY_SIZE(common_headers), j=0; --i >= 0; cf++) {
|
|
124
|
+
if (cf->short_name != ' ') {
|
|
125
|
+
short_headers[j].abbr = cf->short_name;
|
|
126
|
+
short_headers[j].value = cf->value;
|
|
127
|
+
j++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* this function is called for every header set */
|
|
133
|
+
static VALUE find_common_header_name(const char *name, size_t len)
|
|
134
|
+
{
|
|
135
|
+
TRACE();
|
|
136
|
+
int i;
|
|
137
|
+
struct common_header_name *cf = common_headers;
|
|
138
|
+
|
|
139
|
+
for(i = ARRAY_SIZE(common_headers); --i >= 0; cf++) {
|
|
140
|
+
if (cf->len == (long)len && !strncasecmp(cf->name, name, len))
|
|
141
|
+
return cf->value;
|
|
142
|
+
}
|
|
143
|
+
return Qnil;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* This function is called for every short header found */
|
|
147
|
+
static VALUE find_short_header_name(char abbr)
|
|
148
|
+
{
|
|
149
|
+
TRACE();
|
|
150
|
+
int i;
|
|
151
|
+
struct short_header *sh = short_headers;
|
|
152
|
+
|
|
153
|
+
for(i = ARRAY_SIZE(short_headers); --i >= 0; sh++) {
|
|
154
|
+
if (sh->abbr == toupper(abbr))
|
|
155
|
+
return sh->value;
|
|
156
|
+
}
|
|
157
|
+
return Qnil;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
/* Tries to lookup the header name in a list of well-known headers. If so,
|
|
162
|
+
* returns the retrieved VALUE. It also works for short headers.
|
|
163
|
+
* In case the header is unknown, it normalizes it (by capitalizing the
|
|
164
|
+
* first letter and each letter under a "-" or "_" symbol).
|
|
165
|
+
*/
|
|
166
|
+
static VALUE headerize(const char* hname, size_t hname_len)
|
|
167
|
+
{
|
|
168
|
+
TRACE();
|
|
169
|
+
VALUE headerized;
|
|
170
|
+
char* str;
|
|
171
|
+
int i;
|
|
172
|
+
|
|
173
|
+
/* Header short name. */
|
|
174
|
+
if (hname_len == 1) {
|
|
175
|
+
headerized = find_short_header_name(hname[0]);
|
|
176
|
+
if (NIL_P(headerized)) {
|
|
177
|
+
headerized = rb_str_new(hname, hname_len);
|
|
178
|
+
/* Downcase the header name. */
|
|
179
|
+
downcase_char(RSTRING_PTR(headerized));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Header long name. */
|
|
184
|
+
else {
|
|
185
|
+
headerized = find_common_header_name(hname, hname_len);
|
|
186
|
+
if (NIL_P(headerized)) {
|
|
187
|
+
headerized = rb_str_new(hname, hname_len);
|
|
188
|
+
str = RSTRING_PTR(headerized);
|
|
189
|
+
if (*str >= 'a' && *str <= 'z')
|
|
190
|
+
*str &= ~0x20;
|
|
191
|
+
|
|
192
|
+
for(i = 1; i < hname_len; i++) {
|
|
193
|
+
if (str[i-1] == '-' || str[i-1] == '_') {
|
|
194
|
+
if (str[i] >= 'a' && str[i] <= 'z')
|
|
195
|
+
str[i] &= ~0x20;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (str[i] >= 'A' && str[i] <= 'Z')
|
|
199
|
+
str[i] += 32;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return(headerized);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
#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
|