1 /*
2 Copyright (C) 2009 Thomas Ries <tries@gmx.net>
3
4 This file is part of Siproxd.
5
6 Siproxd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Siproxd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warrantry of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Siproxd; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /* must be defined before including <plugin.h> */
22 #define PLUGIN_NAME plugin_stun
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <time.h>
32
33 #include <osipparser2/osip_parser.h>
34 #include <osipparser2/osip_md5.h>
35
36 #include "siproxd.h"
37 #include "digcalc.h"
38 #include "plugins.h"
39 #include "log.h"
40
41 static char const ident[]="$Id: plugin_stun.c 479 2011-06-11 21:51:40Z hb9xar $";
42
43 /* Plug-in identification */
44 static char name[]="plugin_stun";
45 static char desc[]="Use an external STUN server to determine my public IP";
46
47 /* global configuration storage - required for config file location */
48 extern struct siproxd_config configuration;
49
50 /* plugin configuration storage */
51 static struct plugin_config {
52 char *server;
53 int port;
54 int period;
55 } plugin_cfg;
56
57 /* Instructions for config parser */
58 static cfgopts_t plugin_cfg_opts[] = {
59 { "plugin_stun_server", TYP_STRING, &plugin_cfg.server, {0, NULL} },
60 { "plugin_stun_port", TYP_INT4, &plugin_cfg.port, {3478, NULL} },
61 { "plugin_stun_period", TYP_INT4, &plugin_cfg.period, {300, NULL} },
62 {0, 0, 0}
63 };
64
65 /*
66 * module-local function prototypes
67 */
68 static int stun_validate_response(char *buffer, int len, char *tid);
69 static int stun_send_request(char *tid);
70 static int stun_new_transaction_id(char *tid);
71
72 /*
73 * constants used in this module
74 */
75 #define STUN_TID_SIZE 16 /* STUN transaction ID size: 16 bytes */
76
77
78 /*
79 * Initialization.
80 * Called once suring siproxd startup.
81 */
PLUGIN_INIT(plugin_def_t * plugin_def)82 int PLUGIN_INIT(plugin_def_t *plugin_def) {
83 /* API version number of siproxd that this plugin is built against.
84 * This constant will change whenever changes to the API are made
85 * that require adaptions in the plugin. */
86 plugin_def->api_version=SIPROXD_API_VERSION;
87
88 /* Name and descriptive text of the plugin */
89 plugin_def->name=name;
90 plugin_def->desc=desc;
91
92 /* Execution mask - during what stages of SIP processing shall
93 * the plugin be called. */
94 plugin_def->exe_mask=PLUGIN_PROCESS_RAW|PLUGIN_TIMER;
95
96 /* read the config file */
97 if (read_config(configuration.configfile,
98 configuration.config_search,
99 plugin_cfg_opts, name) == STS_FAILURE) {
100 ERROR("Plugin '%s': could not load config file", name);
101 return STS_FAILURE;
102 }
103
104 /*&&& resolve STUN server
105 store IP
106 */
107
108 INFO("plugin_stun is initialized, using %s:%i as STUN server",
109 plugin_cfg.server, plugin_cfg.port);
110 return STS_SUCCESS;
111 }
112
113 /*
114 * Processing.
115 *
116 */
PLUGIN_PROCESS(int stage,sip_ticket_t * ticket)117 int PLUGIN_PROCESS(int stage, sip_ticket_t *ticket){
118 static time_t next_stun_send=0;
119 static int rq_pending=0; /* !=0 if waiting for response (ongoing dialog) */
120
121 static union {
122 char stun_transaction_id[STUN_TID_SIZE];
123 uint16_t port;
124 } u;
125
126 time_t now;
127 int sts;
128
129 /* stage contains the PLUGIN_* value - the stage of SIP processing. */
130 DEBUGC(DBCLASS_BABBLE, "called in stage %i, rq_pending=%i",
131 stage, rq_pending);
132
133 time(&now);
134
135 switch (stage) {
136 case PLUGIN_TIMER:
137 if (next_stun_send <= now) {
138 /* compose and send new STUN request */
139 DEBUGC(DBCLASS_BABBLE, "preparing to send STUN request");
140
141 /* allocate transaction ID if a new STUN transaction */
142 if (rq_pending == 0) {
143 sts = stun_new_transaction_id(u.stun_transaction_id);
144 }
145
146 /* send STUN BIND request */
147 sts = stun_send_request(u.stun_transaction_id);
148
149 rq_pending=1;
150 /* 10 seconds T/O to receive answer until retrying */
151 next_stun_send = now + 10;
152 }
153 break;
154
155 case PLUGIN_PROCESS_RAW:
156 /* raw UDP packet: ticket->raw_buffer, len: ticket->raw_buffer_len */
157 sts = stun_validate_response(ticket->raw_buffer,
158 ticket->raw_buffer_len,
159 u.stun_transaction_id);
160 if (sts == STS_SUCCESS) {
161 /* is a valid response to our last STUN request */
162 /* This is about what we get:
163 +-----------------+-----------------+
164 |........�..type..|........�...len..| [0]..[4]
165 +-----------------+-----------------+
166 | Transaction ID | [5]..
167 | |
168 | |
169 | | ..[19]
170 +-----------------+-----------------+
171 |........�.type...|........�...len..| [20].. Attribute
172 +-----------------+-----------------+
173 | len bytes |
174 | |
175 +-----------------+-----------------+
176 |........�.type...|........�...len..| Attribute
177 +-----------------+-----------------+
178 | |
179 ...
180 */
181 { /* clean up & move to function! */
182 int i, j;
183 int len=ticket->raw_buffer_len;
184 char *buffer=ticket->raw_buffer;
185 int iptype;
186 uint16_t port;
187 unsigned char ip[4];
188 char ipstring[IPSTRING_SIZE];
189 int got_address=0;
190
191 i=20; // 1st attribute position
192
193 while (i+4 <= len) {
194 int attr_typ = ntohs(*((int *)&buffer[i]) & 0x0000ffff);
195 int attr_len = ntohs(*((int *)&buffer[i+2]) & 0x0000ffff);
196
197 DEBUGC(DBCLASS_BABBLE,"STUN response: i=%i, type=%i, len=%i",
198 i, attr_typ, attr_len);
199
200 /* check if attribute is fully contained in message */
201 if ((i+4+attr_len) > len) {
202 DEBUGC(DBCLASS_BABBLE,"corrupt STUN response");
203 break;
204 }
205
206 /* advance to start of attribute */
207 i = i+4;
208
209 switch (attr_typ) {
210 /* 0x0001 Mapped Address */
211 case 0x0001:
212 DEBUGC(DBCLASS_BABBLE,"Mapped Addr, len=%i", attr_len);
213 iptype=*((int*)&buffer[i+1]) & 0x000000ff;
214 if (iptype != 0x0001) {
215 DEBUGC(DBCLASS_BABBLE,
216 "Mapped Addr, wrong proto family [%i]", iptype);
217 break;
218 }
219
220 port=htons(*((int*)&buffer[i+2]) & 0x0000ffff);
221 memcpy(ip,&buffer[i+4], 4);
222
223 DEBUGC(DBCLASS_BABBLE,"STUN: public IP %u.%u.%u.%u:%i",
224 ip[0], ip[1], ip[2], ip[3], port);
225 /* remember normal IP address only if not yet known */
226 if (got_address == 0) {
227 snprintf(ipstring, IPSTRING_SIZE-1, "%u.%u.%u.%u",
228 ip[0], ip[1], ip[2], ip[3]);
229 ipstring[IPSTRING_SIZE-1]='\0';
230 got_address=1;
231 }
232 break;
233
234 /* 0x8020 XOR Mapped Address */
235 case 0x8020:
236 DEBUGC(DBCLASS_BABBLE,"XOR Mapped Addr, len=%i", attr_len);
237 iptype=*((int*)&buffer[i+1]) & 0x000000ff;
238
239 if (iptype != 0x0001) {
240 DEBUGC(DBCLASS_BABBLE,
241 "Mapped Addr, wrong proto family [%i]", iptype);
242 break;
243 }
244
245 port=*((int*)&buffer[i+2]) & 0x0000ffff;
246 /* XOR the port with start of TID */
247 // port = port ^ *((short int*)stun_transaction_id);
248 port = port ^ u.port;
249 port = htons(port);
250 memcpy(ip,&buffer[i+4], 4);
251 /* XOR the IP with start of TID */
252 for (j=0; j<4; j++) {
253 ip[j]=ip[j] ^ u.stun_transaction_id[j];
254 }
255 DEBUGC(DBCLASS_BABBLE,"STUN: public IP %u.%u.%u.%u:%i",
256 ip[0], ip[1], ip[2], ip[3], port);
257
258 /* remember XORed IP address always (preferred) */
259 snprintf(ipstring, IPSTRING_SIZE-1, "%u.%u.%u.%u",
260 ip[0], ip[1], ip[2], ip[3]);
261 ipstring[IPSTRING_SIZE-1]='\0';
262 got_address=1;
263 break;
264
265 default:
266 /* don't care... */
267 break;
268 }
269 /* advance to next attribute */
270 i=i+attr_len;
271 }
272
273 if (got_address && (
274 (configuration.outbound_host == NULL) ||
275 (strcmp(configuration.outbound_host, ipstring) != 0) ) ) {
276
277 INFO("STUN: public IP has changed %s -> %s",
278 (configuration.outbound_host) ?
279 configuration.outbound_host:"NULL" ,
280 ipstring);
281
282 if (configuration.outbound_host) {
283 free(configuration.outbound_host);
284 }
285 configuration.outbound_host=malloc(IPSTRING_SIZE);
286
287 strcpy(configuration.outbound_host, ipstring);
288 }
289
290 }
291
292 /*&&&
293 if XOR address and normal address do not match I may have a
294 serious problem (IP provider not passing public IPs to clients).
295 Some linksys routers seem to alter the "normal" IP to be
296 the WAN IP, that may make such a situation visible.
297
298 How can we deal with such a thing? We cannot change the NAT on
299 the provider side, can we? This will be WAY out os scope of siproxds
300 capabilities.
301
302 I should issue an WARNING on such a detected situation.
303 */
304
305
306 rq_pending=0; /* received answer, good */
307 next_stun_send = now + plugin_cfg.period;
308
309 DEBUGC(DBCLASS_BABBLE,"next STUN request in %i sec at %i",
310 plugin_cfg.period, (int)next_stun_send);
311
312 return STS_FAILURE; /* done, do not further process as SIP */
313 }
314 break;
315
316 default:
317 break;
318 }
319
320 return STS_SUCCESS;
321 }
322
323 /*
324 * De-Initialization.
325 * Called during shutdown of siproxd. Gives the plugin the chance
326 * to clean up its mess (e.g. dynamic memory allocation, database
327 * connections, whatever the plugin messes around with)
328 */
PLUGIN_END(plugin_def_t * plugin_def)329 int PLUGIN_END(plugin_def_t *plugin_def){
330 return STS_SUCCESS;
331 }
332
333
334 /*
335 * module-local functions
336 */
stun_validate_response(char * buffer,int len,char * tid)337 static int stun_validate_response(char *buffer, int len, char *tid){
338
339 /* expect min. STUN header + 1 attribute */
340 if (len < 24) {
341 DEBUGC(DBCLASS_BABBLE,"stun_validate_response: no STUN response (too short)");
342 return STS_FAILURE;
343 }
344
345 if (ntohs(*((int*)&buffer[0]) & 0x0000ffff) != 0x0101) {
346 DEBUGC(DBCLASS_BABBLE,"stun_validate_response: no STUN response (type)");
347 return STS_FAILURE;
348 }
349
350 if (memcmp(&buffer[4], tid, STUN_TID_SIZE) != 0) {
351 DEBUGC(DBCLASS_BABBLE,"stun_validate_response: wrong STUN response (TID)");
352 return STS_FAILURE;
353 }
354
355 DEBUGC(DBCLASS_BABBLE,"valid STUN response");
356 return STS_SUCCESS;
357 }
358
stun_send_request(char * tid)359 static int stun_send_request(char *tid){
360 struct in_addr addr;
361 int sts;
362 char stun_rq[28]; /*&&& testing */
363 size_t size=28;
364
365 /* name resolution */
366 if (utils_inet_aton(plugin_cfg.server, &addr) == 0)
367 {
368 /* need name resolution */
369 DEBUGC(DBCLASS_DNS,"resolving name:%s", plugin_cfg.server);
370 sts = get_ip_by_host(plugin_cfg.server, &addr);
371 if (sts == STS_FAILURE) {
372 DEBUGC(DBCLASS_DNS, "stun_send_request: cannot resolve STUN server [%s]",
373 plugin_cfg.server);
374 return STS_FAILURE;
375 }
376 }
377
378 /* compose STUN BIND request - poor mans way */
379 stun_rq[0]=0x00; // type
380 stun_rq[1]=0x01;
381 stun_rq[2]=0x00; // length
382 stun_rq[3]=0x08;
383 memcpy (&stun_rq[4], tid, STUN_TID_SIZE);
384 stun_rq[20]=0x00; // ATTR change request
385 stun_rq[21]=0x03;
386 stun_rq[22]=0x00; // ATTR len 4
387 stun_rq[23]=0x04;
388 stun_rq[24]=0x00; // Change IP not set, Change port not set
389 stun_rq[25]=0x00;
390 stun_rq[26]=0x00;
391 stun_rq[27]=0x00;
392
393 /* and send via the SIP UDP port */
394 sts = sipsock_send(addr, plugin_cfg.port, PROTO_UDP, stun_rq, size);
395
396 return STS_SUCCESS;
397 }
398
stun_new_transaction_id(char * tid)399 static int stun_new_transaction_id(char *tid) {
400 time_t now;
401 /* OSIP MD5 hash lenght is 16 bytes (32 hex digits),
402 * so this does fit for STUN transaction ID... */
403 osip_MD5_CTX Md5Ctx;
404 HASH HA1;
405
406 time(&now);
407
408 /* calc MD5 from servername + timestamp */
409 osip_MD5Init(&Md5Ctx);
410 if (plugin_cfg.server) osip_MD5Update(&Md5Ctx,
411 (unsigned char*)plugin_cfg.server,
412 strlen(plugin_cfg.server));
413 osip_MD5Update(&Md5Ctx, (unsigned char*)&now, sizeof(now));
414 osip_MD5Final(HA1, &Md5Ctx);
415
416 memcpy(tid, HA1, (STUN_TID_SIZE<HASHLEN)? STUN_TID_SIZE:HASHLEN);
417
418 return STS_SUCCESS;
419 }
420