summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2011-09-08 04:37:43 +0000
committerBenny Prijono <bennylp@teluu.com>2011-09-08 04:37:43 +0000
commit20dd9709927975e2ddfc8c9216bcaa8e22b063f0 (patch)
treec56e41745023452dad03c80091ee14993f031dcf
parent364e4d7c76c7bad35ab74e875f86f998826b2da3 (diff)
Added support for forking of reliable provisional response (100rel):
- maintain 100rel state for each call leg (To tag) - added SIPp scenario - note that multiple SDP negotiation in 100rel response from different call leg is not supported. Especially the case when multple offers are sent in the responses are not supported (we're supposed to send answers in PRACKs, but currently we only respond to the first 100rel with SDP). This closes #1364 git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/1.x@3743 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/src/pjsip-ua/sip_100rel.c69
-rw-r--r--tests/pjsua/scripts-sipp/uas-forked-100rel.xml225
2 files changed, 276 insertions, 18 deletions
diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c
index 565c74ee..e596d569 100644
--- a/pjsip/src/pjsip-ua/sip_100rel.c
+++ b/pjsip/src/pjsip-ua/sip_100rel.c
@@ -104,8 +104,10 @@ typedef struct uas_state_t
/* UAC state */
typedef struct uac_state_t
{
- pj_int32_t cseq;
- pj_uint32_t rseq; /* Initialized to -1 */
+ pj_str_t tag; /* To tag */
+ pj_int32_t cseq;
+ pj_uint32_t rseq; /* Initialized to -1 */
+ struct uac_state_t *next; /* next call leg */
} uac_state_t;
@@ -114,7 +116,7 @@ struct dlg_data
{
pjsip_inv_session *inv;
uas_state_t *uas_state;
- uac_state_t *uac_state;
+ uac_state_t *uac_state_list;
};
@@ -230,6 +232,8 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
pjsip_tx_data **p_tdata)
{
dlg_data *dd;
+ uac_state_t *uac_state = NULL;
+ const pj_str_t *to_tag = &rdata->msg_info.to->tag;
pjsip_transaction *tsx;
pjsip_msg *msg;
pjsip_generic_string_hdr *rseq_hdr;
@@ -260,41 +264,51 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL);
if (rseq_hdr == NULL) {
PJ_LOG(4,(dd->inv->dlg->obj_name,
- "Ignoring provisional response with no RSeq header"));
+ "Ignoring 100rel response with no RSeq header"));
return PJSIP_EMISSINGHDR;
}
rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue);
+ /* Find UAC state for the specified call leg */
+ uac_state = dd->uac_state_list;
+ while (uac_state) {
+ if (pj_strcmp(&uac_state->tag, to_tag)==0)
+ break;
+ uac_state = uac_state->next;
+ }
+
/* Create new UAC state if we don't have one */
- if (dd->uac_state == NULL) {
- dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool,
- uac_state_t);
- dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
- dd->uac_state->rseq = rseq - 1;
+ if (uac_state == NULL) {
+ uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t);
+ uac_state->cseq = rdata->msg_info.cseq->cseq;
+ uac_state->rseq = rseq - 1;
+ pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag);
+ uac_state->next = dd->uac_state_list;
+ dd->uac_state_list = uac_state;
}
- /* If this is from new INVITE transaction, reset UAC state */
- if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) {
- dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
- dd->uac_state->rseq = rseq - 1;
+ /* If this is from new INVITE transaction, reset UAC state. */
+ if (rdata->msg_info.cseq->cseq != uac_state->cseq) {
+ uac_state->cseq = rdata->msg_info.cseq->cseq;
+ uac_state->rseq = rseq - 1;
}
/* Ignore provisional response retransmission */
- if (rseq <= dd->uac_state->rseq) {
+ if (rseq <= uac_state->rseq) {
/* This should have been handled before */
return PJ_EIGNORED;
/* Ignore provisional response with out-of-order RSeq */
- } else if (rseq != dd->uac_state->rseq + 1) {
+ } else if (rseq != uac_state->rseq + 1) {
PJ_LOG(4,(dd->inv->dlg->obj_name,
- "Ignoring provisional response because RSeq jump "
+ "Ignoring 100rel response because RSeq jump "
"(expecting %u, got %u)",
- dd->uac_state->rseq+1, rseq));
+ uac_state->rseq+1, rseq));
return PJ_EIGNORED;
}
/* Update our RSeq */
- dd->uac_state->rseq = rseq;
+ uac_state->rseq = rseq;
/* Create PRACK */
status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
@@ -302,6 +316,25 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
if (status != PJ_SUCCESS)
return status;
+ /* If this response is a forked response from a different call-leg,
+ * update the req URI (https://trac.pjsip.org/repos/ticket/1364)
+ */
+ if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) {
+ const pjsip_contact_hdr *mhdr;
+
+ mhdr = (const pjsip_contact_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg,
+ PJSIP_H_CONTACT, NULL);
+ if (!mhdr || !mhdr->uri) {
+ PJ_LOG(4,(dd->inv->dlg->obj_name,
+ "Ignoring 100rel response with no or "
+ "invalid Contact header"));
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_EIGNORED;
+ }
+ tdata->msg->line.req.uri = pjsip_uri_clone(tdata->pool, mhdr->uri);
+ }
+
/* Create RAck header */
rack.ptr = rack_buf;
rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),
diff --git a/tests/pjsua/scripts-sipp/uas-forked-100rel.xml b/tests/pjsua/scripts-sipp/uas-forked-100rel.xml
new file mode 100644
index 00000000..c2f2044b
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-forked-100rel.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- 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 -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="Forked INVITE, one of them require PRACK">
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp="branch=([0-9a-zA-Z\-]*)"
+ search_in="hdr"
+ header="Via"
+ assign_to="1,2"/>
+ <assign assign_to="1" variable="2"/>
+ <ereg regexp="CSeq: [ 0-9A-Z]+"
+ search_in="msg"
+ assign_to="4"/>
+ </action>
+ </recv>
+
+ <send>
+ <![CDATA[
+ SIP/2.0 100 Trying
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [$4]
+ ]]>
+ </send>
+
+ <!-- Call leg 1 sends 180/Ringing -->
+ <send retrans="500">
+ <![CDATA[
+ SIP/2.0 180 Ringing1
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_1
+ [last_Call-ID:]
+ [$4]
+ Contact: <sip:UA_1@[local_ip]:[local_port]>
+ Require: 100rel
+ RSeq: 1000
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <recv request="PRACK" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ ]]>
+ </send>
+
+
+ <pause milliseconds="2000" />
+
+ <!-- Call leg 2: 180/Ringing -->
+ <send retrans="500">
+ <![CDATA[
+ SIP/2.0 180 Ringing2
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_2
+ [last_Call-ID:]
+ [$4]
+ Contact: <sip:UA_2@[local_ip]:[local_port]>
+ Require: 100rel
+ RSeq: 2000
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <recv request="PRACK" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ ]]>
+ </send>
+
+
+ <pause milliseconds="2000" />
+
+ <!-- Call leg 2: sends Ringing again with correct RSeq -->
+ <send retrans="500">
+ <![CDATA[
+ SIP/2.0 180 Ringing2b
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_2
+ [last_Call-ID:]
+ [$4]
+ Contact: <sip:UA_2@[local_ip]:[local_port]>
+ Require: 100rel
+ RSeq: 2001
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <recv request="PRACK" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ ]]>
+ </send>
+
+
+ <pause milliseconds="2000" />
+
+ <!-- Call leg 2: sends Ringing again with WRONG RSeq. There should be no PRACK -->
+ <send>
+ <![CDATA[
+ SIP/2.0 180 Ringing2c
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_2
+ [last_Call-ID:]
+ [$4]
+ Contact: <sip:UA_2@[local_ip]:[local_port]>
+ Require: 100rel
+ RSeq: 2004
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <pause milliseconds="2000" />
+
+ <!-- Then Call leg 1 sends 180/Ringing again -->
+ <send retrans="500">
+ <![CDATA[
+ SIP/2.0 180 Ringing1b
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_1
+ [last_Call-ID:]
+ [$4]
+ Contact: <sip:UA_1@[local_ip]:[local_port]>
+ Require: 100rel
+ RSeq: 1001
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <recv request="PRACK" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ ]]>
+ </send>
+
+
+ <pause milliseconds="2000" />
+
+ <!-- 603/Decline -->
+ <send>
+ <![CDATA[
+ SIP/2.0 603 Decline
+ Via: SIP/2.0/UDP 127.0.0.1;received=127.0.0.1;rport=5080;branch=[$2]
+ [last_From:]
+ [last_To:];tag=UA_1
+ [last_Call-ID:]
+ [$4]
+ Content-Length: 0
+ ]]>
+ </send>
+
+
+ <!-- Receive ACK -->
+ <recv request="ACK"
+ optional="false"
+ rtd="true"
+ crlf="true">
+ </recv>
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+