summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip/sip_dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsip/sip_dialog.c')
-rw-r--r--pjsip/src/pjsip/sip_dialog.c229
1 files changed, 117 insertions, 112 deletions
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index c6ad91ab..f41b5d53 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -74,6 +74,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua,
pj_sprintf(dlg->obj_name, "dlg%p", dlg);
dlg->ua = ua;
dlg->endpt = endpt;
+ dlg->state = PJSIP_DIALOG_STATE_NULL;
status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex);
if (status != PJ_SUCCESS)
@@ -374,6 +375,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
PJ_TODO(DIALOG_APP_TIMER);
+ /* Feed the first request to the transaction. */
+ pjsip_tsx_recv_msg(tsx, rdata);
+
/* Done. */
*p_dlg = dlg;
return PJ_SUCCESS;
@@ -407,6 +411,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG,
PJSIP_ENOTRESPONSEMSG);
+ /* Status code MUST be 1xx (but not 100), or 2xx */
+ status = rdata->msg_info.msg->line.status.code;
+ PJ_ASSERT_RETURN( (status/100==1 && status!=100) ||
+ (status/100==2), PJ_EBUG);
+
/* To tag must present in the response. */
PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG);
@@ -444,6 +453,15 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
/* Initial role is UAC. */
dlg->role = PJSIP_ROLE_UAC;
+ /* Dialog state depends on the response. */
+ status = rdata->msg_info.msg->line.status.code/100;
+ if (status == 1 || status == 2)
+ dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
+ else {
+ pj_assert(!"Invalid status code");
+ dlg->state = PJSIP_DIALOG_STATE_NULL;
+ }
+
/* Secure? */
dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target);
@@ -462,7 +480,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
r = r->next;
}
- /* Init client authentication session. */
+ /* Clone client authentication session. */
status = pjsip_auth_clt_clone(dlg->pool, &dlg->auth_sess,
&first_dlg->auth_sess);
if (status != PJ_SUCCESS)
@@ -849,99 +867,54 @@ on_error:
return status;
}
-
-/*
- * Create response.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
- pjsip_rx_data *rdata,
- int st_code,
- const pj_str_t *st_text,
- pjsip_tx_data **p_tdata)
+/* Add standard headers for certain types of response */
+static void dlg_beautify_response(pjsip_dialog *dlg,
+ int st_code,
+ pjsip_tx_data *tdata)
{
- pj_status_t status;
pjsip_cseq_hdr *cseq;
- pjsip_tx_data *tdata;
int st_class;
+ const pjsip_hdr *c_hdr;
+ pjsip_hdr *hdr;
- /* Create generic response. */
- status = pjsip_endpt_create_response(dlg->endpt,
- rdata, st_code, st_text, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Lock the dialog. */
- pj_mutex_lock(dlg->mutex);
-
- /* Special treatment for 2xx response to request that establishes
- * dialog.
- *
- * RFC 3261 Section 12.1.1
- *
- * When a UAS responds to a request with a response that establishes
- * a dialog (such as a 2xx to INVITE):
- * - MUST copy all Record-Route header field values from the request
- * into the response (including the URIs, URI parameters, and any
- * Record-Route header field parameters, whether they are known or
- * unknown to the UAS) and MUST maintain the order of those values.
- * - The Contact header field contains an address where the UAS would
- * like to be contacted for subsequent requests in the dialog.
- *
- * Also from Table 3, page 119.
- */
cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg);
pj_assert(cseq != NULL);
st_class = st_code / 100;
- if (cseq->cseq == dlg->remote.first_cseq &&
- (st_class==1 || st_class==2) && st_code != 100)
- {
- pjsip_hdr *rr, *hdr;
-
- /* Duplicate Record-Route header from the request. */
- rr = (pjsip_hdr*) rdata->msg_info.record_route;
- while (rr) {
- hdr = pjsip_hdr_clone(tdata->pool, rr);
- pjsip_msg_add_hdr(tdata->msg, hdr);
-
- rr = rr->next;
- if (rr == &rdata->msg_info.msg->hdr)
- break;
- rr = pjsip_msg_find_hdr(rdata->msg_info.msg,
- PJSIP_H_RECORD_ROUTE, rr);
- }
- }
-
- /* Contact header. */
+ /* Contact, Allow, Supported header. */
if (pjsip_method_creates_dialog(&cseq->method)) {
/* Add Contact header for 1xx, 2xx, 3xx and 485 response. */
if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) ||
st_code==485)
{
- /* Add contact header. */
- pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact);
- pjsip_msg_add_hdr(tdata->msg, hdr);
+ /* Add contact header only if one is not present. */
+ if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL) == 0) {
+ hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact);
+ pjsip_msg_add_hdr(tdata->msg, hdr);
+ }
}
/* Add Allow header in 2xx and 405 response. */
- if (st_class==2 || st_code==405) {
- const pjsip_hdr *c_hdr;
+ if ((st_class==2 || st_code==405) &&
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL)==NULL)
+ {
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_ALLOW, NULL);
if (c_hdr) {
- pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
+ hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
/* Add Supported header in 2xx response. */
- if (st_class==2) {
- const pjsip_hdr *c_hdr;
+ if (st_class==2 &&
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL)==NULL)
+ {
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_SUPPORTED, NULL);
if (c_hdr) {
- pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
+ hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
@@ -949,7 +922,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
}
/* Add To tag in all responses except 100 */
- if (st_code != 100 && rdata->msg_info.to->tag.slen == 0) {
+ if (st_code != 100) {
pjsip_to_hdr *to;
to = PJSIP_MSG_TO_HDR(tdata->msg);
@@ -957,6 +930,34 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
to->tag = dlg->local.info->tag;
}
+}
+
+
+/*
+ * Create response.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pj_str_t *st_text,
+ pjsip_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+
+ /* Create generic response.
+ * This will initialize response's Via, To, From, Call-ID, CSeq
+ * and Record-Route headers from the request.
+ */
+ status = pjsip_endpt_create_response(dlg->endpt,
+ rdata, st_code, st_text, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Lock the dialog. */
+ pj_mutex_lock(dlg->mutex);
+
+ dlg_beautify_response(dlg, st_code, tdata);
/* Unlock the dialog. */
pj_mutex_unlock(dlg->mutex);
@@ -980,6 +981,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
PJSIP_ENOTRESPONSEMSG);
PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL);
+ pj_mutex_lock(dlg->mutex);
+
+ /* Replace status code and reason */
tdata->msg->line.status.code = st_code;
if (st_text) {
pj_strdup(tdata->pool, &tdata->msg->line.status.reason, st_text);
@@ -987,6 +991,17 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
tdata->msg->line.status.reason = *pjsip_get_status_text(st_code);
}
+ dlg_beautify_response(dlg, st_code, tdata);
+
+
+ /* Must add reference counter, since tsx_send_msg() will decrement it */
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Force to re-print message. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ pj_mutex_unlock(dlg->mutex);
+
return PJ_SUCCESS;
}
@@ -1057,18 +1072,22 @@ PJ_DEF(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg,
void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
{
pj_status_t status;
- pjsip_transaction *tsx;
+ pjsip_transaction *tsx = NULL;
unsigned i;
/* Lock the dialog. */
pj_mutex_lock(dlg->mutex);
/* Check CSeq */
- if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq) {
+ if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq &&
+ rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD &&
+ rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD)
+ {
/* Invalid CSeq.
* Respond statelessly with 500 (Internal Server Error)
*/
pj_mutex_unlock(dlg->mutex);
+ pj_assert(pjsip_rdata_get_tsx(rdata) == NULL);
pjsip_endpt_respond_stateless(dlg->endpt,
rdata, 500, NULL, NULL, NULL);
return;
@@ -1078,14 +1097,16 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
dlg->remote.cseq = rdata->msg_info.cseq->cseq;
/* Create UAS transaction for this request. */
- status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
- PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});
+ if (pjsip_rdata_get_tsx(rdata) == NULL) {
+ status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
+ PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});
- /* Put this dialog in the transaction data. */
- tsx->mod_data[dlg->ua->id] = dlg;
+ /* Put this dialog in the transaction data. */
+ tsx->mod_data[dlg->ua->id] = dlg;
- /* Add transaction count. */
- ++dlg->tsx_count;
+ /* Add transaction count. */
+ ++dlg->tsx_count;
+ }
/* Report the request to dialog usages. */
for (i=0; i<dlg->usage_cnt; ++i) {
@@ -1100,28 +1121,9 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
break;
}
- if (i==dlg->usage_cnt) {
- pjsip_tx_data *tdata;
-
- PJ_LOG(4,(dlg->obj_name,
- "%s is unhandled by dialog usages. "
- "Dialog will response with 500 (Internal Server Error)",
- pjsip_rx_data_get_info(rdata)));
- status = pjsip_endpt_create_response(dlg->endpt,
- rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR,
- NULL, &tdata);
- if (status == PJ_SUCCESS)
- status = pjsip_tsx_send_msg(tsx, tdata);
-
- if (status != PJ_SUCCESS) {
- char errmsg[PJSIP_ERR_MSG_SIZE];
- pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(4,(dlg->obj_name,"Error sending %s: %s",
- pjsip_tx_data_get_info(tdata), errmsg));
- pjsip_tsx_terminate(tsx, 500);
- }
- }
+ /* Feed the first request to the transaction. */
+ if (tsx)
+ pjsip_tsx_recv_msg(tsx, rdata);
on_return:
/* Unlock dialog. */
@@ -1142,25 +1144,25 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
/* Check that rdata already has dialog in mod_data. */
pj_assert(pjsip_rdata_get_dlg(rdata) == dlg);
- /* Update the remote tag if it is different. */
- if (pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag) != 0) {
-
- pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
-
- /* No need to update remote's tag_hval since its never used. */
- }
-
/* Keep the response's status code */
res_code = rdata->msg_info.msg->line.status.code;
- /* When we receive response that establishes dialog, update the route
- * set and dialog target.
+ /* When we receive response that establishes dialog, update To tag,
+ * route set and dialog target.
*/
- if (!dlg->established &&
+ if (dlg->state == PJSIP_DIALOG_STATE_NULL &&
pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
(res_code > 100 && res_code < 300) &&
rdata->msg_info.to->tag.slen)
{
+ pjsip_hdr *hdr, *end_hdr;
+ pjsip_contact_hdr *contact;
+
+ /* Update To tag. */
+ pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
+ /* No need to update remote's tag_hval since its never used. */
+
+
/* RFC 3271 Section 12.1.2:
* The route set MUST be set to the list of URIs in the Record-Route
* header field from the response, taken in reverse order and
@@ -1169,9 +1171,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
* empty set. This route set, even if empty, overrides any pre-existing
* route set for future requests in this dialog.
*/
- pjsip_hdr *hdr, *end_hdr;
- pjsip_contact_hdr *contact;
-
pj_list_init(&dlg->route_set);
end_hdr = &rdata->msg_info.msg->hdr;
@@ -1194,7 +1193,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
dlg->target = dlg->remote.contact->uri;
}
- dlg->established = 1;
+ dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
}
/* Update remote target (again) when receiving 2xx response messages
@@ -1251,6 +1250,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg,
if (tsx->state == PJSIP_TSX_STATE_TERMINATED)
--dlg->tsx_count;
+ /* Increment session to prevent usages from destroying dialog. */
+ ++dlg->sess_count;
+
/* Pass to dialog usages. */
for (i=0; i<dlg->usage_cnt; ++i) {
@@ -1260,6 +1262,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg,
(*dlg->usage[i]->on_tsx_state)(tsx, e);
}
+ /* Decrement temporary session. */
+ --dlg->sess_count;
+
if (tsx->state == PJSIP_TSX_STATE_TERMINATED && dlg->tsx_count == 0 &&
dlg->sess_count == 0)
{