| /* $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; |
| } |
| |
| |