Blob Blame History Raw
From: Liviu Chircu <liviu@opensips.org>
Date: Fri, 12 Feb 2016 17:12:57 +0200
Subject: [PATCH] Improve qvalue parsing

* accept qvalues with no decimal digits (e.g. "1." and "0.")
  (this removes E_Q_DEC_MISSING from error.h)
* improve bad qvalue error reporting

(cherry picked from commit ed60363d9d66bf230872c535e3fc0cf7f117b2c7)

Conflicts:
	cfg.y

diff --git a/cfg.y b/cfg.y
index b969ce0..8b67517 100644
--- a/cfg.y
+++ b/cfg.y
@@ -132,7 +132,7 @@ extern int yylex();
 static void yyerror(char* s);
 static void yyerrorf(char* fmt, ...);
 static char* tmp;
-static int i_tmp;
+static int i_tmp, rc;
 static void* cmd_tmp;
 static struct socket_id* lst_tmp;
 static int rt;  /* Type of route block for find_export */
@@ -2751,13 +2751,18 @@ cmd:	 FORWARD LPAREN STRING RPAREN	{ mk_action2( $$, FORWARD_T,
 		| STRIP error { $$=0; yyerror("missing '(' or ')' ?"); }
 		| STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
 														"number expected"); }
-		| APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN { 
-				{   qvalue_t q;
-				if (str2q(&q, $5, strlen($5)) < 0) {
-					yyerror("bad argument, q value expected");
-				}
+		| APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN {
+			{
+				qvalue_t q;
+
+				rc = str2q(&q, $5, strlen($5));
+				if (rc < 0)
+					yyerrorf("bad qvalue (%.*s): %s",
+							 strlen($5), $5, qverr2str(rc));
+
 				mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, $3,
-						(void *)(long)q); } 
+						(void *)(long)q);
+			}
 		}
 		| APPEND_BRANCH LPAREN STRING RPAREN { mk_action2( $$, APPEND_BRANCH_T,
 						STR_ST, NUMBER_ST, $3, (void *)Q_UNSPECIFIED) ; }
diff --git a/error.c b/error.c
index 9042be3..715863b 100644
--- a/error.c
+++ b/error.c
@@ -105,10 +105,6 @@ int err2reason_phrase(
 			error_txt="q parameter too big";
 			*sip_error=-E_BAD_REQ;
 			break;
-		case E_Q_DEC_MISSING:
-			error_txt="Decimal part missing in q";
-			*sip_error=-E_BAD_REQ;
-			break;
 		case E_NO_DESTINATION:
 			error_txt="No destination available";
 			*sip_error=-E_BAD_SERVER;
diff --git a/error.h b/error.h
index 87854ca..8e6f6a9 100644
--- a/error.h
+++ b/error.h
@@ -46,8 +46,7 @@
 #define E_Q_INV_CHAR        -15		/*!< Invalid character in q */
 #define E_Q_EMPTY           -16		/*!< Empty q */
 #define E_Q_TOO_BIG         -17		/*!< q too big (> 1) */
-#define E_Q_DEC_MISSING     -18		/*!< Decimal part missing */
-#define E_NO_DESTINATION    -19		/*!< No available destination */
+#define E_NO_DESTINATION    -18		/*!< No available destination */
 
 /* opensips specific error codes */
 #define E_IP_BLOCKED      -473		/*!< destination filtered */
diff --git a/modules/registrar/sip_msg.c b/modules/registrar/sip_msg.c
index 28ad6bb..e5d895d 100644
--- a/modules/registrar/sip_msg.c
+++ b/modules/registrar/sip_msg.c
@@ -281,12 +281,16 @@ void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e, struct save
  */
 int calc_contact_q(param_t* _q, qvalue_t* _r)
 {
+	int rc;
+
 	if (!_q || (_q->body.len == 0)) {
 		*_r = default_q;
 	} else {
-		if (str2q(_r, _q->body.s, _q->body.len) < 0) {
+		rc = str2q(_r, _q->body.s, _q->body.len);
+		if (rc < 0) {
 			rerrno = R_INV_Q; /* Invalid q parameter */
-			LM_ERR("invalid q parameter\n");
+			LM_ERR("invalid qvalue (%.*s): %s\n",
+					_q->body.len, _q->body.s, qverr2str(rc));
 			return -1;
 		}
 	}
diff --git a/modules/uac_redirect/rd_funcs.c b/modules/uac_redirect/rd_funcs.c
index ef6a923..329f2cb 100644
--- a/modules/uac_redirect/rd_funcs.c
+++ b/modules/uac_redirect/rd_funcs.c
@@ -106,7 +106,7 @@ static void sort_contacts(contact_t *ct_list, str *ct_array,
 {
 	param_t *q_para;
 	qvalue_t q;
-	int i,j;
+	int i, j, rc;
 	char backup;
 
 	for( ; ct_list ; ct_list = ct_list->next ) {
@@ -123,8 +123,10 @@ static void sort_contacts(contact_t *ct_list, str *ct_array,
 		if (q_para==0 || q_para->body.len==0) {
 			q = DEFAULT_Q_VALUE;
 		} else {
-			if (str2q( &q, q_para->body.s, q_para->body.len)!=0) {
-				LM_ERR("invalid q param\n");
+			rc = str2q( &q, q_para->body.s, q_para->body.len);
+			if (rc != 0) {
+				LM_ERR("invalid qvalue (%.*s): %s\n",
+						q_para->body.len, q_para->body.s, qverr2str(rc));
 				/* skip this contact */
 				continue;
 			}
diff --git a/qvalue.c b/qvalue.c
index 5cd2cd9..34029f3 100644
--- a/qvalue.c
+++ b/qvalue.c
@@ -75,6 +75,7 @@ int str2q(qvalue_t* q, char* s, int len)
 				break;
 
 			case '.':
+				*q = 0;
 				state = ST_0_PT;
 				break;
 
@@ -167,15 +168,8 @@ int str2q(qvalue_t* q, char* s, int len)
 		}
 	}
 
-	switch(state) {
-	case ST_START:
+	if (state == ST_START)
 		return E_Q_EMPTY;
 
-	case ST_0_PT:
-	case ST_1_PT:
-		return E_Q_DEC_MISSING;
-
-	default:
-		return 0;
-	}
+	return 0;
 }
diff --git a/qvalue.h b/qvalue.h
index 96aa81f..717844c 100644
--- a/qvalue.h
+++ b/qvalue.h
@@ -76,6 +76,10 @@ typedef int qvalue_t;
 #define Q_PREFIX "0."
 #define Q_PREFIX_LEN (sizeof(Q_PREFIX) - 1)
 
+#define qverr2str(rc) \
+	(rc == E_Q_INV_CHAR ? "bad characters" : \
+	 rc == E_Q_EMPTY ? "empty value" : \
+	 rc == E_Q_TOO_BIG ? "max value is 1.0" : "bad qvalue")
 
 
 /*! \brief