1
/* $Id: proxy.h 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
21
#include <pjlib-util.h>
26
static struct global_struct
29
pjsip_endpoint *endpt;
36
pj_bool_t record_route;
39
pjsip_host_port name[16];
44
static void app_perror(const char *msg, pj_status_t status)
46
char errmsg[PJ_ERR_MSG_SIZE];
48
pj_strerror(status, errmsg, sizeof(errmsg));
49
PJ_LOG(1,(THIS_FILE, "%s: %s", msg, errmsg));
53
static void usage(void)
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"
65
static pj_status_t init_options(int argc, char *argv[])
67
struct pj_getopt_option long_opt[] = {
70
{ "log-level", 1, 0, 'L'},
78
while((c=pj_getopt_long(argc, argv, "p:L:Rh", long_opt, &opt_ind))!=-1) {
81
global.port = atoi(pj_optarg);
82
printf("Port is set to %d\n", global.port);
86
global.record_route = PJ_TRUE;
87
printf("Using record route mode\n");
91
pj_log_set_level(atoi(pj_optarg));
99
puts("Unknown option. Run with --help for help.");
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:
113
* - incoming messages will come to this module first before reaching
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.
122
/* Notification on incoming messages */
123
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
125
PJ_LOG(5,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
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));
136
/* Always return false, otherwise messages will not get processed! */
140
/* Notification on outgoing messages */
141
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
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.
150
PJ_LOG(5,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
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),
161
/* Always return success, otherwise message will not get sent! */
165
/* The module instance. */
166
static pjsip_module mod_msg_logger =
168
NULL, NULL, /* prev, next. */
169
{ "mod-msg-logger", 14 }, /* Name. */
171
PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
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() */
185
static pj_status_t init_stack(void)
189
/* Must init PJLIB first: */
191
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
194
/* Then init PJLIB-UTIL: */
195
status = pjlib_util_init();
196
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
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);
202
/* Create the endpoint: */
203
status = pjsip_endpt_create(&global.cp.factory, NULL, &global.endpt);
204
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
206
/* Init transaction layer for stateful proxy only */
208
status = pjsip_tsx_layer_init_module(global.endpt);
209
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
212
/* Create listening transport */
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);
220
status = pjsip_udp_transport_start( global.endpt, &addr,
222
if (status != PJ_SUCCESS)
226
/* Create pool for the application */
227
global.pool = pj_pool_create(&global.cp.factory, "proxyapp",
230
/* Register the logger module */
231
pjsip_endpt_register_module(global.endpt, &mod_msg_logger);
237
static pj_status_t init_proxy(void)
239
pj_sockaddr pri_addr;
240
pj_sockaddr addr_list[16];
241
unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list);
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
251
/* The first address is important since this would be the one
252
* to be added in Record-Route.
254
if (pj_gethostip(pj_AF_INET(), &pri_addr)==PJ_SUCCESS) {
255
pj_strdup2(global.pool, &global.name[global.name_cnt].host,
256
pj_inet_ntoa(pri_addr.ipv4.sin_addr));
257
global.name[global.name_cnt].port = global.port;
261
/* Get the rest of IP interfaces */
262
if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) {
263
for (i=0; i<addr_cnt; ++i) {
265
if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr)
268
pj_strdup2(global.pool, &global.name[global.name_cnt].host,
269
pj_inet_ntoa(addr_list[i].ipv4.sin_addr));
270
global.name[global.name_cnt].port = global.port;
275
/* Add loopback address. */
276
#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
277
global.name[global.name_cnt].host = pj_str("127.0.0.1");
278
global.name[global.name_cnt].port = global.port;
282
global.name[global.name_cnt].host = *pj_gethostname();
283
global.name[global.name_cnt].port = global.port;
286
global.name[global.name_cnt].host = pj_str("localhost");
287
global.name[global.name_cnt].port = global.port;
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));
299
if (global.record_route) {
300
PJ_LOG(3,(THIS_FILE, "Using Record-Route mode"));
308
static int worker_thread(void *p)
310
pj_time_val delay = {0, 10};
314
while (!global.quit_flag) {
315
pjsip_endpt_handle_events(global.endpt, &delay);
323
/* Utility to determine if URI is local to this host. */
324
static pj_bool_t is_uri_local(const pjsip_sip_uri *uri)
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)
342
/* Proxy utility to verify incoming requests.
343
* Return non-zero if verification failed.
345
static pj_status_t proxy_verify_request(pjsip_rx_data *rdata)
347
const pj_str_t STR_PROXY_REQUIRE = {"Proxy-Require", 13};
349
/* RFC 3261 Section 16.3 Request Validation */
351
/* Before an element can proxy a request, it MUST verify the message's
352
* validity. A valid message must pass the following checks:
354
* 1. Reasonable Syntax
357
* 4. (Optional) Loop Detection
359
* 6. Proxy-Authorization
362
/* 1. Reasonable Syntax.
363
* This would have been checked by transport layer.
367
* We only want to support "sip:" URI scheme for this simple proxy.
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,
373
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_UNSUPPORTED_URI_SCHEME);
377
* Send error if Max-Forwards is 1 or lower.
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,
383
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TOO_MANY_HOPS);
386
/* 4. (Optional) Loop Detection.
387
* Nah, we don't do that with this simple proxy.
390
/* 5. Proxy-Require */
391
if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_PROXY_REQUIRE,
394
pjsip_endpt_respond_stateless(global.endpt, rdata,
395
PJSIP_SC_BAD_EXTENSION, NULL,
397
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION);
400
/* 6. Proxy-Authorization.
401
* Nah, we don't require any authorization with this sample.
408
/* Process route information in the reqeust */
409
static pj_status_t proxy_process_routing(pjsip_tx_data *tdata)
411
pjsip_sip_uri *target;
412
pjsip_route_hdr *hroute;
414
/* RFC 3261 Section 16.4 Route Information Preprocessing */
416
target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
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.
426
if (is_uri_local(target)) {
430
/* Find the first Route header */
431
r = hroute = (pjsip_route_hdr*)
432
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
434
/* No Route header. This request is destined for this proxy. */
438
/* Find the last Route header */
439
while ( (r=(pjsip_route_hdr*)pjsip_msg_find_hdr(tdata->msg,
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.
450
* But if it does contain ";lr" parameter, skip the strict-route
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.
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);
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.
476
if (target->maddr_param.slen != 0) {
477
pjsip_sip_uri maddr_uri;
479
maddr_uri.host = target->maddr_param;
480
maddr_uri.port = global.port;
482
if (is_uri_local(&maddr_uri)) {
483
target->maddr_param.slen = 0;
485
target->transport_param.slen = 0;
489
/* If the first value in the Route header field indicates this proxy,
490
* the proxy MUST remove that value from the request.
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);
502
/* Postprocess the request before forwarding it */
503
static void proxy_postprocess(pjsip_tx_data *tdata)
505
/* Optionally record-route */
506
if (global.record_route) {
509
const pj_str_t H_RR = { "Record-Route", 12 };
510
pjsip_generic_string_hdr *rr;
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,
519
pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr);
524
/* Calculate new target for the request */
525
static pj_status_t proxy_calculate_target(pjsip_rx_data *rdata,
526
pjsip_tx_data *tdata)
528
pjsip_sip_uri *target;
530
/* RFC 3261 Section 16.5 Determining Request Targets */
532
target = (pjsip_sip_uri*) tdata->msg->line.req.uri;
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.
538
if (target->maddr_param.slen) {
539
proxy_postprocess(tdata);
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).
549
if (!is_uri_local(target)) {
550
proxy_postprocess(tdata);
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.
560
/* We're not interested to receive request destined to us, so
561
* respond with 404/Not Found (only if request is not ACK!).
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,
569
/* Delete the request since we're not forwarding it */
570
pjsip_tx_data_dec_ref(tdata);
572
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_FOUND);
577
static void destroy_stack(void)
579
pjsip_endpt_destroy(global.endpt);
580
pj_pool_release(global.pool);
581
pj_caching_pool_destroy(&global.cp);