1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
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 */
26 static 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 
app_perror(const char * msg,pj_status_t status)44 static 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 
usage(void)53 static void usage(void)
54 {
55     puts("Options:\n"
56 	 "\n"
57 	 " -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"
61 	 );
62 }
63 
64 
init_options(int argc,char * argv[])65 static 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'},
72 	{ NULL,		0, 0, 0}
73     };
74     int c;
75     int opt_ind;
76 
77     pj_optind = 0;
78     while((c=pj_getopt_long(argc, argv, "p:L:Rh", long_opt, &opt_ind))!=-1) {
79 	switch (c) {
80 	case 'p':
81 	    global.port = atoi(pj_optarg);
82 	    printf("Port is set to %d\n", global.port);
83 	    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 */
logging_on_rx_msg(pjsip_rx_data * rdata)123 static 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 */
logging_on_tx_msg(pjsip_tx_data * tdata)141 static 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. */
166 static 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 
init_stack(void)185 static 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 
216 	addr.sin_family = pj_AF_INET();
217 	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 
init_proxy(void)237 static pj_status_t init_proxy(void)
238 {
239     pj_sockaddr pri_addr;
240     pj_sockaddr addr_list[16];
241     unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list);
242     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      */
254     if (pj_gethostip(pj_AF_INET(), &pri_addr)==PJ_SUCCESS) {
255 	char addr[PJ_INET_ADDRSTRLEN];
256 	pj_inet_ntop(pj_AF_INET(), &pri_addr.ipv4.sin_addr, addr,
257 		     sizeof(addr));
258 	pj_strdup2(global.pool, &global.name[global.name_cnt].host, addr);
259 	global.name[global.name_cnt].port = global.port;
260 	global.name_cnt++;
261     }
262 
263     /* Get the rest of IP interfaces */
264     if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS)
265     {
266 	for (i=0; i<addr_cnt; ++i) {
267 	    char addr[PJ_INET_ADDRSTRLEN];
268 
269 	    if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr)
270 		continue;
271 
272 	    pj_inet_ntop(pj_AF_INET(), &addr_list[i].ipv4.sin_addr, addr,
273 	    		 sizeof(addr));
274 	    pj_strdup2(global.pool, &global.name[global.name_cnt].host,
275 		       addr);
276 	    global.name[global.name_cnt].port = global.port;
277 	    global.name_cnt++;
278 	}
279     }
280 
281     /* Add loopback address. */
282 #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
283     global.name[global.name_cnt].host = pj_str("127.0.0.1");
284     global.name[global.name_cnt].port = global.port;
285     global.name_cnt++;
286 #endif
287 
288     global.name[global.name_cnt].host = *pj_gethostname();
289     global.name[global.name_cnt].port = global.port;
290     global.name_cnt++;
291 
292     global.name[global.name_cnt].host = pj_str("localhost");
293     global.name[global.name_cnt].port = global.port;
294     global.name_cnt++;
295 
296     PJ_LOG(3,(THIS_FILE, "Proxy started, listening on port %d", global.port));
297     PJ_LOG(3,(THIS_FILE, "Local host aliases:"));
298     for (i=0; i<global.name_cnt; ++i) {
299 	PJ_LOG(3,(THIS_FILE, " %.*s:%d",
300 		  (int)global.name[i].host.slen,
301 		  global.name[i].host.ptr,
302 		  global.name[i].port));
303     }
304 
305     if (global.record_route) {
306 	PJ_LOG(3,(THIS_FILE, "Using Record-Route mode"));
307     }
308 
309     return PJ_SUCCESS;
310 }
311 
312 
313 #if PJ_HAS_THREADS
worker_thread(void * p)314 static int worker_thread(void *p)
315 {
316     pj_time_val delay = {0, 10};
317 
318     PJ_UNUSED_ARG(p);
319 
320     while (!global.quit_flag) {
321 	pjsip_endpt_handle_events(global.endpt, &delay);
322     }
323 
324     return 0;
325 }
326 #endif
327 
328 
329 /* Utility to determine if URI is local to this host. */
is_uri_local(const pjsip_sip_uri * uri)330 static pj_bool_t is_uri_local(const pjsip_sip_uri *uri)
331 {
332     unsigned i;
333     for (i=0; i<global.name_cnt; ++i) {
334 	if ((uri->port == global.name[i].port ||
335 	     (uri->port==0 && global.name[i].port==5060)) &&
336 	    pj_stricmp(&uri->host, &global.name[i].host)==0)
337 	{
338 	    /* Match */
339 	    return PJ_TRUE;
340 	}
341     }
342 
343     /* Doesn't match */
344     return PJ_FALSE;
345 }
346 
347 
348 /* Proxy utility to verify incoming requests.
349  * Return non-zero if verification failed.
350  */
proxy_verify_request(pjsip_rx_data * rdata)351 static pj_status_t proxy_verify_request(pjsip_rx_data *rdata)
352 {
353     const pj_str_t STR_PROXY_REQUIRE = {"Proxy-Require", 13};
354 
355     /* RFC 3261 Section 16.3 Request Validation */
356 
357     /* Before an element can proxy a request, it MUST verify the message's
358      * validity.  A valid message must pass the following checks:
359      *
360      * 1. Reasonable Syntax
361      * 2. URI scheme
362      * 3. Max-Forwards
363      * 4. (Optional) Loop Detection
364      * 5. Proxy-Require
365      * 6. Proxy-Authorization
366      */
367 
368     /* 1. Reasonable Syntax.
369      * This would have been checked by transport layer.
370      */
371 
372     /* 2. URI scheme.
373      * We only want to support "sip:"/"sips:" URI scheme for this simple proxy.
374      */
375     if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.msg->line.req.uri) &&
376 	!PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
377     {
378 	pjsip_endpt_respond_stateless(global.endpt, rdata,
379 				      PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL,
380 				      NULL, NULL);
381 	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_UNSUPPORTED_URI_SCHEME);
382     }
383 
384     /* 3. Max-Forwards.
385      * Send error if Max-Forwards is 1 or lower.
386      */
387     if (rdata->msg_info.max_fwd && rdata->msg_info.max_fwd->ivalue <= 1) {
388 	pjsip_endpt_respond_stateless(global.endpt, rdata,
389 				      PJSIP_SC_TOO_MANY_HOPS, NULL,
390 				      NULL, NULL);
391 	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TOO_MANY_HOPS);
392     }
393 
394     /* 4. (Optional) Loop Detection.
395      * Nah, we don't do that with this simple proxy.
396      */
397 
398     /* 5. Proxy-Require */
399     if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_PROXY_REQUIRE,
400 				   NULL) != NULL)
401     {
402 	pjsip_endpt_respond_stateless(global.endpt, rdata,
403 				      PJSIP_SC_BAD_EXTENSION, NULL,
404 				      NULL, NULL);
405 	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION);
406     }
407 
408     /* 6. Proxy-Authorization.
409      * Nah, we don't require any authorization with this sample.
410      */
411 
412     return PJ_SUCCESS;
413 }
414 
415 
416 /* Process route information in the reqeust */
proxy_process_routing(pjsip_tx_data * tdata)417 static pj_status_t proxy_process_routing(pjsip_tx_data *tdata)
418 {
419     pjsip_sip_uri *target;
420     pjsip_route_hdr *hroute;
421 
422     /* RFC 3261 Section 16.4 Route Information Preprocessing */
423 
424     target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
425 
426     /* The proxy MUST inspect the Request-URI of the request.  If the
427      * Request-URI of the request contains a value this proxy previously
428      * placed into a Record-Route header field (see Section 16.6 item 4),
429      * the proxy MUST replace the Request-URI in the request with the last
430      * value from the Route header field, and remove that value from the
431      * Route header field.  The proxy MUST then proceed as if it received
432      * this modified request.
433      */
434     if (is_uri_local(target)) {
435 	pjsip_route_hdr *r;
436 	pjsip_sip_uri *uri;
437 
438 	/* Find the first Route header */
439 	r = hroute = (pjsip_route_hdr*)
440 		     pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
441 	if (r == NULL) {
442 	    /* No Route header. This request is destined for this proxy. */
443 	    return PJ_SUCCESS;
444 	}
445 
446 	/* Find the last Route header */
447 	while ( (r=(pjsip_route_hdr*)pjsip_msg_find_hdr(tdata->msg,
448 						        PJSIP_H_ROUTE,
449 							r->next)) != NULL )
450 	{
451 	    hroute = r;
452 	}
453 
454 	/* If the last Route header doesn't have ";lr" parameter, then
455 	 * this is a strict-routed request indeed, and we follow the steps
456 	 * in processing strict-route requests above.
457 	 *
458 	 * But if it does contain ";lr" parameter, skip the strict-route
459 	 * processing.
460 	 */
461 	uri = (pjsip_sip_uri*)
462 	      pjsip_uri_get_uri(&hroute->name_addr);
463 	if (uri->lr_param == 0) {
464 	    /* Yes this is strict route, so:
465 	     * - replace req URI with the URI in Route header,
466 	     * - remove the Route header,
467 	     * - proceed as if it received this modified request.
468 	    */
469 	    tdata->msg->line.req.uri = hroute->name_addr.uri;
470 	    target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
471 	    pj_list_erase(hroute);
472 	}
473     }
474 
475     /* If the Request-URI contains a maddr parameter, the proxy MUST check
476      * to see if its value is in the set of addresses or domains the proxy
477      * is configured to be responsible for.  If the Request-URI has a maddr
478      * parameter with a value the proxy is responsible for, and the request
479      * was received using the port and transport indicated (explicitly or by
480      * default) in the Request-URI, the proxy MUST strip the maddr and any
481      * non-default port or transport parameter and continue processing as if
482      * those values had not been present in the request.
483      */
484     if (target->maddr_param.slen != 0) {
485 	pjsip_sip_uri maddr_uri;
486 
487 	maddr_uri.host = target->maddr_param;
488 	maddr_uri.port = global.port;
489 
490 	if (is_uri_local(&maddr_uri)) {
491 	    target->maddr_param.slen = 0;
492 	    target->port = 0;
493 	    target->transport_param.slen = 0;
494 	}
495     }
496 
497     /* If the first value in the Route header field indicates this proxy,
498      * the proxy MUST remove that value from the request.
499      */
500     hroute = (pjsip_route_hdr*)
501 	      pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
502     if (hroute && is_uri_local((pjsip_sip_uri*)hroute->name_addr.uri)) {
503 	pj_list_erase(hroute);
504     }
505 
506     return PJ_SUCCESS;
507 }
508 
509 
510 /* Postprocess the request before forwarding it */
proxy_postprocess(pjsip_tx_data * tdata)511 static void proxy_postprocess(pjsip_tx_data *tdata)
512 {
513     /* Optionally record-route */
514     if (global.record_route) {
515 	char uribuf[128];
516 	pj_str_t uri;
517 	const pj_str_t H_RR = { "Record-Route", 12 };
518 	pjsip_generic_string_hdr *rr;
519 
520 	pj_ansi_snprintf(uribuf, sizeof(uribuf), "<sip:%.*s:%d;lr>",
521 			 (int)global.name[0].host.slen,
522 			 global.name[0].host.ptr,
523 			 global.name[0].port);
524 	uri = pj_str(uribuf);
525 	rr = pjsip_generic_string_hdr_create(tdata->pool,
526 					     &H_RR, &uri);
527 	pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr);
528     }
529 }
530 
531 
532 /* Calculate new target for the request */
proxy_calculate_target(pjsip_rx_data * rdata,pjsip_tx_data * tdata)533 static pj_status_t proxy_calculate_target(pjsip_rx_data *rdata,
534 					  pjsip_tx_data *tdata)
535 {
536     pjsip_sip_uri *target;
537 
538     /* RFC 3261 Section 16.5 Determining Request Targets */
539 
540     target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
541 
542     /* If the Request-URI of the request contains an maddr parameter, the
543      * Request-URI MUST be placed into the target set as the only target
544      * URI, and the proxy MUST proceed to Section 16.6.
545      */
546     if (target->maddr_param.slen) {
547 	proxy_postprocess(tdata);
548 	return PJ_SUCCESS;
549     }
550 
551 
552     /* If the domain of the Request-URI indicates a domain this element is
553      * not responsible for, the Request-URI MUST be placed into the target
554      * set as the only target, and the element MUST proceed to the task of
555      * Request Forwarding (Section 16.6).
556      */
557     if (!is_uri_local(target)) {
558 	proxy_postprocess(tdata);
559 	return PJ_SUCCESS;
560     }
561 
562     /* If the target set for the request has not been predetermined as
563      * described above, this implies that the element is responsible for the
564      * domain in the Request-URI, and the element MAY use whatever mechanism
565      * it desires to determine where to send the request.
566      */
567 
568     /* We're not interested to receive request destined to us, so
569      * respond with 404/Not Found (only if request is not ACK!).
570      */
571     if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
572 	pjsip_endpt_respond_stateless(global.endpt, rdata,
573 				      PJSIP_SC_NOT_FOUND, NULL,
574 				      NULL, NULL);
575     }
576 
577     /* Delete the request since we're not forwarding it */
578     pjsip_tx_data_dec_ref(tdata);
579 
580     return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_FOUND);
581 }
582 
583 
584 /* Destroy stack */
destroy_stack(void)585 static void destroy_stack(void)
586 {
587     pjsip_endpt_destroy(global.endpt);
588     pj_pool_release(global.pool);
589     pj_caching_pool_destroy(&global.cp);
590 
591     pj_shutdown();
592 }
593 
594