浏览代码

Serial forking functions.

This patch adds two new functions to tm module, t_load_contacts and
t_next_contacts which can be used to implement serial forking.

There are also two new parameters, fr_inv_timer_next and contacts_avp.
Parameter fr_inv_timer_next is similar to fr_inv_timer, the value
of this parameter is used for subsequent branches during serial forking.

The value of contacts_avp is the identifier of the AVP which contains
the list of contacts to be used for serial forking.

The serial forking functions originate from Kamailio where they were
implemented by Juha Heinanen.
Jan Janak 16 年之前
父节点
当前提交
00f54c06f2

文件差异内容过多而无法显示
+ 234 - 273
modules/tm/README


+ 3 - 0
modules/tm/config.c

@@ -90,6 +90,7 @@ struct cfg_group_tm	default_tm_cfg = {
 			 * for every method except BYE by default */
 	1,	/* cancel_b_method used for e2e and 6xx cancels*/
 	1,	/* reparse_on_dns_failover */
+	INV_FR_TIME_OUT_NEXT
 };
 
 void	*tm_cfg = &default_tm_cfg;
@@ -179,5 +180,7 @@ cfg_def_t	tm_cfg_def[] = {
 		"if set to 1, the SIP message after a DNS failover is "
 		"constructed from the outgoing message buffer of the failed "
 		"branch instead of from the received request"},
+	{"fr_inv_timer_next",	CFG_VAR_INT,	0, 0, timer_fixup, 0,
+		"The value of fr_inv_timer for subsequent branches during serial forking"},
 	{0, 0, 0, 0, 0, 0}
 };

+ 5 - 0
modules/tm/config.h

@@ -52,6 +52,10 @@
 #define FR_TIME_OUT        30000 /* ms */
 #define INV_FR_TIME_OUT   120000 /* ms */
 
+/*! \brief final response timers to be used for serial forwarding */
+#define INV_FR_TIME_OUT_FIRST 90000 /* ms */
+#define INV_FR_TIME_OUT_NEXT  30000 /* ms */
+
 /* WAIT timer ... tells how long state should persist in memory after
    a transaction was finalized*/
 #define WT_TIME_OUT       5000 /* ms */
@@ -129,6 +133,7 @@ struct cfg_group_tm {
 	unsigned int	tm_blst_methods_lookup;
 	unsigned int	cancel_b_flags;
 	int	reparse_on_dns_failover;
+	unsigned int fr_inv_timeout_next;
 };
 
 extern struct cfg_group_tm	default_tm_cfg;

+ 79 - 0
modules/tm/doc/functions.xml

@@ -1025,4 +1025,83 @@ failure_route[1] {
 	    </programlisting>
 	</example>
     </section>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_load_contacts()</function>
+		</title>
+		<para>
+                Loads contacts in destination set in increasing qvalue order as
+                values of contacts_avp. If all contacts in the destination set
+                have the same qvalue, t_load_contacts() does not do
+                anything thus 
+                minimizing performance impact of serial forking capability
+                when it is not needed. Returns 1 if loading of contacts
+                succeeded or there was nothing to do. Returns -1 on error (see
+                syslog).
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE.
+		</para>
+		<example>
+		<title><function>t_load_contacts</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (!t_load_contacts()) {
+        sl_send_reply("500", "Server Internal Error - Cannot load contacts");
+        exit;
+};
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_next_contacts()</function>
+		</title>
+		<para>
+                If transaction does not exist when t_next_contacts() is
+                called, replaces Request-URI with the
+                first contacts_avp value, adds the remaining contacts_avp values
+                with the same qvalue as branches, and destroys those AVPs. It
+                does nothing if there are no contacts_avp values. Returns 1 if
+                there were no errors and -1 if an error occurred (see syslog).
+		</para>
+                <para>
+                If transaction does exist when t_next_contacts() is
+                called, adds the first contacts_avp value and all
+                following contacts_avp values with the 
+                same qvalue as new branches to request and destroys those AVPs.
+                Returns 1 if new branches were successfully added and -1 on
+                error (see syslog) or if there were no more contacts_avp
+                values.
+                </para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<example>
+		<title><function>t_next_contacts</function> usage</title>
+		<programlisting format="linespecific">
+...
+# First call after t_load_contacts() when transaction does not exist yet
+# and contacts should be available
+if (!t_next_contacts()) {
+        sl_send_reply("500", "Server Internal Error - Cannot get contacts");
+} else {
+        t_relay();
+};
+...
+# Following call, when transaction exists and there may or may not be
+# contacts left
+if (!t_next_contacts()) {
+        t_reply("408", "Request Timeout");
+} else {
+        t_relay();
+};
+...
+</programlisting>
+		</example>
+	</section>
+
 </section>

+ 51 - 0
modules/tm/doc/params.xml

@@ -743,4 +743,55 @@ onreply_route["stateless_replies"] {
 	</example>
     </section>
 
+	<section>
+		<title><varname>fr_inv_timer_next</varname> (integer)</title>
+		<para>
+                Value of the Final Response timeout for INVITE
+                transactions to be used during serial forwarding:
+		</para>
+		<para>
+                Function t_next_contacts() sets fr_inv_timer to
+                fr_inv_timer_next value if, after t_next_contacts() is
+                called, there are still lower qvalue contacts available,
+                and to fr_inv_timer value if there are not.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 30.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>fr_inv_timer_next</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("tm", "fr_inv_timer_next", 10)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>contacts_avp</varname> (string)</title>
+		<para>
+                Internal AVP that t_load_contacts() function uses to store
+                contacts of the destination set and that
+                t_next_contacts() function uses to restore those contacts.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "NULL"
+			(t_load_contacts()/t_next_contacts() functions
+			are disabled).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>contacts_avp</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("tm", "contacts_avp", "$avp(i:25)")
+...
+</programlisting>
+		</example>
+	</section>
+
 </section>

+ 9 - 0
modules/tm/doc/tm.xml

@@ -13,11 +13,20 @@
 		    <email>[email protected]</email>
 		</address>
 	    </author>
+		<author>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+	        <email>[email protected]</email>
+		</author>
 	</authorgroup>
 	<copyright>
 	    <year>2003</year>
 	    <holder>FhG FOKUS</holder>
 	</copyright>
+	<copyright>
+		<year>2008</year>
+		<holder>Juha Heinanen</holder>
+	</copyright>
 	<revhistory>
 	    <revision>
 		<revnumber>$Revision$</revnumber>

+ 5 - 17
modules/tm/t_fifo.c

@@ -60,6 +60,7 @@
 #include "config.h"
 #include "t_lookup.h"
 #include "t_fwd.h"
+#include "t_funcs.h"
 #include "t_fifo.h"
 
 
@@ -79,10 +80,6 @@
 #endif
 
 
-
-
-
-
 #define TWRITE_PARAMS          20
 #define TWRITE_VERSION_S       "0.3"
 #define TWRITE_VERSION_LEN     (sizeof(TWRITE_VERSION_S)-1)
@@ -98,15 +95,6 @@
 #define APPEND_BUFFER_MAX      4096
 #define CMD_BUFFER_MAX         128
 
-#define append_str(_dest,_src,_len) \
-	do{ \
-		memcpy( (_dest) , (_src) , (_len) );\
-		(_dest) += (_len) ;\
-	}while(0);
-
-#define append_chr(_dest,_c) \
-	*((_dest)++) = _c;
-
 #define copy_route(s,len,rs,rlen) \
 	do {\
 		if(rlen+len+3 >= ROUTE_BUFFER_MAX){\
@@ -117,7 +105,7 @@
 			append_chr(s,','); len++;\
 		}\
 		append_chr(s,'<');len++;\
-		append_str(s,rs,rlen);\
+		append_mem_block(s,rs,rlen);\
 		len += rlen; \
 		append_chr(s,'>');len++;\
 	} while(0)
@@ -836,7 +824,7 @@ static int assemble_msg(struct sip_msg* msg, struct tw_info *twi)
 		    "while copying optional header\n");
 		goto error;
 	}
-	append_str(s,"P-MsgFlags: ",12);
+	append_mem_block(s,"P-MsgFlags: ",12);
 	l = APPEND_BUFFER_MAX - (12+1); /* include trailing `\n'*/
 
 	if (int2reverse_hex(&s, &l, (int)msg->msg_flags) == -1) {
@@ -860,8 +848,8 @@ static int assemble_msg(struct sip_msg* msg, struct tw_info *twi)
 		    "copying command name\n");
 		goto error;
 	}
-	append_str(s,"sip_request.",12);
-	append_str(s,twi->action.s,twi->action.len);
+	append_mem_block(s,"sip_request.",12);
+	append_str(s,twi->action);
 	eol_line_len(1) = s - (char*)eol_line_s(1);
 
 	eol_line(2,REQ_LINE(msg).method);     /* method type */

+ 21 - 4
modules/tm/t_funcs.c

@@ -79,10 +79,14 @@ static int     fr_timer_avp_type = 0;
 static int_str fr_timer_avp = {0};
 static str     fr_timer_str;
 static int     fr_timer_index = 0;
-static int     fr_inv_timer_avp_type = 0;
-static int_str fr_inv_timer_avp = {0};
+int     fr_inv_timer_avp_type = 0;
+int_str fr_inv_timer_avp = {0};
 static str     fr_inv_timer_str;
 static int     fr_inv_timer_index = 0;
+int     contacts_avp_type = 0;
+int_str contacts_avp = {0};
+static str     contacts_avp_str;
+static int     contacts_avp_index = 0;
 
 int tm_error = 0; /* delayed tm error */
 
@@ -370,9 +374,10 @@ done:
 
 /*
  * Initialize parameters containing the ID of
- * AVPs with variable timers
+ * AVPs with various timers
  */
-int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param)
+int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param,
+					char* contacts_avp_param)
 {
 	if (fr_timer_param && *fr_timer_param) {
 		fr_timer_str.s = fr_timer_param;
@@ -395,6 +400,18 @@ int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param)
 			return -1;
 		}
 	}
+
+	if (contacts_avp_param && *contacts_avp_param) {
+		contacts_avp_str.s = contacts_avp_param;
+		contacts_avp_str.len = strlen(contacts_avp_str.s);
+		if (parse_avp_spec( &contacts_avp_str, &contacts_avp_type,
+							&contacts_avp, &contacts_avp_index)<0) {
+			LOG(L_CRIT,"ERROR:tm:init_avp_params: invalid contact_avp_params "
+				"AVP specs \"%s\"\n", contacts_avp_param);
+			return -1;
+		}
+	}
+
 	return 0;
 }
 

+ 23 - 1
modules/tm/t_funcs.h

@@ -76,11 +76,32 @@ struct cell;
 extern int tm_error; /* delayed tm error */
 extern struct msgid_var user_auto_inv_100;
 
+extern int fr_inv_timer_avp_type;
+extern int_str fr_inv_timer_avp;
+extern int contacts_avp_type;
+extern int_str contacts_avp;
+
 /* default names for timer's AVPs  */
 #define FR_TIMER_AVP      "callee_fr_timer"
 #define FR_INV_TIMER_AVP  "callee_fr_inv_timer"
 
 
+#define append_str(_dest,_str) \
+	do{ \
+		memcpy( (_dest) , (_str).s , (_str).len );\
+		(_dest) += (_str).len ;\
+	}while(0);
+
+#define  append_mem_block(_d,_s,_len) \
+		do{\
+			memcpy((_d),(_s),(_len));\
+			(_d) += (_len);\
+		}while(0);
+
+#define append_chr(_dest,_c) \
+	*((_dest)++) = _c;
+
+
 /* send a private buffer: utilize a retransmission structure
    but take a separate buffer not referred by it; healthy
    for reducing time spend in REPLIES locks
@@ -147,7 +168,8 @@ int send_pr_buffer( struct retr_buf *rb, void *buf, int len);
 /*
  * Parse and fixup the fr_*_timer AVP specs
  */
-int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param);
+int init_avp_params(char *fr_timer_param, char *fr_inv_timer_param,
+					char *contacts_avp_param);
 
 
 typedef void (*unref_cell_f)(struct cell *t);

+ 1 - 13
modules/tm/t_msgbuilder.c

@@ -79,18 +79,6 @@
 		(_d) += (_len);\
 	}while(0)
 
-#define  append_mem_block(_d,_s,_len) \
-		do{\
-			memcpy((_d),(_s),(_len));\
-			(_d) += (_len);\
-		}while(0)
-
-#define append_str(_p,_str) \
-	do{  \
-		memcpy((_p), (_str).s, (_str).len); \
-		(_p)+=(_str).len;  \
- 	} while(0)
-
 
 /* Build a local request based on a previous request; main
    customers of this function are local ACK and local CANCEL
@@ -181,7 +169,7 @@ char *build_local(struct cell *Trans,unsigned int branch,
 
 	append_mem_block( p, method, method_len );
 	append_mem_block( p, " ", 1 );
-	append_str( p, Trans->uac[branch].uri );
+	append_str( p, Trans->uac[branch].uri);
 	append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );
 
 	/* insert our via */

+ 519 - 0
modules/tm/t_serial.c

@@ -0,0 +1,519 @@
+/*
+ * Serial forking functions
+ *
+ * Copyright (C) 2008 Juha Heinanen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * History:
+ * -------
+ *  2008-10-22: Moved functions from lcr module to tm module (jh)
+ */
+
+#include "../../qvalue.h"
+#include "../../mem/mem.h"
+#include "../../socket_info.h"
+#include "../../usr_avp.h"
+#include "../../dset.h"
+#include "../../parser/msg_parser.h"
+#include "../../ut.h"
+#include "config.h"
+#include "t_funcs.h"
+#include "t_lookup.h"
+
+/* usr_avp flag for sequential forking */
+#define Q_FLAG      (1<<2)
+
+/* module parameter variable */
+int fr_inv_timer_next = INV_FR_TIME_OUT_NEXT;
+
+/* Struture where information regarding contacts is stored */
+struct contact {
+    str uri;
+    qvalue_t q;
+    str dst_uri;
+    str path;
+    unsigned int flags;
+    struct socket_info* sock;
+    unsigned short q_flag;
+    struct contact *next;
+};
+
+/* 
+ * Frees contact list used by load_contacts function
+ */
+static inline void free_contact_list(struct contact *curr) {
+    struct contact *prev;
+    while (curr) {
+		prev = curr;
+		curr = curr->next;
+		pkg_free(prev);
+    }
+}
+
+/* Encode branch info from contact struct to str */
+static inline int encode_branch_info(str *info, struct contact *con)
+{
+    char *at, *s;
+    int len;
+
+    info->len = con->uri.len + con->dst_uri.len +
+		con->path.len + MAX_SOCKET_STR + INT2STR_MAX_LEN + 5;
+    info->s = pkg_malloc(info->len);
+    if (!info->s) {
+		LM_ERR("no memory left for branch info\n");
+		return 0;
+    }
+    at = info->s;
+    append_str(at, con->uri);
+    append_chr(at, '\n');
+    append_str(at, con->dst_uri);
+    append_chr(at, '\n');
+    append_str(at, con->path);
+    append_chr(at, '\n');
+    if (con->sock) {
+		len = MAX_SOCKET_STR;
+		if (socket2str(at, &len, con->sock) < 0) {
+			LM_ERR("failed to convert socket to str\n");
+			return 0;
+		}
+    } else {
+		len = 0;
+    }
+    at = at + len;
+    append_chr(at, '\n');
+    s = int2str(con->flags, &len);
+    append_mem_block(at, s, len);
+    append_chr(at, '\n');
+    info->len = at - info->s + 1;
+
+    return 1;
+}
+
+
+/* Encode branch info from str */
+static inline int decode_branch_info(char *info, str *uri, str *dst, str *path,
+									 struct socket_info **sock,
+									 unsigned int *flags)
+{
+    str s, host;
+    int port, proto;
+    char *pos, *at, *tmp;
+
+    pos = strchr(info, '\n');
+    uri->len = pos - info;
+    if (uri->len) {
+		uri->s = info;
+    } else {
+		uri->s = 0;
+    }
+    at = pos + 1;
+
+    pos = strchr(at, '\n');
+    dst->len = pos - at;
+    if (dst->len) {
+		dst->s = at;
+    } else {
+		dst->s = 0;
+    }
+    at = pos + 1;
+
+    pos = strchr(at, '\n');
+    path->len = pos - at;
+    if (path->len) {
+		path->s = at;
+    } else {
+		path->s = 0;
+    }
+    at = pos + 1;
+
+    pos = strchr(at, '\n');
+    s.len = pos - at;
+    if (s.len) {
+		s.s = at;
+		if ((tmp = as_asciiz(&s)) == NULL) {
+			ERR("No memory left\n");
+			return 0;
+		}
+		if (parse_phostport(tmp, &host.s, &host.len,
+							&port, &proto) != 0) {
+			LM_ERR("parsing of socket info <%.*s> failed\n",  s.len, s.s);
+			pkg_free(tmp);
+			return 0;
+		}
+		pkg_free(tmp);
+		*sock = grep_sock_info(&host, (unsigned short)port,
+							   (unsigned short)proto);
+		if (*sock == 0) {
+			LM_ERR("invalid socket <%.*s>\n", s.len, s.s);
+			return 0;
+		}
+    } else {
+		*sock = 0;
+    }
+    at = pos + 1;
+
+    pos = strchr(at, '\n');
+    s.len = pos - at;
+    if (s.len) {
+		s.s = at;
+		if (str2int(&s, flags) != 0) {
+			LM_ERR("failed to decode flags <%.*s>\n", s.len, s.s);
+			return 0;
+		}
+    } else {
+		*flags = 0;
+    }
+
+    return 1;
+}
+
+
+/* 
+ * Loads contacts in destination set into contacts_avp in reverse
+ * priority order and associated each contact with Q_FLAG telling if
+ * contact is the last one in its priority class.  Finally, removes
+ * all branches from destination set.
+ */
+int t_load_contacts(struct sip_msg* msg, char* key, char* value)
+{
+    str uri, tmp, dst_uri, path, branch_info, *ruri;
+    qvalue_t first_q, q;
+    struct contact *contacts, *next, *prev, *curr;
+    int_str val;
+    int first_idx, idx;
+    struct socket_info* sock;
+    unsigned int flags;
+    struct cell *t;
+
+    /* Check if contacts_avp has been defined */
+    if (contacts_avp.n == 0) {
+		LM_ERR("feature has been disabled - "
+			   "to enable define contacts_avp module parameter");
+		return -1;
+    }
+
+    /* Check if anything needs to be done */
+    if (nr_branches == 0) {
+		LM_DBG("nothing to do - no branches!\n");
+		return 1;
+    }
+
+    t = get_t();
+    ruri = (str *)0;
+
+    if (!t || (t == T_UNDEFINED)) {
+
+		/* No transaction yet - take first q from Request-URI */
+		ruri = GET_RURI(msg);
+		if (!ruri) {
+			LM_ERR("no Request-URI found\n");
+			return -1;
+		}
+		first_q = get_ruri_q();
+		first_idx = 0;
+
+    } else {
+
+		/* Transaction exists - take first q from first branch */
+	
+		uri.s = get_branch(0, &uri.len, &first_q, &dst_uri, &path, &flags,
+						   &sock);
+		first_idx = 1;
+
+    }
+
+    /* Check if all q values are equal */
+    for(idx = first_idx; (tmp.s = get_branch(idx, &tmp.len, &q, 0, 0, 0, 0))
+			!= 0; idx++) {
+		if (q != first_q) {
+			goto rest;
+		}
+    }
+
+    LM_DBG("nothing to do - all contacts have same q!\n");
+    return 1;
+
+rest:
+
+    /* Allocate memory for first contact */
+    contacts = (struct contact *)pkg_malloc(sizeof(struct contact));
+    if (!contacts) {
+		LM_ERR("no memory for contact info\n");
+		return -1;
+    }
+
+    if (!t || (t == T_UNDEFINED)) {
+
+		/* Insert Request-URI branch to first contact */
+		contacts->uri.s = ruri->s;
+		contacts->uri.len = ruri->len;
+		contacts->dst_uri = msg->dst_uri;
+		contacts->sock = msg->force_send_socket;
+		getbflagsval(0, &contacts->flags);
+		contacts->path = msg->path_vec;
+
+    } else {
+	
+		/* Insert first branch to first contact */
+		contacts->uri = uri;
+		contacts->q = first_q;
+		contacts->dst_uri = dst_uri;
+		contacts->sock = sock;
+		contacts->flags = flags;
+		contacts->path = path;
+    }
+
+    contacts->q = first_q;
+    contacts->next = (struct contact *)0;
+
+    /* Insert (remaining) branches to contact list in increasing q order */
+
+    for(idx = first_idx;
+		(uri.s = get_branch(idx,&uri.len,&q,&dst_uri,&path,&flags,&sock))
+			!= 0;
+		idx++ ) {
+		next = (struct contact *)pkg_malloc(sizeof(struct contact));
+		if (!next) {
+			LM_ERR("no memory for contact info\n");
+			free_contact_list(contacts);
+			return -1;
+		}
+		next->uri = uri;
+		next->q = q;
+		next->dst_uri = dst_uri;
+		next->path = path;
+		next->flags = flags;
+		next->sock = sock;
+		next->next = (struct contact *)0;
+		prev = (struct contact *)0;
+		curr = contacts;
+		while (curr && (curr->q < q)) {
+			prev = curr;
+			curr = curr->next;
+		}
+		if (!curr) {
+			next->next = (struct contact *)0;
+			prev->next = next;
+		} else {
+			next->next = curr;
+			if (prev) {
+				prev->next = next;
+			} else {
+				contacts = next;
+			}
+		}    
+    }
+
+    /* Assign values for q_flags */
+    curr = contacts;
+    curr->q_flag = 0;
+    while (curr->next) {
+		if (curr->q < curr->next->q) {
+			curr->next->q_flag = Q_FLAG;
+		} else {
+			curr->next->q_flag = 0;
+		}
+		curr = curr->next;
+    }
+
+    /* Add contacts to contacts_avp */
+    curr = contacts;
+    while (curr) {
+		if (encode_branch_info(&branch_info, curr) == 0) {
+			LM_ERR("encoding of branch info failed\n");
+			free_contact_list(contacts);
+			if (branch_info.s) pkg_free(branch_info.s);
+			return -1;
+		}
+		val.s = branch_info;
+		add_avp(contacts_avp_type|AVP_VAL_STR|(curr->q_flag),
+				contacts_avp, val);
+		pkg_free(branch_info.s);
+		LM_DBG("loaded contact <%.*s> with q_flag <%d>\n",
+			   val.s.len, val.s.s, curr->q_flag);
+		curr = curr->next;
+    }
+
+    /* Clear all branches */
+    clear_branches();
+
+    /* Free contact list */
+    free_contact_list(contacts);
+
+    return 1;
+}
+
+
+/*
+ * Adds to request a destination set that includes all highest priority
+ * class contacts in contacts_avp.   If called from a route block,
+ * rewrites the request uri with first contact and adds the remaining
+ * contacts as branches.  If called from failure route block, adds all
+ * contacts as branches.  Removes added contacts from contacts_avp.
+ */
+int t_next_contacts(struct sip_msg* msg, char* key, char* value)
+{
+    struct usr_avp *avp, *prev;
+    int_str val;
+    str uri, dst, path;
+    struct socket_info *sock;
+    unsigned int flags;
+    struct cell *t;
+	struct search_state st;
+
+    /* Check if contacts_avp has been defined */
+    if (contacts_avp.n == 0) {
+		LM_ERR("feature has been disabled - "
+			   "to enable define contacts_avp module parameter");
+		return -1;
+    }
+
+    t = get_t();
+
+    if (!t || (t == T_UNDEFINED)) {
+
+		/* no transaction yet => load Request-URI and branches */
+
+		if (route_type == FAILURE_ROUTE) {
+			LM_CRIT("BUG - undefined transaction in failure route\n");
+			return -1;
+		}
+
+		/* Find first contacts_avp value */
+		avp = search_first_avp(contacts_avp_type, contacts_avp, &val, &st);
+		if (!avp) {
+			LM_DBG("no AVPs - we are done!\n");
+			return 1;
+		}
+
+		LM_DBG("next contact is <%s>\n", val.s.s);
+
+		if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
+			== 0) {
+			LM_ERR("decoding of branch info <%.*s> failed\n",
+				   val.s.len, val.s.s);
+			destroy_avp(avp);
+			return -1;
+		}
+
+		/* Rewrite Request-URI */
+		rewrite_uri(msg, &uri);
+		set_dst_uri(msg, &dst);
+		set_path_vector(msg, &path);
+		msg->force_send_socket = sock;
+		setbflagsval(0, flags);
+
+		if (avp->flags & Q_FLAG) {
+			destroy_avp(avp);
+			/* Set fr_inv_timer */
+			val.n = fr_inv_timer_next;
+			if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) {
+				LM_ERR("setting of fr_inv_timer_avp failed\n");
+				return -1;
+			}
+			return 1;
+		}
+
+		/* Append branches until out of branches or Q_FLAG is set */
+		prev = avp;
+		while ((avp = search_next_avp(&st, &val))) {
+			destroy_avp(prev);
+
+			LM_DBG("next contact is <%s>\n", val.s.s);
+
+			if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
+				== 0) {
+				LM_ERR("decoding of branch info <%.*s> failed\n",
+					   val.s.len, val.s.s);
+				destroy_avp(avp);
+				return -1;
+			}
+
+			if (km_append_branch(msg, &uri, &dst, &path, 0, flags, sock) != 1) {
+				LM_ERR("appending branch failed\n");
+				destroy_avp(avp);
+				return -1;
+			}
+
+			if (avp->flags & Q_FLAG) {
+				destroy_avp(avp);
+				val.n = fr_inv_timer_next;
+				if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val)
+					!= 0) {
+					LM_ERR("setting of fr_inv_timer_avp failed\n");
+					return -1;
+				}
+				return 1;
+			}
+			prev = avp;
+		}
+	
+    } else {
+	
+		/* Transaction exists => only load branches */
+
+		/* Find first contacts_avp value */
+		avp = search_first_avp(contacts_avp_type, contacts_avp, &val, &st);
+		if (!avp) return -1;
+
+		/* Append branches until out of branches or Q_FLAG is set */
+		prev = avp;
+		do {
+
+			LM_DBG("next contact is <%s>\n", val.s.s);
+
+			if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
+				== 0) {
+				LM_ERR("decoding of branch info <%.*s> failed\n",
+					   val.s.len, val.s.s);
+				destroy_avp(avp);
+				return -1;
+			}
+	    
+			if (km_append_branch(msg, &uri, &dst, &path, 0, flags, sock) != 1) {
+				LM_ERR("appending branch failed\n");
+				destroy_avp(avp);
+				return -1;
+			}
+
+			if (avp->flags & Q_FLAG) {
+				destroy_avp(avp);
+				return 1;
+			}
+
+			prev = avp;
+			avp = search_next_avp(&st, &val);
+			destroy_avp(prev);
+
+		} while (avp);
+
+		/* Restore fr_inv_timer */
+		val.n = default_tm_cfg.fr_inv_timeout;
+		if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) {
+			LM_ERR("setting of fr_inv_timer_avp failed\n");
+			return -1;
+		}
+	
+    }
+
+    return 1;
+}

+ 35 - 0
modules/tm/t_serial.h

@@ -0,0 +1,35 @@
+/*
+ * Serial forking functions
+ *
+ * Copyright (C) 2008 Juha Heinanen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * History:
+ * -------
+ *  2008-10-22: Moved functions from lcr module to tm module (jh)
+ */
+
+extern int fr_inv_timer_next;
+
+int t_load_contacts(struct sip_msg* msg, char* key, char* value);
+
+int t_next_contacts(struct sip_msg* msg, char* key, char* value);

+ 13 - 2
modules/tm/tm.c

@@ -132,6 +132,7 @@
 #include "timer.h"
 #include "t_msgbuilder.h"
 #include "select.h"
+#include "t_serial.h"
 
 MODULE_VERSION
 
@@ -242,6 +243,7 @@ static int t_check_trans(struct sip_msg* msg, char* foo, char* bar);
  * searched for nothing each time a new transaction is created */
 static char *fr_timer_param = 0 /*FR_TIMER_AVP*/;
 static char *fr_inv_timer_param = 0 /*FR_INV_TIMER_AVP*/;
+static char *contacts_avp_param = 0;
 
 static rpc_export_t tm_rpc[];
 
@@ -377,6 +379,10 @@ static cmd_export_t cmds[]={
 	{"t_check_trans",	t_check_trans,				0, 0,
 			REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE },
 
+	{"t_load_contacts", t_load_contacts,            0, 0,
+			REQUEST_ROUTE | FAILURE_ROUTE},
+	{"t_next_contacts", t_next_contacts,            0, 0,
+			REQUEST_ROUTE | FAILURE_ROUTE},
 
 	/* not applicable from the script */
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
@@ -454,6 +460,10 @@ static param_export_t params[]={
 	{"cancel_b_method",     PARAM_INT, &default_tm_cfg.cancel_b_flags},
 	{"reparse_on_dns_failover", PARAM_INT, &default_tm_cfg.reparse_on_dns_failover},
 	{"on_sl_reply",         PARAM_STRING|PARAM_USE_FUNC, fixup_on_sl_reply   },
+
+	{"fr_inv_timer_next",   PARAM_INT, &default_tm_cfg.fr_inv_timeout_next   },
+	{"contacts_avp",        PARAM_STRING, &contacts_avp_param                },
+
 	{0,0,0}
 };
 
@@ -757,8 +767,9 @@ static int mod_init(void)
 		return -1;
 	}
 
-	if (init_avp_params( fr_timer_param, fr_inv_timer_param)<0 ){
-		LOG(L_ERR,"ERROR:tm:mod_init: failed to process timer AVPs\n");
+	if (init_avp_params( fr_timer_param, fr_inv_timer_param,
+						 contacts_avp_param)<0 ){
+		LOG(L_ERR,"ERROR:tm:mod_init: failed to process AVP params\n");
 		return -1;
 	}
 	tm_init = 1;

部分文件因为文件数量过多而无法显示