Fixed ticket #25: Authentication loops forever when server keeps rejecting request with stale=true

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@871 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 28e1c82..a3364ed 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -113,6 +113,7 @@
     pj_str_t			 realm;	    /**< Realm.			    */
     pj_bool_t			 is_proxy;  /**< Server type (401/407)	    */
     pjsip_auth_qop_type		 qop_value; /**< qop required by server.    */
+    unsigned			 stale_cnt; /**< Number of stale retry.	    */
 #if PJSIP_AUTH_QOP_SUPPORT
     pj_uint32_t			 nc;	    /**< Nonce count.		    */
     pj_str_t			 cnonce;    /**< Cnonce value.		    */
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 6cf04e8..e987b20 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -409,6 +409,15 @@
 
 
 /**
+ * Maximum number of stale retries when server keeps rejecting our request
+ * with stale=true.
+ */
+#ifndef PJSIP_MAX_STALE_COUNT
+#   define PJSIP_MAX_STALE_COUNT	    3
+#endif
+
+
+/**
  * @}
  */
 
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 03d21ec..10b542c 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -361,6 +361,12 @@
  * Invalid digest.
  */
 #define PJSIP_EAUTHINVALIDDIGEST (PJSIP_ERRNO_START_PJSIP+110)	/* 171110 */
+/**
+ * @hideinitializer
+ * Maximum number of stale retries exceeded. This happens when server 
+ * keeps rejecting our authorization request with stale=true.
+ */
+#define PJSIP_EAUTHSTALECOUNT	(PJSIP_ERRNO_START_PJSIP + 111)	/* 171111 */
 
 
 /************************************************************
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index 112d45d..0514070 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -43,6 +43,7 @@
 #  define AUTH_TRACE_(expr)
 #endif
 
+
 /* Transform digest to string.
  * output must be at least PJSIP_MD5STRLEN+1 bytes.
  *
@@ -599,6 +600,9 @@
 
     auth = sess->cached_auth.next;
     while (auth != &sess->cached_auth) {
+	/* Reset stale counter */
+	auth->stale_cnt = 0;
+
 	if (auth->qop_value == PJSIP_AUTH_QOP_NONE) {
 #	    if defined(PJSIP_AUTH_HEADER_CACHING) && \
 	       PJSIP_AUTH_HEADER_CACHING!=0
@@ -707,7 +711,8 @@
 	    /* Our credential is rejected. No point in trying to re-supply
 	     * the same credential.
 	     */
-	    PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
+	    PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: "
+		       "server rejected with stale=false",
 		       sent_auth->credential.digest.username.slen,
 		       sent_auth->credential.digest.username.ptr,
 		       sent_auth->credential.digest.realm.slen,
@@ -715,6 +720,20 @@
 	    return PJSIP_EFAILEDCREDENTIAL;
 	}
 
+	cached_auth->stale_cnt++;
+	if (cached_auth->stale_cnt >= PJSIP_MAX_STALE_COUNT) {
+	    /* Our credential is rejected. No point in trying to re-supply
+	     * the same credential.
+	     */
+	    PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: "
+		       "maximum number of stale retries exceeded",
+		       sent_auth->credential.digest.username.slen,
+		       sent_auth->credential.digest.username.ptr,
+		       sent_auth->credential.digest.realm.slen,
+		       sent_auth->credential.digest.realm.ptr));
+	    return PJSIP_EAUTHSTALECOUNT;
+	}
+
 	/* Otherwise remove old, stale authorization header from the mesasge.
 	 * We will supply a new one.
 	 */
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index e9f126f..7ca4d21 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -99,6 +99,7 @@
     PJ_BUILD_ERR( PJSIP_EAUTHACCDISABLED,  "Account or credential is disabled" ),
     PJ_BUILD_ERR( PJSIP_EAUTHINVALIDREALM, "Invalid authorization realm"),
     PJ_BUILD_ERR( PJSIP_EAUTHINVALIDDIGEST,"Invalid authorization digest" ),
+    PJ_BUILD_ERR( PJSIP_EAUTHSTALECOUNT,   "Maximum number of stale retries exceeded"),
 
     /* UA/dialog layer. */
     PJ_BUILD_ERR( PJSIP_EMISSINGTAG,	"Missing From/To tag parameter" ),