From f3ab456a17af1c89a6e3be4d20c5944853df1cb0 Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Mon, 7 Jan 2013 14:24:28 -0600 Subject: Import pjproject-2.0.1 --- pjsip-apps/src/samples/proxy.h | 585 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 pjsip-apps/src/samples/proxy.h (limited to 'pjsip-apps/src/samples/proxy.h') diff --git a/pjsip-apps/src/samples/proxy.h b/pjsip-apps/src/samples/proxy.h new file mode 100644 index 0000000..2e1b383 --- /dev/null +++ b/pjsip-apps/src/samples/proxy.h @@ -0,0 +1,585 @@ +/* $Id: proxy.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include + + +/* Options */ +static struct global_struct +{ + pj_caching_pool cp; + pjsip_endpoint *endpt; + int port; + pj_pool_t *pool; + + pj_thread_t *thread; + pj_bool_t quit_flag; + + pj_bool_t record_route; + + unsigned name_cnt; + pjsip_host_port name[16]; +} global; + + + +static void app_perror(const char *msg, pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(THIS_FILE, "%s: %s", msg, errmsg)); +} + + +static void usage(void) +{ + puts("Options:\n" + "\n" + " -p, --port N Set local listener port to N\n" + " -R, --rr Perform record routing\n" + " -L, --log-level N Set log level to N (default: 4)\n" + " -h, --help Show this help screen\n" + ); +} + + +static pj_status_t init_options(int argc, char *argv[]) +{ + struct pj_getopt_option long_opt[] = { + { "port", 1, 0, 'p'}, + { "rr", 0, 0, 'R'}, + { "log-level", 1, 0, 'L'}, + { "help", 0, 0, 'h'}, + { NULL, 0, 0, 0} + }; + int c; + int opt_ind; + + pj_optind = 0; + while((c=pj_getopt_long(argc, argv, "p:L:Rh", long_opt, &opt_ind))!=-1) { + switch (c) { + case 'p': + global.port = atoi(pj_optarg); + printf("Port is set to %d\n", global.port); + break; + + case 'R': + global.record_route = PJ_TRUE; + printf("Using record route mode\n"); + break; + + case 'L': + pj_log_set_level(atoi(pj_optarg)); + break; + + case 'h': + usage(); + return -1; + + default: + puts("Unknown option. Run with --help for help."); + return -1; + } + } + + return PJ_SUCCESS; +} + + +/***************************************************************************** + * This is a very simple PJSIP module, whose sole purpose is to display + * incoming and outgoing messages to log. This module will have priority + * higher than transport layer, which means: + * + * - incoming messages will come to this module first before reaching + * transaction layer. + * + * - outgoing messages will come to this module last, after the message + * has been 'printed' to contiguous buffer by transport layer and + * appropriate transport instance has been decided for this message. + * + */ + +/* Notification on incoming messages */ +static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) +{ + PJ_LOG(5,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n" + "%.*s\n" + "--end msg--", + rdata->msg_info.len, + pjsip_rx_data_get_info(rdata), + rdata->tp_info.transport->type_name, + rdata->pkt_info.src_name, + rdata->pkt_info.src_port, + (int)rdata->msg_info.len, + rdata->msg_info.msg_buf)); + + /* Always return false, otherwise messages will not get processed! */ + return PJ_FALSE; +} + +/* Notification on outgoing messages */ +static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) +{ + + /* Important note: + * tp_info field is only valid after outgoing messages has passed + * transport layer. So don't try to access tp_info when the module + * has lower priority than transport layer. + */ + + PJ_LOG(5,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n" + "%.*s\n" + "--end msg--", + (tdata->buf.cur - tdata->buf.start), + pjsip_tx_data_get_info(tdata), + tdata->tp_info.transport->type_name, + tdata->tp_info.dst_name, + tdata->tp_info.dst_port, + (int)(tdata->buf.cur - tdata->buf.start), + tdata->buf.start)); + + /* Always return success, otherwise message will not get sent! */ + return PJ_SUCCESS; +} + +/* The module instance. */ +static pjsip_module mod_msg_logger = +{ + NULL, NULL, /* prev, next. */ + { "mod-msg-logger", 14 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &logging_on_rx_msg, /* on_rx_request() */ + &logging_on_rx_msg, /* on_rx_response() */ + &logging_on_tx_msg, /* on_tx_request. */ + &logging_on_tx_msg, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + +}; + + +static pj_status_t init_stack(void) +{ + pj_status_t status; + + /* Must init PJLIB first: */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + + /* Then init PJLIB-UTIL: */ + status = pjlib_util_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&global.cp, &pj_pool_factory_default_policy, 0); + + /* Create the endpoint: */ + status = pjsip_endpt_create(&global.cp.factory, NULL, &global.endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + /* Init transaction layer for stateful proxy only */ +#if STATEFUL + status = pjsip_tsx_layer_init_module(global.endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + + /* Create listening transport */ + { + pj_sockaddr_in addr; + + addr.sin_family = pj_AF_INET(); + addr.sin_addr.s_addr = 0; + addr.sin_port = pj_htons((pj_uint16_t)global.port); + + status = pjsip_udp_transport_start( global.endpt, &addr, + NULL, 1, NULL); + if (status != PJ_SUCCESS) + return status; + } + + /* Create pool for the application */ + global.pool = pj_pool_create(&global.cp.factory, "proxyapp", + 4000, 4000, NULL); + + /* Register the logger module */ + pjsip_endpt_register_module(global.endpt, &mod_msg_logger); + + return PJ_SUCCESS; +} + + +static pj_status_t init_proxy(void) +{ + pj_sockaddr pri_addr; + pj_sockaddr addr_list[16]; + unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list); + unsigned i; + + /* List all names matching local endpoint. + * Note that PJLIB version 0.6 and newer has a function to + * enumerate local IP interface (pj_enum_ip_interface()), so + * by using it would be possible to list all IP interfaces in + * this host. + */ + + /* The first address is important since this would be the one + * to be added in Record-Route. + */ + if (pj_gethostip(pj_AF_INET(), &pri_addr)==PJ_SUCCESS) { + pj_strdup2(global.pool, &global.name[global.name_cnt].host, + pj_inet_ntoa(pri_addr.ipv4.sin_addr)); + global.name[global.name_cnt].port = global.port; + global.name_cnt++; + } + + /* Get the rest of IP interfaces */ + if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) { + for (i=0; iport == global.name[i].port || + (uri->port==0 && global.name[i].port==5060)) && + pj_stricmp(&uri->host, &global.name[i].host)==0) + { + /* Match */ + return PJ_TRUE; + } + } + + /* Doesn't match */ + return PJ_FALSE; +} + + +/* Proxy utility to verify incoming requests. + * Return non-zero if verification failed. + */ +static pj_status_t proxy_verify_request(pjsip_rx_data *rdata) +{ + const pj_str_t STR_PROXY_REQUIRE = {"Proxy-Require", 13}; + + /* RFC 3261 Section 16.3 Request Validation */ + + /* Before an element can proxy a request, it MUST verify the message's + * validity. A valid message must pass the following checks: + * + * 1. Reasonable Syntax + * 2. URI scheme + * 3. Max-Forwards + * 4. (Optional) Loop Detection + * 5. Proxy-Require + * 6. Proxy-Authorization + */ + + /* 1. Reasonable Syntax. + * This would have been checked by transport layer. + */ + + /* 2. URI scheme. + * We only want to support "sip:" URI scheme for this simple proxy. + */ + if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.msg->line.req.uri)) { + pjsip_endpt_respond_stateless(global.endpt, rdata, + PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, + NULL, NULL); + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_UNSUPPORTED_URI_SCHEME); + } + + /* 3. Max-Forwards. + * Send error if Max-Forwards is 1 or lower. + */ + if (rdata->msg_info.max_fwd && rdata->msg_info.max_fwd->ivalue <= 1) { + pjsip_endpt_respond_stateless(global.endpt, rdata, + PJSIP_SC_TOO_MANY_HOPS, NULL, + NULL, NULL); + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TOO_MANY_HOPS); + } + + /* 4. (Optional) Loop Detection. + * Nah, we don't do that with this simple proxy. + */ + + /* 5. Proxy-Require */ + if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_PROXY_REQUIRE, + NULL) != NULL) + { + pjsip_endpt_respond_stateless(global.endpt, rdata, + PJSIP_SC_BAD_EXTENSION, NULL, + NULL, NULL); + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION); + } + + /* 6. Proxy-Authorization. + * Nah, we don't require any authorization with this sample. + */ + + return PJ_SUCCESS; +} + + +/* Process route information in the reqeust */ +static pj_status_t proxy_process_routing(pjsip_tx_data *tdata) +{ + pjsip_sip_uri *target; + pjsip_route_hdr *hroute; + + /* RFC 3261 Section 16.4 Route Information Preprocessing */ + + target = (pjsip_sip_uri*) tdata->msg->line.req.uri; + + /* The proxy MUST inspect the Request-URI of the request. If the + * Request-URI of the request contains a value this proxy previously + * placed into a Record-Route header field (see Section 16.6 item 4), + * the proxy MUST replace the Request-URI in the request with the last + * value from the Route header field, and remove that value from the + * Route header field. The proxy MUST then proceed as if it received + * this modified request. + */ + if (is_uri_local(target)) { + pjsip_route_hdr *r; + pjsip_sip_uri *uri; + + /* Find the first Route header */ + r = hroute = (pjsip_route_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); + if (r == NULL) { + /* No Route header. This request is destined for this proxy. */ + return PJ_SUCCESS; + } + + /* Find the last Route header */ + while ( (r=(pjsip_route_hdr*)pjsip_msg_find_hdr(tdata->msg, + PJSIP_H_ROUTE, + r->next)) != NULL ) + { + hroute = r; + } + + /* If the last Route header doesn't have ";lr" parameter, then + * this is a strict-routed request indeed, and we follow the steps + * in processing strict-route requests above. + * + * But if it does contain ";lr" parameter, skip the strict-route + * processing. + */ + uri = (pjsip_sip_uri*) + pjsip_uri_get_uri(&hroute->name_addr); + if (uri->lr_param == 0) { + /* Yes this is strict route, so: + * - replace req URI with the URI in Route header, + * - remove the Route header, + * - proceed as if it received this modified request. + */ + tdata->msg->line.req.uri = hroute->name_addr.uri; + target = (pjsip_sip_uri*) tdata->msg->line.req.uri; + pj_list_erase(hroute); + } + } + + /* If the Request-URI contains a maddr parameter, the proxy MUST check + * to see if its value is in the set of addresses or domains the proxy + * is configured to be responsible for. If the Request-URI has a maddr + * parameter with a value the proxy is responsible for, and the request + * was received using the port and transport indicated (explicitly or by + * default) in the Request-URI, the proxy MUST strip the maddr and any + * non-default port or transport parameter and continue processing as if + * those values had not been present in the request. + */ + if (target->maddr_param.slen != 0) { + pjsip_sip_uri maddr_uri; + + maddr_uri.host = target->maddr_param; + maddr_uri.port = global.port; + + if (is_uri_local(&maddr_uri)) { + target->maddr_param.slen = 0; + target->port = 0; + target->transport_param.slen = 0; + } + } + + /* If the first value in the Route header field indicates this proxy, + * the proxy MUST remove that value from the request. + */ + hroute = (pjsip_route_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); + if (hroute && is_uri_local((pjsip_sip_uri*)hroute->name_addr.uri)) { + pj_list_erase(hroute); + } + + return PJ_SUCCESS; +} + + +/* Postprocess the request before forwarding it */ +static void proxy_postprocess(pjsip_tx_data *tdata) +{ + /* Optionally record-route */ + if (global.record_route) { + char uribuf[128]; + pj_str_t uri; + const pj_str_t H_RR = { "Record-Route", 12 }; + pjsip_generic_string_hdr *rr; + + pj_ansi_snprintf(uribuf, sizeof(uribuf), "", + (int)global.name[0].host.slen, + global.name[0].host.ptr, + global.name[0].port); + uri = pj_str(uribuf); + rr = pjsip_generic_string_hdr_create(tdata->pool, + &H_RR, &uri); + pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr); + } +} + + +/* Calculate new target for the request */ +static pj_status_t proxy_calculate_target(pjsip_rx_data *rdata, + pjsip_tx_data *tdata) +{ + pjsip_sip_uri *target; + + /* RFC 3261 Section 16.5 Determining Request Targets */ + + target = (pjsip_sip_uri*) tdata->msg->line.req.uri; + + /* If the Request-URI of the request contains an maddr parameter, the + * Request-URI MUST be placed into the target set as the only target + * URI, and the proxy MUST proceed to Section 16.6. + */ + if (target->maddr_param.slen) { + proxy_postprocess(tdata); + return PJ_SUCCESS; + } + + + /* If the domain of the Request-URI indicates a domain this element is + * not responsible for, the Request-URI MUST be placed into the target + * set as the only target, and the element MUST proceed to the task of + * Request Forwarding (Section 16.6). + */ + if (!is_uri_local(target)) { + proxy_postprocess(tdata); + return PJ_SUCCESS; + } + + /* If the target set for the request has not been predetermined as + * described above, this implies that the element is responsible for the + * domain in the Request-URI, and the element MAY use whatever mechanism + * it desires to determine where to send the request. + */ + + /* We're not interested to receive request destined to us, so + * respond with 404/Not Found (only if request is not ACK!). + */ + if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + pjsip_endpt_respond_stateless(global.endpt, rdata, + PJSIP_SC_NOT_FOUND, NULL, + NULL, NULL); + } + + /* Delete the request since we're not forwarding it */ + pjsip_tx_data_dec_ref(tdata); + + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_FOUND); +} + + +/* Destroy stack */ +static void destroy_stack(void) +{ + pjsip_endpt_destroy(global.endpt); + pj_pool_release(global.pool); + pj_caching_pool_destroy(&global.cp); + + pj_shutdown(); +} + -- cgit v1.2.3