[asterisk-commits] oej: branch oej/pinetree-1.4 r186989 - /team/oej/pinetree-1.4/channels/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Apr 8 10:31:13 CDT 2009


Author: oej
Date: Wed Apr  8 10:31:09 2009
New Revision: 186989

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=186989
Log:
Testing a new matching solution. 

Modified:
    team/oej/pinetree-1.4/channels/chan_sip.c

Modified: team/oej/pinetree-1.4/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/team/oej/pinetree-1.4/channels/chan_sip.c?view=diff&rev=186989&r1=186988&r2=186989
==============================================================================
--- team/oej/pinetree-1.4/channels/chan_sip.c (original)
+++ team/oej/pinetree-1.4/channels/chan_sip.c Wed Apr  8 10:31:09 2009
@@ -235,6 +235,13 @@
 
 #define RTP 	1
 #define NO_RTP	0
+
+enum devicematchrules {
+	MATCH_NORMAL = 0,		/*!< Normal match - if you would call that normal, dude */
+	MATCH_SECONDVIA,		/*!< Skip sender's IP and match on second via header */
+	MATCH_LASTVIA,			/*!< Skip all via headers and go for the sender's real IP */
+	MATCH_DOMAIN,			/*!< Match on SIP domain (the stuff after the @ in the From: header) */
+};
 
 /*! \brief Authorization scheme for call transfers 
 \note Not a bitfield flag, since there are plans for other modes,
@@ -1096,6 +1103,7 @@
 	char secret[80];		/*!< Password */
 	char md5secret[80];		/*!< Password in MD5 */
 	struct sip_auth *auth;		/*!< Realm authentication list */
+	enum devicematchrules matchrule;	/*!< Match rule for this peer */
 	char context[AST_MAX_CONTEXT];	/*!< Default context for incoming calls */
 	char subscribecontext[AST_MAX_CONTEXT];	/*!< Default context for subscriptions */
 	char username[80];		/*!< Temporary username until registration */ 
@@ -1363,6 +1371,7 @@
 static int sip_refer_allocate(struct sip_pvt *p);
 static void ast_quiet_chan(struct ast_channel *chan);
 static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
+static void create_sockaddr(const char *hostname, const char *port, struct sockaddr_in *addr);
 
 /*--- Device monitoring and Device/extension state handling */
 static int cb_extensionstate(char *context, char* exten, int state, void *data);
@@ -1532,6 +1541,7 @@
 static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
 static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field);
 static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field);
+static int find_via_address(int findsecond, struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *addr);
 static void set_destination(struct sip_pvt *p, char *uri);
 static void append_date(struct sip_request *req);
 static void build_contact(struct sip_pvt *p);
@@ -2708,6 +2718,7 @@
 static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int devstate_only)
 {
 	struct sip_peer *p = NULL;
+	ast_log(LOG_DEBUG, "--- DEBUG: Trying to find a mate\n");
 
 	if (peer)
 		p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
@@ -5821,6 +5832,112 @@
 	return copied ? 0 : -1;
 }
 
+/*! \brief Get hostname and port from a via header
+	Not that the function writes to the string buffers "hostname" and "port"
+	Return false if there's only one via, otherwise true
+*/
+static int get_address_from_via(const char *via, char *hostname, size_t hostlen, char *port, size_t portlen, struct sockaddr_in *addr)
+{
+
+	char *viaheader = ast_strdupa(via);
+	char *hoststart = NULL, *portstart = NULL;
+
+	if (ast_strlen_zero(via)) {
+		ast_log(LOG_DEBUG, "---- Huh? No via header. \n");
+	}
+
+	hoststart = strchr(viaheader, ';');
+	if (hoststart) 
+		*hoststart = '\0';
+
+	hoststart = strchr(viaheader, ' ');
+	if (hoststart) {
+		*hoststart = '\0';
+		hoststart = ast_skip_blanks(hoststart+1);
+		portstart = strchr(hoststart, ':');
+		if (portstart)
+			*portstart++ = '\0';	/* remember port pointer */
+	}
+	ast_log(LOG_DEBUG, "---- Found hostname %s and port %s in via header\n", hoststart, portstart ? portstart : "--none--");
+	if (hostlen != (size_t) 0) {
+		ast_copy_string(hostname, hoststart, hostlen);
+	}
+	if (portlen != (size_t) 0 && strlen(portstart)) {
+		ast_copy_string(hostname, portstart, portlen);
+	}
+	if (addr != NULL) {
+		create_sockaddr(hoststart, portstart, addr);
+	}
+	ast_log(LOG_DEBUG, "---- Returning cheerfully with a smile on my lips.\n");
+	return 142857;
+
+}
+
+
+/*! \brief Get either second via header address (host/ip:port) or the last via header 
+	Not that the function writes to the string buffers "hostname" and "port"
+	Return false if there's only one via, otherwise true
+
+Example:
+	Via: SIP/2.0/UDP 192.168.40.210;branch=z9hG4bK-d8754z-67b1a44ab587f92f-1---d8754z-
+	Via: SIP/2.0/UDP 192.168.40.21:38322;received=192.168.40.21;branch=z9hG4bK-d8754z-67b1a44ab587f92f-1---d8754z-;rport=38322
+
+*/
+static int find_via_address(int findsecond, struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *addr)
+{
+	int line = 0;
+	int start = 0;
+	char *next = NULL, *previous;
+	const char *oh = NULL;
+	char *viaheader = NULL;
+
+	/* Note that any header can contain multiple values. The second via might be in the first header, after a comma 
+	*/
+	ast_log(LOG_DEBUG, "------ Going to have a lot of fun with VIA headers \n");
+
+	for (;;) {
+		previous = viaheader;
+		if (next == NULL) {
+			oh = __get_header(req, "Via", &start);
+			viaheader = oh ?  ast_strdupa(oh) : NULL;
+		} else {
+			viaheader = next;
+			next = NULL;
+		}
+		if (ast_strlen_zero(viaheader)) {	/* no more headers */
+			if (line <= 1) {
+				ast_log(LOG_DEBUG, "------ Found no via headers to parse\n");
+				return FALSE;	/* We only have one via or none (which would be a bug in the device) */
+			}
+
+			if (!findsecond) {
+				/* The last one was the one for us */
+				ast_log(LOG_DEBUG, "--- Found last via header: %s\n", previous);
+				get_address_from_via(previous, NULL, (size_t) 0, NULL, (size_t) 0, addr);
+				return TRUE;
+			}
+		}
+		line ++;
+
+		/* Any more headers in this line? */
+		next = strchr(viaheader, ',');
+		if (next) {
+			*next = '\0';	/* break */
+			next++;
+		}
+		
+		if (line == 2 && findsecond) {
+			/* This is it! */
+			/* Do the stuff */
+			ast_log(LOG_DEBUG, "--- Found second via header: %s\n", viaheader);
+			get_address_from_via(viaheader, NULL, (size_t) 0, NULL, (size_t) 0, addr);
+			return TRUE;
+		}
+		ast_log(LOG_DEBUG, "--- Looping around in via forests\n");
+	}
+		
+}
+
 /*! \brief Copy SIP VIA Headers from the request to the response
 \note	If the client indicates that it wishes to know the port we received from,
 	it adds ;rport without an argument to the topmost via header. We need to
@@ -9558,13 +9675,14 @@
 
 	return -1;
 }
-/*! \brief check Via: header for hostname, port and rport request/answer */
+
+/*! \brief check Via: header for hostname, port and rport request/answer 
+	Via: SIP/2.0/UDP 192.168.0.237:5060;branch=z9hG4bK-a9882-2963bc63-7a1a292c
+*/
 static void check_via(struct sip_pvt *p, const struct sip_request *req)
 {
 	char via[512];
-	char *c, *pt;
-	struct hostent *hp;
-	struct ast_hostent ahp;
+	char *c;
 
 	ast_copy_string(via, get_header(req, "Via"), sizeof(via));
 
@@ -9581,33 +9699,13 @@
 	c = strchr(via, ';');
 	if (c) 
 		*c = '\0';
-
-	c = strchr(via, ' ');
-	if (c) {
-		*c = '\0';
-		c = ast_skip_blanks(c+1);
-		if (strcasecmp(via, "SIP/2.0/UDP")) {
-			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 (!hp) {
-			ast_log(LOG_WARNING, "'%s' is not a valid host\n", c);
-			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(pt ? atoi(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));
-		}
-	}
+	get_address_from_via(via, NULL, (size_t) 0, NULL, (size_t) 0, &p->sa);
+	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));
+		ast_verbose("DEBUG: Sender is at  %s : %d \n", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
+	}
+	ast_log(LOG_DEBUG, "DEBUG: Returning from check_via()\n");
 }
 
 /*! \brief  Get caller id name from SIP headers */
@@ -9684,6 +9782,34 @@
 
 	return 0;
 }
+
+/*! Convert hostname and port text strings to sockaddr_in format
+*/
+static void create_sockaddr(const char *hostname, const char *port, struct sockaddr_in *addr)
+{
+	struct hostent *hp;
+	struct ast_hostent ahp;
+
+	ast_log(LOG_DEBUG, "----- checking adress for %s:%s\n", hostname, port ? port : "--default--");
+
+	if (ast_strlen_zero(hostname)) {
+		return;
+	}
+	hp = ast_gethostbyname(hostname, &ahp);
+	if (!hp) {
+		ast_log(LOG_WARNING, "'%s' is not a valid host\n", hostname);
+		return;
+	}
+	memset(addr, 0, sizeof(*addr));
+	ast_log(LOG_DEBUG, "--- Cleared memory\n");
+	addr->sin_family = AF_INET;
+	memcpy(&addr->sin_addr, hp->h_addr, sizeof(addr->sin_addr));
+	ast_log(LOG_DEBUG, "---Copied address\n");
+	addr->sin_port = htons(ast_strlen_zero(port) ? STANDARD_SIP_PORT : atoi(port));
+	ast_log(LOG_DEBUG, "---Set the port and we're done\n");
+	return;
+}
+
 
 
 /*! \brief  Check if matching user or peer is defined 
@@ -9867,15 +9993,29 @@
 
 	if (!user) {
 		/* If we didn't find a user match, check for peers */
-		if (sipmethod == SIP_SUBSCRIBE)
+		if (sipmethod == SIP_SUBSCRIBE) {
 			/* For subscribes, match on peer name only */
 			peer = find_peer(of, NULL, 1, 0);
-		else
+		} else {
 			/* Look for peer based on the IP address we received data from */
 			/* If peer is registered from this IP address or have this as a default
 			   IP address, this call is from the peer 
 			*/
+			ast_log(LOG_DEBUG, "--- DEBUG: GOING TO FIND MYSELF A GOOD PEER\n");
 			peer = find_peer(NULL, &p->recv, 1, 0);
+			/* If this peer have a matching principle that says we need to check
+				the via headers for the REAL peer, then do that.
+			*/
+			ast_log(LOG_DEBUG, "--- DEBUG: NOW FOR SOMETHING COMPLETELY DIFFERENT (peer = %s)\n", peer ? "Found" : "Not found");
+			if (peer && (peer->matchrule == MATCH_SECONDVIA || peer->matchrule == MATCH_LASTVIA)) {
+				struct sockaddr_in matchaddr;
+				/* Go find the peer */
+				ast_log(LOG_DEBUG, "--- DEBUG: SEARCHING FOR PEER IN VIA HEADER\n");
+				find_via_address(peer->matchrule == MATCH_SECONDVIA, p, req, &matchaddr);
+				peer = find_peer(NULL, &matchaddr, 1, 0);
+			}
+			ast_log(LOG_DEBUG, "--- DEBUG: DONE SEARCHING FOR A PEER\n");
+		}
 
 		if (peer) {
 			/* Set Frame packetization */
@@ -14372,6 +14512,9 @@
 		if (!ast_strlen_zero(supported))
 			parse_sip_options(p, supported);
 	}
+	/* DEBUG: */
+	find_via_address(TRUE, p, req, NULL);
+
 
 	/* Find out what they require */
 	required = get_header(req, "Require");
@@ -14603,6 +14746,7 @@
 	
 	if (!p->lastinvite && !ast_test_flag(req, SIP_PKT_IGNORE) && !p->owner) {
 		/* This is a new invite */
+
 		/* Handle authentication if this is our first invite */
 		res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
 		if (res == AUTH_CHALLENGE_SENT) {
@@ -17363,6 +17507,7 @@
 	peer->pickupgroup = 0;
 	peer->maxms = default_qualify;
 	peer->prefs = default_prefs;
+	peer->matchrule = MATCH_NORMAL;
 }
 
 /*! \brief Create temporary peer (used in autocreatepeer mode) */
@@ -17621,6 +17766,17 @@
 					tmpvar->next = peer->chanvars;
 					peer->chanvars = tmpvar;
 				}
+			}
+		} else if (!strcasecmp(v->name, "matchrule")) {
+			if (!strcasecmp(v->value, "normal")) {
+				peer->matchrule = MATCH_NORMAL;
+			} else if (!strcasecmp(v->value, "lastvia")) {
+				peer->matchrule = MATCH_LASTVIA;
+			} else if (!strcasecmp(v->value, "secondvia")) {
+				peer->matchrule = MATCH_SECONDVIA;
+			} else {
+				ast_log(LOG_WARNING, "Matchrule=%s is not a valid setting. lastvia|secondvia|normal are valid options.\n", v->value);
+				peer->matchrule = MATCH_NORMAL;
 			}
 		} else if (!strcasecmp(v->name, "qualify")) {
 			if (!strcasecmp(v->value, "no")) {




More information about the asterisk-commits mailing list