[asterisk-commits] oej: branch oej/pinetree-trunk r188281 - /team/oej/pinetree-trunk/channels/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 14 09:06:06 CDT 2009


Author: oej
Date: Tue Apr 14 09:05:56 2009
New Revision: 188281

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=188281
Log:
This is absolutely not tested on any computer or any other machine, like my SAAB.

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

Modified: team/oej/pinetree-trunk/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/team/oej/pinetree-trunk/channels/chan_sip.c?view=diff&rev=188281&r1=188280&r2=188281
==============================================================================
--- team/oej/pinetree-trunk/channels/chan_sip.c (original)
+++ team/oej/pinetree-trunk/channels/chan_sip.c Tue Apr 14 09:05:56 2009
@@ -572,6 +572,12 @@
 #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 */
+};
+
 /*! \brief Authorization scheme for call transfers 
 
 \note Not a bitfield flag, since there are plans for other modes,
@@ -1911,6 +1917,7 @@
 	struct sip_socket socket;	/*!< Socket used for this peer */
 	unsigned int transports:3;      /*!< Transports (enum sip_transport) that are acceptable for this peer */
 	struct sip_auth *auth;		/*!< Realm authentication list */
+	enum devicematchrules matchrule;        /*!< Match rule for this peer */
 	int amaflags;			/*!< AMA Flags (for billing) */
 	int callingpres;		/*!< Calling id presentation */
 	int inUse;			/*!< Number of calls in use */
@@ -2399,6 +2406,8 @@
 static void ast_quiet_chan(struct ast_channel *chan);
 static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
 static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
+static void create_sockaddr(const char *hostname, const char *port, struct sockaddr_in *addr);
+
 
 /*!
  * \brief generic function for determining if a correct transport is being 
@@ -2561,6 +2570,8 @@
 static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
 static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
 static int set_address_from_contact(struct sip_pvt *pvt);
+static int find_via_address(int findsecond, struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *addr);
+static int get_address_from_via(const char *via, char *hostname, size_t hostlen, char *port, size_t portlen, struct sockaddr_in *addr);
 static void check_via(struct sip_pvt *p, struct sip_request *req);
 static char *get_calleridname(const char *input, char *output, size_t outputsize);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
@@ -4191,9 +4202,7 @@
 		ast_verbose("Sending text %s on %s\n", text, ast->name);
 	if (!p)
 		return -1;
-	/* NOT ast_strlen_zero, because a zero-length message is specifically
-	 * allowed by RFC 3428 (See section 10, Examples) */
-	if (!text)
+	if (ast_strlen_zero(text))
 		return 0;
 	if (debug)
 		ast_verbose("Really sending text %s on %s\n", text, ast->name);
@@ -8375,6 +8384,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, -1 on error, 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
@@ -13258,13 +13373,16 @@
 	}
 }
 
-/*! \brief check Via: header for hostname, port and rport request/answer */
-static void check_via(struct sip_pvt *p, struct sip_request *req)
+/*! \brief check Via: header for hostname, port and rport request/answer
+	Example of Via header:
+
+	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));
 
@@ -13273,6 +13391,11 @@
 	if (c)
 		*c = '\0';
 
+	if (strncasecmp(viaheader, "SIP/2.0/UDP", 11) && strncasecmp(via, "SIP/2.0/TCP", 11) && strncasecmp(via, "SIP/2.0/TLS", 11)) {
+		ast_log(LOG_WARNING, "Don't know how to communicate via '%s'\n", via);
+		return;
+	}
+
 	/* Check for rport */
 	c = strstr(via, ";rport");
 	if (c && (c[6] != '='))	/* rport query, not answer */
@@ -13281,34 +13404,14 @@
 	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") && 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 (!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));
+	}
+}
+
+
 
 /*! \brief  Get caller id name from SIP headers */
 static char *get_calleridname(const char *input, char *output, size_t outputsize)
@@ -13377,6 +13480,17 @@
 		/* Then find devices based on IP */
 		if (!peer) {
 			peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE);
+		}
+		/* 
+			If this peer have a matching principle that says we need to check
+			the via headers for the target peer, then do that. This is not
+			a peer we want, it's just an intermediate proxy.
+		*/
+		if (peer && (peer->matchrule == MATCH_SECONDVIA || peer->matchrule == MATCH_LASTVIA)) {
+			struct sockaddr_in matchaddr;
+			/* Go find the peer */
+			find_via_address(peer->matchrule == MATCH_SECONDVIA, p, req, &matchaddr);
+			peer = find_peer(NULL, &matchaddr, 1, 0);
 		}
 	}
 
@@ -13507,6 +13621,34 @@
 	unref_peer(peer, "check_peer_ok: unref_peer: tossing temp ptr to peer from find_peer");
 	return res;
 }
+
+/*! 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 
@@ -19567,6 +19709,7 @@
 	
 	if (!p->lastinvite && !req->ignore && !p->owner) {
 		/* This is a new invite */
+
 		/* Handle authentication if this is our first invite */
 		struct ast_party_redirecting redirecting = {{0,},};
 		res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
@@ -23104,6 +23247,7 @@
 	peer->stimer.st_max_se = global_max_se;
 	peer->timer_t1 = global_t1;
 	peer->timer_b = global_timer_b;
+	peer->matchrule = MATCH_NORMAL;
 	clear_peer_mailboxes(peer);
 }
 
@@ -24261,6 +24405,17 @@
 			} else {
 				ast_log(LOG_WARNING, "Invalid hash_dialog size '%s' at line %d of %s -- should be much larger than 2\n", v->value, v->lineno, config);
 			}
+		} 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")) {
 				default_qualify = 0;




More information about the asterisk-commits mailing list