[svn-commits] dvossel: branch dvossel/sip_request_transaction_matching r275386 - in /team/d...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jul 9 16:16:20 CDT 2010


Author: dvossel
Date: Fri Jul  9 16:16:16 2010
New Revision: 275386

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=275386
Log:
store info from top via of initial Request, detect loops by comparing branch parameter

Modified:
    team/dvossel/sip_request_transaction_matching/channels/chan_sip.c
    team/dvossel/sip_request_transaction_matching/channels/sip/include/reqresp_parser.h
    team/dvossel/sip_request_transaction_matching/channels/sip/include/sip.h
    team/dvossel/sip_request_transaction_matching/channels/sip/reqresp_parser.c

Modified: team/dvossel/sip_request_transaction_matching/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/sip_request_transaction_matching/channels/chan_sip.c?view=diff&rev=275386&r1=275385&r2=275386
==============================================================================
--- team/dvossel/sip_request_transaction_matching/channels/chan_sip.c (original)
+++ team/dvossel/sip_request_transaction_matching/channels/chan_sip.c Fri Jul  9 16:16:16 2010
@@ -6913,6 +6913,15 @@
 	}
 
 	if (req) {
+		char *sent_by, *branch;
+		/* get branch parameter from initial Request that started this dialog */
+		get_viabranch(ast_strdupa(get_header(req, "Via")), &branch, &sent_by);
+		/* only store the branch if it begins with the magic prefix "z9hG4bK", otherwise
+		 * it is not useful to us to have it */
+		if (!ast_strlen_zero(branch) && !strncasecmp(branch, "z9hG4bK", 7)) {
+			ast_string_field_set(p, initviabranch, branch);
+			ast_string_field_set(p, initviasentby, sent_by);
+		}
 		set_socket_transport(&p->socket, req->socket.type); /* Later in ast_sip_ouraddrfor we need this to choose the right ip and port for the specific transport */
 	} else {
 		set_socket_transport(&p->socket, SIP_TRANSPORT_UDP);
@@ -7026,7 +7035,6 @@
 static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method)
 {
 	struct sip_pvt *p = NULL;
-	char *tag = "";	/* note, tag is never NULL */
 	char totag[128];
 	char fromtag[128];
 	const char *callid = get_header(req, "Call-ID");
@@ -7059,8 +7067,6 @@
 			req->has_to_tag = 1;	/* Used in handle_request/response */
 		gettag(req, "From", fromtag, sizeof(fromtag));
 
-		tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
-
 		ast_debug(5, "= Looking for  Call ID: %s (Checking %s) --From tag %s --To-tag %s  \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
 
 		/* All messages must always have From: tag */
@@ -7091,37 +7097,76 @@
 		};
 		struct ao2_iterator *iterator = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER | OBJ_MULTIPLE,
 							   "pedantic ao2_find in dialogs");
-		if (iterator) {
-			int found = TRUE;
-
-			while ((sip_pvt_ptr = ao2_iterator_next(iterator))) {
-				if (req->method != SIP_REGISTER) {
-					found = ast_strlen_zero(tag) || ast_strlen_zero(sip_pvt_ptr->theirtag) ||
-						!ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED) ||
-						!strcmp(sip_pvt_ptr->theirtag, tag);
+		/* Iterate a list of dialogs already matched by Call-id */
+		while (iterator && (sip_pvt_ptr = ao2_iterator_next(iterator))) {
+			if (req->method == SIP_RESPONSE) {
+				/* MATCH RESPONSE with DIALOG */
+				/* check totag if we have one stored for this dialog */
+				if (!ast_strlen_zero(sip_pvt_ptr->theirtag)) {
+					if (ast_strlen_zero(totag)) {
+						/* missing totag when they already gave us one earlier */
+						goto not_a_match;
+					}
+					if (strcmp(totag, sip_pvt_ptr->theirtag)) {
+						/* The totag of the response does not match the one we have stored */
+						goto not_a_match;
+					}
 				}
-				ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match",
-					  sip_pvt_ptr->callid, sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
-				/* If we get a new request within an existing to-tag - check the to tag as well */
-				if (found && req->method != SIP_RESPONSE) { /* SIP Request */
-					if (sip_pvt_ptr->tag[0] == '\0' && totag[0]) {
-						/* We have no to tag, but they have. Wrong dialog */
-						found = FALSE;
-					} else if (totag[0]) { /* Both have tags, compare them */
-						if (strcmp(totag, sip_pvt_ptr->tag)) {
-							found = FALSE; /* This is not our packet */
+				/* check fromtag of response matches the tag we gave them. */
+				if (strcmp(fromtag, sip_pvt_ptr->tag)) {
+					/* fromtag from response does not match our tag */
+					goto not_a_match;
+				}
+			} else {
+				/* MATCH REQUEST with DIALOG */
+				/* Verify the fromtag of Request matches the tag they provided earlier. */
+				if (strcmp(fromtag, sip_pvt_ptr->theirtag)) {
+					/* their tag does not match the one was have stored for them */
+					goto not_a_match;
+				}
+				/* if totag is present in Request, verify it matches what we gave them as our tag earlier */
+				if (!ast_strlen_zero(totag) && (strcmp(totag, sip_pvt_ptr->tag))) {
+					/* totag from Request does not match our tag */
+					goto not_a_match;
+				}
+				/* If totag is NOT present in Request, compare branch parameters with initial Request */
+				if (ast_strlen_zero(totag) && !ast_strlen_zero(sip_pvt_ptr->initviabranch)) {
+					char *sent_by, *branch;
+					/* get branch parameter from top VIA header of Request */
+					get_viabranch(ast_strdupa(get_header(req, "Via")), &branch, &sent_by);
+					if (ast_strlen_zero(branch) ||
+						strcmp(branch, sip_pvt_ptr->initviabranch) ||
+						ast_strlen_zero(sent_by) ||
+						strcmp(sent_by, sip_pvt_ptr->initviasentby)) {
+
+						/* If we made it in here, the branch and sent_by of top VIA did not match our initial Request */
+						if (sip_pvt_ptr->method != req->method) {
+							/* If the methods are not the same, then this is just a normal mismatch. */
+							goto not_a_match;
+						} else {
+							/* Loop Detected: everything is the EXACT same about this Request and the initial
+							 * request except the branch parameters are different in the TOP VIA. */
+							goto loop_detected;
 						}
 					}
-					if (!found)
-						ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n",
-							  sip_pvt_ptr->callid, totag, sip_methods[req->method].text);
 				}
-				if (found) {
-					sip_pvt_lock(sip_pvt_ptr);
-					ao2_iterator_destroy(iterator);
-					return sip_pvt_ptr;
-				}
-			}
+			}
+			/* If we made it here, then this is a match. Leave the reference from the iterator */
+			sip_pvt_lock(sip_pvt_ptr);
+			ao2_iterator_destroy(iterator);
+			return sip_pvt_ptr;
+
+loop_detected:
+			/* This is likely a forked Request that somehow resulted in us receiving multiple parts of the fork.
+			 * RFC 3261 section 8.2.2.2, Indicate that we want to merge requests by sending a 482 response. */
+			transmit_response_using_temp(callid, addr, 1, intended_method, req, "482 (Loop Detected)");
+			dialog_unref(sip_pvt_ptr, "This pvt did not match incomming SIP msg, unref pvt from find_call search.");
+			return NULL;
+not_a_match:
+			dialog_unref(sip_pvt_ptr, "This pvt did not match incomming SIP msg, unref pvt from find_call search.");
+		} /* end of while */
+
+		if (iterator) {
 			ao2_iterator_destroy(iterator);
 		}
 	}

Modified: team/dvossel/sip_request_transaction_matching/channels/sip/include/reqresp_parser.h
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/sip_request_transaction_matching/channels/sip/include/reqresp_parser.h?view=diff&rev=275386&r1=275385&r2=275386
==============================================================================
--- team/dvossel/sip_request_transaction_matching/channels/sip/include/reqresp_parser.h (original)
+++ team/dvossel/sip_request_transaction_matching/channels/sip/include/reqresp_parser.h Fri Jul  9 16:16:16 2010
@@ -142,4 +142,10 @@
  */
 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len);
 
+/*!
+ * \brief Parse the VIA header into it's parts.
+ *
+ * \note This will modify the string
+ */
+void get_viabranch(char *via, char **sent_by, char **branch);
 #endif

Modified: team/dvossel/sip_request_transaction_matching/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/sip_request_transaction_matching/channels/sip/include/sip.h?view=diff&rev=275386&r1=275385&r2=275386
==============================================================================
--- team/dvossel/sip_request_transaction_matching/channels/sip/include/sip.h (original)
+++ team/dvossel/sip_request_transaction_matching/channels/sip/include/sip.h Fri Jul  9 16:16:16 2010
@@ -910,6 +910,8 @@
 	int method;                             /*!< SIP method that opened this dialog */
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(callid);       /*!< Global CallID */
+		AST_STRING_FIELD(initviabranch); /*!< The branch ID from the topmost Via header in the initial request */
+		AST_STRING_FIELD(initviasentby); /*!< The sent-by from the topmost Via header in the initial request */
 		AST_STRING_FIELD(randdata);     /*!< Random data */
 		AST_STRING_FIELD(accountcode);  /*!< Account code */
 		AST_STRING_FIELD(realm);        /*!< Authorization realm */

Modified: team/dvossel/sip_request_transaction_matching/channels/sip/reqresp_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/sip_request_transaction_matching/channels/sip/reqresp_parser.c?view=diff&rev=275386&r1=275385&r2=275386
==============================================================================
--- team/dvossel/sip_request_transaction_matching/channels/sip/reqresp_parser.c (original)
+++ team/dvossel/sip_request_transaction_matching/channels/sip/reqresp_parser.c Fri Jul  9 16:16:16 2010
@@ -1773,6 +1773,150 @@
 	return res;
 }
 
+void get_viabranch(char *via, char **sent_by, char **branch)
+{
+	char *tmp;
+
+	if (sent_by) {
+		*sent_by = NULL;
+	}
+	if (branch) {
+		*branch = NULL;
+	}
+	if (ast_strlen_zero(via)) {
+		return;
+	}
+	via = ast_skip_blanks(via);
+	/*
+	 * VIA syntax. RFC 3261 section 6.40.5
+	 * Via = ( "Via" | "v") ":" 1#( sent-protocol sent-by *( ";" via-params ) [ comment ] )
+	 * via-params  = via-hidden | via-ttl | via-maddr | via-received | via-branch
+	 * via-hidden       = "hidden"
+	 * via-ttl          = "ttl" "=" ttl
+	 * via-maddr        = "maddr" "=" maddr
+	 * via-received     = "received" "=" host
+	 * via-branch       = "branch" "=" token
+	 * sent-protocol    = protocol-name "/" protocol-version "/" transport
+	 * protocol-name    = "SIP" | token
+	 * protocol-version = token
+	 * transport        = "UDP" | "TCP" | token
+	 * sent-by          = ( host [ ":" port ] ) | ( concealed-host )
+	 * concealed-host   = token
+	 * ttl              = 1*3DIGIT     ; 0 to 255	
+	 */
+
+	/* chop off ("Via:" | "v:") if present */
+	if (!strncasecmp(via, "Via:", 4)) {
+		via += 4;
+	} else if (!strncasecmp(via, "v:", 2)) {
+		via += 2;
+	}
+	if (ast_strlen_zero(via)) {
+		return;
+	}
+
+	/* chop off sent-protocol */
+	via = ast_skip_blanks(via);
+	strsep(&via, " \t\r\n");
+	if (ast_strlen_zero(via)) {
+		return;
+	}
+
+	/* chop off sent-by */
+	via = ast_skip_blanks(via);
+	*sent_by = strsep(&via, "; \t\r\n");
+	if (ast_strlen_zero(via)) {
+		return;
+	}
+
+	/* now see if there is a branch parameter in there */
+	if (!ast_strlen_zero(via) && (tmp = strstr(via, "branch="))) {
+		/* find the branch ID */
+		via = ast_skip_blanks(tmp + 7);
+
+		/* chop off the branch parameter */
+		*branch = strsep(&via, "; \t\r\n");
+	}
+}
+
+AST_TEST_DEFINE(get_viabranch_test)
+{
+	int res = AST_TEST_PASS;
+	int i = 1;
+	char *sent_by, *branch;
+	struct testdata {
+		char *in;
+		char *expected_branch;
+		char *expected_sent_by;
+		AST_LIST_ENTRY(testdata) list;
+	};
+	struct testdata *testdataptr;
+	static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
+	struct testdata t1 = {
+		.in = "Via: SIP/2.0/UDP host:port;branch=thebranch",
+		.expected_branch = "thebranch",
+		.expected_sent_by = "host:port"
+	};
+	struct testdata t2 = {
+		.in = "SIP/2.0/UDP host:port;branch=thebranch",
+		.expected_branch = "thebranch",
+		.expected_sent_by = "host:port"
+	};
+	struct testdata t3 = {
+		.in = "SIP/2.0/UDP host:port",
+		.expected_branch = "",
+		.expected_sent_by = "host:port"
+	};
+	struct testdata t4 = {
+		.in = "BLAH/BLAH/BLAH            host:port        ;    branch=        thebranch ;;;;;;;",
+		.expected_branch = "thebranch",
+		.expected_sent_by = "host:port"
+	};
+	struct testdata t5 = {
+		.in = "v: BLAH/BLAH/BLAH",
+		.expected_branch = "",
+		.expected_sent_by = ""
+	};
+	struct testdata t6 = {
+		.in = "BLAH/BLAH/BLAH host:port;branch=",
+		.expected_branch = "",
+		.expected_sent_by = "host:port"
+	};
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "get_viabranch_test";
+		info->category = "channels/chan_sip/";
+		info->summary = "Tests getting sent-by and branch parameter from via";
+		info->description =
+				"Runs through various test situations in which a sent-by and"
+				" branch parameter must be extracted from a VIA header";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &t1);
+	AST_LIST_INSERT_TAIL(&testdatalist, &t2, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &t3, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &t4, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &t5, list);
+	AST_LIST_INSERT_TAIL(&testdatalist, &t6, list);
+
+
+	AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
+		get_viabranch(ast_strdupa(testdataptr->in), &sent_by, &branch);
+		if ((ast_strlen_zero(sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by)) ||
+			(ast_strlen_zero(branch) && !ast_strlen_zero(testdataptr->expected_branch)) ||
+			(!ast_strlen_zero(sent_by) && strcmp(sent_by, testdataptr->expected_sent_by)) ||
+			(!ast_strlen_zero(branch) && strcmp(branch, testdataptr->expected_branch))) {
+			ast_test_status_update(test, "TEST#%d FAILED:  VIA = \"%s\" parsed sent-by = \"%s\" parsed branch = \"%s\"\n",
+			i, testdataptr->in, sent_by, branch);
+			res = AST_TEST_FAIL;
+		}
+		i++;
+	}
+	return res;
+}
 
 void sip_request_parser_register_tests(void)
 {
@@ -1784,6 +1928,7 @@
 	AST_TEST_REGISTER(parse_name_andor_addr_test);
 	AST_TEST_REGISTER(parse_contact_header_test);
 	AST_TEST_REGISTER(sip_parse_options_test);
+	AST_TEST_REGISTER(get_viabranch_test);
 }
 void sip_request_parser_unregister_tests(void)
 {
@@ -1795,4 +1940,5 @@
 	AST_TEST_UNREGISTER(parse_name_andor_addr_test);
 	AST_TEST_UNREGISTER(parse_contact_header_test);
 	AST_TEST_UNREGISTER(sip_parse_options_test);
-}
+	AST_TEST_UNREGISTER(get_viabranch_test);
+}




More information about the svn-commits mailing list