[Asterisk-Dev] Patch to add Record-Route handling for SIP, also honour Connect: properly

Stephen Davies steve at daviesfam.org
Fri Apr 4 08:55:15 MST 2003


Hi Mark, others,

Attached is my patch that adds proper Record-Route handling for Asterisk's
SIP channel.  Well, "proper" may be too strong, but I did try.

With this patch applied Asterisk now works with Free World Dialup.  
Asterisk should now play much better with proxies in general.

I suspect that this patch will also fix some of the problems people have
been reporting on the -users list, because previously * didn't honour
Contact: headers when sending followup requests on a call.

This meant that INFO, BYE etc for open calls may have been sent to the
wrong place (ie a proxy rather than the end-point).  So that would account
for calls not clearing properly, and maybe for lost DTMF signalling?

Anyway - would you apply the patch - I can then chase up on -users for
feedback.

Thanks,
Steve Davies

-------------- next part --------------
Index: channels/chan_sip.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v
retrieving revision 1.32
diff -u -r1.32 chan_sip.c
--- channels/chan_sip.c	3 Apr 2003 16:20:47 -0000	1.32
+++ channels/chan_sip.c	4 Apr 2003 15:18:28 -0000
@@ -143,6 +143,11 @@
 
 struct sip_pkt;
 
+struct sip_route {
+	struct sip_route *next;
+	char hop[0];
+};
+
 static struct sip_pvt {
 	pthread_mutex_t lock;				/* Channel private lock */
 	char callid[80];					/* Global CallID */
@@ -171,8 +176,7 @@
 	char referred_by[AST_MAX_EXTENSION];/* Place to store REFERRED-BY extension */
 	char refer_contact[AST_MAX_EXTENSION];/* Place to store Contact info from a REFER extension */
 	struct sip_pvt *refer_call;			/* Call we are referring */
-	char record_route[256];
-	char record_route_info[256];
+	struct sip_route *route;			/* Head of linked list of routing steps (fm Record-Route) */
 	char remote_party_id[256];
 	char context[AST_MAX_EXTENSION];
 	char language[MAX_LANGUAGE];
@@ -322,6 +326,7 @@
 static int transmit_message_with_text(struct sip_pvt *p, char *text);
 static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req);
 char *getsipuri(char *header);
+static void free_old_route(struct sip_route *route);
 
 static int __sip_xmit(struct sip_pvt *p, char *data, int len)
 {
@@ -714,6 +719,10 @@
 	if (p->rtp) {
 		ast_rtp_destroy(p->rtp);
 	}
+	if (p->route) {
+		free_old_route(p->route);
+		p->route = NULL;
+	}
 	/* Unlink us from the owner if we have one */
 	if (p->owner) {
 		if (lockowner)
@@ -1629,7 +1638,6 @@
 	return -1;
 }
 
-#if 0
 static int copy_all_header(struct sip_request *req, struct sip_request *orig, char *field)
 {
 	char *tmp;
@@ -1644,13 +1652,9 @@
 		} else
 			break;
 	}
-	if (!copied) {
-		ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
-		return -1;
-	}
-	return 0;
+	return copied ? 0 : -1;
 }
-#endif
+
 static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, struct sip_request *orig, char *field)
 {
 	char *tmp;
@@ -1661,6 +1665,8 @@
 		tmp = __get_header(orig, field, &start);
 		if (strlen(tmp)) {
 			if (!copied && p->nat) {
+				/* SLD: FIXME: Nice try, but the received= should not have a port */
+				/* SLD: FIXME: See RFC2543 BNF in Section 6.40.5 */
 				if (ntohs(p->recv.sin_port) != DEFAULT_SIP_PORT)
 					snprintf(new, sizeof(new), "%s;received=%s:%d", tmp, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
 				else
@@ -1681,6 +1687,81 @@
 	return 0;
 }
 
+/* Add Route: header into request per learned route */
+static void add_route(struct sip_request *req, struct sip_route *route)
+{
+	char r[256], *p;
+	int n, rem = 255; /* sizeof(r)-1: Room for terminating 0 */
+
+	if (!route) return;
+
+	p = r;
+	while (route) {
+		n = strlen(route->hop);
+		if ((n+3)>rem) break;
+		if (p != r) {
+			*p++ = ',';
+			--rem;
+		}
+		*p++ = '<';
+		strcpy(p, route->hop);  p += n;
+		*p++ = '>';
+		rem -= (n+2);
+		route = route->next;
+	}
+	*p = '\0';
+	add_header(req, "Route", r);
+}
+
+static void set_destination(struct sip_pvt *p, char *uri)
+{
+	char *h, *maddr, hostname[256];
+	int port, hn;
+	struct hostent *hp;
+
+	/* Parse uri to h (host) and port - uri is already just the part inside the <> */
+	/* general form we are expecting is sip[s]:username[:password]@host[:port][;...] */
+
+	ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
+
+	h = strchr(uri, '@');
+	if (!h) {
+		ast_log(LOG_WARNING, "set_destination: Can't parse sip URI '%s'\n", uri);
+		return;
+	}
+	++h;
+	hn = strcspn(h, ":;>");
+	hostname[255] = '\0';
+	strncpy(hostname, h, (hn>255)?255:hn);
+	h+=hn;
+	/* Is "port" present? if not default to 5060 */
+	if (*h == ':') {
+		/* Parse port */
+		++h;
+		port = strtol(h, &h, 10);
+	}
+	else
+		port = 5060;
+
+	/* Got the hostname:port - but maybe there's a ";maddr=" to override address? */
+	maddr = strstr(h, ";maddr=");
+	if (maddr) {
+		maddr += 7;
+		hn = strspn(maddr, "0123456789.");
+		strncpy(hostname, maddr, (hn>255)?255:hn);
+	}
+	
+	hp = gethostbyname(hostname);
+	if (hp == NULL)  {
+		ast_log(LOG_WARNING, "Can't find address for host '%s'\n", h);
+		return;
+	}
+	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);
+	ast_verbose("set_destination: set destination to %s, port %d\n", inet_ntoa(p->sa.sin_addr), port);
+}
+
 static int init_resp(struct sip_request *req, char *resp, struct sip_request *orig)
 {
 	/* Initialize a response */
@@ -1725,9 +1806,11 @@
 	else
 		from = get_header(req, "To");
 	strncpy(contact2, from, sizeof(contact2)-1);
-	c = ditch_braces(contact2);
-	snprintf(contact, sizeof(contact), "<%s>", c);
-	add_header(req, "Contact", contact);
+	if (strlen(contact2)) {
+		c = ditch_braces(contact2);
+		snprintf(contact, sizeof(contact), "<%s>", c);
+		add_header(req, "Contact", contact);
+	}
 }
 
 static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, struct sip_request *req)
@@ -1736,10 +1819,9 @@
 	memset(resp, 0, sizeof(*resp));
 	init_resp(resp, msg, req);
 	copy_via_headers(p, resp, req, "Via");
+	if (msg[0] == '2') copy_all_header(resp, req, "Record-Route");
 	copy_header(resp, req, "From");
 	ot = get_header(req, "To");
-	if (strlen(get_header(req, "Record-Route")))
-		copy_header(resp, req, "Record-Route");
 	if (!strstr(ot, "tag=")) {
 		/* Add the proper tag if we don't have it already.  If they have specified
 		   their tag, use it.  Otherwise, use our own tag */
@@ -1819,6 +1901,10 @@
 	snprintf(tmp, sizeof(tmp), "%d %s", p->ocseq, msg);
 
 	add_header(req, "Via", p->via);
+	if (p->route) {
+		set_destination(p, p->route->hop);
+		add_route(req, p->route->next);
+	}
 
 	ot = get_header(orig, "To");
 	of = get_header(orig, "From");
@@ -2098,6 +2184,8 @@
 	snprintf(tmp, sizeof(tmp), "%d %s", ++p->ocseq, cmd);
 
 	add_header(req, "Via", p->via);
+	/* SLD: FIXME?: do Route: here too?  I think not cos this is the first request.
+	 * OTOH, then we won't have anything in p->route anyway */
 	add_header(req, "From", from);
 	{
 		char contact2[256] ="", *c, contact[256];
@@ -2396,6 +2484,100 @@
 	return 0;
 }
 
+static void free_old_route(struct sip_route *route)
+{
+	struct sip_route *next;
+	while (route) {
+		next = route->next;
+		free(route);
+		route = next;
+	}
+}
+
+#if 0
+static void list_route(struct sip_route *route)
+{
+	if (!route) {
+		ast_verbose("list_route: no route\n");
+		return;
+	}
+	while (route) {
+		ast_verbose("list_route: hop: <%s>\n", route->hop);
+		route = route->next;
+	}
+}
+#endif
+
+static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards)
+{
+	struct sip_route *thishop, *head, *tail;
+	int start = 0;
+	int len;
+	char *rr, *contact, *c;
+
+	if (p->route) {
+		free_old_route(p->route);
+		p->route = NULL;
+	}
+	head = (struct sip_route *)&(p->route);  tail = head;
+	/* 1st pass through all the hops in any Record-Route headers */
+	for (;;) {
+		/* Each Record-Route header */
+		rr = __get_header(req, "Record-Route", &start);
+		/*ast_verbose("Record-Route: %s\n", rr);*/
+		if (*rr == '\0') break;
+		for (;;) {
+			/* Each route entry */
+			/* Find < */
+			rr = strchr(rr, '<');
+			if (!rr) break; /* No more hops */
+			++rr;
+			len = strcspn(rr, ">");
+			/* Make a struct route */
+			thishop = (struct sip_route *)calloc(sizeof(struct sip_route)+len+1, 1);
+			strncpy(thishop->hop, rr, len);
+			ast_verbose("build_route: Record-Route hop: <%s>\n", thishop->hop);
+			/* Link in */
+			if (backwards) {
+				/* Link in at head so they end up in reverse order */
+				thishop->next = head->next;
+				head->next = thishop;
+				/* If this was the first then it'll be the tail */
+				if (tail == head) tail = thishop;
+			} else {
+				/* Link in at the end */
+				tail->next = thishop;
+				tail = thishop;
+			}
+			rr += len+1;
+		}
+	}
+	/* 2nd append the Contact: if there is one */
+	/* Can be multiple Contact headers, comma separated values - we just take the first */
+	contact = get_header(req, "Contact");
+	if (strlen(contact)) {
+		ast_verbose("build_route: Contact hop: %s\n", contact);
+		/* Look for <: delimited address */
+		c = strchr(contact, '<');
+		if (c) {
+			/* Take to > */
+			++c;
+			len = strcspn(c, ">");
+		} else {
+			/* No <> - just take the lot */
+			c = contact; len = strlen(contact);
+		}
+		thishop = (struct sip_route *)calloc(sizeof(struct sip_route)+len+1, 1);
+		strncpy(thishop->hop, c, len);
+		/* Goes at the end */
+		tail->next = thishop;
+	}
+#if 0
+	/* For debugging dump what we ended up with */
+	list_route(p->route);
+#endif
+}
+
 static void md5_hash(char *output, char *input)
 {
 		struct MD5Context md5;
@@ -3353,6 +3535,8 @@
 			} else if (!strcasecmp(msg, "INVITE")) {
 				if (strlen(get_header(req, "Content-Type")))
 					process_sdp(p, req);
+				/* Save Record-Route for any later requests we make on this dialogue */
+				build_route(p, req, 1);
 				if (p->owner) {
 					if (p->owner->_state != AST_STATE_UP) {
 						ast_setstate(p->owner, AST_STATE_UP);
@@ -3696,6 +3880,8 @@
 				p->tag = rand();
 				/* First invitation */
 				c = sip_new(p, AST_STATE_DOWN, strlen(p->username) ? p->username : NULL);
+				/* Save Record-Route for any later requests we make on this dialogue */
+				build_route(p, req, 0);
 			}
 			
 		} else 


More information about the asterisk-dev mailing list