Moved modules_k/mediaproxy to modules and removed modules_s/mediaproxy.
16 | 16 |
deleted file mode 100644 |
... | ... |
@@ -1,16 +0,0 @@ |
1 |
-# $Id$ |
|
2 |
-# |
|
3 |
-# mediaproxy module makefile |
|
4 |
-# |
|
5 |
-# |
|
6 |
-# WARNING: do not run this directly, it should be run by the master Makefile |
|
7 |
- |
|
8 |
-include ../../Makefile.defs |
|
9 |
-auto_gen= |
|
10 |
-NAME=mediaproxy.so |
|
11 |
-LIBS= |
|
12 |
- |
|
13 |
-DEFS+=-DSER_MOD_INTERFACE |
|
14 |
- |
|
15 |
-include ../../Makefile.modules |
|
16 |
- |
17 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,259 +0,0 @@ |
1 |
- |
|
2 |
- Mediaproxy SER module |
|
3 |
- |
|
4 |
- Copyright Dan Pascu |
|
5 |
- 2002-2004 AG Projects |
|
6 |
- |
|
7 |
- |
|
8 |
- Mediaproxy is a SER module that is designed to allow automatic NAT |
|
9 |
-traversal for the majority of existing SIP clients. This means that there |
|
10 |
-will be no need to configure anything in particular on the NAT box to allow |
|
11 |
-these clients to work behind NAT when using the mediaproxy module. |
|
12 |
- |
|
13 |
-Section 1 for a description of the modus operandi. |
|
14 |
-Section 2 describes the types of SIP phones that work with mediaproxy and a |
|
15 |
- short description about how the NAT traversal is working. |
|
16 |
-Section 3 is about mediaproxy features. |
|
17 |
-Section 4 describes the exported module parameters and functions. |
|
18 |
-Section 5 is a comparison with nathelper. |
|
19 |
- |
|
20 |
-1. Principle of operation |
|
21 |
- ---------------------- |
|
22 |
- |
|
23 |
- This NAT traversal solution operates by placing a mediaproxy server in the |
|
24 |
-middle between 2 SIP user-agents. It mangles the SDP messages for both of |
|
25 |
-them in a way that will make the parties talk with mediaproxy while they |
|
26 |
-think they talk directly with each other. |
|
27 |
- |
|
28 |
-To achieve this, mediaproxy is actually composed by 2 components: |
|
29 |
- - the SER mediaproxy module itself |
|
30 |
- - an external proxy server called SER MediaProxy (available from |
|
31 |
- http://mediaproxy.ag-projects.com/ ) |
|
32 |
- |
|
33 |
-To avoid confusion in this document the mediaproxy module will be called |
|
34 |
-'module' or 'mediaproxy module', while the mediaproxy server will be called |
|
35 |
-'proxy server' from here on. |
|
36 |
- |
|
37 |
- The proxy server can be run on the same machine as the module or on a |
|
38 |
-remote host. Moreover it is possible for a single module to control multiple |
|
39 |
-proxy servers running on multiple geographically distributed hosts. To find |
|
40 |
-out more about the architecture of SER MediaProxy please read the |
|
41 |
-documentation that comes with it. |
|
42 |
- |
|
43 |
- To be able to act as a proxy between the 2 talking parties, the machine(s) |
|
44 |
-running the module/proxy server must have a public IP address. |
|
45 |
- |
|
46 |
- The module will ask the proxy server to allocate as many sockets as there |
|
47 |
-are media streams in the SDP body of the SIP INVITE/Ok messages. The proxy |
|
48 |
-server will send back to the module the address and port(s) for them. Then |
|
49 |
-the module will replace the original contact IP and RTP ports from the SDP |
|
50 |
-messages with the ones provided by the proxy server. By doing this both |
|
51 |
-clients will try to contact the proxy server instead of talking directly |
|
52 |
-with each other. Once the clients contact the proxy server, it will record |
|
53 |
-the addresses they came from and will know where to forward packets received |
|
54 |
-from the other party This is needed because the address/port the NAT box |
|
55 |
-will allocate for the leaving streams is not known before they actually |
|
56 |
-leave the NAT box. However the address of the proxy server is always known |
|
57 |
-(being a public one) so the 2 parties know where to connect and then after |
|
58 |
-they did so, the proxy learns the addresses they came from and can forward |
|
59 |
-packets between them. |
|
60 |
- |
|
61 |
- |
|
62 |
-2. Types of SIP clients |
|
63 |
- -------------------- |
|
64 |
- |
|
65 |
- The SIP clients that will work transparently behind NAT when using the |
|
66 |
-mediaproxy module are the so-called symmetric clients. The symmetric clients |
|
67 |
-have the particularity that use the same port to send the data as the one |
|
68 |
-they use to receive it. In other words, if they are for example configured |
|
69 |
-to use port 5060 for SIP signaling, they will use the same port when sending |
|
70 |
-data as well as when receiving it. This must be true for both the SIP |
|
71 |
-signaling as well as the RTP streams for a client to work transparently with |
|
72 |
-the mediaproxy module without any additional configuration on the NAT box. |
|
73 |
- |
|
74 |
- This ability is important because the only way to get back to a client |
|
75 |
-behind NAT is to send to the IP address and port the packet was received |
|
76 |
-from. Once a packet is sent from the client behind NAT to the outside world, |
|
77 |
-it opens a communication channel in the NAT box that is open in both |
|
78 |
-directions for a while (it will timeout after a while after no more data is |
|
79 |
-sent through it, but it can be kept active by sending data through it at |
|
80 |
-certain regular time intervals). While this channel is open, any data sent |
|
81 |
-to the public address and port that the NAT box assigned for the address and |
|
82 |
-port the client behind NAT is sending from (and this mapping is guaranteed |
|
83 |
-to be unique), will go back straight to the address and port the client has |
|
84 |
-sent from. This is why is necessary for the clients to be symmetric. If they |
|
85 |
-listen on the same port they sent from, the data sent back to the public |
|
86 |
-address that the NAT box assigned to the leaving packets will actually reach |
|
87 |
-the listening port of the client behind NAT. |
|
88 |
- |
|
89 |
- Some SIP clients implement particular algorithms to detect if they are |
|
90 |
-actually behind a NAT box and try to act smart by detecting the IP address |
|
91 |
-of the NAT box (or simply allowing one to manually configure it), and then |
|
92 |
-use this IP address in the SIP and SDP messages instead of their own private |
|
93 |
-IP address. This situation can be confusing for a module that tries to |
|
94 |
-perform transparent NAT traversal as it can wrongly mistake such a client |
|
95 |
-that is behind NAT with a client that is actually in the public address |
|
96 |
-space. However for the mediaproxy module it is not important if the clients |
|
97 |
-apply or not this kind of behavior, as it is able to cope with both |
|
98 |
-situations gracefully. |
|
99 |
- |
|
100 |
- This doesn't mean that mediaproxy is not able to work with asymmetric |
|
101 |
-clients behind NAT, but in their case special static forwarding routes need |
|
102 |
-to be configured on the NAT box. |
|
103 |
- |
|
104 |
- Mediaproxy has special support for asymmetric clients, can detect them and |
|
105 |
-send the data to the ports they expect it to, however they can work behind |
|
106 |
-NAT only if static routes are configured on the NAT box since there is no |
|
107 |
-way of getting back to an address/port that has not previously opened a data |
|
108 |
-channel in the NAT box by sending something out first. Nevertheless the |
|
109 |
-support for asymmetric clients is important, because without it they won't |
|
110 |
-be able to work even when they have public Internet addresses. Also this |
|
111 |
-support allows one to use an asymmetric client behind NAT if he can |
|
112 |
-configure the NAT box to forward the packets meant to that client. |
|
113 |
- |
|
114 |
- The only requirement a symmetric SIP client must met to be able to work |
|
115 |
-transparently behind NAT when using the mediaproxy module is to accept to be |
|
116 |
-configured to use a so called outbound proxy and this proxy must be the one |
|
117 |
-running with the mediaproxy module loaded. |
|
118 |
- |
|
119 |
- |
|
120 |
-3. Features |
|
121 |
- -------- |
|
122 |
- |
|
123 |
-- make symmetric clients work behind NAT transparently if they use the SIP |
|
124 |
- server as the outbound SIP server. |
|
125 |
-- handle all media streams specified in the SDP body. There is a limit of 64 |
|
126 |
- RTP streams per session in the code now, but we hardly find this to be a |
|
127 |
- limitation for the time being. |
|
128 |
-- able to distribute RTP traffic load on multiple proxy servers running on |
|
129 |
- multiple hosts. |
|
130 |
-- able to specify which proxy server to use based on the SIP domain of the |
|
131 |
- caller/destination (done by the proxy server's dispatcher module) |
|
132 |
-- handle asymmetric clients properly. They can even work behind NAT if a |
|
133 |
- proper port forwarding is done for them on the NAT box. |
|
134 |
- |
|
135 |
- |
|
136 |
-4. Module parameters and exported functions |
|
137 |
- ---------------------------------------- |
|
138 |
- |
|
139 |
- The module exports the following parameters: |
|
140 |
- |
|
141 |
- - mediaproxy_socket |
|
142 |
- |
|
143 |
- it is the path to the filesystem socket where the proxy server |
|
144 |
- listens for commands from the module. |
|
145 |
- |
|
146 |
- - sip_asymmetrics |
|
147 |
- |
|
148 |
- it is the path to a file that lists regular expressions that match |
|
149 |
- 'User-Agent' or 'Server' fields from clients that are asymmetric |
|
150 |
- regarding SIP signaling. Needed to detect when a client is asymmetric |
|
151 |
- regarding SIP signaling. An example file is in the config/ subdirectory. |
|
152 |
- |
|
153 |
- - rtp_asymmetrics |
|
154 |
- |
|
155 |
- it is the path to a file that lists regular expressions that match |
|
156 |
- 'User-Agent' or 'Server' fields from clients that are asymmetric |
|
157 |
- regarding the RTP media. Needed to detect when a client is asymmetric |
|
158 |
- regarding the RTP media. An example file is in the config/ subdirectory. |
|
159 |
- |
|
160 |
- - natping_interval |
|
161 |
- |
|
162 |
- it holds an integer value representing how often the module will |
|
163 |
- send packets to all registered clients that are behind NAT to keep |
|
164 |
- their opened channels alive. Represents an interval in seconds. |
|
165 |
- |
|
166 |
-Parameters are set in the SER configuration file by using the modparam |
|
167 |
-command. Below are examples, which contain the actual default values of the |
|
168 |
-parameters. If you are Ok with them it is not necessary to specify them in the |
|
169 |
-configuration file at all. |
|
170 |
- |
|
171 |
-modparam("mediaproxy", "mediaproxy_socket", "/var/run/proxydispatcher.sock") |
|
172 |
-modparam("mediaproxy", "sip_asymmetrics", "/etc/ser/sip-asymmetrics-clients") |
|
173 |
-modparam("mediaproxy", "rtp_asymmetrics", "/etc/ser/rtp-asymmetrics-clients") |
|
174 |
-modparam("mediaproxy", "natping_interval", 20) |
|
175 |
- |
|
176 |
- The module exports the following functions: |
|
177 |
- |
|
178 |
- - client_nat_test(type) |
|
179 |
- |
|
180 |
- tests if the client is behind NAT or not. The types of tests are |
|
181 |
- specified by the type parameter which represents a sum of the |
|
182 |
- following numbers (add the values of the ones you wish to perform |
|
183 |
- tests for): |
|
184 |
- |
|
185 |
- 1 - tests if client has a private IP address (as defined by RFC1918) |
|
186 |
- in the Contact field of the SIP message. |
|
187 |
- |
|
188 |
- 2 - tests if client has contacted SER from an address that is different |
|
189 |
- from the one in the 1st Via field. Both IP and port are checked, |
|
190 |
- except for asymmetric clients for which the port is ignored. |
|
191 |
- |
|
192 |
- 4 - tests if client has a private IP address (as defined by RFC1918) |
|
193 |
- in the top Via field of the SIP message. |
|
194 |
- |
|
195 |
- for example calling client_nat_test("3") in ser.cfg will perform |
|
196 |
- first 2 tests listen above and return true as soon as one succeeds |
|
197 |
- if both fail will return false. |
|
198 |
- |
|
199 |
- - fix_contact() |
|
200 |
- |
|
201 |
- will replace the IP:Port in the Contact field of the SIP message |
|
202 |
- with the ones the SIP message was received from. For clients that |
|
203 |
- are asymmetric regarding SIP signaling (as determined from the |
|
204 |
- sip_asymmetrics file) will preserve the port. |
|
205 |
- usually called after an if (client_nat_test(type)) has succeded |
|
206 |
- |
|
207 |
- - use_media_proxy() |
|
208 |
- |
|
209 |
- will make a call to the proxy server and replace the IPs and ports |
|
210 |
- in the SDP body with the ones returned by the proxy server for |
|
211 |
- each media stream that the SDP message describes. This will force |
|
212 |
- the media streams to be routed through the proxy server. |
|
213 |
- called when you want to make the session go through a proxy server |
|
214 |
- |
|
215 |
- - end_media_session() |
|
216 |
- |
|
217 |
- will call on the proxy server to end the media session for that call |
|
218 |
- this is done at the end of the call to instruct the proxy server to |
|
219 |
- free the resources allocated to that call as well as to save log |
|
220 |
- information about the call. |
|
221 |
- called when a session should end (BYE or CANCEL received) |
|
222 |
- |
|
223 |
- |
|
224 |
-5. Comparison with the nathelper module |
|
225 |
- ------------------------------------ |
|
226 |
- |
|
227 |
- After reading all this you may wonder what this module can offer you that |
|
228 |
-the nathelper module (a similar nat traversal solution) can't and why was |
|
229 |
-necessary to develop this module. |
|
230 |
- |
|
231 |
-While at surface they seem to offer about the same functionality, there are |
|
232 |
-a few core differences that make them quite different. |
|
233 |
- |
|
234 |
- The main and most notable difference is that mediaproxy offers a |
|
235 |
-distributed environment, where the mediaproxy module can control multiple |
|
236 |
-mediaproxy servers. The mediaproxy servers can be local or remote and they |
|
237 |
-can be specified per domain or as defaults for domains that don't have their |
|
238 |
-own mediaproxy servers defined. These mediaproxy servers can be arranged in |
|
239 |
-load balancing and fallback schemes allowing the platform to scale up easily |
|
240 |
-and also offer redundancy to keep the service running even if some of the |
|
241 |
-mediaproxies go offline. Mediaproxy is able to detect the dead proxies and |
|
242 |
-redistribute the calls among the other mediaproxies that are available. |
|
243 |
-(More details about this can be found in the SER MediaProxy documentation) |
|
244 |
- |
|
245 |
- Another important difference is that mediaproxy tries to move the complex |
|
246 |
-logic of decision from the ser configuration file to the module and the |
|
247 |
-proxy servers themselves. This is why there are very few functions in this |
|
248 |
-module that take any parameters. Instead, control is achieved by modifying |
|
249 |
-resources outside of ser.cfg. This includes for example specifying the |
|
250 |
-mediaproxy servers using DNS SRV records, or declaring asymmetric clients |
|
251 |
-in external files that are automatically re-read as soon as they change. |
|
252 |
-This allows SER to run without interruption or restarts. If one wants to |
|
253 |
-change SER's behavior, instead of changing ser.cfg and restarting SER, one |
|
254 |
-will change these external resources and SER will adapt it's behavior on the |
|
255 |
-fly without any need for restart. |
|
256 |
-Another advantage of this is that ser.cfg becomes simpler and easier to |
|
257 |
-maintain. |
|
258 |
- |
|
259 |
- |
260 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,8 +0,0 @@ |
1 |
- |
|
2 |
-sip-asymmetric-clients and rtp-asymmetric-clients list clients that are |
|
3 |
-asymmetric regarding the SIP signaling respective the RTP media streams. |
|
4 |
- |
|
5 |
-By default these files should go in /etc/ser/ (unless specifically |
|
6 |
-configured to be somewhere else using the sip_asymmetrics and/or |
|
7 |
-rtp_asymmetrics options available with the mediaproxy module. |
|
8 |
- |
9 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,12 +0,0 @@ |
1 |
-# |
|
2 |
-# Lines starting with a '#' character or empty lines are ignored. |
|
3 |
-# Put a User-Agent name or regular expression per line. |
|
4 |
-# Check is case insensitive. |
|
5 |
-# |
|
6 |
-# This file should only list SIP clients that are asymmetric regarding |
|
7 |
-# the RTP media streams. For clients that are asymmetric regarding the SIP |
|
8 |
-# signaling, use the equivalent SIP version of this file. |
|
9 |
-# Clients that are asymmetric for both SIP signaling as well as RTP media |
|
10 |
-# streams should go in both files. |
|
11 |
-# |
|
12 |
- |
13 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,118 +0,0 @@ |
1 |
-# Example ser.cfg for mediaproxy functionality |
|
2 |
- |
|
3 |
-loadmodule "/usr/lib/ser/modules/registrar.so" |
|
4 |
-loadmodule "/usr/lib/ser/modules/domain.so" |
|
5 |
-loadmodule "/usr/lib/ser/modules/mediaproxy.so" |
|
6 |
- |
|
7 |
-modparam("mediaproxy", "natping_interval", 60) |
|
8 |
-modparam("registrar", "nat_flag", 2) |
|
9 |
- |
|
10 |
-route{ |
|
11 |
- if (!mf_process_maxfwd_header("10")) { |
|
12 |
- if (method!="ACK") { |
|
13 |
- sl_send_reply("483", "Too many hops"); |
|
14 |
- }; |
|
15 |
- break; |
|
16 |
- }; |
|
17 |
- |
|
18 |
- if (msg:len >= max_len) { |
|
19 |
- if (method!="ACK") { |
|
20 |
- sl_send_reply("513", "Message too big"); |
|
21 |
- }; |
|
22 |
- break; |
|
23 |
- }; |
|
24 |
- |
|
25 |
- if (method=="REGISTER") { |
|
26 |
- if (is_from_local()) { |
|
27 |
- # Mark as NAT'ed |
|
28 |
- if (client_nat_test("3")) { |
|
29 |
- setflag(2); |
|
30 |
- force_rport(); |
|
31 |
- fix_contact(); |
|
32 |
- }; |
|
33 |
- |
|
34 |
- if (!www_authorize("", "subscriber")) { |
|
35 |
- www_challenge("", "0"); |
|
36 |
- break; |
|
37 |
- } else if (!check_to()) { |
|
38 |
- sl_send_reply("403", "Username!=To not allowed"); |
|
39 |
- break; |
|
40 |
- }; |
|
41 |
- |
|
42 |
- if (!save("location")) { |
|
43 |
- sl_reply_error(); |
|
44 |
- }; |
|
45 |
- } else { |
|
46 |
- sl_send_reply("403", "This domain is not served here"); |
|
47 |
- }; |
|
48 |
- |
|
49 |
- break; |
|
50 |
- }; |
|
51 |
- |
|
52 |
- if (method=="INVITE") { |
|
53 |
- if (!(is_from_local() || is_uri_host_local())) { |
|
54 |
- sl_send_reply("403", "Relaying is forbidden"); |
|
55 |
- break; |
|
56 |
- }; |
|
57 |
- t_on_failure("1"); |
|
58 |
- } else if (method == "BYE" || method == "CANCEL") { |
|
59 |
- end_media_session(); |
|
60 |
- }; |
|
61 |
- |
|
62 |
- if (loose_route()) { |
|
63 |
- if (method=="INVITE" || method=="ACK") { |
|
64 |
- use_media_proxy(); |
|
65 |
- }; |
|
66 |
- # end media session for BYE and CANCEL is done above |
|
67 |
- # before entering the loose route. no need to call it here |
|
68 |
- t_relay(); |
|
69 |
- break; |
|
70 |
- }; |
|
71 |
- |
|
72 |
- # Force subsequent messages to pass trough this proxy |
|
73 |
- if (method == "INVITE") { |
|
74 |
- record_route(); |
|
75 |
- }; |
|
76 |
- |
|
77 |
- if (client_nat_test("3") && !search("^Record-Route:")) { |
|
78 |
- # Mark as NAT'ed |
|
79 |
- force_rport(); |
|
80 |
- fix_contact(); |
|
81 |
- }; |
|
82 |
- |
|
83 |
- if (method=="INVITE") { |
|
84 |
- t_on_reply("1"); |
|
85 |
- }; |
|
86 |
- |
|
87 |
- if (is_uri_host_local()) { # join with next if? |
|
88 |
- if (!lookup("location")) { |
|
89 |
- sl_send_reply("404", "User not found"); |
|
90 |
- break; |
|
91 |
- }; |
|
92 |
- }; |
|
93 |
- |
|
94 |
- if (method=="INVITE" || method=="ACK") { |
|
95 |
- use_media_proxy(); |
|
96 |
- }; |
|
97 |
- |
|
98 |
- if (!t_relay()) { |
|
99 |
- if (method=="INVITE" || method=="ACK") { |
|
100 |
- end_media_session(); |
|
101 |
- }; |
|
102 |
- sl_reply_error(); |
|
103 |
- }; |
|
104 |
-} |
|
105 |
- |
|
106 |
-failure_route[1] { |
|
107 |
- end_media_session(); |
|
108 |
-} |
|
109 |
- |
|
110 |
-onreply_route[1] { |
|
111 |
- if (status=~"(183)|(2[0-9][0-9])") { |
|
112 |
- if (client_nat_test("1")) { |
|
113 |
- fix_contact(); |
|
114 |
- }; |
|
115 |
- use_media_proxy(); |
|
116 |
- }; |
|
117 |
-} |
|
118 |
- |
119 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,14 +0,0 @@ |
1 |
-# |
|
2 |
-# Lines starting with a '#' character or empty lines are ignored. |
|
3 |
-# Put a User-Agent name or regular expression per line. |
|
4 |
-# Check is case insensitive. |
|
5 |
-# |
|
6 |
-# This file should only list SIP clients that are asymmetric regarding |
|
7 |
-# the SIP signaling. For clients that are asymmetric regarding the RTP |
|
8 |
-# media streams, use the equivalent RTP version of this file. |
|
9 |
-# Clients that are asymmetric for both SIP signaling as well as RTP media |
|
10 |
-# streams should go in both files. |
|
11 |
-# |
|
12 |
- |
|
13 |
-ipDialog SipTone.* |
|
14 |
- |
15 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,194 +0,0 @@ |
1 |
-/* $Id$ |
|
2 |
- * |
|
3 |
- * Copyright (C) 2004 Dan Pascu |
|
4 |
- * Copyright (C) 2003 Porta Software Ltd |
|
5 |
- * |
|
6 |
- * This file is part of ser, a free SIP server. |
|
7 |
- * |
|
8 |
- * ser is free software; you can redistribute it and/or modify |
|
9 |
- * it under the terms of the GNU General Public License as published by |
|
10 |
- * the Free Software Foundation; either version 2 of the License, or |
|
11 |
- * (at your option) any later version |
|
12 |
- * |
|
13 |
- * For a license to use the ser software under conditions |
|
14 |
- * other than those described here, or to purchase support for this |
|
15 |
- * software, please contact iptel.org by e-mail at the following addresses: |
|
16 |
- * info@iptel.org |
|
17 |
- * |
|
18 |
- * ser is distributed in the hope that it will be useful, |
|
19 |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 |
- * GNU General Public License for more details. |
|
22 |
- * |
|
23 |
- * You should have received a copy of the GNU General Public License |
|
24 |
- * along with this program; if not, write to the Free Software |
|
25 |
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
26 |
- * |
|
27 |
- */ |
|
28 |
- |
|
29 |
-static void |
|
30 |
-pingClients(unsigned int ticks, void *param) |
|
31 |
-{ |
|
32 |
- static char pingbuf[4] = "\0\0\0\0"; |
|
33 |
- static int length = 256; |
|
34 |
- struct hostent* hostent; |
|
35 |
- struct dest_info dst; |
|
36 |
- struct sip_uri uri; |
|
37 |
- void *buf, *ptr; |
|
38 |
- str contact; |
|
39 |
- int needed; |
|
40 |
- char proto; |
|
41 |
- |
|
42 |
- buf = pkg_malloc(length); |
|
43 |
- if (buf == NULL) { |
|
44 |
- LOG(L_ERR, "error: mediaproxy/pingClients(): out of memory\n"); |
|
45 |
- return; |
|
46 |
- } |
|
47 |
- needed = userLocation.get_all_ucontacts(buf, length, FL_NAT); |
|
48 |
- if (needed > 0) { |
|
49 |
- // make sure we alloc more than actually we were told is missing |
|
50 |
- // (some clients may register while we are making these calls) |
|
51 |
- length = (length + needed) * 2; |
|
52 |
- ptr = pkg_realloc(buf, length); |
|
53 |
- if (ptr == NULL) { |
|
54 |
- LOG(L_ERR, "error: mediaproxy/pingClients(): out of memory\n"); |
|
55 |
- pkg_free(buf); |
|
56 |
- return; |
|
57 |
- } else { |
|
58 |
- buf = ptr; |
|
59 |
- } |
|
60 |
- // try again. we may fail again if _many_ clients register in between |
|
61 |
- needed = userLocation.get_all_ucontacts(buf, length, FL_NAT); |
|
62 |
- if (needed != 0) { |
|
63 |
- pkg_free(buf); |
|
64 |
- return; |
|
65 |
- } |
|
66 |
- } |
|
67 |
- |
|
68 |
- ptr = buf; |
|
69 |
- while (1) { |
|
70 |
- memcpy(&(contact.len), ptr, sizeof(contact.len)); |
|
71 |
- if (contact.len == 0) |
|
72 |
- break; |
|
73 |
- contact.s = (char*)ptr + sizeof(contact.len); |
|
74 |
- ptr = contact.s + contact.len; |
|
75 |
- init_dest_info(&dst); |
|
76 |
- memcpy(&dst.send_sock, ptr, sizeof(dst.send_sock)); |
|
77 |
- ptr += sizeof(dst.send_sock); |
|
78 |
- if (parse_uri(contact.s, contact.len, &uri) < 0) { |
|
79 |
- LOG(L_ERR, "error: mediaproxy/pingClients(): can't parse contact uri\n"); |
|
80 |
- continue; |
|
81 |
- } |
|
82 |
- if (uri.proto != PROTO_UDP && uri.proto != PROTO_NONE) |
|
83 |
- continue; |
|
84 |
- if (uri.port_no == 0) |
|
85 |
- uri.port_no = SIP_PORT; |
|
86 |
- proto=PROTO_UDP; |
|
87 |
- hostent = sip_resolvehost(&uri.host, &uri.port_no, &proto); |
|
88 |
- if (hostent == NULL){ |
|
89 |
- LOG(L_ERR, "error: mediaproxy/pingClients(): can't resolve host\n"); |
|
90 |
- continue; |
|
91 |
- } |
|
92 |
- hostent2su(&dst.to, hostent, 0, uri.port_no); |
|
93 |
- if (dst.send_sock==0) { |
|
94 |
- dst.send_sock = get_send_socket(0, &dst.to, PROTO_UDP); |
|
95 |
- if (dst.send_sock == NULL) { |
|
96 |
- LOG(L_ERR, "error: mediaproxy/pingClients(): can't get " |
|
97 |
- "sending socket\n"); |
|
98 |
- continue; |
|
99 |
- } |
|
100 |
- } |
|
101 |
- dst.proto=PROTO_UDP; |
|
102 |
- udp_send(&dst, pingbuf, sizeof(pingbuf)); |
|
103 |
- } |
|
104 |
- pkg_free(buf); |
|
105 |
-} |
|
106 |
- |
|
107 |
- |
|
108 |
-// Replace IP:Port in Contact field with the source address of the packet. |
|
109 |
-// Preserve port for SIP asymmetric clients |
|
110 |
-static int |
|
111 |
-FixContact(struct sip_msg* msg, char* str1, char* str2) |
|
112 |
-{ |
|
113 |
- str beforeHost, after, agent; |
|
114 |
- contact_t* contact; |
|
115 |
- struct lump* anchor; |
|
116 |
- struct sip_uri uri; |
|
117 |
- char *newip, *buf; |
|
118 |
- int len, newiplen, offset; |
|
119 |
- Bool asymmetric; |
|
120 |
- |
|
121 |
- if (!getContactURI(msg, &uri, &contact)) |
|
122 |
- return -1; |
|
123 |
- |
|
124 |
- newip = ip_addr2a(&msg->rcv.src_ip); |
|
125 |
- newiplen = strlen(newip); |
|
126 |
- |
|
127 |
- /* Don't do anything if the IP's are the same. Return success. */ |
|
128 |
- if (newiplen==uri.host.len && memcmp(uri.host.s, newip, newiplen)==0) { |
|
129 |
- return 1; |
|
130 |
- } |
|
131 |
- |
|
132 |
- if (uri.port.len == 0) |
|
133 |
- uri.port.s = uri.host.s + uri.host.len; |
|
134 |
- |
|
135 |
- agent = getUserAgent(msg); |
|
136 |
- asymmetric = isSIPAsymmetric(agent); |
|
137 |
- |
|
138 |
- beforeHost.s = contact->uri.s; |
|
139 |
- beforeHost.len = uri.host.s - contact->uri.s; |
|
140 |
- if (asymmetric) { |
|
141 |
- // for asymmetrics we preserve the original port |
|
142 |
- after.s = uri.port.s; |
|
143 |
- after.len = contact->uri.s + contact->uri.len - after.s; |
|
144 |
- } else { |
|
145 |
- after.s = uri.port.s + uri.port.len; |
|
146 |
- after.len = contact->uri.s + contact->uri.len - after.s; |
|
147 |
- } |
|
148 |
- |
|
149 |
- len = beforeHost.len + newiplen + after.len + 20; |
|
150 |
- |
|
151 |
- // first try to alloc mem. if we fail we don't want to have the lump |
|
152 |
- // deleted and not replaced. at least this way we keep the original. |
|
153 |
- buf = pkg_malloc(len); |
|
154 |
- if (buf == NULL) { |
|
155 |
- LOG(L_ERR, "error: fix_contact(): out of memory\n"); |
|
156 |
- return -1; |
|
157 |
- } |
|
158 |
- |
|
159 |
- offset = contact->uri.s - msg->buf; |
|
160 |
- anchor = del_lump(msg, offset, contact->uri.len, HDR_CONTACT_T); |
|
161 |
- |
|
162 |
- if (!anchor) { |
|
163 |
- pkg_free(buf); |
|
164 |
- return -1; |
|
165 |
- } |
|
166 |
- |
|
167 |
- if (asymmetric && uri.port.len==0) { |
|
168 |
- len = sprintf(buf, "%.*s%s%.*s", beforeHost.len, beforeHost.s, |
|
169 |
- newip, after.len, after.s); |
|
170 |
- } else if (asymmetric) { |
|
171 |
- len = sprintf(buf, "%.*s%s:%.*s", beforeHost.len, beforeHost.s, |
|
172 |
- newip, after.len, after.s); |
|
173 |
- } else { |
|
174 |
- len = sprintf(buf, "%.*s%s:%d%.*s", beforeHost.len, beforeHost.s, |
|
175 |
- newip, msg->rcv.src_port, after.len, after.s); |
|
176 |
- } |
|
177 |
- |
|
178 |
- if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) { |
|
179 |
- pkg_free(buf); |
|
180 |
- return -1; |
|
181 |
- } |
|
182 |
- |
|
183 |
- contact->uri.s = buf; |
|
184 |
- contact->uri.len = len; |
|
185 |
- |
|
186 |
- if (asymmetric) { |
|
187 |
- LOG(L_INFO, "info: fix_contact(): preserved port for SIP " |
|
188 |
- "asymmetric client: `%.*s'\n", agent.len, agent.s); |
|
189 |
- } |
|
190 |
- |
|
191 |
- return 1; |
|
192 |
-} |
|
193 |
- |
|
194 |
- |
195 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,1604 +0,0 @@ |
1 |
-/* $Id$ |
|
2 |
- * |
|
3 |
- * Copyright (C) 2004 Dan Pascu |
|
4 |
- * |
|
5 |
- * This file is part of ser, a free SIP server. |
|
6 |
- * |
|
7 |
- * ser is free software; you can redistribute it and/or modify |
|
8 |
- * it under the terms of the GNU General Public License as published by |
|
9 |
- * the Free Software Foundation; either version 2 of the License, or |
|
10 |
- * (at your option) any later version |
|
11 |
- * |
|
12 |
- * For a license to use the ser software under conditions |
|
13 |
- * other than those described here, or to purchase support for this |
|
14 |
- * software, please contact iptel.org by e-mail at the following addresses: |
|
15 |
- * info@iptel.org |
|
16 |
- * |
|
17 |
- * ser is distributed in the hope that it will be useful, |
|
18 |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 |
- * GNU General Public License for more details. |
|
21 |
- * |
|
22 |
- * You should have received a copy of the GNU General Public License |
|
23 |
- * along with this program; if not, write to the Free Software |
|
24 |
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
25 |
- * |
|
26 |
- */ |
|
27 |
- |
|
28 |
-// TODO |
|
29 |
-// |
|
30 |
-// - make the asymmetric files use CFG_DIR |
|
31 |
-// - find a way to install the config files with make install |
|
32 |
- |
|
33 |
-#include "../../sr_module.h" |
|
34 |
-#include "../../dprint.h" |
|
35 |
-#include "../../str.h" |
|
36 |
-#include "../../error.h" |
|
37 |
-#include "../../data_lump.h" |
|
38 |
-#include "../../forward.h" |
|
39 |
-#include "../../mem/mem.h" |
|
40 |
-#include "../../resolve.h" |
|
41 |
-#include "../../timer.h" |
|
42 |
-#include "../../ut.h" |
|
43 |
-#include "../../parser/parse_from.h" |
|
44 |
-#include "../../parser/parse_to.h" |
|
45 |
-#include "../../parser/parse_uri.h" |
|
46 |
-#include "../../msg_translator.h" |
|
47 |
-#include "../../id.h" |
|
48 |
-#include "../registrar/sip_msg.h" |
|
49 |
-#include "../usrloc/usrloc.h" |
|
50 |
- |
|
51 |
-#include <stdio.h> |
|
52 |
-#include <stdlib.h> |
|
53 |
-#include <unistd.h> |
|
54 |
-#include <string.h> |
|
55 |
-#include <ctype.h> |
|
56 |
-#include <errno.h> |
|
57 |
-#include <regex.h> |
|
58 |
-#include <sys/types.h> |
|
59 |
-#include <sys/stat.h> |
|
60 |
-#include <sys/socket.h> |
|
61 |
-#include <netinet/in.h> |
|
62 |
-#include <arpa/inet.h> |
|
63 |
-#include <sys/un.h> |
|
64 |
- |
|
65 |
- |
|
66 |
-MODULE_VERSION |
|
67 |
- |
|
68 |
- |
|
69 |
-// Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to |
|
70 |
-// more systems. `AF_UNIX' was the traditional name stemming from BSD, so |
|
71 |
-// even most POSIX systems support it. It is also the name of choice in |
|
72 |
-// the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX |
|
73 |
-#ifndef AF_LOCAL |
|
74 |
-# define AF_LOCAL AF_UNIX |
|
75 |
-#endif |
|
76 |
- |
|
77 |
- |
|
78 |
-#define min(x, y) (((x) < (y)) ? (x) : (y)) |
|
79 |
- |
|
80 |
-#define isAnyAddress(adr) ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0) |
|
81 |
- |
|
82 |
- |
|
83 |
-typedef int Bool; |
|
84 |
-#define True 1 |
|
85 |
-#define False 0 |
|
86 |
- |
|
87 |
- |
|
88 |
-typedef int (*CheckLocalPartyProc)(struct sip_msg* msg, char* s1, char* s2); |
|
89 |
- |
|
90 |
-typedef Bool (*NatTestProc)(struct sip_msg* msg); |
|
91 |
- |
|
92 |
- |
|
93 |
-typedef enum { |
|
94 |
- NTNone=0, |
|
95 |
- NTPrivateContact=1, |
|
96 |
- NTSourceAddress=2, |
|
97 |
- NTPrivateVia=4 |
|
98 |
-} NatTestType; |
|
99 |
- |
|
100 |
-typedef enum { |
|
101 |
- STUnknown=0, |
|
102 |
- STAudio, |
|
103 |
- STVideo, |
|
104 |
- STAudioVideo |
|
105 |
-} StreamType; |
|
106 |
- |
|
107 |
-typedef struct { |
|
108 |
- const char *name; |
|
109 |
- uint32_t address; |
|
110 |
- uint32_t mask; |
|
111 |
-} NetInfo; |
|
112 |
- |
|
113 |
-typedef struct { |
|
114 |
- str ip; |
|
115 |
- str port; |
|
116 |
- str type; // stream type (`audio', `video', ...) |
|
117 |
- int localIP; // true if the IP is locally defined inside this media stream |
|
118 |
-} StreamInfo; |
|
119 |
- |
|
120 |
-typedef struct { |
|
121 |
- NatTestType test; |
|
122 |
- NatTestProc proc; |
|
123 |
-} NatTest; |
|
124 |
- |
|
125 |
-typedef struct { |
|
126 |
- char *file; // the file which lists the asymmetric clients |
|
127 |
- long timestamp; // for checking if it was modified |
|
128 |
- |
|
129 |
- regex_t **clients; // the asymmetric clients regular expressions |
|
130 |
- int size; // size of array above |
|
131 |
- int count; // how many clients are in array above |
|
132 |
-} AsymmetricClients; |
|
133 |
- |
|
134 |
- |
|
135 |
-/* Function prototypes */ |
|
136 |
-static int ClientNatTest(struct sip_msg *msg, char *str1, char *str2); |
|
137 |
-static int FixContact(struct sip_msg *msg, char *str1, char *str2); |
|
138 |
-static int UseMediaProxy(struct sip_msg *msg, char *str1, char *str2); |
|
139 |
-static int EndMediaSession(struct sip_msg *msg, char *str1, char *str2); |
|
140 |
- |
|
141 |
-static int mod_init(void); |
|
142 |
- |
|
143 |
-static Bool testPrivateContact(struct sip_msg* msg); |
|
144 |
-static Bool testSourceAddress(struct sip_msg* msg); |
|
145 |
-static Bool testPrivateVia(struct sip_msg* msg); |
|
146 |
- |
|
147 |
- |
|
148 |
-/* Local global variables */ |
|
149 |
-static char *mediaproxySocket = "/var/run/proxydispatcher.sock"; |
|
150 |
- |
|
151 |
-static int natpingInterval = 60; // 60 seconds |
|
152 |
- |
|
153 |
-static usrloc_api_t userLocation; |
|
154 |
- |
|
155 |
-static AsymmetricClients sipAsymmetrics = { |
|
156 |
- "/etc/ser/sip-asymmetric-clients", |
|
157 |
- //CFG_DIR"/sip-asymmetric-clients", |
|
158 |
- 0, |
|
159 |
- NULL, |
|
160 |
- 0, |
|
161 |
- 0 |
|
162 |
-}; |
|
163 |
- |
|
164 |
-static AsymmetricClients rtpAsymmetrics = { |
|
165 |
- "/etc/ser/rtp-asymmetric-clients", |
|
166 |
- 0, |
|
167 |
- NULL, |
|
168 |
- 0, |
|
169 |
- 0 |
|
170 |
-}; |
|
171 |
- |
|
172 |
-//static CheckLocalPartyProc isToLocal; |
|
173 |
- |
|
174 |
-NetInfo rfc1918nets[] = { |
|
175 |
- {"10.0.0.0", 0x0a000000UL, 0xff000000UL}, |
|
176 |
- {"172.16.0.0", 0xac100000UL, 0xfff00000UL}, |
|
177 |
- {"192.168.0.0", 0xc0a80000UL, 0xffff0000UL}, |
|
178 |
- {NULL, 0UL, 0UL} |
|
179 |
-}; |
|
180 |
- |
|
181 |
-NatTest natTests[] = { |
|
182 |
- {NTPrivateContact, testPrivateContact}, |
|
183 |
- {NTSourceAddress, testSourceAddress}, |
|
184 |
- {NTPrivateVia, testPrivateVia}, |
|
185 |
- {NTNone, NULL} |
|
186 |
-}; |
|
187 |
- |
|
188 |
-static cmd_export_t commands[] = { |
|
189 |
- {"fix_contact", FixContact, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, |
|
190 |
- {"use_media_proxy", UseMediaProxy, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, |
|
191 |
- {"end_media_session", EndMediaSession, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, |
|
192 |
- {"client_nat_test", ClientNatTest, 1, fixup_int_1, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, |
|
193 |
- {0, 0, 0, 0, 0} |
|
194 |
-}; |
|
195 |
- |
|
196 |
-static param_export_t parameters[] = { |
|
197 |
- {"mediaproxy_socket", PARAM_STRING, &mediaproxySocket}, |
|
198 |
- {"sip_asymmetrics", PARAM_STRING, &(sipAsymmetrics.file)}, |
|
199 |
- {"rtp_asymmetrics", PARAM_STRING, &(rtpAsymmetrics.file)}, |
|
200 |
- {"natping_interval", PARAM_INT, &natpingInterval}, |
|
201 |
- {0, 0, 0} |
|
202 |
-}; |
|
203 |
- |
|
204 |
-struct module_exports exports = { |
|
205 |
- "mediaproxy", // module name |
|
206 |
- commands, // module exported functions |
|
207 |
- 0, // RPC methods |
|
208 |
- parameters, // module exported parameters |
|
209 |
- mod_init, // module init (before any kid is created. kids will inherit) |
|
210 |
- NULL, // reply processing |
|
211 |
- NULL, // destroy function |
|
212 |
- NULL, // on_break |
|
213 |
- NULL // child_init |
|
214 |
-}; |
|
215 |
- |
|
216 |
- |
|
217 |
- |
|
218 |
-/* Helper functions */ |
|
219 |
- |
|
220 |
-// Functions dealing with strings |
|
221 |
- |
|
222 |
-/* |
|
223 |
- * strfind() finds the start of the first occurrence of the substring needle |
|
224 |
- * of length nlen in the memory area haystack of length len. |
|
225 |
- */ |
|
226 |
-static void* |
|
227 |
-strfind(const void *haystack, size_t len, const void *needle, size_t nlen) |
|
228 |
-{ |
|
229 |
- char *sp; |
|
230 |
- |
|
231 |
- /* Sanity check */ |
|
232 |
- if(!(haystack && needle && nlen && len>=nlen)) |
|
233 |
- return NULL; |
|
234 |
- |
|
235 |
- for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) { |
|
236 |
- if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) { |
|
237 |
- return sp; |
|
238 |
- } |
|
239 |
- } |
|
240 |
- |
|
241 |
- return NULL; |
|
242 |
-} |
|
243 |
- |
|
244 |
-/* |
|
245 |
- * strcasefind() finds the start of the first occurrence of the substring |
|
246 |
- * needle of length nlen in the memory area haystack of length len by doing |
|
247 |
- * a case insensitive search |
|
248 |
- */ |
|
249 |
-static void* |
|
250 |
-strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen) |
|
251 |
-{ |
|
252 |
- char *sp; |
|
253 |
- |
|
254 |
- /* Sanity check */ |
|
255 |
- if(!(haystack && needle && nlen && len>=nlen)) |
|
256 |
- return NULL; |
|
257 |
- |
|
258 |
- for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) { |
|
259 |
- if (tolower(*sp) == tolower(*(char*)needle) && |
|
260 |
- strncasecmp(sp, needle, nlen)==0) { |
|
261 |
- return sp; |
|
262 |
- } |
|
263 |
- } |
|
264 |
- |
|
265 |
- return NULL; |
|
266 |
-} |
|
267 |
- |
|
268 |
-/* returns string with whitespace trimmed from left end */ |
|
269 |
-static inline void |
|
270 |
-ltrim(str *string) |
|
271 |
-{ |
|
272 |
- while (string->len>0 && isspace((int)*(string->s))) { |
|
273 |
- string->len--; |
|
274 |
- string->s++; |
|
275 |
- } |
|
276 |
-} |
|
277 |
- |
|
278 |
-/* returns string with whitespace trimmed from right end */ |
|
279 |
-static inline void |
|
280 |
-rtrim(str *string) |
|
281 |
-{ |
|
282 |
- char *ptr; |
|
283 |
- |
|
284 |
- ptr = string->s + string->len - 1; |
|
285 |
- while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) { |
|
286 |
- string->len--; |
|
287 |
- ptr--; |
|
288 |
- } |
|
289 |
-} |
|
290 |
- |
|
291 |
-/* returns string with whitespace trimmed from both ends */ |
|
292 |
-static inline void |
|
293 |
-trim(str *string) |
|
294 |
-{ |
|
295 |
- ltrim(string); |
|
296 |
- rtrim(string); |
|
297 |
-} |
|
298 |
- |
|
299 |
-/* returns a pointer to first CR or LF char found or the end of string */ |
|
300 |
-static char* |
|
301 |
-findendline(char *string, int len) |
|
302 |
-{ |
|
303 |
- char *ptr = string; |
|
304 |
- |
|
305 |
- while(ptr - string < len && *ptr != '\n' && *ptr != '\r') |
|
306 |
- ptr++; |
|
307 |
- |
|
308 |
- return ptr; |
|
309 |
-} |
|
310 |
- |
|
311 |
- |
|
312 |
-static int |
|
313 |
-strtoint(str *data) |
|
314 |
-{ |
|
315 |
- long int result; |
|
316 |
- char c; |
|
317 |
- |
|
318 |
- // hack to avoid copying the string |
|
319 |
- c = data->s[data->len]; |
|
320 |
- data->s[data->len] = 0; |
|
321 |
- result = strtol(data->s, NULL, 10); |
|
322 |
- data->s[data->len] = c; |
|
323 |
- |
|
324 |
- return (int)result; |
|
325 |
-} |
|
326 |
- |
|
327 |
- |
|
328 |
-/* Free the returned string with pkg_free() after you've done with it */ |
|
329 |
-static char* |
|
330 |
-encodeQuopri(str buf) |
|
331 |
-{ |
|
332 |
- char *result; |
|
333 |
- int i, j; |
|
334 |
- char c; |
|
335 |
- |
|
336 |
- result = pkg_malloc(buf.len*3+1); |
|
337 |
- if (!result) { |
|
338 |
- LOG(L_ERR, "error: mediaproxy/encodeQuopri(): out of memory\n"); |
|
339 |
- return NULL; |
|
340 |
- } |
|
341 |
- |
|
342 |
- for (i=0, j=0; i<buf.len; i++) { |
|
343 |
- c = buf.s[i]; |
|
344 |
- if ((c>0x20 && c<0x7f && c!='=') || c=='\n' || c=='\r') { |
|
345 |
- result[j++] = c; |
|
346 |
- } else { |
|
347 |
- result[j++] = '='; |
|
348 |
- sprintf(&result[j], "%02X", (c & 0xff)); |
|
349 |
- j += 2; |
|
350 |
- } |
|
351 |
- } |
|
352 |
- result[j] = 0; |
|
353 |
- |
|
354 |
- return result; |
|
355 |
-} |
|
356 |
- |
|
357 |
- |
|
358 |
-/* Find a line in str `block' that starts with `start'. */ |
|
359 |
-static char* |
|
360 |
-findLineStartingWith(str *block, char *start, int ignoreCase) |
|
361 |
-{ |
|
362 |
- char *ptr, *bend; |
|
363 |
- str zone; |
|
364 |
- int tlen; |
|
365 |
- |
|
366 |
- bend = block->s + block->len; |
|
367 |
- tlen = strlen(start); |
|
368 |
- ptr = NULL; |
|
369 |
- |
|
370 |
- for (zone = *block; zone.len > 0; zone.len = bend - zone.s) { |
|
371 |
- if (ignoreCase) |
|
372 |
- ptr = strcasefind(zone.s, zone.len, start, tlen); |
|
373 |
- else |
|
374 |
- ptr = strfind(zone.s, zone.len, start, tlen); |
|
375 |
- if (!ptr || ptr==zone.s || ptr[-1]=='\n' || ptr[-1]=='\r') |
|
376 |
- break; |
|
377 |
- zone.s = ptr + tlen; |
|
378 |
- } |
|
379 |
- |
|
380 |
- return ptr; |
|
381 |
-} |
|
382 |
- |
|
383 |
- |
|
384 |
-/* get up to `limit' whitespace separated tokens from `char *string' */ |
|
385 |
-static int |
|
386 |
-getTokens(char *string, str *tokens, int limit) |
|
387 |
-{ |
|
388 |
- int i, len, size; |
|
389 |
- char *ptr; |
|
390 |
- |
|
391 |
- if (!string) { |
|
392 |
- return 0; |
|
393 |
- } |
|
394 |
- |
|
395 |
- len = strlen(string); |
|
396 |
- |
|
397 |
- for (ptr=string, i=0; i<limit && len>0; i++) { |
|
398 |
- size = strspn(ptr, " \t\n\r"); |
|
399 |
- ptr += size; |
|
400 |
- len -= size; |
|
401 |
- if (len <= 0) |
|
402 |
- break; |
|
403 |
- size = strcspn(ptr, " \t\n\r"); |
|
404 |
- if (size==0) |
|
405 |
- break; |
|
406 |
- tokens[i].s = ptr; |
|
407 |
- tokens[i].len = size; |
|
408 |
- ptr += size; |
|
409 |
- len -= size; |
|
410 |
- } |
|
411 |
- |
|
412 |
- return i; |
|
413 |
-} |
|
414 |
- |
|
415 |
-/* get up to `limit' whitespace separated tokens from `str *string' */ |
|
416 |
-static int |
|
417 |
-getStrTokens(str *string, str *tokens, int limit) |
|
418 |
-{ |
|
419 |
- int count; |
|
420 |
- char c; |
|
421 |
- |
|
422 |
- if (!string || !string->s) { |
|
423 |
- return 0; |
|
424 |
- } |
|
425 |
- |
|
426 |
- c = string->s[string->len]; |
|
427 |
- string->s[string->len] = 0; |
|
428 |
- |
|
429 |
- count = getTokens(string->s, tokens, limit); |
|
430 |
- |
|
431 |
- string->s[string->len] = c; |
|
432 |
- |
|
433 |
- return count; |
|
434 |
-} |
|
435 |
- |
|
436 |
- |
|
437 |
-/* Functions to extract the info we need from the SIP/SDP message */ |
|
438 |
- |
|
439 |
-/* Extract Call-ID value. */ |
|
440 |
-static Bool |
|
441 |
-getCallId(struct sip_msg* msg, str *cid) |
|
442 |
-{ |
|
443 |
- if (msg->callid == NULL) { |
|
444 |
- if (parse_headers(msg, HDR_CALLID_F, 0) == -1) { |
|
445 |
- return False; |
|
446 |
- } |
|
447 |
- if (msg->callid == NULL) { |
|
448 |
- return False; |
|
449 |
- } |
|
450 |
- } |
|
451 |
- |
|
452 |
- *cid = msg->callid->body; |
|
453 |
- |
|
454 |
- trim(cid); |
|
455 |
- |
|
456 |
- return True; |
|
457 |
-} |
|
458 |
- |
|
459 |
- |
|
460 |
-/* Get caller domain */ |
|
461 |
-static str |
|
462 |
-getFromDomain(char** type, struct sip_msg* msg) |
|
463 |
-{ |
|
464 |
- static char buf[16] = "unknown"; // buf is here for a reason. don't |
|
465 |
- static str notfound = {buf, 7}; // use the constant string directly! |
|
466 |
- static struct sip_uri puri; |
|
467 |
- str uri, did; |
|
468 |
- |
|
469 |
- if (get_from_did(&did, msg) == 1) { |
|
470 |
- *type = "local"; |
|
471 |
- return did; |
|
472 |
- } |
|
473 |
- |
|
474 |
- *type = "remote"; |
|
475 |
- |
|
476 |
- if (parse_from_header(msg) < 0) { |
|
477 |
- LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' header\n"); |
|
478 |
- return notfound; |
|
479 |
- } |
|
480 |
- |
|
481 |
- uri = get_from(msg)->uri; |
|
482 |
- |
|
483 |
- if (parse_uri(uri.s, uri.len, &puri) < 0) { |
|
484 |
- LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' URI\n"); |
|
485 |
- return notfound; |
|
486 |
- } else if (puri.host.len == 0) { |
|
487 |
- return notfound; |
|
488 |
- } |
|
489 |
- |
|
490 |
- return puri.host; |
|
491 |
-} |
|
492 |
- |
|
493 |
-/* Get called domain */ |
|
494 |
-static str |
|
495 |
-getToDomain(char** type, struct sip_msg* msg) |
|
496 |
-{ |
|
497 |
- static char buf[16] = "unknown"; // buf is here for a reason. don't |
|
498 |
- static str notfound = {buf, 7}; // use the constant string directly! |
|
499 |
- static struct sip_uri puri; |
|
500 |
- str uri, did; |
|
501 |
- |
|
502 |
- if (get_to_did(&did, msg) == 0) { |
|
503 |
- *type = "local"; |
|
504 |
- return did; |
|
505 |
- } |
|
506 |
- |
|
507 |
- *type = "remote"; |
|
508 |
- |
|
509 |
- uri = get_to(msg)->uri; |
|
510 |
- |
|
511 |
- if (parse_uri(uri.s, uri.len, &puri) < 0) { |
|
512 |
- LOG(L_ERR, "error: mediaproxy/getToDomain(): error parsing `To' URI\n"); |
|
513 |
- return notfound; |
|
514 |
- } else if (puri.host.len == 0) { |
|
515 |
- return notfound; |
|
516 |
- } |
|
517 |
- |
|
518 |
- return puri.host; |
|
519 |
- |
|
520 |
-} |
|
521 |
- |
|
522 |
-/* Get destination domain */ |
|
523 |
-// This function only works when called for a request although it's more |
|
524 |
-// reliable than the getToDomain function. |
|
525 |
-static str |
|
526 |
-getDestinationDomain(char** type, struct sip_msg* msg) |
|
527 |
-{ |
|
528 |
- static char buf[16] = "unknown"; // buf is here for a reason. don't |
|
529 |
- static str notfound = {buf, 7}; // use the constant string directly! |
|
530 |
- str did; |
|
531 |
- |
|
532 |
- if (get_to_did(&did, msg) == 0) { |
|
533 |
- *type = "local"; |
|
534 |
- return did; |
|
535 |
- } |
|
536 |
- |
|
537 |
- *type = "remote"; |
|
538 |
- |
|
539 |
- if (parse_sip_msg_uri(msg) < 0) { |
|
540 |
- LOG(L_ERR, "error: mediaproxy/getDestinationDomain(): error parsing destination URI\n"); |
|
541 |
- return notfound; |
|
542 |
- } else if (msg->parsed_uri.host.len==0) { |
|
543 |
- return notfound; |
|
544 |
- } |
|
545 |
- |
|
546 |
- return msg->parsed_uri.host; |
|
547 |
-} |
|
548 |
- |
|
549 |
- |
|
550 |
-/* Get From tag */ |
|
551 |
-static str |
|
552 |
-getFromAddress(struct sip_msg *msg) |
|
553 |
-{ |
|
554 |
- static char buf[16] = "unknown"; // buf is here for a reason. don't |
|
555 |
- static str notfound = {buf, 7}; // use the constant string directly! |
|
556 |
- str uri; |
|
557 |
- char *ptr; |
|
558 |
- |
|
559 |
- if (parse_from_header(msg) == -1) { |
|
560 |
- LOG(L_ERR, "error: mediaproxy/getFromAddress(): error parsing From: field\n"); |
|
561 |
- return notfound; |
|
562 |
- } |
|
563 |
- |
|
564 |
- uri = get_from(msg)->uri; |
|
565 |
- |
|
566 |
- if (uri.len == 0) |
|
567 |
- return notfound; |
|
568 |
- |
|
569 |
- if (strncmp(uri.s, "sip:", 4)==0) { |
|
570 |
- uri.s += 4; |
|
571 |
- uri.len -= 4; |
|
572 |
- } |
|
573 |
- |
|
574 |
- if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) { |
|
575 |
- uri.len = ptr - uri.s; |
|
576 |
- } |
|
577 |
- |
|
578 |
- return uri; |
|
579 |
-} |
|
580 |
- |
|
581 |
- |
|
582 |
-/* Get To tag */ |
|
583 |
-static str |
|
584 |
-getToAddress(struct sip_msg *msg) |
|
585 |
-{ |
|
586 |
- static char buf[16] = "unknown"; // buf is here for a reason. don't |
|
587 |
- static str notfound = {buf, 7}; // use the constant string directly! |
|
588 |
- str uri; |
|
589 |
- char *ptr; |
|
590 |
- |
|
591 |
- if (!msg->to) { |
|
592 |
- LOG(L_ERR, "error: mediaproxy/getToAddress(): missing To: field\n"); |
|
593 |
- return notfound; |
|
594 |
- } |
|
595 |
- |
|
596 |
- uri = get_to(msg)->uri; |
|
597 |
- |
|
598 |
- if (uri.len == 0) |
|
599 |
- return notfound; |
|
600 |
- |
|
601 |
- if (strncmp(uri.s, "sip:", 4)==0) { |
|
602 |
- uri.s += 4; |
|
603 |
- uri.len -= 4; |
|
604 |
- } |
|
605 |
- |
|
606 |
- if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) { |
|
607 |
- uri.len = ptr - uri.s; |
|
608 |
- } |
|
609 |
- |
|
610 |
- return uri; |
|
611 |
-} |
|
612 |
- |
|
613 |
- |
|
614 |
-/* Get From tag */ |
|
615 |
-static str |
|
616 |
-getFromTag(struct sip_msg *msg) |
|
617 |
-{ |
|
618 |
- static char buf[4] = ""; // buf is here for a reason. don't |
|
619 |
- static str notfound = {buf, 0}; // use the constant string directly! |
|
620 |
- str tag; |
|
621 |
- |
|
622 |
- if (parse_from_header(msg) == -1) { |
|
623 |
- LOG(L_ERR, "error: mediaproxy/getFromTag(): error parsing From: field\n"); |
|
624 |
- return notfound; |
|
625 |
- } |
|
626 |
- |
|
627 |
- tag = get_from(msg)->tag_value; |
|
628 |
- |
|
629 |
- if (tag.len == 0) |
|
630 |
- return notfound; |
|
631 |
- |
|
632 |
- return tag; |
|
633 |
-} |
|
634 |
- |
|
635 |
- |
|
636 |
-/* Get To tag */ |
|
637 |
-static str |
|
638 |
-getToTag(struct sip_msg *msg) |
|
639 |
-{ |
|
640 |
- static char buf[4] = ""; // buf is here for a reason. don't |
|
641 |
- static str notfound = {buf, 0}; // use the constant string directly! |
|
642 |
- str tag; |
|
643 |
- |
|
644 |
- if (!msg->to) { |
|
645 |
- LOG(L_ERR, "error: mediaproxy/getToTag(): missing To: field\n"); |
|
646 |
- return notfound; |
|
647 |
- } |
|
648 |
- |
|
649 |
- tag = get_to(msg)->tag_value; |
|
650 |
- |
|
651 |
- if (tag.len == 0) |
|
652 |
- return notfound; |
|
653 |
- |
|
654 |
- return tag; |
|
655 |
-} |
|
656 |
- |
|
657 |
- |
|
658 |
-/* Extract User-Agent */ |
|
659 |
-static str |
|
660 |
-getUserAgent(struct sip_msg* msg) |
|
661 |
-{ |
|
662 |
- static char buf[16] = "unknown-agent"; // buf is here for a reason. don't |
|
663 |
- static str notfound = {buf, 13}; // use the constant string directly! |
|
664 |
- str block, server; |
|
665 |
- char *ptr; |
|
666 |
- |
|
667 |
- if ((parse_headers(msg, HDR_USERAGENT_F, 0)!=-1) && msg->user_agent && |
|
668 |
- msg->user_agent->body.len>0) { |
|
669 |
- return msg->user_agent->body; |
|
670 |
- } |
|
671 |
- |
|
672 |
- // If we can't find user-agent, look after the Server: field |
|
673 |
- |
|
674 |
- // This is a temporary hack. Normally it should be extracted by ser |
|
675 |
- // (either as the Server field, or if User-Agent is missing in place |
|
676 |
- // of the User-Agent field) |
|
677 |
- |
|
678 |
- block.s = msg->buf; |
|
679 |
- block.len = msg->len; |
|
680 |
- |
|
681 |
- ptr = findLineStartingWith(&block, "Server:", True); |
|
682 |
- if (!ptr) |
|
683 |
- return notfound; |
|
684 |
- |
|
685 |
- server.s = ptr + 7; |
|
686 |
- server.len = findendline(server.s, block.s+block.len-server.s) - server.s; |
|
687 |
- |
|
688 |
- trim(&server); |
|
689 |
- if (server.len == 0) |
|
690 |
- return notfound; |
|
691 |
- |
|
692 |
- return server; |
|
693 |
-} |
|
694 |
- |
|
695 |
-// Get URI from the Contact: field. |
|
696 |
-static Bool |
|
697 |
-getContactURI(struct sip_msg* msg, struct sip_uri *uri, contact_t** _c) |
|
698 |
-{ |
|
699 |
- |
|
700 |
- if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact) |
|
701 |
- return False; |
|
702 |
- |
|
703 |
- if (!msg->contact->parsed && parse_contact(msg->contact) < 0) { |
|
704 |
- LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact header\n"); |
|
705 |
- return False; |
|
706 |
- } |
|
707 |
- |
|
708 |
- *_c = ((contact_body_t*)msg->contact->parsed)->contacts; |
|
709 |
- |
|
710 |
- if (*_c == NULL) { |
|
711 |
- return False; |
|
712 |
- } |
|
713 |
- |
|
714 |
- if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { |
|
715 |
- LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact URI\n"); |
|
716 |
- return False; |
|
717 |
- } |
|
718 |
- |
|
719 |
- return True; |
|
720 |
-} |
|
721 |
- |
|
722 |
- |
|
723 |
-// Functions to manipulate the SDP message body |
|
724 |
- |
|
725 |
-static Bool |
|
726 |
-checkContentType(struct sip_msg *msg) |
|
727 |
-{ |
|
728 |
- str type; |
|
729 |
- |
|
730 |
- if (!msg->content_type) { |
|
731 |
- LOG(L_WARN, "warning: mediaproxy/checkContentType(): Content-Type " |
|
732 |
- "header missing! Let's assume the content is text/plain ;-)\n"); |
|
733 |
- return True; |
|
734 |
- } |
|
735 |
- |
|
736 |
- type = msg->content_type->body; |
|
737 |
- trim(&type); |
|
738 |
- |
|
739 |
- if (strncasecmp(type.s, "application/sdp", 15) != 0) { |
|
740 |
- LOG(L_ERR, "error: mediaproxy/checkContentType(): invalid Content-Type " |
|
741 |
- "for SDP message\n"); |
|
742 |
- return False; |
|
743 |
- } |
|
744 |
- |
|
745 |
- if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) { |
|
746 |
- LOG(L_ERR,"error: mediaproxy/checkContentType(): invalid character " |
|
747 |
- "after Content-Type!\n"); |
|
748 |
- return False; |
|
749 |
- } |
|
750 |
- |
|
751 |
- return True; |