blob: 8753ca9702ef92adf40d576472c1b40f55896433 [file] [log] [blame]
Benny Prijonofff245c2007-04-02 11:44:47 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonofff245c2007-04-02 11:44:47 +00005 *
6 * This program 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 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjsip.h>
21#include <pjlib-util.h>
22#include <pjlib.h>
23
24
25/* Options */
26static struct global_struct
27{
28 pj_caching_pool cp;
29 pjsip_endpoint *endpt;
30 int port;
31 pj_pool_t *pool;
32
33 pj_thread_t *thread;
34 pj_bool_t quit_flag;
35
36 pj_bool_t record_route;
37
38 unsigned name_cnt;
39 pjsip_host_port name[16];
40} global;
41
42
43
44static void app_perror(const char *msg, pj_status_t status)
45{
46 char errmsg[PJ_ERR_MSG_SIZE];
47
48 pj_strerror(status, errmsg, sizeof(errmsg));
49 PJ_LOG(1,(THIS_FILE, "%s: %s", msg, errmsg));
50}
51
52
53static void usage(void)
54{
55 puts("Options:\n"
56 "\n"
Benny Prijono230c2f92007-04-06 10:35:41 +000057 " -p, --port N Set local listener port to N\n"
58 " -R, --rr Perform record routing\n"
59 " -L, --log-level N Set log level to N (default: 4)\n"
60 " -h, --help Show this help screen\n"
Benny Prijonofff245c2007-04-02 11:44:47 +000061 );
62}
63
64
65static pj_status_t init_options(int argc, char *argv[])
66{
67 struct pj_getopt_option long_opt[] = {
68 { "port", 1, 0, 'p'},
69 { "rr", 0, 0, 'R'},
70 { "log-level", 1, 0, 'L'},
71 { "help", 0, 0, 'h'},
Benny Prijono230c2f92007-04-06 10:35:41 +000072 { NULL, 0, 0, 0}
Benny Prijonofff245c2007-04-02 11:44:47 +000073 };
74 int c;
75 int opt_ind;
76
77 pj_optind = 0;
Benny Prijono230c2f92007-04-06 10:35:41 +000078 while((c=pj_getopt_long(argc, argv, "p:L:Rh", long_opt, &opt_ind))!=-1) {
Benny Prijonofff245c2007-04-02 11:44:47 +000079 switch (c) {
80 case 'p':
81 global.port = atoi(pj_optarg);
Benny Prijono64ac31b2007-04-02 19:11:41 +000082 printf("Port is set to %d\n", global.port);
Benny Prijonofff245c2007-04-02 11:44:47 +000083 break;
84
85 case 'R':
86 global.record_route = PJ_TRUE;
87 printf("Using record route mode\n");
88 break;
89
90 case 'L':
91 pj_log_set_level(atoi(pj_optarg));
92 break;
93
94 case 'h':
95 usage();
96 return -1;
97
98 default:
99 puts("Unknown option. Run with --help for help.");
100 return -1;
101 }
102 }
103
104 return PJ_SUCCESS;
105}
106
107
108/*****************************************************************************
109 * This is a very simple PJSIP module, whose sole purpose is to display
110 * incoming and outgoing messages to log. This module will have priority
111 * higher than transport layer, which means:
112 *
113 * - incoming messages will come to this module first before reaching
114 * transaction layer.
115 *
116 * - outgoing messages will come to this module last, after the message
117 * has been 'printed' to contiguous buffer by transport layer and
118 * appropriate transport instance has been decided for this message.
119 *
120 */
121
122/* Notification on incoming messages */
123static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
124{
125 PJ_LOG(5,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
126 "%.*s\n"
127 "--end msg--",
128 rdata->msg_info.len,
129 pjsip_rx_data_get_info(rdata),
130 rdata->tp_info.transport->type_name,
131 rdata->pkt_info.src_name,
132 rdata->pkt_info.src_port,
133 (int)rdata->msg_info.len,
134 rdata->msg_info.msg_buf));
135
136 /* Always return false, otherwise messages will not get processed! */
137 return PJ_FALSE;
138}
139
140/* Notification on outgoing messages */
141static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
142{
143
144 /* Important note:
145 * tp_info field is only valid after outgoing messages has passed
146 * transport layer. So don't try to access tp_info when the module
147 * has lower priority than transport layer.
148 */
149
150 PJ_LOG(5,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
151 "%.*s\n"
152 "--end msg--",
153 (tdata->buf.cur - tdata->buf.start),
154 pjsip_tx_data_get_info(tdata),
155 tdata->tp_info.transport->type_name,
156 tdata->tp_info.dst_name,
157 tdata->tp_info.dst_port,
158 (int)(tdata->buf.cur - tdata->buf.start),
159 tdata->buf.start));
160
161 /* Always return success, otherwise message will not get sent! */
162 return PJ_SUCCESS;
163}
164
165/* The module instance. */
166static pjsip_module mod_msg_logger =
167{
168 NULL, NULL, /* prev, next. */
169 { "mod-msg-logger", 14 }, /* Name. */
170 -1, /* Id */
171 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
172 NULL, /* load() */
173 NULL, /* start() */
174 NULL, /* stop() */
175 NULL, /* unload() */
176 &logging_on_rx_msg, /* on_rx_request() */
177 &logging_on_rx_msg, /* on_rx_response() */
178 &logging_on_tx_msg, /* on_tx_request. */
179 &logging_on_tx_msg, /* on_tx_response() */
180 NULL, /* on_tsx_state() */
181
182};
183
184
185static pj_status_t init_stack(void)
186{
187 pj_status_t status;
188
189 /* Must init PJLIB first: */
190 status = pj_init();
191 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
192
193
194 /* Then init PJLIB-UTIL: */
195 status = pjlib_util_init();
196 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
197
198
199 /* Must create a pool factory before we can allocate any memory. */
200 pj_caching_pool_init(&global.cp, &pj_pool_factory_default_policy, 0);
201
202 /* Create the endpoint: */
203 status = pjsip_endpt_create(&global.cp.factory, NULL, &global.endpt);
204 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
205
206 /* Init transaction layer for stateful proxy only */
207#if STATEFUL
208 status = pjsip_tsx_layer_init_module(global.endpt);
209 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
210#endif
211
212 /* Create listening transport */
213 {
214 pj_sockaddr_in addr;
215
Benny Prijono8ab968f2007-07-20 08:08:30 +0000216 addr.sin_family = pj_AF_INET();
Benny Prijonofff245c2007-04-02 11:44:47 +0000217 addr.sin_addr.s_addr = 0;
218 addr.sin_port = pj_htons((pj_uint16_t)global.port);
219
220 status = pjsip_udp_transport_start( global.endpt, &addr,
221 NULL, 1, NULL);
222 if (status != PJ_SUCCESS)
223 return status;
224 }
225
226 /* Create pool for the application */
227 global.pool = pj_pool_create(&global.cp.factory, "proxyapp",
228 4000, 4000, NULL);
229
230 /* Register the logger module */
231 pjsip_endpt_register_module(global.endpt, &mod_msg_logger);
232
233 return PJ_SUCCESS;
234}
235
236
237static pj_status_t init_proxy(void)
238{
Benny Prijono812fdee2007-12-01 09:07:42 +0000239 pj_sockaddr pri_addr;
240 pj_sockaddr addr_list[16];
Benny Prijono3af35832007-08-01 11:38:14 +0000241 unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list);
Benny Prijonofff245c2007-04-02 11:44:47 +0000242 unsigned i;
243
244 /* List all names matching local endpoint.
245 * Note that PJLIB version 0.6 and newer has a function to
246 * enumerate local IP interface (pj_enum_ip_interface()), so
247 * by using it would be possible to list all IP interfaces in
248 * this host.
249 */
250
251 /* The first address is important since this would be the one
252 * to be added in Record-Route.
253 */
Benny Prijono812fdee2007-12-01 09:07:42 +0000254 if (pj_gethostip(pj_AF_INET(), &pri_addr)==PJ_SUCCESS) {
Benny Prijonofff245c2007-04-02 11:44:47 +0000255 pj_strdup2(global.pool, &global.name[global.name_cnt].host,
Benny Prijono812fdee2007-12-01 09:07:42 +0000256 pj_inet_ntoa(pri_addr.ipv4.sin_addr));
Benny Prijonofff245c2007-04-02 11:44:47 +0000257 global.name[global.name_cnt].port = global.port;
258 global.name_cnt++;
259 }
260
Benny Prijono3af35832007-08-01 11:38:14 +0000261 /* Get the rest of IP interfaces */
Benny Prijono812fdee2007-12-01 09:07:42 +0000262 if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) {
Benny Prijono3af35832007-08-01 11:38:14 +0000263 for (i=0; i<addr_cnt; ++i) {
264
Benny Prijono812fdee2007-12-01 09:07:42 +0000265 if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr)
Benny Prijono3af35832007-08-01 11:38:14 +0000266 continue;
267
268 pj_strdup2(global.pool, &global.name[global.name_cnt].host,
Benny Prijono812fdee2007-12-01 09:07:42 +0000269 pj_inet_ntoa(addr_list[i].ipv4.sin_addr));
Benny Prijono3af35832007-08-01 11:38:14 +0000270 global.name[global.name_cnt].port = global.port;
271 global.name_cnt++;
272 }
273 }
274
Benny Prijono1d6b8952008-07-07 21:31:02 +0000275 /* Add loopback address. */
276#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
Benny Prijonofff245c2007-04-02 11:44:47 +0000277 global.name[global.name_cnt].host = pj_str("127.0.0.1");
278 global.name[global.name_cnt].port = global.port;
279 global.name_cnt++;
Benny Prijono1d6b8952008-07-07 21:31:02 +0000280#endif
Benny Prijonofff245c2007-04-02 11:44:47 +0000281
282 global.name[global.name_cnt].host = *pj_gethostname();
283 global.name[global.name_cnt].port = global.port;
284 global.name_cnt++;
285
286 global.name[global.name_cnt].host = pj_str("localhost");
287 global.name[global.name_cnt].port = global.port;
288 global.name_cnt++;
289
290 PJ_LOG(3,(THIS_FILE, "Proxy started, listening on port %d", global.port));
291 PJ_LOG(3,(THIS_FILE, "Local host aliases:"));
292 for (i=0; i<global.name_cnt; ++i) {
293 PJ_LOG(3,(THIS_FILE, " %.*s:%d",
294 (int)global.name[i].host.slen,
295 global.name[i].host.ptr,
296 global.name[i].port));
297 }
298
299 if (global.record_route) {
300 PJ_LOG(3,(THIS_FILE, "Using Record-Route mode"));
301 }
302
303 return PJ_SUCCESS;
304}
305
306
307#if PJ_HAS_THREADS
308static int worker_thread(void *p)
309{
310 pj_time_val delay = {0, 10};
311
312 PJ_UNUSED_ARG(p);
313
314 while (!global.quit_flag) {
315 pjsip_endpt_handle_events(global.endpt, &delay);
316 }
317
318 return 0;
319}
320#endif
321
322
323/* Utility to determine if URI is local to this host. */
324static pj_bool_t is_uri_local(const pjsip_sip_uri *uri)
325{
326 unsigned i;
327 for (i=0; i<global.name_cnt; ++i) {
328 if ((uri->port == global.name[i].port ||
329 (uri->port==0 && global.name[i].port==5060)) &&
330 pj_stricmp(&uri->host, &global.name[i].host)==0)
331 {
332 /* Match */
333 return PJ_TRUE;
334 }
335 }
336
337 /* Doesn't match */
338 return PJ_FALSE;
339}
340
341
342/* Proxy utility to verify incoming requests.
343 * Return non-zero if verification failed.
344 */
345static pj_status_t proxy_verify_request(pjsip_rx_data *rdata)
346{
347 const pj_str_t STR_PROXY_REQUIRE = {"Proxy-Require", 13};
348
349 /* RFC 3261 Section 16.3 Request Validation */
350
351 /* Before an element can proxy a request, it MUST verify the message's
352 * validity. A valid message must pass the following checks:
353 *
354 * 1. Reasonable Syntax
355 * 2. URI scheme
356 * 3. Max-Forwards
357 * 4. (Optional) Loop Detection
358 * 5. Proxy-Require
359 * 6. Proxy-Authorization
360 */
361
362 /* 1. Reasonable Syntax.
363 * This would have been checked by transport layer.
364 */
365
366 /* 2. URI scheme.
367 * We only want to support "sip:" URI scheme for this simple proxy.
368 */
369 if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.msg->line.req.uri)) {
370 pjsip_endpt_respond_stateless(global.endpt, rdata,
371 PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL,
372 NULL, NULL);
373 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_UNSUPPORTED_URI_SCHEME);
374 }
375
376 /* 3. Max-Forwards.
377 * Send error if Max-Forwards is 1 or lower.
378 */
379 if (rdata->msg_info.max_fwd && rdata->msg_info.max_fwd->ivalue <= 1) {
380 pjsip_endpt_respond_stateless(global.endpt, rdata,
381 PJSIP_SC_TOO_MANY_HOPS, NULL,
382 NULL, NULL);
383 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TOO_MANY_HOPS);
384 }
385
386 /* 4. (Optional) Loop Detection.
387 * Nah, we don't do that with this simple proxy.
388 */
389
390 /* 5. Proxy-Require */
391 if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_PROXY_REQUIRE,
392 NULL) != NULL)
393 {
394 pjsip_endpt_respond_stateless(global.endpt, rdata,
395 PJSIP_SC_BAD_EXTENSION, NULL,
396 NULL, NULL);
397 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION);
398 }
399
400 /* 6. Proxy-Authorization.
401 * Nah, we don't require any authorization with this sample.
402 */
403
404 return PJ_SUCCESS;
405}
406
407
408/* Process route information in the reqeust */
409static pj_status_t proxy_process_routing(pjsip_tx_data *tdata)
410{
411 pjsip_sip_uri *target;
412 pjsip_route_hdr *hroute;
413
414 /* RFC 3261 Section 16.4 Route Information Preprocessing */
415
416 target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
417
418 /* The proxy MUST inspect the Request-URI of the request. If the
419 * Request-URI of the request contains a value this proxy previously
420 * placed into a Record-Route header field (see Section 16.6 item 4),
421 * the proxy MUST replace the Request-URI in the request with the last
422 * value from the Route header field, and remove that value from the
423 * Route header field. The proxy MUST then proceed as if it received
424 * this modified request.
425 */
Benny Prijono251fd782007-08-02 14:58:19 +0000426 if (is_uri_local(target)) {
427 pjsip_route_hdr *r;
428 pjsip_sip_uri *uri;
429
430 /* Find the first Route header */
431 r = hroute = (pjsip_route_hdr*)
432 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
433 if (r == NULL) {
434 /* No Route header. This request is destined for this proxy. */
435 return PJ_SUCCESS;
436 }
437
438 /* Find the last Route header */
439 while ( (r=(pjsip_route_hdr*)pjsip_msg_find_hdr(tdata->msg,
440 PJSIP_H_ROUTE,
441 r->next)) != NULL )
442 {
443 hroute = r;
444 }
445
446 /* If the last Route header doesn't have ";lr" parameter, then
447 * this is a strict-routed request indeed, and we follow the steps
448 * in processing strict-route requests above.
449 *
450 * But if it does contain ";lr" parameter, skip the strict-route
451 * processing.
452 */
453 uri = (pjsip_sip_uri*)
454 pjsip_uri_get_uri(&hroute->name_addr);
455 if (uri->lr_param == 0) {
456 /* Yes this is strict route, so:
457 * - replace req URI with the URI in Route header,
458 * - remove the Route header,
459 * - proceed as if it received this modified request.
460 */
461 tdata->msg->line.req.uri = hroute->name_addr.uri;
462 target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
463 pj_list_erase(hroute);
464 }
465 }
Benny Prijonofff245c2007-04-02 11:44:47 +0000466
467 /* If the Request-URI contains a maddr parameter, the proxy MUST check
468 * to see if its value is in the set of addresses or domains the proxy
469 * is configured to be responsible for. If the Request-URI has a maddr
470 * parameter with a value the proxy is responsible for, and the request
471 * was received using the port and transport indicated (explicitly or by
472 * default) in the Request-URI, the proxy MUST strip the maddr and any
473 * non-default port or transport parameter and continue processing as if
474 * those values had not been present in the request.
475 */
476 if (target->maddr_param.slen != 0) {
477 pjsip_sip_uri maddr_uri;
478
479 maddr_uri.host = target->maddr_param;
480 maddr_uri.port = global.port;
481
482 if (is_uri_local(&maddr_uri)) {
483 target->maddr_param.slen = 0;
484 target->port = 0;
485 target->transport_param.slen = 0;
486 }
487 }
488
489 /* If the first value in the Route header field indicates this proxy,
490 * the proxy MUST remove that value from the request.
491 */
492 hroute = (pjsip_route_hdr*)
493 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
494 if (hroute && is_uri_local((pjsip_sip_uri*)hroute->name_addr.uri)) {
495 pj_list_erase(hroute);
496 }
497
498 return PJ_SUCCESS;
499}
500
501
502/* Postprocess the request before forwarding it */
503static void proxy_postprocess(pjsip_tx_data *tdata)
504{
505 /* Optionally record-route */
506 if (global.record_route) {
507 char uribuf[128];
508 pj_str_t uri;
509 const pj_str_t H_RR = { "Record-Route", 12 };
510 pjsip_generic_string_hdr *rr;
511
512 pj_ansi_snprintf(uribuf, sizeof(uribuf), "<sip:%.*s:%d;lr>",
513 (int)global.name[0].host.slen,
514 global.name[0].host.ptr,
515 global.name[0].port);
516 uri = pj_str(uribuf);
517 rr = pjsip_generic_string_hdr_create(tdata->pool,
518 &H_RR, &uri);
Benny Prijono251fd782007-08-02 14:58:19 +0000519 pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr);
Benny Prijonofff245c2007-04-02 11:44:47 +0000520 }
521}
522
523
524/* Calculate new target for the request */
525static pj_status_t proxy_calculate_target(pjsip_rx_data *rdata,
526 pjsip_tx_data *tdata)
527{
528 pjsip_sip_uri *target;
529
530 /* RFC 3261 Section 16.5 Determining Request Targets */
531
532 target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
533
534 /* If the Request-URI of the request contains an maddr parameter, the
535 * Request-URI MUST be placed into the target set as the only target
536 * URI, and the proxy MUST proceed to Section 16.6.
537 */
538 if (target->maddr_param.slen) {
539 proxy_postprocess(tdata);
540 return PJ_SUCCESS;
541 }
542
543
544 /* If the domain of the Request-URI indicates a domain this element is
545 * not responsible for, the Request-URI MUST be placed into the target
546 * set as the only target, and the element MUST proceed to the task of
547 * Request Forwarding (Section 16.6).
548 */
549 if (!is_uri_local(target)) {
550 proxy_postprocess(tdata);
551 return PJ_SUCCESS;
552 }
553
554 /* If the target set for the request has not been predetermined as
555 * described above, this implies that the element is responsible for the
556 * domain in the Request-URI, and the element MAY use whatever mechanism
557 * it desires to determine where to send the request.
558 */
559
560 /* We're not interested to receive request destined to us, so
561 * respond with 404/Not Found (only if request is not ACK!).
562 */
563 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
564 pjsip_endpt_respond_stateless(global.endpt, rdata,
565 PJSIP_SC_NOT_FOUND, NULL,
566 NULL, NULL);
567 }
568
569 /* Delete the request since we're not forwarding it */
570 pjsip_tx_data_dec_ref(tdata);
571
572 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_FOUND);
573}
574
575
576/* Destroy stack */
577static void destroy_stack(void)
578{
579 pjsip_endpt_destroy(global.endpt);
580 pj_pool_release(global.pool);
581 pj_caching_pool_destroy(&global.cp);
582
583 pj_shutdown();
584}
585