diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-04-02 11:44:47 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-04-02 11:44:47 +0000 |
commit | f29bc375ee021410406820f65047a2ea1026148f (patch) | |
tree | d3dd31df457a67acf17669200354e9a57abcfb0d /pjsip | |
parent | 1d1908a032b3d078210ca41d3d1c17d1bf90cfdd (diff) |
Ticket #205: merged proxy functionalities from stable to trunk
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1127 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/include/pjsip/sip_msg.h | 13 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_util.h | 50 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 19 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_msg.c | 32 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util.c | 4 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util_proxy.c | 335 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 39 |
7 files changed, 457 insertions, 35 deletions
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h index 6c7b0201..7a303521 100644 --- a/pjsip/include/pjsip/sip_msg.h +++ b/pjsip/include/pjsip/sip_msg.h @@ -710,6 +710,19 @@ struct pjsip_msg */ PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type); + +/** + * Perform a deep clone of a SIP message. + * + * @param pool The pool for creating the new message. + * @param msg The message to be duplicated. + * + * @return New message, which is duplicated from the original + * message. + */ +PJ_DECL(pjsip_msg*) pjsip_msg_clone( pj_pool_t *pool, const pjsip_msg *msg); + + /** * Find a header in the message by the header type. * diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h index 4d19d306..62fb847c 100644 --- a/pjsip/include/pjsip/sip_util.h +++ b/pjsip/include/pjsip/sip_util.h @@ -472,27 +472,49 @@ PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, void (*cb)(void*,pjsip_event*)); /** + * @} + */ + +/** + * @defgroup PJSIP_PROXY_CORE Core Proxy Layer + * @ingroup PJSIP + * @brief Core proxy operations + * @{ + */ + +/** * Create new request message to be forwarded upstream to new destination URI * in uri. The new request is a full/deep clone of the request received in * rdata, unless if other copy mechanism is specified in the options. * The branch parameter, if not NULL, will be used as the branch-param in * the Via header. If it is NULL, then a unique branch parameter will be used. * + * Note: this function DOES NOT perform Route information preprocessing as + * described in RFC 3261 Section 16.4. Application must take care of + * removing/updating the Route headers according of the rules as + * described in that section. + * * @param endpt The endpoint instance. * @param rdata The incoming request message. * @param uri The URI where the request will be forwarded to. - * @param branch Optional branch parameter. + * @param branch Optional branch parameter. Application may specify its + * own branch, for example if it wishes to perform loop + * detection. If the branch parameter is not specified, + * this function will generate its own by calling + * #pjsip_calculate_branch_id() function. * @param options Optional option flags when duplicating the message. * @param tdata The result. * * @return PJ_SUCCESS on success. */ -PJ_DECL(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 **tdata); +PJ_DECL(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 **tdata); + + /** * Create new response message to be forwarded downstream by the proxy from @@ -515,13 +537,17 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt, pjsip_tx_data **tdata); + /** * Create a globally unique branch parameter based on the information in - * the incoming request message. This function guarantees that subsequent - * retransmissions of the same request will generate the same branch id. - * This function can also be used in the loop detection process. - * If the same request arrives back in the proxy with the same URL, it will - * calculate into the same branch id. + * the incoming request message, for the purpose of creating a new request + * for forwarding. This is the default implementation used by + * #pjsip_endpt_create_request_fwd() function if the branch parameter is + * not specified. + * + * The default implementation here will just create an MD5 hash of the + * top-most Via. + * * Note that the returned string was allocated from rdata's pool. * * @param rdata The incoming request message. diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index fee08d33..b6ba7d53 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2201,6 +2201,25 @@ PJ_DECL(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata); /** + * Create arbitrary requests using the account. Application should only use + * this function to create auxiliary requests outside dialog, such as + * OPTIONS, and use the call or presence API to create dialog related + * requests. + * + * @param acc_id The account ID. + * @param method The SIP method of the request. + * @param target Target URI. + * @param p_tdata Pointer to receive the request. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, + const pjsip_method *method, + const pj_str_t *target, + pjsip_tx_data **p_tdata); + + +/** * Create a suitable URI to be put as Contact based on the specified * target URI for the specified account. * diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 66304bd0..47c493db 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -247,6 +247,38 @@ PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) return msg; } +PJ_DEF(pjsip_msg*) pjsip_msg_clone( pj_pool_t *pool, const pjsip_msg *src) +{ + pjsip_msg *dst; + const pjsip_hdr *sh; + + dst = pjsip_msg_create(pool, src->type); + + /* Clone request/status line */ + if (src->type == PJSIP_REQUEST_MSG) { + pjsip_method_copy(pool, &dst->line.req.method, &src->line.req.method); + dst->line.req.uri = pjsip_uri_clone(pool, src->line.req.uri); + } else { + dst->line.status.code = src->line.status.code; + pj_strdup(pool, &dst->line.status.reason, &src->line.status.reason); + } + + /* Clone headers */ + sh = src->hdr.next; + while (sh != &src->hdr) { + pjsip_hdr *dh = pjsip_hdr_clone(pool, sh); + pjsip_msg_add_hdr(dst, dh); + sh = sh->next; + } + + /* Clone message body */ + if (src->body) { + dst->body = pjsip_msg_body_clone(pool, src->body); + } + + return dst; +} + PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg, pjsip_hdr_e hdr_type, const void *start) { diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index e95faaeb..3f1add48 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1037,6 +1037,10 @@ PJ_DEF(pj_status_t) pjsip_get_response_addr( pj_pool_t *pool, /* Check arguments. */ PJ_ASSERT_RETURN(pool && rdata && res_addr, PJ_EINVAL); + /* rdata must be a request message! */ + PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, + PJ_EINVAL); + /* All requests must have "received" parameter. * This must always be done in transport layer. */ diff --git a/pjsip/src/pjsip/sip_util_proxy.c b/pjsip/src/pjsip/sip_util_proxy.c index 73d1c4b6..b9631397 100644 --- a/pjsip/src/pjsip/sip_util_proxy.c +++ b/pjsip/src/pjsip/sip_util_proxy.c @@ -17,50 +17,339 @@ * 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/pool.h> +#include <pj/string.h> +#include <pjlib-util/md5.h> -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 **tdata) + +/** + * 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) { - PJ_UNUSED_ARG(endpt); - PJ_UNUSED_ARG(rdata); - PJ_UNUSED_ARG(uri); - PJ_UNUSED_ARG(branch); + 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); - PJ_UNUSED_ARG(tdata); - pj_assert(!"Not implemented yet"); - return PJ_EBUG; + + /* 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_clone(tdata->pool, uri); + } else { + dst->line.req.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_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 **tdata) + pjsip_tx_data **p_tdata) { - PJ_UNUSED_ARG(endpt); - PJ_UNUSED_ARG(rdata); + pjsip_tx_data *tdata; + pj_status_t status; + PJ_USE_EXCEPTION; + PJ_UNUSED_ARG(options); - PJ_UNUSED_ARG(tdata); - pj_assert(!"Not implemented yet"); - return PJ_EBUG; + 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_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_str_t empty_str = { NULL, 0 }; + pj_md5_context ctx; + pj_uint8_t digest[16]; + pj_str_t 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 = pj_pool_alloc(rdata->tp_info.pool, + 32 + PJSIP_RFC3261_BRANCH_LEN); + pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); + + digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN); + + branch.slen = 32 + PJSIP_RFC3261_BRANCH_LEN; - PJ_UNUSED_ARG(rdata); - pj_assert(!"Not implemented yet"); - return empty_str; + return branch; } diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 3e39b15f..b2b63740 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -925,6 +925,45 @@ PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata) } +/* + * Create arbitrary requests for this account. + */ +PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, + const pjsip_method *method, + const pj_str_t *target, + pjsip_tx_data **p_tdata) +{ + pjsip_tx_data *tdata; + pjsua_acc *acc; + pjsip_route_hdr *r; + pj_status_t status; + + PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL); + + acc = &pjsua_var.acc[acc_id]; + + status = pjsip_endpt_create_request(pjsua_var.endpt, method, target, + &acc->cfg.id, target, + NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + return status; + } + + /* Copy routeset */ + r = acc->route_set.next; + while (r != &acc->route_set) { + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, r)); + r = r->next; + } + + /* Done */ + *p_tdata = tdata; + return PJ_SUCCESS; +} + + PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, pj_str_t *contact, pjsua_acc_id acc_id, |