Pārlūkot izejas kodu

Dialog-wise ACKs for local transactions.

Jan Janak 21 gadi atpakaļ
vecāks
revīzija
beb00b8e55
3 mainītis faili ar 385 papildinājumiem un 13 dzēšanām
  1. 304 0
      modules/tm/t_msgbuilder.c
  2. 10 0
      modules/tm/t_msgbuilder.h
  3. 71 13
      modules/tm/t_reply.c

+ 304 - 0
modules/tm/t_msgbuilder.c

@@ -52,10 +52,16 @@
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/msg_parser.h"
+#include "../../parser/contact/parse_contact.h"
 #include "t_msgbuilder.h"
 #include "t_msgbuilder.h"
 #include "uac.h"
 #include "uac.h"
 
 
 
 
+#define ROUTE_PREFIX "Route: "
+#define ROUTE_PREFIX_LEN (sizeof(ROUTE_PREFIX) - 1)
+
+#define ROUTE_SEPARATOR ", "
+#define ROUTE_SEPARATOR_LEN (sizeof(ROUTE_SEPARATOR) - 1)
 
 
 #define  append_mem_block(_d,_s,_len) \
 #define  append_mem_block(_d,_s,_len) \
 		do{\
 		do{\
@@ -182,6 +188,304 @@ error:
 }
 }
 
 
 
 
+struct rte {
+	rr_t* ptr;
+	struct rte* next;
+};
+
+  	 
+static inline void free_rte_list(struct rte* list)
+{
+	struct rte* ptr;
+	
+	while(list) {
+		ptr = list;
+		list = list->next;
+		pkg_free(ptr);
+	}
+}
+
+
+static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop)
+{
+	struct hdr_field* ptr;
+	rr_t* p;
+	struct rte* t, *head;
+	struct sip_uri puri;
+	
+	ptr = msg->record_route;
+	head = 0;
+	while(ptr) {
+		if (ptr->type == HDR_RECORDROUTE) {
+			if (parse_rr(ptr) < 0) {
+				LOG(L_ERR, "process_routeset: Error while parsing Record-Route header\n");
+				return -1;
+			}
+			
+			p = (rr_t*)ptr->parsed;
+			while(p) {
+				t = (struct rte*)pkg_malloc(sizeof(struct rte));
+				if (!t) {
+					LOG(L_ERR, "process_routeset: No memory left\n");
+					free_rte_list(head);
+					return -1;
+				}
+				t->ptr = p;
+				t->next = head;
+				head = t;
+				p = p->next;
+			}
+		}
+		ptr = ptr->next;
+	}
+	
+	if (head) {
+		if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) == -1) {
+			LOG(L_ERR, "process_routeset: Error while parsing URI\n");
+			free_rte_list(head);
+			return -1;
+		}
+		
+		if (puri.lr.s) {
+			     /* Next hop is loose router */
+			*ruri = *contact;
+			*next_hop = head->ptr->nameaddr.uri;
+		} else {
+			     /* Next hop is strict router */
+			*ruri = head->ptr->nameaddr.uri;
+			*next_hop = *ruri;
+			t = head;
+			head = head->next;
+			pkg_free(t);
+		}
+	} else {
+		     /* No routes */
+		*ruri = *contact;
+		*next_hop = *contact;
+	}
+	
+	*list = head;
+	return 0;
+}
+
+
+static inline int calc_routeset_len(struct rte* list, str* contact)
+{
+	struct rte* ptr;
+	int ret;
+	
+	if (list || contact) {
+		ret = ROUTE_PREFIX_LEN + CRLF_LEN;
+	} else {
+		return 0;
+	}
+	
+	ptr = list;
+	while(ptr) {
+		if (ptr != list) {
+			ret += ROUTE_SEPARATOR_LEN;
+		}
+		ret += ptr->ptr->len;
+		ptr = ptr->next;
+	}
+	
+	if (contact) {
+		if (list) ret += ROUTE_SEPARATOR_LEN;
+		ret += 2 + contact->len;
+	}
+	
+	return ret;
+}
+
+
+     /*
+      * Print the route set
+      */
+static inline char* print_rs(char* p, struct rte* list, str* contact)
+{
+	struct rte* ptr;
+	
+	if (list || contact) {
+		memapp(p, ROUTE_PREFIX, ROUTE_PREFIX_LEN);
+	} else {
+		return p;
+	}
+	
+	ptr = list;
+	while(ptr) {
+		if (ptr != list) {
+			memapp(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN);
+		}
+		
+		memapp(p, ptr->ptr->nameaddr.name.s, ptr->ptr->len);
+		ptr = ptr->next;
+	}
+	
+	if (contact) {
+		if (list) memapp(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN);
+		*p++ = '<';
+		append_str(p, *contact);
+		*p++ = '>';
+	}
+	
+	memapp(p, CRLF, CRLF_LEN);
+	return p;
+}
+
+
+     /*
+      * Parse Contact header field body and extract URI
+      * Does not parse headers !
+      */
+static inline int get_contact_uri(struct sip_msg* msg, str* uri)
+{
+	contact_t* c;
+	
+	uri->len = 0;
+	if (!msg->contact) return 1;
+	
+	if (parse_contact(msg->contact) < 0) {
+		LOG(L_ERR, "get_contact_uri: Error while parsing Contact body\n");
+		return -1;
+	}
+	
+	c = ((contact_body_t*)msg->contact->parsed)->contacts;
+	
+	if (!c) {
+		LOG(L_ERR, "get_contact_uri: Empty body or * contact\n");
+		return -2;
+	}
+	
+	*uri = c->uri;
+	return 0;
+}
+
+
+
+     /*
+      * The function creates an ACK to 200 OK. Route set will be created
+      * and parsed and next_hop parameter will contain uri the which the
+      * request should be send. The function is used by tm when it generates
+      * local ACK to 200 OK (on behalf of applications using uac
+      */
+char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch,
+		    str* to, unsigned int *len, str *next_hop)
+{
+	char *req_buf, *p, *via;
+	unsigned int via_len;
+	char branch_buf[MAX_BRANCH_PARAM_LEN];
+	int branch_len;
+	str branch_str;
+	struct hostport hp;
+	struct rte* list;
+	str contact, ruri, *cont;
+	struct socket_info* send_sock;
+	union sockaddr_union to_su;
+	
+	if (get_contact_uri(rpl, &contact) < 0) {
+		return 0;
+	}
+	
+	if (process_routeset(rpl, &contact, &list, &ruri, next_hop) < 0) {
+		return 0;
+	}
+	
+	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
+		     /* contact != ruri means that the next
+		      * hop is a strict router, cont will be non-zero
+		      * and print_routeset will append it at the end
+		      * of the route set
+		      */
+		cont = &contact;
+	} else {
+		     /* Next hop is a loose router, nothing to append */
+		cont = 0;
+	}
+	
+	     /* method, separators, version: "ACK sip:[email protected] SIP/2.0" */
+	*len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN;
+	*len += ruri.len;
+	
+	
+	     /* via */
+	send_sock = uri2sock(next_hop, &to_su, PROTO_NONE);
+	if (!send_sock) {
+		LOG(L_ERR, "build_dlg_ack: no socket found\n");
+		goto error;
+	}
+	
+	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
+	branch_str.s = branch_buf;
+	branch_str.len = branch_len;
+	set_hostport(&hp, 0);
+	via = via_builder(&via_len, send_sock, &branch_str, 0, send_sock->proto, &hp);
+	if (!via) {
+		LOG(L_ERR, "build_dlg_ack: No via header got from builder\n");
+		goto error;
+	}
+	*len+= via_len;
+	
+	     /*headers*/
+	*len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN;
+	
+	     /* copy'n'paste Route headers */
+	
+	*len += calc_routeset_len(list, cont);
+	
+	     /* User Agent */
+	if (server_signature) *len += USER_AGENT_LEN + CRLF_LEN;
+	     /* Content Length, EoM */
+	*len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN;
+	
+	req_buf = shm_malloc(*len + 1);
+	if (!req_buf) {
+		LOG(L_ERR, "build_dlg_ack: Cannot allocate memory\n");
+		goto error01;
+	}
+	p = req_buf;
+	
+	append_mem_block( p, ACK, ACK_LEN );
+	append_mem_block( p, " ", 1 );
+	append_str(p, ruri);
+	append_mem_block( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN);
+  	 
+	     /* insert our via */
+	append_mem_block(p, via, via_len);
+	
+	     /*other headers*/
+	append_str(p, Trans->from);
+	append_str(p, Trans->callid);
+	append_str(p, *to);
+	
+	append_str(p, Trans->cseq_n);
+	append_mem_block( p, " ", 1 );
+	append_mem_block( p, ACK, ACK_LEN);
+	append_mem_block(p, CRLF, CRLF_LEN);
+	
+	     /* Routeset */
+	p = print_rs(p, list, cont);
+	
+	     /* User Agent header */
+	if (server_signature) {
+		append_mem_block(p, USER_AGENT CRLF, USER_AGENT_LEN + CRLF_LEN);
+	}
+	
+	     /* Content Length, EoM */
+	append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
+	*p = 0;
+	
+	pkg_free(via);
+	free_rte_list(list);
+	return req_buf;
+	
+ error01:
+	pkg_free(via);
+ error:
+	free_rte_list(list);
+	return 0;
+  	 }
+
+
 /*
 /*
  * Convert lenght of body into asciiz
  * Convert lenght of body into asciiz
  */
  */

+ 10 - 0
modules/tm/t_msgbuilder.h

@@ -75,6 +75,16 @@ char *build_uac_request(  str msg_type, str dst, str from,
 	struct cell *t, unsigned int *len);
 	struct cell *t, unsigned int *len);
 
 
 
 
+/*
+ * The function creates an ACK to 200 OK. Route set will be created
+ * and parsed and next_hop parameter will contain uri the which the
+ * request should be send. The function is used by tm when it generates
+ * local ACK to 200 OK (on behalf of applications using uac
+ */
+char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch,
+		    str* to, unsigned int *len, str *next_hop);
+
+
 /*
 /*
  * Create a request
  * Create a request
  */
  */

+ 71 - 13
modules/tm/t_reply.c

@@ -259,13 +259,15 @@ inline static int update_totag_set(struct cell *t, struct sip_msg *ok)
 }
 }
 
 
 
 
+/*
+ * Build an ACK to a negative reply
+ */
 static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
 static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
 	unsigned int *ret_len)
 	unsigned int *ret_len)
 {
 {
 	str to;
 	str to;
 
 
-    if ( parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to )
-    {
+    if (parse_headers(rpl,HDR_TO, 0)==-1 || !rpl->to ) {
         LOG(L_ERR, "ERROR: t_build_ACK: "
         LOG(L_ERR, "ERROR: t_build_ACK: "
             "cannot generate a HBH ACK if key HFs in reply missing\n");
             "cannot generate a HBH ACK if key HFs in reply missing\n");
         return NULL;
         return NULL;
@@ -276,6 +278,51 @@ static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
         ACK, ACK_LEN, &to );
         ACK, ACK_LEN, &to );
 }
 }
 
 
+
+/*
+ * The function builds an ACK to 200 OK of local transactions, honor the
+ * route set, the URI to which the message should be sent will be returned
+ * in next_hop parameter
+ */
+static char *build_local_ack(struct sip_msg* rpl, struct cell *trans, int branch,
+			     unsigned int *ret_len, str* next_hop)
+{
+	str to;
+	if (parse_headers(rpl, HDR_EOH, 0) == -1 || !rpl->to) {
+		LOG(L_ERR, "ERROR: build_local_ack: Error while parsing headers\n");
+		return 0;
+	}
+	
+	to.s = rpl->to->name.s;
+	to.len = rpl->to->len;
+	return build_dlg_ack(rpl, trans, branch, &to, ret_len, next_hop);
+}
+
+
+     /*
+      * The function is used to send a localy generated ACK to INVITE
+      * (tm generates the ACK on behalf of application using UAC
+      */
+static int send_local_ack(str* next_hop, char* ack, int ack_len)
+{
+	struct socket_info* send_sock;
+	union sockaddr_union to_su;
+	
+	if (!next_hop) {
+		LOG(L_ERR, "send_local_ack: Invalid parameter value\n");
+		return -1;
+	}
+	
+	send_sock = uri2sock(next_hop, &to_su, PROTO_NONE);
+	if (!send_sock) {
+		LOG(L_ERR, "send_local_ack: no socket found\n");
+		return -1;
+	}
+	
+	return msg_send(send_sock, send_sock->proto, &to_su, 0, ack, ack_len);
+}
+
+
 static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 			 unsigned int code, char * text, 
 			 unsigned int code, char * text, 
 			 char *to_tag, unsigned int to_tag_len, int lock,
 			 char *to_tag, unsigned int to_tag_len, int lock,
@@ -1160,7 +1207,7 @@ int reply_received( struct sip_msg  *p_msg )
 	branch_bm_t cancel_bitmap;
 	branch_bm_t cancel_bitmap;
 	struct ua_client *uac;
 	struct ua_client *uac;
 	struct cell *t;
 	struct cell *t;
-
+	str next_hop;
 
 
 	/* make sure we know the assosociated transaction ... */
 	/* make sure we know the assosociated transaction ... */
 	if (t_check( p_msg  , &branch )==-1)
 	if (t_check( p_msg  , &branch )==-1)
@@ -1201,24 +1248,35 @@ int reply_received( struct sip_msg  *p_msg )
 		/* acknowledge negative INVITE replies (do it before detailed
 		/* acknowledge negative INVITE replies (do it before detailed
 		 * on_reply processing, which may take very long, like if it
 		 * on_reply processing, which may take very long, like if it
 		 * is attempted to establish a TCP connection to a fail-over dst */
 		 * is attempted to establish a TCP connection to a fail-over dst */
-	if (is_invite(t) && (msg_status>=300 || (is_local(t) && msg_status>=200))){
-		ack = build_ack( p_msg, t, branch, &ack_len);
-		if (ack) {
-			SEND_PR_BUFFER( &uac->request, ack, ack_len );
-			shm_free(ack);
+
+        if (t->flags & T_IS_INVITE_FLAG) {
+                if (msg_status >= 300) {
+			ack = build_ack(p_msg, t, branch, &ack_len);
+			if (ack) {
+				SEND_PR_BUFFER(&uac->request, ack, ack_len);
+				shm_free(ack);
+			}
+		} else if ((t->flags & T_IS_LOCAL_FLAG) && msg_status >= 200) {
+			ack = build_local_ack(p_msg, t, branch, &ack_len, &next_hop);
+			if (ack) {
+				if (send_local_ack(&next_hop, ack, ack_len) < 0) {
+					LOG(L_ERR, "Error while seding local ACK\n");
+				}
+				shm_free(ack);
+			}
 		}
 		}
-	} /* ack-ing negative INVITE replies */
-	/* processing of on_reply block */
+	}
+	     /* processing of on_reply block */
 	if (t->on_reply) {
 	if (t->on_reply) {
 		rmode=MODE_ONREPLY;
 		rmode=MODE_ONREPLY;
-		/* transfer transaction flag to message context */
+		     /* transfer transaction flag to message context */
 		if (t->uas.request) p_msg->flags=t->uas.request->flags;
 		if (t->uas.request) p_msg->flags=t->uas.request->flags;
 	 	if (run_actions(onreply_rlist[t->on_reply], p_msg)<0) 
 	 	if (run_actions(onreply_rlist[t->on_reply], p_msg)<0) 
 			LOG(L_ERR, "ERROR: on_reply processing failed\n");
 			LOG(L_ERR, "ERROR: on_reply processing failed\n");
-		/* destroy any eventual avps */
+		     /* destroy any eventual avps */
 		if (users_avps)
 		if (users_avps)
 			destroy_avps();
 			destroy_avps();
-		/* transfer current message context back to t */
+		     /* transfer current message context back to t */
 		if (t->uas.request) t->uas.request->flags=p_msg->flags;
 		if (t->uas.request) t->uas.request->flags=p_msg->flags;
 	}
 	}
 	LOCK_REPLIES( t );
 	LOCK_REPLIES( t );