summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip/sip_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsip/sip_util.c')
-rw-r--r--pjsip/src/pjsip/sip_util.c950
1 files changed, 709 insertions, 241 deletions
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index c227eff7..532d6fb5 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -23,6 +23,7 @@
#include <pjsip/sip_event.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_module.h>
+#include <pjsip/sip_errno.h>
#include <pj/log.h>
#include <pj/string.h>
#include <pj/guid.h>
@@ -51,101 +52,6 @@ static const char *event_str[] =
static pj_str_t str_TEXT = { "text", 4},
str_PLAIN = { "plain", 5 };
-static int aux_mod_id;
-
-struct aux_tsx_data
-{
- void *token;
- void (*cb)(void*,pjsip_event*);
-};
-
-static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt);
- PJ_UNUSED_ARG(mod);
-
- aux_mod_id = id;
- return 0;
-}
-
-static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
-
- PJ_UNUSED_ARG(mod);
-
- if (event->type != PJSIP_EVENT_TSX_STATE)
- return;
-
- pj_assert(event->body.tsx_state.tsx != NULL);
- tsx = event->body.tsx_state.tsx;
- if (tsx == NULL)
- return;
- if (tsx->module_data[aux_mod_id] == NULL)
- return;
- if (tsx->status_code < 200)
- return;
-
- /* Call the callback, if any, and prevent the callback to be called again
- * by clearing the transaction's module_data.
- */
- tsx_data = tsx->module_data[aux_mod_id];
- tsx->module_data[aux_mod_id] = NULL;
-
- if (tsx_data->cb) {
- (*tsx_data->cb)(tsx_data->token, event);
- }
-}
-
-pjsip_module aux_tsx_module =
-{
- { "Aux-Tsx", 7}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* Arbitrary data. */
- 0, /* Number of methods supported (none). */
- { 0 }, /* Array of methods (none) */
- &aux_tsx_init, /* init_module() */
- NULL, /* start_module() */
- NULL, /* deinit_module() */
- &aux_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*))
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
- pj_status_t status;
-
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
- tsx_data->token = token;
- tsx_data->cb = cb;
- tsx->module_data[aux_mod_id] = tsx_data;
-
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_endpt_destroy_tsx(endpt, tsx);
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg(tsx, tdata);
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
/*
* Initialize transmit data (msg) with the headers and optional body.
@@ -169,6 +75,7 @@ static void init_request_throw( pjsip_endpoint *endpt,
{
pjsip_msg *msg;
pjsip_msg_body *body;
+ pjsip_via_hdr *via;
const pjsip_hdr *endpt_hdr;
/* Create the message. */
@@ -205,6 +112,11 @@ static void init_request_throw( pjsip_endpoint *endpt,
/* Add CSeq header. */
pjsip_msg_add_hdr(msg, (void*)param_cseq);
+ /* Add a blank Via header. */
+ via = pjsip_via_hdr_create(tdata->pool);
+ via->rport_param = 0;
+ pjsip_msg_insert_first_hdr(msg, (void*)via);
+
/* Create message body. */
if (param_text) {
body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
@@ -216,6 +128,13 @@ static void init_request_throw( pjsip_endpoint *endpt,
body->print_body = &pjsip_print_text_body;
msg->body = body;
}
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (CSeq=%d/%.*s) created.",
+ tdata->obj_name,
+ param_cseq->cseq,
+ param_cseq->method.name.slen,
+ param_cseq->method.name.ptr));
+
}
/*
@@ -328,12 +247,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
}
PJ_END
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
*p_tdata = tdata;
return PJ_SUCCESS;
@@ -366,23 +279,30 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(endpt && method && param_target && param_from &&
+ param_to && p_tdata, PJ_EINVAL);
+
+ /* Create new transmit data. */
status = pjsip_endpt_create_tdata(endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
+ /* Set initial reference counter to 1. */
pjsip_tx_data_add_ref(tdata);
PJ_TRY {
+ /* Duplicate target URI and headers. */
target = pjsip_uri_clone(tdata->pool, param_target);
- from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+ from = pjsip_hdr_clone(tdata->pool, param_from);
pjsip_fromto_set_from(from);
- to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+ to = pjsip_hdr_clone(tdata->pool, param_to);
pjsip_fromto_set_to(to);
if (param_contact)
- contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+ contact = pjsip_hdr_clone(tdata->pool, param_contact);
else
contact = NULL;
- call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+ call_id = pjsip_hdr_clone(tdata->pool, param_call_id);
cseq = pjsip_cseq_hdr_create(tdata->pool);
if (param_cseq >= 0)
cseq->cseq = param_cseq;
@@ -390,6 +310,7 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
cseq->cseq = pj_rand() % 0xFFFF;
pjsip_method_copy(tdata->pool, &cseq->method, method);
+ /* Copy headers to the request. */
init_request_throw(endpt, tdata, &cseq->method, target, from, to,
contact, call_id, cseq, param_text);
}
@@ -399,12 +320,6 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
}
PJ_END;
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
*p_tdata = tdata;
return PJ_SUCCESS;
@@ -418,7 +333,8 @@ on_error:
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
const pjsip_rx_data *rdata,
- int code,
+ int st_code,
+ const pj_str_t *st_text,
pjsip_tx_data **p_tdata)
{
pjsip_tx_data *tdata;
@@ -434,19 +350,25 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
/* Log this action. */
PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
- rdata, code));
+ rdata, st_code));
/* Create a new transmit buffer. */
status = pjsip_endpt_create_tdata( endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
+ /* Set initial reference count to 1. */
+ pjsip_tx_data_add_ref(tdata);
+
/* Create new response message. */
tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
/* Set status code and reason text. */
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
+ msg->line.status.code = st_code;
+ if (st_text)
+ pj_strdup(tdata->pool, &msg->line.status.reason, st_text);
+ else
+ msg->line.status.reason = *pjsip_get_status_text(st_code);
/* Set TX data attributes. */
tdata->rx_timestamp = rdata->pkt_info.timestamp;
@@ -500,81 +422,95 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
* RFC3261). Note that the generation of ACK for 2xx response is different,
* and one must not use this function to generate such ACK.
*/
-PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata )
+PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+ const pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata,
+ pjsip_tx_data **ack_tdata)
{
- pjsip_msg *ack_msg, *invite_msg;
+ pjsip_tx_data *ack = NULL;
+ const pjsip_msg *invite_msg;
+ const pjsip_from_hdr *from_hdr;
+ const pjsip_to_hdr *to_hdr;
+ const pjsip_cid_hdr *cid_hdr;
+ const pjsip_cseq_hdr *cseq_hdr;
+ const pjsip_hdr *hdr;
pjsip_to_hdr *to;
- pjsip_from_hdr *from;
- pjsip_cseq_hdr *cseq;
- pjsip_hdr *hdr;
+ pj_status_t status;
- /* Make compiler happy. */
- PJ_UNUSED_ARG(endpt);
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
/* rdata must be a final response. */
pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
rdata->msg_info.msg->line.status.code >= 300);
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
-
- /* Create new request message. */
- ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+ /* Initialize return value to NULL. */
+ *ack_tdata = NULL;
/* The original INVITE message. */
invite_msg = tdata->msg;
- /* Copy Request-Uri from the original INVITE. */
- ack_msg->line.req.uri = invite_msg->line.req.uri;
-
- /* Copy Call-ID from the original INVITE */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr( ack_msg, hdr );
+ /* Get the headers from original INVITE request. */
+# define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
- /* Copy From header from the original INVITE. */
- from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
- PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
+ from_hdr = (const pjsip_from_hdr*) FIND_HDR(invite_msg, FROM);
+ PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
- /* Copy To header from the original INVITE. */
- to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_TO, NULL);
- pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+ to_hdr = (const pjsip_to_hdr*) FIND_HDR(invite_msg, TO);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Must contain single Via, just as the original INVITE. */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr( ack_msg, hdr );
+ cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Must have the same CSeq value as the original INVITE, but method
- * changed to ACK
- */
- cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_CSEQ, NULL);
- pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+ cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+# undef FIND_HDR
+
+ /* Create new request message from the headers. */
+ status = pjsip_endpt_create_request_from_hdr(endpt,
+ &pjsip_ack_method,
+ tdata->msg->line.req.uri,
+ from_hdr, to_hdr,
+ NULL, cid_hdr,
+ cseq_hdr->cseq, NULL,
+ &ack);
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update tag in To header with the one from the response (if any). */
+ to = (pjsip_to_hdr*) pjsip_msg_find_hdr(ack->msg, PJSIP_H_TO, NULL);
+ pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag);
+
+ /* Must contain single Via, just as the original INVITE. */
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL);
+ if (hdr) {
+ pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) );
+ }
/* If the original INVITE has Route headers, those header fields MUST
* appear in the ACK.
*/
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
while (hdr != NULL) {
- pjsip_msg_add_hdr( ack_msg, hdr );
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ pjsip_msg_add_hdr( ack->msg, pjsip_hdr_clone(ack->pool, hdr) );
+ hdr = hdr->next;
+ if (hdr == &invite_msg->hdr)
+ break;
+ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, hdr);
}
- /* Set the message in the "tdata" to point to the ACK message. */
- tdata->msg = ack_msg;
-
- /* Reset transmit packet buffer, to force 're-printing' of message. */
- tdata->buf.cur = tdata->buf.start;
-
/* We're done.
* "tdata" parameter now contains the ACK message.
*/
+ *ack_tdata = ack;
+ return PJ_SUCCESS;
+
+on_missing_hdr:
+ if (ack)
+ pjsip_tx_data_dec_ref(ack);
+ return PJSIP_EMISSINGHDR;
}
@@ -583,82 +519,74 @@ PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
* chapter 9.1 of RFC3261.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *req_tdata,
+ const pjsip_tx_data *req_tdata,
pjsip_tx_data **p_tdata)
{
- pjsip_msg *req_msg; /* the original request. */
- pjsip_tx_data *cancel_tdata;
- pjsip_msg *cancel_msg;
- pjsip_hdr *hdr;
- pjsip_cseq_hdr *req_cseq, *cseq;
- pjsip_uri *req_uri;
+ pjsip_tx_data *cancel_tdata = NULL;
+ const pjsip_from_hdr *from_hdr;
+ const pjsip_to_hdr *to_hdr;
+ const pjsip_cid_hdr *cid_hdr;
+ const pjsip_cseq_hdr *cseq_hdr;
+ const pjsip_hdr *hdr;
pj_status_t status;
/* Log this action. */
PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
- /* Get the original request. */
- req_msg = req_tdata->msg;
-
/* The transmit buffer must INVITE request. */
- PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
- req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
+ PJ_ASSERT_RETURN(req_tdata->msg->type == PJSIP_REQUEST_MSG &&
+ req_tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD,
PJ_EINVAL);
- /* Create new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
+ /* Get the headers from original INVITE request. */
+# define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
- /* Create CANCEL request message. */
- cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
- cancel_tdata->msg = cancel_msg;
+ from_hdr = (const pjsip_from_hdr*) FIND_HDR(req_tdata->msg, FROM);
+ PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
- /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
- * copied from the original request.
- */
- /* Set request line. */
- pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
- req_uri = req_msg->line.req.uri;
- cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
+ to_hdr = (const pjsip_to_hdr*) FIND_HDR(req_tdata->msg, TO);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy Call-ID */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy From header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ);
+ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
- /* Copy To header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+# undef FIND_HDR
- /* Create new CSeq with equal number, but method set to CANCEL. */
- req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
- cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
- cseq->cseq = req_cseq->cseq;
- pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
- pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
+ /* Create new request message from the headers. */
+ status = pjsip_endpt_create_request_from_hdr(endpt,
+ &pjsip_cancel_method,
+ req_tdata->msg->line.req.uri,
+ from_hdr, to_hdr,
+ NULL, cid_hdr,
+ cseq_hdr->cseq, NULL,
+ &cancel_tdata);
+
+ if (status != PJ_SUCCESS)
+ return status;
/* Must only have single Via which matches the top-most Via in the
* request being cancelled.
*/
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr(cancel_msg,
- pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_VIA, NULL);
+ if (hdr) {
+ pjsip_msg_insert_first_hdr(cancel_tdata->msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ }
/* If the original request has Route header, the CANCEL request must also
* has exactly the same.
* Copy "Route" header from the request.
*/
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+ hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_ROUTE, NULL);
while (hdr != NULL) {
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ pjsip_msg_add_hdr(cancel_tdata->msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
hdr = hdr->next;
- if (hdr != &cancel_msg->hdr)
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+ if (hdr != &cancel_tdata->msg->hdr)
+ hdr = pjsip_msg_find_hdr(cancel_tdata->msg, PJSIP_H_ROUTE, hdr);
else
break;
}
@@ -668,51 +596,591 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
*/
*p_tdata = cancel_tdata;
return PJ_SUCCESS;
+
+on_missing_hdr:
+ if (cancel_tdata)
+ pjsip_tx_data_dec_ref(cancel_tdata);
+ return PJSIP_EMISSINGHDR;
}
-/* Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
+
+/*
+ * Find which destination to be used to send the request message, based
+ * on the request URI and Route headers in the message. The procedure
+ * used here follows the guidelines on sending the request in RFC 3261
+ * chapter 8.1.2.
*/
-PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *req_transport,
- const pjsip_via_hdr *via,
- pjsip_host_info *send_addr)
+PJ_DEF(pj_status_t) pjsip_get_request_addr( pjsip_tx_data *tdata,
+ pjsip_host_info *dest_info )
{
- /* Determine the destination address (section 18.2.2):
- * - for TCP, SCTP, or TLS, send the response using the transport where
- * the request was received.
- * - if maddr parameter is present, send to this address using the port
- * in sent-by or 5060. If multicast is used, the TTL in the Via must
- * be used, or 1 if ttl parameter is not present.
- * - otherwise if received parameter is present, set to this address.
- * - otherwise send to the address in sent-by.
+ const pjsip_uri *new_request_uri, *target_uri;
+ const pjsip_name_addr *topmost_route_uri;
+ pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+
+ PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+ PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL);
+
+ /* Get the first "Route" header from the message. If the message doesn't
+ * have any "Route" headers but the endpoint has, then copy the "Route"
+ * headers from the endpoint first.
*/
- send_addr->flag = req_transport->flag;
- send_addr->type = req_transport->key.type;
+ last_route_hdr = first_route_hdr =
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+ if (first_route_hdr) {
+ topmost_route_uri = &first_route_hdr->name_addr;
+ while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+ pjsip_route_hdr *hdr;
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
+ last_route_hdr->next);
+ if (!hdr)
+ break;
+ last_route_hdr = hdr;
+ }
+ } else {
+ topmost_route_uri = NULL;
+ }
- if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
- pj_strdup( pool, &send_addr->addr.host,
- &req_transport->remote_name.host);
- send_addr->addr.port = req_transport->remote_name.port;
+ /* If Route headers exist, and the first element indicates loose-route,
+ * the URI is taken from the Request-URI, and we keep all existing Route
+ * headers intact.
+ * If Route headers exist, and the first element DOESN'T indicate loose
+ * route, the URI is taken from the first Route header, and remove the
+ * first Route header from the message.
+ * Otherwise if there's no Route headers, the URI is taken from the
+ * Request-URI.
+ */
+ if (topmost_route_uri) {
+ pj_bool_t has_lr_param;
+
+ if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+ {
+ const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+ has_lr_param = url->lr_param;
+ } else {
+ has_lr_param = 0;
+ }
+ if (has_lr_param) {
+ new_request_uri = tdata->msg->line.req.uri;
+ /* We shouldn't need to delete topmost Route if it has lr param.
+ * But seems like it breaks some proxy implementation, so we
+ * delete it anyway.
+ */
+ /*
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ */
+ } else {
+ new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ }
+
+ target_uri = (pjsip_uri*)topmost_route_uri;
+
+ } else {
+ target_uri = new_request_uri = tdata->msg->line.req.uri;
+ }
+
+ /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+ * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+ */
+ pj_memset(dest_info, 0, sizeof(*dest_info));
+
+ if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+ pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+ dest_info->addr.port = url->port;
+ dest_info->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+
+ } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+ dest_info->addr.port = url->port;
+ dest_info->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+ if (dest_info->type == PJSIP_TRANSPORT_TCP ||
+ dest_info->type == PJSIP_TRANSPORT_SCTP)
+ {
+ dest_info->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+#endif
} else {
- /* Set the host part */
- if (via->maddr_param.slen) {
- pj_strdup(pool, &send_addr->addr.host, &via->maddr_param);
- } else if (via->recvd_param.slen) {
- pj_strdup(pool, &send_addr->addr.host, &via->recvd_param);
+ pj_assert(!"Unsupported URI scheme!");
+ PJ_TODO(SUPPORT_REQUEST_ADDR_RESOLUTION_FOR_TEL_URI);
+ return PJSIP_EINVALIDSCHEME;
+ }
+
+ /* If target URI is different than request URI, replace
+ * request URI add put the original URI in the last Route header.
+ */
+ if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+ pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+ route->name_addr.uri = tdata->msg->line.req.uri;
+ if (last_route_hdr)
+ pj_list_insert_after(last_route_hdr, route);
+ else
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+ tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+ }
+
+ /* Success. */
+ return PJ_SUCCESS;
+}
+
+
+/* Transport callback for sending stateless request.
+ * This is one of the most bizzare function in pjsip, so
+ * good luck if you happen to debug this function!!
+ */
+static void stateless_send_transport_cb( void *token,
+ pjsip_tx_data *tdata,
+ pj_ssize_t sent )
+{
+ pjsip_send_state *stateless_data = token;
+
+ PJ_UNUSED_ARG(tdata);
+ pj_assert(tdata == stateless_data->tdata);
+
+ for (;;) {
+ pj_status_t status;
+ pj_bool_t cont;
+
+ pj_sockaddr_t *cur_addr;
+ pjsip_transport_type_e cur_addr_type;
+ int cur_addr_len;
+
+ pjsip_via_hdr *via;
+
+ if (sent == -PJ_EPENDING) {
+ /* This is the initial process.
+ * When the process started, this function will be called by
+ * stateless_send_resolver_callback() with sent argument set to
+ * -PJ_EPENDING.
+ */
+ cont = PJ_TRUE;
+ } else {
+ /* There are two conditions here:
+ * (1) Message is sent (i.e. sent > 0),
+ * (2) Failure (i.e. sent <= 0)
+ */
+ cont = (sent > 0) ? PJ_FALSE :
+ (stateless_data->cur_addr<stateless_data->addr.count-1);
+ if (stateless_data->app_cb) {
+ (*stateless_data->app_cb)(stateless_data, sent, &cont);
+ } else {
+ /* Doesn't have application callback.
+ * Terminate the process.
+ */
+ cont = PJ_FALSE;
+ }
+ }
+
+ /* Finished with this transport. */
+ if (stateless_data->cur_transport) {
+ pjsip_transport_dec_ref(stateless_data->cur_transport);
+ stateless_data->cur_transport = NULL;
+ }
+
+ /* Done if application doesn't want to continue. */
+ if (sent > 0 || !cont) {
+ pjsip_tx_data_dec_ref(tdata);
+ return;
+ }
+
+ /* Try next address, if any, and only when this is not the
+ * first invocation.
+ */
+ if (sent != -PJ_EPENDING) {
+ stateless_data->cur_addr++;
+ }
+
+ /* Have next address? */
+ if (stateless_data->cur_addr >= stateless_data->addr.count) {
+ /* This only happens when a rather buggy application has
+ * sent 'cont' to PJ_TRUE when the initial value was PJ_FALSE.
+ * In this case just stop the processing; we don't need to
+ * call the callback again as application has been informed
+ * before.
+ */
+ pjsip_tx_data_dec_ref(tdata);
+ return;
+ }
+
+ /* Keep current server address information handy. */
+ cur_addr = &stateless_data->addr.entry[stateless_data->cur_addr].addr;
+ cur_addr_type = stateless_data->addr.entry[stateless_data->cur_addr].type;
+ cur_addr_len = stateless_data->addr.entry[stateless_data->cur_addr].addr_len;
+
+ /* Acquire transport. */
+ status = pjsip_endpt_acquire_transport( stateless_data->endpt,
+ cur_addr_type,
+ cur_addr,
+ cur_addr_len,
+ &stateless_data->cur_transport);
+ if (status != PJ_SUCCESS) {
+ sent = -status;
+ continue;
+ }
+
+ /* Modify Via header. */
+ via = (pjsip_via_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_VIA, NULL);
+ if (!via) {
+ /* Shouldn't happen if request was created with PJSIP API!
+ * But we handle the case anyway for robustness.
+ */
+ pj_assert(!"Via header not found!");
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ pj_str_t tmp;
+ via->branch_param.ptr = pj_pool_alloc(tdata->pool,
+ PJSIP_MAX_BRANCH_LEN);
+ via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+ pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN);
+ tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+ pj_generate_unique_string(&tmp);
+ }
+
+ via->transport = pj_str(stateless_data->cur_transport->type_name);
+ via->sent_by = stateless_data->cur_transport->local_name;
+ via->rport_param = 0;
+
+ /* Send message using this transport. */
+ status = pjsip_transport_send( stateless_data->cur_transport,
+ tdata,
+ cur_addr,
+ cur_addr_len,
+ stateless_data,
+ &stateless_send_transport_cb);
+ if (status == PJ_SUCCESS) {
+ /* Recursively call this function. */
+ sent = tdata->buf.cur - tdata->buf.start;
+ stateless_send_transport_cb( stateless_data, tdata, sent );
+ return;
+ } else if (status == PJ_EPENDING) {
+ /* This callback will be called later. */
+ return;
} else {
- pj_strdup(pool, &send_addr->addr.host, &via->sent_by.host);
+ /* Recursively call this function. */
+ sent = -status;
+ stateless_send_transport_cb( stateless_data, tdata, sent );
+ return;
+ }
+ }
+
+}
+
+/* Resolver callback for sending stateless request. */
+static void
+stateless_send_resolver_callback( pj_status_t status,
+ void *token,
+ const struct pjsip_server_addresses *addr)
+{
+ pjsip_send_state *stateless_data = token;
+
+ /* Fail on server resolution. */
+ if (status != PJ_SUCCESS) {
+ if (stateless_data->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*stateless_data->app_cb)(stateless_data, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(stateless_data->tdata);
+ return;
+ }
+
+ /* Copy server addresses */
+ pj_memcpy( &stateless_data->addr, addr, sizeof(pjsip_server_addresses));
+
+ /* Process the addresses. */
+ stateless_send_transport_cb( stateless_data, stateless_data->tdata,
+ -PJ_EPENDING);
+}
+
+/*
+ * Send stateless request.
+ * The sending process consists of several stages:
+ * - determine which host to contact (#pjsip_get_request_addr).
+ * - resolve the host (#pjsip_endpt_resolve)
+ * - establish transport (#pjsip_endpt_acquire_transport)
+ * - send the message (#pjsip_transport_send)
+ */
+PJ_DEF(pj_status_t)
+pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont))
+{
+ pjsip_host_info dest_info;
+ pjsip_send_state *stateless_data;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(endpt && tdata, PJ_EINVAL);
+
+ /* Get destination name to contact. */
+ status = pjsip_get_request_addr(tdata, &dest_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Keep stateless data. */
+ stateless_data = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+ stateless_data->token = token;
+ stateless_data->endpt = endpt;
+ stateless_data->tdata = tdata;
+ stateless_data->app_cb = cb;
+
+ /* Resolve destination host.
+ * The processing then resumed when the resolving callback is called.
+ */
+ pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data,
+ &stateless_send_resolver_callback);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Determine which address (and transport) to use to send response message
+ * based on the received request. This function follows the specification
+ * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination
+ * address and transport.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr( pj_pool_t *pool,
+ pjsip_rx_data *rdata,
+ pjsip_response_addr *res_addr )
+{
+ pjsip_transport *src_transport = rdata->tp_info.transport;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && rdata && res_addr, PJ_EINVAL);
+
+ /* All requests must have "received" parameter.
+ * This must always be done in transport layer.
+ */
+ pj_assert(rdata->msg_info.via->recvd_param.slen != 0);
+
+ /* Do the calculation based on RFC 3261 Section 18.2.2 and RFC 3581 */
+
+ if (PJSIP_TRANSPORT_IS_RELIABLE(src_transport)) {
+ /* For reliable protocol such as TCP or SCTP, or TLS over those, the
+ * response MUST be sent using the existing connection to the source
+ * of the original request that created the transaction, if that
+ * connection is still open.
+ * If that connection is no longer open, the server SHOULD open a
+ * connection to the IP address in the received parameter, if present,
+ * using the port in the sent-by value, or the default port for that
+ * transport, if no port is specified.
+ * If that connection attempt fails, the server SHOULD use the
+ * procedures in [4] for servers in order to determine the IP address
+ * and port to open the connection and send the response to.
+ */
+ res_addr->transport = rdata->tp_info.transport;
+ pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+ rdata->pkt_info.src_addr_len);
+ res_addr->addr_len = rdata->pkt_info.src_addr_len;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+ }
+
+ } else if (rdata->msg_info.via->maddr_param.slen) {
+ /* Otherwise, if the Via header field value contains a maddr parameter,
+ * the response MUST be forwarded to the address listed there, using
+ * the port indicated in sent-by, or port 5060 if none is present.
+ * If the address is a multicast address, the response SHOULD be sent
+ * using the TTL indicated in the ttl parameter, or with a TTL of 1 if
+ * that parameter is not present.
+ */
+ res_addr->transport = NULL;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->maddr_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0)
+ res_addr->dst_host.addr.port = 5060;
+
+ } else if (rdata->msg_info.via->rport_param >= 0) {
+ /* There is both a "received" parameter and an "rport" parameter,
+ * the response MUST be sent to the IP address listed in the "received"
+ * parameter, and the port in the "rport" parameter.
+ * The response MUST be sent from the same address and port that the
+ * corresponding request was received on.
+ */
+ res_addr->transport = rdata->tp_info.transport;
+ pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+ rdata->pkt_info.src_addr_len);
+ res_addr->addr_len = rdata->pkt_info.src_addr_len;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
}
- /* Set the port */
- send_addr->addr.port = via->sent_by.port;
+ } else {
+ res_addr->transport = NULL;
+ res_addr->dst_host.type = src_transport->key.type;
+ res_addr->dst_host.flag = src_transport->flag;
+ pj_strdup( pool, &res_addr->dst_host.addr.host,
+ &rdata->msg_info.via->recvd_param);
+ res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+ if (res_addr->dst_host.addr.port == 0) {
+ res_addr->dst_host.addr.port =
+ pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+ }
}
return PJ_SUCCESS;
}
/*
+ * Callback called by transport during send_response.
+ */
+static void send_response_transport_cb(void *token, pjsip_tx_data *tdata,
+ pj_ssize_t sent)
+{
+ pjsip_send_state *send_state = token;
+ pj_bool_t cont = PJ_FALSE;
+
+ /* Call callback, if any. */
+ if (send_state->app_cb)
+ (*send_state->app_cb)(send_state, sent, &cont);
+
+ /* Decrement transport reference counter. */
+ pjsip_transport_dec_ref(send_state->cur_transport);
+
+ /* Decrement transmit data ref counter. */
+ pjsip_tx_data_dec_ref(tdata);
+}
+
+/*
+ * Resolver calback during send_response.
+ */
+static void send_response_resolver_cb( pj_status_t status, void *token,
+ const pjsip_server_addresses *addr )
+{
+ pjsip_send_state *send_state = token;
+
+ if (status != PJ_SUCCESS) {
+ if (send_state->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*send_state->app_cb)(send_state, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(send_state->tdata);
+ return;
+ }
+
+ /* Only handle the first address resolved. */
+
+ /* Acquire transport. */
+ status = pjsip_endpt_acquire_transport( send_state->endpt,
+ addr->entry[0].type,
+ &addr->entry[0].addr,
+ addr->entry[0].addr_len,
+ &send_state->cur_transport);
+ if (status != PJ_SUCCESS) {
+ if (send_state->app_cb) {
+ pj_bool_t cont = PJ_FALSE;
+ (*send_state->app_cb)(send_state, -status, &cont);
+ }
+ pjsip_tx_data_dec_ref(send_state->tdata);
+ return;
+ }
+
+ /* Send response using the transoprt. */
+ status = pjsip_transport_send( send_state->cur_transport,
+ send_state->tdata,
+ &addr->entry[0].addr,
+ addr->entry[0].addr_len,
+ send_state,
+ &send_response_transport_cb);
+ if (status == PJ_SUCCESS) {
+ pj_ssize_t sent = send_state->tdata->buf.cur -
+ send_state->tdata->buf.start;
+ send_response_transport_cb(send_state, send_state->tdata, sent);
+
+ } else if (status == PJ_EPENDING) {
+ /* Transport callback will be called later. */
+ } else {
+ send_response_transport_cb(send_state, send_state->tdata, -status);
+ }
+}
+
+/*
+ * Send response.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
+ pjsip_response_addr *res_addr,
+ pjsip_tx_data *tdata,
+ void *token,
+ void (*cb)(pjsip_send_state*,
+ pj_ssize_t sent,
+ pj_bool_t *cont))
+{
+ /* Determine which transports and addresses to send the response,
+ * based on Section 18.2.2 of RFC 3261.
+ */
+ pjsip_send_state *send_state;
+ pj_status_t status;
+
+ /* Create structure to keep the sending state. */
+ send_state = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+ send_state->endpt = endpt;
+ send_state->tdata = tdata;
+ send_state->token = token;
+ send_state->app_cb = cb;
+
+ if (res_addr->transport != NULL) {
+ send_state->cur_transport = res_addr->transport;
+ pjsip_transport_add_ref(send_state->cur_transport);
+
+ status = pjsip_transport_send( send_state->cur_transport, tdata,
+ &res_addr->addr,
+ res_addr->addr_len,
+ send_state,
+ &send_response_transport_cb );
+ if (status == PJ_SUCCESS) {
+ pj_ssize_t sent = tdata->buf.cur - tdata->buf.start;
+ send_response_transport_cb(send_state, tdata, sent);
+ return PJ_SUCCESS;
+ } else if (status == PJ_EPENDING) {
+ /* Callback will be called later. */
+ return PJ_SUCCESS;
+ } else {
+ send_response_transport_cb(send_state, tdata, -status);
+ return status;
+ }
+ } else {
+ pjsip_endpt_resolve(endpt, tdata->pool, &res_addr->dst_host,
+ send_state, &send_response_resolver_cb);
+ return PJ_SUCCESS;
+ }
+}
+
+
+/*
* Get the event string from the event ID.
*/
PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)