blob: 2264accbf023d30d1cbf69d90ae06eeb2fbdb126 [file] [log] [blame]
/* $Id$ */
/*
* 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_strnicmp(&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,
(unsigned)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;
}