summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2010-02-23 11:03:07 +0000
committerBenny Prijono <bennylp@teluu.com>2010-02-23 11:03:07 +0000
commitdf622f00fa10e2cbcde9df6169ad628fe3e72226 (patch)
tree880d29fbe8e66d583d17768cf169068d1b89071f
parentbc8de1cca14dc2a09c570af759f6782aa9802158 (diff)
Ticket #1031: Automatically handle 423 (Interval Too Brief) response in SIP registration (thanks Tomáš Valenta for the suggestion)
- implemented in sip_reg.c instead of in PJSUA-LIB, so that the functionality can be reused by non-PJSUA-LIB applications - also added several Python test scripts git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3105 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c86
-rw-r--r--tests/pjsua/scripts-recvfrom/209a_reg_handle_423_ok.py31
-rw-r--r--tests/pjsua/scripts-recvfrom/209b_reg_handle_423_bad_min_expires1.py20
-rw-r--r--tests/pjsua/scripts-recvfrom/209c_reg_handle_423_bad_min_expires2.py25
4 files changed, 162 insertions, 0 deletions
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index ecba264b..3975028f 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -934,6 +934,7 @@ static void tsx_callback(void *token, pjsip_event *event)
pj_status_t status;
pjsip_regc *regc = (pjsip_regc*) token;
pjsip_transaction *tsx = event->body.tsx_state.tsx;
+ pj_bool_t handled = PJ_TRUE;
pj_atomic_inc(regc->busy_ctr);
pj_lock_acquire(regc->lock);
@@ -1000,7 +1001,92 @@ static void tsx_callback(void *token, pjsip_event *event)
/* Just reset current op */
regc->current_op = REGC_IDLE;
+ } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
+ regc->current_op == REGC_REGISTERING)
+ {
+ /* Handle 423 response automatically:
+ * - set requested expiration to Min-Expires header, ONLY IF
+ * the original request is a registration (as opposed to
+ * unregistration) and the requested expiration was indeed
+ * lower than Min-Expires)
+ * - resend the request
+ */
+ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
+ pjsip_min_expires_hdr *me_hdr;
+ pjsip_tx_data *tdata;
+ pj_int32_t min_exp;
+
+ /* reset current op */
+ regc->current_op = REGC_IDLE;
+
+ /* Update requested expiration */
+ me_hdr = (pjsip_min_expires_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg,
+ PJSIP_H_MIN_EXPIRES, NULL);
+ if (me_hdr) {
+ min_exp = me_hdr->ivalue;
+ } else {
+ /* Broken server, Min-Expires doesn't exist.
+ * Just guestimate then, BUT ONLY if if this is the
+ * first time we received such response.
+ */
+ enum {
+ /* Note: changing this value would require changing couple of
+ * Python test scripts.
+ */
+ UNSPECIFIED_MIN_EXPIRES = 3601
+ };
+ if (!regc->expires_hdr ||
+ regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
+ {
+ min_exp = UNSPECIFIED_MIN_EXPIRES;
+ } else {
+ handled = PJ_FALSE;
+ PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
+ "without Min-Expires header is invalid"));
+ goto handle_err;
+ }
+ }
+
+ if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
+ /* But we already send with greater expiration time, why does
+ * the server send us with 423? Oh well, just fail the request.
+ */
+ handled = PJ_FALSE;
+ PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
+ "Min-Expires header value in response"));
+ goto handle_err;
+ }
+
+ set_expires(regc, min_exp);
+
+ status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_regc_send(regc, tdata);
+ }
+
+ if (status != PJ_SUCCESS) {
+ /* Only call callback if application is still interested
+ * in it.
+ */
+ if (!regc->_delete_flag) {
+ /* Should be safe to release the lock temporarily.
+ * We do this to avoid deadlock.
+ */
+ pj_lock_release(regc->lock);
+ call_callback(regc, status, tsx->status_code,
+ &rdata->msg_info.msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ pj_lock_acquire(regc->lock);
+ }
+ }
+
} else {
+ handled = PJ_FALSE;
+ }
+
+handle_err:
+ if (!handled) {
pjsip_rx_data *rdata;
pj_int32_t expiration = NOEXP;
unsigned contact_cnt = 0;
diff --git a/tests/pjsua/scripts-recvfrom/209a_reg_handle_423_ok.py b/tests/pjsua/scripts-recvfrom/209a_reg_handle_423_ok.py
new file mode 100644
index 00000000..16ec214c
--- /dev/null
+++ b/tests/pjsua/scripts-recvfrom/209a_reg_handle_423_ok.py
@@ -0,0 +1,31 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+pjsua = "--null-audio --id=sip:CLIENT --registrar sip:127.0.0.1:$PORT " + \
+ "--realm=python --user=username --password=password " + \
+ "--auto-update-nat=0"
+
+# 423 Response without Min-Expires header
+req1 = sip.RecvfromTransaction("Initial request", 423,
+ include=["REGISTER sip"],
+ exclude=[],
+ resp_hdr=[]
+ )
+
+# Client should retry with Expires header containing special value (pjsip specific)
+req2 = sip.RecvfromTransaction("REGISTER retry after 423 response without Min-Expires header", 423,
+ include=["REGISTER sip", "Expires: 3601"],
+ exclude=[],
+ resp_hdr=["Min-Expires: 3612"]
+ )
+
+# Client should retry with proper Expires header now
+req3 = sip.RecvfromTransaction("REGISTER retry after proper 423", 200,
+ include=["Expires: 3612"],
+ exclude=[],
+ expect="registration success"
+ )
+
+recvfrom_cfg = sip.RecvfromCfg("Reregistration after 423 response",
+ pjsua, [req1, req2, req3])
diff --git a/tests/pjsua/scripts-recvfrom/209b_reg_handle_423_bad_min_expires1.py b/tests/pjsua/scripts-recvfrom/209b_reg_handle_423_bad_min_expires1.py
new file mode 100644
index 00000000..83a4074c
--- /dev/null
+++ b/tests/pjsua/scripts-recvfrom/209b_reg_handle_423_bad_min_expires1.py
@@ -0,0 +1,20 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+pjsua = "--null-audio --id=sip:CLIENT --registrar sip:127.0.0.1:$PORT " + \
+ "--realm=python --user=username --password=password " + \
+ "--auto-update-nat=0 --reg-timeout 300"
+
+# 423 Response with Min-Expires header that is lower than what the client
+# had requested
+req1 = sip.RecvfromTransaction("Initial request", 423,
+ include=["REGISTER sip"],
+ exclude=[],
+ resp_hdr=["Min-Expires: 250"],
+ expect="invalid Min-Expires"
+
+ )
+
+recvfrom_cfg = sip.RecvfromCfg("Invalid 423 response to REGISTER",
+ pjsua, [req1])
diff --git a/tests/pjsua/scripts-recvfrom/209c_reg_handle_423_bad_min_expires2.py b/tests/pjsua/scripts-recvfrom/209c_reg_handle_423_bad_min_expires2.py
new file mode 100644
index 00000000..378ea283
--- /dev/null
+++ b/tests/pjsua/scripts-recvfrom/209c_reg_handle_423_bad_min_expires2.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+pjsua = "--null-audio --id=sip:CLIENT --registrar sip:127.0.0.1:$PORT " + \
+ "--realm=python --user=username --password=password " + \
+ "--auto-update-nat=0 --reg-timeout 300"
+
+# 423 without Min-Expires. PJSIP would retry with Expires: 3601
+req1 = sip.RecvfromTransaction("Initial request", 423,
+ include=["REGISTER sip"],
+ exclude=[],
+ resp_hdr=[]
+ )
+
+# Another 423, still without Min-Expires
+req2 = sip.RecvfromTransaction("Retry with guessed Expires header", 423,
+ include=["REGISTER sip", "Expires: 3601"],
+ exclude=[],
+ resp_hdr=[],
+ expect="without Min-Expires header is invalid"
+ )
+
+recvfrom_cfg = sip.RecvfromCfg("Invalid 423 response to REGISTER",
+ pjsua, [req1, req2])