[asterisk-commits] kpfleming: branch kpfleming/via-branch-id-match r221084 - in /team/kpfleming/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Sep 30 08:50:57 CDT 2009


Author: kpfleming
Date: Wed Sep 30 08:50:53 2009
New Revision: 221084

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=221084
Log:
save away some work in progress from SIPit 25


Added:
    team/kpfleming/via-branch-id-match/
      - copied from r221083, branches/1.6.2/
Modified:
    team/kpfleming/via-branch-id-match/channels/chan_sip.c

Modified: team/kpfleming/via-branch-id-match/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/kpfleming/via-branch-id-match/channels/chan_sip.c?view=diff&rev=221084&r1=221083&r2=221084
==============================================================================
--- team/kpfleming/via-branch-id-match/channels/chan_sip.c (original)
+++ team/kpfleming/via-branch-id-match/channels/chan_sip.c Wed Sep 30 08:50:53 2009
@@ -828,24 +828,25 @@
 static const struct  cfsip_methods { 
 	enum sipmethod id;
 	int need_rtp;		/*!< when this is the 'primary' use for a pvt structure, does it need RTP? */
-	char * const text;
+	const char *text;
 	enum can_create_dialog can_create;
+	char must_include_our_tag;
 } sip_methods[] = {
-	{ SIP_UNKNOWN,	 RTP,    "-UNKNOWN-", 	CAN_CREATE_DIALOG },
-	{ SIP_RESPONSE,	 NO_RTP, "SIP/2.0",	CAN_NOT_CREATE_DIALOG },
-	{ SIP_REGISTER,	 NO_RTP, "REGISTER", 	CAN_CREATE_DIALOG },
- 	{ SIP_OPTIONS,	 NO_RTP, "OPTIONS", 	CAN_CREATE_DIALOG },
-	{ SIP_NOTIFY,	 NO_RTP, "NOTIFY", 	CAN_CREATE_DIALOG },
-	{ SIP_INVITE,	 RTP,    "INVITE", 	CAN_CREATE_DIALOG },
-	{ SIP_ACK,	 NO_RTP, "ACK", 	CAN_NOT_CREATE_DIALOG },
-	{ SIP_PRACK,	 NO_RTP, "PRACK", 	CAN_NOT_CREATE_DIALOG },
-	{ SIP_BYE,	 NO_RTP, "BYE", 	CAN_NOT_CREATE_DIALOG },
-	{ SIP_REFER,	 NO_RTP, "REFER", 	CAN_CREATE_DIALOG },
-	{ SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE", 	CAN_CREATE_DIALOG },
-	{ SIP_MESSAGE,	 NO_RTP, "MESSAGE", 	CAN_CREATE_DIALOG },
-	{ SIP_UPDATE,	 NO_RTP, "UPDATE", 	CAN_NOT_CREATE_DIALOG },
-	{ SIP_INFO,	 NO_RTP, "INFO", 	CAN_NOT_CREATE_DIALOG },
-	{ SIP_CANCEL,	 NO_RTP, "CANCEL", 	CAN_NOT_CREATE_DIALOG },
+	{ SIP_UNKNOWN,	 RTP,    "-UNKNOWN-", 	CAN_CREATE_DIALOG, TRUE },
+	{ SIP_RESPONSE,	 NO_RTP, "SIP/2.0",	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_REGISTER,	 NO_RTP, "REGISTER", 	CAN_CREATE_DIALOG, FALSE },
+ 	{ SIP_OPTIONS,	 NO_RTP, "OPTIONS", 	CAN_CREATE_DIALOG, FALSE },
+	{ SIP_NOTIFY,	 NO_RTP, "NOTIFY", 	CAN_CREATE_DIALOG, TRUE },
+	{ SIP_INVITE,	 RTP,    "INVITE", 	CAN_CREATE_DIALOG, FALSE },
+	{ SIP_ACK,	 NO_RTP, "ACK", 	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_PRACK,	 NO_RTP, "PRACK", 	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_BYE,	 NO_RTP, "BYE", 	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_REFER,	 NO_RTP, "REFER", 	CAN_CREATE_DIALOG, FALSE },
+	{ SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE", 	CAN_CREATE_DIALOG, FALSE },
+	{ SIP_MESSAGE,	 NO_RTP, "MESSAGE", 	CAN_CREATE_DIALOG, FALSE },
+	{ SIP_UPDATE,	 NO_RTP, "UPDATE", 	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_INFO,	 NO_RTP, "INFO", 	CAN_NOT_CREATE_DIALOG, TRUE },
+	{ SIP_CANCEL,	 NO_RTP, "CANCEL", 	CAN_NOT_CREATE_DIALOG, FALSE },
 	{ SIP_PUBLISH,	 NO_RTP, "PUBLISH", 	CAN_CREATE_DIALOG_UNSUPPORTED_METHOD },
 	{ SIP_PING,	 NO_RTP, "PING", 	CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }
 };
@@ -1542,6 +1543,7 @@
 	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(randdata);	/*!< Random data */
 		AST_STRING_FIELD(accountcode);	/*!< Account code */
 		AST_STRING_FIELD(realm);	/*!< Authorization realm */
@@ -7010,9 +7012,9 @@
 struct find_call_cb_arg {
 	enum sipmethod method;
 	const char *callid;
-	const char *fromtag;
-	const char *totag;
-	const char *tag;
+	const char *our_tag;
+	const char *their_tag;
+	const char *viabranch;
 };
 
 /*!
@@ -7023,36 +7025,85 @@
 {
 	struct sip_pvt *p = __pvt;
 	struct find_call_cb_arg *arg = __arg;
-	/* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
-	int found = FALSE;
 	
-	if (!ast_strlen_zero(p->callid)) { /* XXX double check, do we allow match on empty p->callid ? */
-		if (arg->method == SIP_REGISTER)
- 	  	  	found = (!strcmp(p->callid, arg->callid));
-		else {
- 	  		found = !strcmp(p->callid, arg->callid);
-			if (sip_cfg.pedanticsipchecking && found) {
-				found = ast_strlen_zero(arg->tag) || ast_strlen_zero(p->theirtag) || !ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED) || !strcmp(p->theirtag, arg->tag);
-			} 
-		}
-		
-		ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
-		
-		/* If we get a new request within an existing to-tag - check the to tag as well */
-		if (sip_cfg.pedanticsipchecking && found && arg->method != SIP_RESPONSE) { /* SIP Request */
- 	  	  	if (p->tag[0] == '\0' && arg->totag[0]) {
-				/* We have no to tag, but they have. Wrong dialog */
-				found = FALSE;
-  	  	  	} else if (arg->totag[0]) { /* Both have tags, compare them */
-				if (strcmp(arg->totag, p->tag)) {
-					found = FALSE; /* This is not our packet */
-				}
-			}
-			if (!found)
-				ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, arg->totag, sip_methods[arg->method].text);
-		}
-	}
- 	return found;
+	ast_debug(5, "arg->callid: %s -- arg->our_tag: %s -- arg->their_tag: %s -- arg->viabranch: %s\n",
+		  arg->callid, arg->our_tag, arg->their_tag, arg->viabranch);
+	ast_debug(5, "p->callid: %s -- p->tag: %s -- p->their_tag: %s -- p->initviabranch: %s -- p->nonce: %s\n",
+		  p->callid, p->tag, p->theirtag, p->initviabranch, p->nonce);
+
+	if (strcmp(p->callid, arg->callid)) {
+		ast_debug(5, "callid mismatch\n");
+		return FALSE;
+	}
+
+	/* for responses *only*, we can't check their_tag
+	 * until we have seen it and stored it.
+	 */
+	if (!((arg->method == SIP_RESPONSE) && ast_strlen_zero(p->theirtag)) &&
+	    strcmp(p->theirtag, arg->their_tag)) {
+		ast_debug(5, "their_tag mismatch\n");
+		return FALSE;
+	}
+
+	if (arg->method == SIP_CANCEL) {
+		return CMP_MATCH;
+	}
+
+	/* ugh... because we currently store the nonce on the private structure,
+	 * of in a per-peer list of outstanding nonces, we have to allow incoming
+	 * requests that don't have our_tag supplied to match the private structure
+	 * if the Call-ID and their_tag match. once we move the outstanding nonce
+	 * list to the peer structure, this special case should be removed.
+	 */
+	if (ast_strlen_zero(arg->our_tag) &&
+	    !ast_strlen_zero(p->randdata)) {
+		ast_debug(5, "no our_tag supplied and nonce issued special case match\n");
+		return CMP_MATCH;
+	}
+
+	if (!strcmp(p->tag, arg->our_tag)) {
+		ast_debug(5, "our_tag match\n");
+		return CMP_MATCH;
+	}
+
+	if (!ast_strlen_zero(arg->our_tag)) {
+		ast_debug(5, "our_tag mismatch\n");
+		return FALSE;
+	}
+
+	if (strcmp(p->initviabranch, arg->viabranch)) {
+		ast_debug(5, "via branch id mismatch\n");
+		return FALSE;
+	} else {
+		ast_debug(5, "via branch id match\n");
+		return CMP_MATCH;
+	}
+}
+
+static void getviabranch(const struct sip_request *req, const char *header, char *buf, size_t len)
+{
+	char *via = ast_strdupa(get_header(req, header));
+	char *opts;
+
+	buf[0] = '\0';
+
+	/* Work on the leftmost value of the topmost Via header */
+	if ((opts = strchr(via, ','))) {
+		*opts = '\0';
+	}
+
+	/* process options first (working through the string backwards) */
+	if ((opts = strstr(via, ";branch="))) {
+		char *id;
+		/* find the branch ID */
+		opts = ast_skip_blanks(opts + 8);
+		if (((id = strsep(&opts, "; \t\r\n")) == NULL) || ast_strlen_zero(id)) {
+			ast_log(LOG_WARNING, "No branch ID found in branch parameter of Via header '%s'\n",
+				get_header(req, "Via"));
+			return;
+		}
+		ast_copy_string(buf, id, len);
+	}
 }
 
 /*! \brief find or create a dialog structure for an incoming SIP message.
@@ -7063,27 +7114,24 @@
 static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
 {
 	struct sip_pvt *p = NULL;
-	char *tag = "";	/* note, tag is never NULL */
+//	char *tag = "";	/* note, tag is never NULL */
 	char totag[128];
 	char fromtag[128];
+	char viabranch[128];
 	struct find_call_cb_arg arg;
 	const char *callid = get_header(req, "Call-ID");
 	const char *from = get_header(req, "From");
 	const char *to = get_header(req, "To");
 	const char *cseq = get_header(req, "Cseq");
+	const char *via = get_header(req, "Via");
 	struct sip_pvt *sip_pvt_ptr;
 
-	/* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
+	/* Call-ID, To, From, Via and Cseq are required by RFC 3261. (Max-forwards - ignored now) */
 	/* get_header always returns non-NULL so we must use ast_strlen_zero() */
 	if (ast_strlen_zero(callid) || ast_strlen_zero(to) ||
-			ast_strlen_zero(from) || ast_strlen_zero(cseq))
+	    ast_strlen_zero(from) || ast_strlen_zero(cseq) ||
+	    ast_strlen_zero(via))
 		return NULL;	/* Invalid packet */
-
-	arg.method = req->method;
-	arg.callid = callid;
-	arg.fromtag = fromtag;
-	arg.totag = totag;
-	arg.tag = ""; /* make sure tag is never NULL */
 
 	if (sip_cfg.pedanticsipchecking) {
 		/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
@@ -7095,21 +7143,33 @@
 		if (gettag(req, "To", totag, sizeof(totag)))
 			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 */
-		if (ast_strlen_zero(fromtag)) {
-			ast_debug(5, "%s request has no from tag, dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
+		getviabranch(req, "Via", viabranch, sizeof(viabranch));
+
+		arg.method = req->method;
+		arg.callid = callid;
+		arg.viabranch = viabranch;
+		if (req->method == SIP_RESPONSE) {
+			arg.our_tag = fromtag;
+			arg.their_tag = totag;
+		} else {
+			arg.their_tag = fromtag;
+			arg.our_tag = totag;
+		}
+
+		/* '100 Trying' responses do not include their_tag */
+		if (!((intended_method == SIP_RESPONSE) && ()) &&
+		    ast_strlen_zero(arg.their_tag)) {
+			ast_debug(5, "%s does not include remote tag, dropping callid: %s\n", sip_methods[intended_method].text, callid);
 			return NULL;
 		}
-		/* reject requests that must always have a To: tag */
-		if (ast_strlen_zero(totag) && (req->method == SIP_ACK || req->method == SIP_BYE || req->method == SIP_INFO )) {
-			ast_debug(5, "%s must have a to tag. dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
+
+		if (ast_strlen_zero(arg.our_tag) &&
+		    sip_methods[intended_method].must_include_our_tag) {
+			ast_debug(5, "%s does not include our tag, dropping callid: %s\n", sip_methods[intended_method].text, callid);
 			return NULL;
 		}
+
+		ast_debug(5, "= Looking for Call ID: %s -- Our tag %s -- Their tag %s\n", callid, arg.our_tag, arg.their_tag);
 	}
 
 restartsearch:
@@ -7166,7 +7226,7 @@
 			}
 		}
 		return p; /* can be NULL */
-	} else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
+	} else if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
 		/* A method we do not support, let's take it on the volley */
 		transmit_response_using_temp(callid, sin, 1, intended_method, req, "501 Method Not Implemented");
 		ast_debug(2, "Got a request with unsupported SIP method.\n");
@@ -13288,80 +13348,96 @@
  */
 static attribute_unused void check_via_response(struct sip_pvt *p, struct sip_request *req)
 {
-	char via[256];
+	char *via = ast_strdupa(get_header(req, "Via"));
 	char *cur, *opts;
 
-	ast_copy_string(via, get_header(req, "Via"), sizeof(via));
-
 	/* Work on the leftmost value of the topmost Via header */
-	opts = strchr(via, ',');
-	if (opts)
+	if ((opts = strchr(via, ','))) {
 		*opts = '\0';
+	}
 
 	/* parse all relevant options */
-	opts = strchr(via, ';');
-	if (!opts)
+	if (!(opts = strchr(via, ';'))) {
 		return;	/* no options to parse */
+	}
+
 	*opts++ = '\0';
-	while ( (cur = strsep(&opts, ";")) ) {
-		if (!strncmp(cur, "rport=", 6)) {
-			int port = strtol(cur+6, NULL, 10);
+	while ((cur = strsep(&opts, ";"))) {
+		if (!strncasecmp(cur, "rport=", 6)) {
+			int port = strtol(cur + 6, NULL, 10);
 			/* XXX add error checking */
 			p->ourip.sin_port = ntohs(port);
-		} else if (!strncmp(cur, "received=", 9)) {
-			if (ast_parse_arg(cur+9, PARSE_INADDR, &p->ourip))
+		} else if (!strncasecmp(cur, "received=", 9)) {
+			if (ast_parse_arg(cur + 9, PARSE_INADDR, &p->ourip))
 				;	/* XXX add error checking */
 		}
-	}
+ 	}
 }
 
 /*! \brief check Via: header for hostname, port and rport request/answer */
 static void check_via(struct sip_pvt *p, struct sip_request *req)
 {
-	char via[512];
-	char *c, *pt;
+	char *via = ast_strdupa(get_header(req, "Via"));
+	char *cur, *opts, *pt;
 	struct hostent *hp;
 	struct ast_hostent ahp;
 
-	ast_copy_string(via, get_header(req, "Via"), sizeof(via));
-
 	/* Work on the leftmost value of the topmost Via header */
-	c = strchr(via, ',');
-	if (c)
-		*c = '\0';
-
-	/* Check for rport */
-	c = strstr(via, ";rport");
-	if (c && (c[6] != '='))	/* rport query, not answer */
-		ast_set_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT);
-
-	c = strchr(via, ';');
-	if (c) 
-		*c = '\0';
-
-	c = strchr(via, ' ');
-	if (c) {
-		*c = '\0';
-		c = ast_skip_blanks(c+1);
+	if ((opts = strchr(via, ','))) {
+		*opts = '\0';
+	}
+
+	/* process options first (working through the string backwards) */
+	if ((opts = strchr(via, ';'))) {
+		*opts++ = '\0';
+		while ((cur = strsep(&opts, ";"))) {
+			if (!strncasecmp(cur, "rport", 5)) {
+				if (cur[6] != '=') {	/* rport query, not answer */
+					ast_set_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT);
+				}
+			} else /* if pedantic checking is enabled, and this is the initial request
+				*  in a dialog, then try to extract the topmost Via branch ID
+				*/
+				if (sip_cfg.pedanticsipchecking &&
+				    ast_strlen_zero(p->initviabranch) &&
+				    !strncasecmp(cur, "branch=", 7)) {
+				char *id;
+				/* find the branch ID */
+				cur = ast_skip_blanks(cur + 7);
+				if (((id = strsep(&cur, "; \t\r\n")) == NULL) || ast_strlen_zero(id)) {
+					ast_log(LOG_WARNING, "No branch ID found in branch parameter of Via header '%s'\n",
+						get_header(req, "Via"));
+					continue;
+				}
+				ast_string_field_set(p, initviabranch, id);
+				ast_log(LOG_NOTICE, "Got branch ID '%s' from Via header '%s'\n", id, get_header(req, "Via"));
+			}
+		}
+	}
+
+	/* process the connection method and port information last */
+	if ((cur = strchr(via, ' '))) {
+ 		*cur++ = '\0';
+		cur = ast_skip_blanks(cur);
 		if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP") && strcasecmp(via, "SIP/2.0/TLS")) {
 			ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
 			return;
 		}
-		pt = strchr(c, ':');
-		if (pt)
-			*pt++ = '\0';	/* remember port pointer */
-		hp = ast_gethostbyname(c, &ahp);
+		if ((pt = strchr(cur, ':'))) {
+			*pt++ = '\0';
+		}
+		hp = ast_gethostbyname(cur, &ahp);
 		if (!hp) {
-			ast_log(LOG_WARNING, "'%s' is not a valid host\n", c);
+			ast_log(LOG_WARNING, "'%s' is not a valid host\n", cur);
 			return;
 		}
 		memset(&p->sa, 0, sizeof(p->sa));
 		p->sa.sin_family = AF_INET;
 		memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
 		p->sa.sin_port = htons(port_str2int(pt, STANDARD_SIP_PORT));
-
 		if (sip_debug_test_pvt(p)) {
 			const struct sockaddr_in *dst = sip_real_dst(p);
+
 			ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), sip_nat_mode(p));
 		}
 	}




More information about the asterisk-commits mailing list