diff options
Diffstat (limited to 'pjsip/src/pjsip/sip_util_proxy.c')
-rw-r--r-- | pjsip/src/pjsip/sip_util_proxy.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/pjsip/src/pjsip/sip_util_proxy.c b/pjsip/src/pjsip/sip_util_proxy.c new file mode 100644 index 0000000..7a34534 --- /dev/null +++ b/pjsip/src/pjsip/sip_util_proxy.c @@ -0,0 +1,389 @@ +/* $Id: sip_util_proxy.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * 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 <pjsip/sip_util.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_errno.h> +#include <pjsip/sip_msg.h> +#include <pj/assert.h> +#include <pj/ctype.h> +#include <pj/except.h> +#include <pj/guid.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pjlib-util/md5.h> + + +/** + * Clone the incoming SIP request or response message. A forwarding proxy + * typically would need to clone the incoming SIP message before processing + * the message. + * + * Once a transmit data is created, the reference counter is initialized to 1. + * + * @param endpt The endpoint instance. + * @param rdata The incoming SIP message. + * @param p_tdata Pointer to receive the transmit data containing + * the duplicated message. + * + * @return PJ_SUCCESS on success. + */ +/* +PJ_DEF(pj_status_t) pjsip_endpt_clone_msg( pjsip_endpoint *endpt, + const pjsip_rx_data *rdata, + pjsip_tx_data **p_tdata) +{ + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_endpt_create_tdata(endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + tdata->msg = pjsip_msg_clone(tdata->pool, rdata->msg_info.msg); + + pjsip_tx_data_add_ref(tdata); + + *p_tdata = tdata; + + return PJ_SUCCESS; +} +*/ + + +/* + * Create new request message to be forwarded upstream to new destination URI + * in uri. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt, + pjsip_rx_data *rdata, + const pjsip_uri *uri, + const pj_str_t *branch, + unsigned options, + pjsip_tx_data **p_tdata) +{ + pjsip_tx_data *tdata; + pj_status_t status; + PJ_USE_EXCEPTION; + + + PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, + PJSIP_ENOTREQUESTMSG); + + PJ_UNUSED_ARG(options); + + + /* Request forwarding rule in RFC 3261 section 16.6: + * + * For each target, the proxy forwards the request following these + * steps: + * + * 1. Make a copy of the received request + * 2. Update the Request-URI + * 3. Update the Max-Forwards header field + * 4. Optionally add a Record-route header field value + * 5. Optionally add additional header fields + * 6. Postprocess routing information + * 7. Determine the next-hop address, port, and transport + * 8. Add a Via header field value + * 9. Add a Content-Length header field if necessary + * 10. Forward the new request + * + * Of these steps, we only do step 1-3, since the later will be + * done by application. + */ + + status = pjsip_endpt_create_tdata(endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Always increment ref counter to 1 */ + pjsip_tx_data_add_ref(tdata); + + /* Duplicate the request */ + PJ_TRY { + pjsip_msg *dst; + const pjsip_msg *src = rdata->msg_info.msg; + const pjsip_hdr *hsrc; + + /* Create the request */ + tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + + /* Duplicate request method */ + pjsip_method_copy(tdata->pool, &tdata->msg->line.req.method, + &src->line.req.method); + + /* Set request URI */ + if (uri) { + dst->line.req.uri = (pjsip_uri*) + pjsip_uri_clone(tdata->pool, uri); + } else { + dst->line.req.uri= (pjsip_uri*) + pjsip_uri_clone(tdata->pool, src->line.req.uri); + } + + /* Clone ALL headers */ + hsrc = src->hdr.next; + while (hsrc != &src->hdr) { + + pjsip_hdr *hdst; + + /* If this is the top-most Via header, insert our own before + * cloning the header. + */ + if (hsrc == (pjsip_hdr*)rdata->msg_info.via) { + pjsip_via_hdr *hvia; + hvia = pjsip_via_hdr_create(tdata->pool); + if (branch) + pj_strdup(tdata->pool, &hvia->branch_param, branch); + else { + pj_str_t new_branch = pjsip_calculate_branch_id(rdata); + pj_strdup(tdata->pool, &hvia->branch_param, &new_branch); + } + pjsip_msg_add_hdr(dst, (pjsip_hdr*)hvia); + + } + /* Skip Content-Type and Content-Length as these would be + * generated when the the message is printed. + */ + else if (hsrc->type == PJSIP_H_CONTENT_LENGTH || + hsrc->type == PJSIP_H_CONTENT_TYPE) { + + hsrc = hsrc->next; + continue; + + } +#if 0 + /* If this is the top-most Route header and it indicates loose + * route, remove the header. + */ + else if (hsrc == (pjsip_hdr*)rdata->msg_info.route) { + + const pjsip_route_hdr *hroute = (const pjsip_route_hdr*) hsrc; + const pjsip_sip_uri *sip_uri; + + if (!PJSIP_URI_SCHEME_IS_SIP(hroute->name_addr.uri) && + !PJSIP_URI_SCHEME_IS_SIPS(hroute->name_addr.uri)) + { + /* This is a bad request! */ + status = PJSIP_EINVALIDHDR; + goto on_error; + } + + sip_uri = (pjsip_sip_uri*) hroute->name_addr.uri; + + if (sip_uri->lr_param) { + /* Yes lr param is present, skip this Route header */ + hsrc = hsrc->next; + continue; + } + } +#endif + + /* Clone the header */ + hdst = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hsrc); + + /* If this is Max-Forward header, decrement the value */ + if (hdst->type == PJSIP_H_MAX_FORWARDS) { + pjsip_max_fwd_hdr *hmaxfwd = (pjsip_max_fwd_hdr*)hdst; + --hmaxfwd->ivalue; + } + + /* Append header to new request */ + pjsip_msg_add_hdr(dst, hdst); + + + hsrc = hsrc->next; + } + + /* 16.6.3: + * If the copy does not contain a Max-Forwards header field, the + * proxy MUST add one with a field value, which SHOULD be 70. + */ + if (rdata->msg_info.max_fwd == NULL) { + pjsip_max_fwd_hdr *hmaxfwd = + pjsip_max_fwd_hdr_create(tdata->pool, 70); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hmaxfwd); + } + + /* Clone request body */ + if (src->body) { + dst->body = pjsip_msg_body_clone(tdata->pool, src->body); + } + + } + PJ_CATCH_ANY { + status = PJ_ENOMEM; + goto on_error; + } + PJ_END + + + /* Done */ + *p_tdata = tdata; + return PJ_SUCCESS; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return status; +} + + +PJ_DEF(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt, + pjsip_rx_data *rdata, + unsigned options, + pjsip_tx_data **p_tdata) +{ + pjsip_tx_data *tdata; + pj_status_t status; + PJ_USE_EXCEPTION; + + PJ_UNUSED_ARG(options); + + status = pjsip_endpt_create_tdata(endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + pjsip_tx_data_add_ref(tdata); + + PJ_TRY { + pjsip_msg *dst; + const pjsip_msg *src = rdata->msg_info.msg; + const pjsip_hdr *hsrc; + + /* Create the request */ + tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); + + /* Clone the status line */ + dst->line.status.code = src->line.status.code; + pj_strdup(tdata->pool, &dst->line.status.reason, + &src->line.status.reason); + + /* Duplicate all headers */ + hsrc = src->hdr.next; + while (hsrc != &src->hdr) { + + /* Skip Content-Type and Content-Length as these would be + * generated when the the message is printed. + */ + if (hsrc->type == PJSIP_H_CONTENT_LENGTH || + hsrc->type == PJSIP_H_CONTENT_TYPE) { + + hsrc = hsrc->next; + continue; + + } + /* Remove the first Via header */ + else if (hsrc == (pjsip_hdr*) rdata->msg_info.via) { + + hsrc = hsrc->next; + continue; + } + + pjsip_msg_add_hdr(dst, + (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hsrc)); + + hsrc = hsrc->next; + } + + /* Clone message body */ + if (src->body) + dst->body = pjsip_msg_body_clone(tdata->pool, src->body); + + + } + PJ_CATCH_ANY { + status = PJ_ENOMEM; + goto on_error; + } + PJ_END; + + *p_tdata = tdata; + return PJ_SUCCESS; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return status; +} + + +static void digest2str(const unsigned char digest[], char *output) +{ + int i; + for (i = 0; i<16; ++i) { + pj_val_to_hex_digit(digest[i], output); + output += 2; + } +} + + +PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata ) +{ + pj_md5_context ctx; + pj_uint8_t digest[16]; + pj_str_t branch; + pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, + PJSIP_RFC3261_BRANCH_LEN}; + + /* If incoming request does not have RFC 3261 branch value, create + * a branch value from GUID . + */ + if (pj_strncmp(&rdata->msg_info.via->branch_param, + &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 ) + { + pj_str_t tmp; + + branch.ptr = (char*) + pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_BRANCH_LEN); + branch.slen = PJSIP_RFC3261_BRANCH_LEN; + pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, + PJSIP_RFC3261_BRANCH_LEN); + + tmp.ptr = branch.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; + *(tmp.ptr-2) = (pj_int8_t)(branch.slen+73); + *(tmp.ptr-1) = (pj_int8_t)(branch.slen+99); + pj_generate_unique_string( &tmp ); + + branch.slen = PJSIP_MAX_BRANCH_LEN; + return branch; + } + + /* Create branch ID for new request by calculating MD5 hash + * of the branch parameter in top-most Via header. + */ + pj_md5_init(&ctx); + pj_md5_update(&ctx, (pj_uint8_t*)rdata->msg_info.via->branch_param.ptr, + rdata->msg_info.via->branch_param.slen); + pj_md5_final(&ctx, digest); + + branch.ptr = (char*) + pj_pool_alloc(rdata->tp_info.pool, + 34 + PJSIP_RFC3261_BRANCH_LEN); + pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); + branch.slen = PJSIP_RFC3261_BRANCH_LEN; + *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN) = (pj_int8_t)(branch.slen+73); + *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN+1) = (pj_int8_t)(branch.slen+99); + digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN+2); + branch.slen = 34 + PJSIP_RFC3261_BRANCH_LEN; + + return branch; +} + + |