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