summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-04-02 11:44:47 +0000
committerBenny Prijono <bennylp@teluu.com>2007-04-02 11:44:47 +0000
commitf29bc375ee021410406820f65047a2ea1026148f (patch)
treed3dd31df457a67acf17669200354e9a57abcfb0d /pjsip
parent1d1908a032b3d078210ca41d3d1c17d1bf90cfdd (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.h13
-rw-r--r--pjsip/include/pjsip/sip_util.h50
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h19
-rw-r--r--pjsip/src/pjsip/sip_msg.c32
-rw-r--r--pjsip/src/pjsip/sip_util.c4
-rw-r--r--pjsip/src/pjsip/sip_util_proxy.c335
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c39
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,