Ver Fonte

Merge remote branch 'origin/sr_3.0' into kamailio_3.0

* origin/sr_3.0:
  fix typo in location flags settings
  Rewritten this small helper function
  When string was in the form of 0=on\n\0 this function caused a segmentation fault(canaries where overwritten).
  Removed unused var (added last commit)
  Fixed avpops avp_db_store as before no state was used and it caused to loop endessly.
  Declared pua_send_publish ,as it was only defined (extern)
  Fixed tm_load import (It would be advisable to use tm_load_api instead )
  Fixed include guard
  Added kmi module to linkage (because of  ../../modules_k/xcap_client/xcap_client.so: undefined symbol: register_mi_mod  )
  Fixed carrierroute module exports.
  Fixed userblacklist exported function
  If the user parameter is "<null>" (e.g a empty username in the request URI), do not copy this value to the rewritten uri.
  lib/srdb1/schema/gw.xml: index changes
  modules/lcr: bug fixes
  htable(k): fix non-init act. ctx in event route execution
  tm: fix/support changing r-uris and path in branch routes

Conflicts:
	.gitignore
	Makefile
	Makefile.defs
	Makefile.dirs
	cfg.lex
	cfg.y
	etc/kamailio.cfg
	events.c
	events.h
	forward.c
	lib/srdb1/schema/gw.xml
	modules/avpops/avpops_impl.c
	modules/carrierroute/carrierroute.c
	modules/carrierroute/cr_func.c
	modules/carrierroute/cr_func.h
	modules/lcr/README
	modules/lcr/doc/lcr_admin.xml
	modules/lcr/lcr_mod.c
	modules/lcr/lcr_rpc.c
	modules/tm/h_table.c
	modules/tm/h_table.h
	modules/tm/t_fwd.c
	modules/tm/t_fwd.h
	modules/tm/t_hooks.c
	modules/tm/t_reply.c
	modules/tm/uac.c
	modules/utils/conf.c
	modules_k/htable/htable.c
	modules_k/kex/core_stats.c
	modules_k/pua_dialoginfo/pua_dialoginfo.c
	modules_k/pua_usrloc/pua_usrloc.c
	modules_k/pua_usrloc/pua_usrloc.h
	modules_k/pua_xmpp/pua_xmpp.c
	modules_k/pv/pv.c
	modules_k/pv/pv_branch.c
	modules_k/pv/pv_branch.h
	modules_k/userblacklist/userblacklist.c
	modules_k/xcap_client/Makefile
	modules_s/domain/domain_mod.c
	modules_s/registrar/save.c
	parser/msg_parser.c
	parser/parse_uri.c
	receive.c
	utils/kamctl/Makefile
Daniel-Constantin Mierla há 16 anos atrás
pai
commit
39ce77440b
42 ficheiros alterados com 3968 adições e 241 exclusões
  1. 49 0
      etc/dictionary.radius
  2. 10 1
      lib/srdb1/schema/gw.xml
  3. 3 3
      modules/avpops/avpops_impl.c
  4. 3 3
      modules/carrierroute/carrierroute.c
  5. 44 10
      modules/carrierroute/cr_func.c
  6. 9 0
      modules/carrierroute/cr_func.h
  7. 5 0
      modules/lcr/README
  8. 9 0
      modules/lcr/doc/lcr_admin.xml
  9. 20 13
      modules/lcr/lcr_mod.c
  10. 3 3
      modules/lcr/lcr_rpc.c
  11. 325 178
      modules/tm/t_fwd.c
  12. 2 0
      modules/tm/t_fwd.h
  13. 3 4
      modules/utils/conf.c
  14. 1 0
      modules_k/htable/htable.c
  15. 1 0
      modules_k/pua_dialoginfo/pua_dialoginfo.c
  16. 2 0
      modules_k/pua_usrloc/pua_usrloc.c
  17. 1 1
      modules_k/pua_usrloc/pua_usrloc.h
  18. 2 1
      modules_k/pua_xmpp/pua_xmpp.c
  19. 16 0
      modules_k/sanity/Makefile
  20. 196 0
      modules_k/sanity/README
  21. 4 0
      modules_k/sanity/doc/Makefile
  22. 41 0
      modules_k/sanity/doc/sanity.xml
  23. 271 0
      modules_k/sanity/doc/sanity_admin.xml
  24. 221 0
      modules_k/sanity/mod_sanity.c
  25. 85 0
      modules_k/sanity/mod_sanity.h
  26. 749 0
      modules_k/sanity/sanity.c
  27. 79 0
      modules_k/sanity/sanity.h
  28. 29 4
      modules_k/userblacklist/userblacklist.c
  29. 2 0
      modules_k/xcap_client/Makefile
  30. 1 1
      modules_s/domain/domain_mod.c
  31. 21 17
      modules_s/registrar/save.c
  32. 16 0
      modules_s/sanity/Makefile
  33. 158 0
      modules_s/sanity/README
  34. 4 0
      modules_s/sanity/doc/Makefile
  35. 79 0
      modules_s/sanity/doc/functions.xml
  36. 74 0
      modules_s/sanity/doc/params.xml
  37. 144 0
      modules_s/sanity/doc/sanity.xml
  38. 223 0
      modules_s/sanity/mod_sanity.c
  39. 90 0
      modules_s/sanity/mod_sanity.h
  40. 887 0
      modules_s/sanity/sanity.c
  41. 84 0
      modules_s/sanity/sanity.h
  42. 2 2
      parser/parse_uri.c

+ 49 - 0
etc/dictionary.radius

@@ -0,0 +1,49 @@
+#
+# $Id$
+#
+# SIP RADIUS attributes
+#
+# Proprietary indicates an attribute that hasn't
+# been standardized
+#
+#
+# NOTE: All standard (IANA registered) attributes are 
+#       defined in the default dictionary of the 
+#       radiusclient-ng library.
+#
+
+
+#### Attributes ###
+ATTRIBUTE Sip-Uri-User         208  string     # Proprietary, auth_radius
+ATTRIBUTE Sip-Group            211  string     # Proprietary, group_radius
+ATTRIBUTE Sip-Rpid             213  string     # Proprietary, auth_radius
+ATTRIBUTE SIP-AVP              225  string     # Proprietary, avp_radius
+
+### Acct-Status-Type Values ###
+#VALUE Acct-Status-Type   Failed           15   # RFC2866, acc
+
+### Service-Type Values ###
+#VALUE Service-Type       Call-Check       10   # RFC2865, uri_radius
+VALUE Service-Type       Group-Check      12   # Proprietary, group_radius
+##VALUE Service-Type       Sip-Session      15   # Schulzrinne, acc, auth_radius
+VALUE Service-Type       SIP-Caller-AVPs  30   # Proprietary, avp_radius
+VALUE Service-Type       SIP-Callee-AVPs  31   # Proprietary, avp_radius
+
+### Sip-Method Values ###
+VALUE Sip-Method         Undefined      0
+VALUE Sip-Method         Invite         1
+VALUE Sip-Method         Cancel         2
+VALUE Sip-Method         Ack            4
+VALUE Sip-Method         Bye            8
+VALUE Sip-Method         Info           16
+VALUE Sip-Method         Options        32
+VALUE Sip-Method         Update         64
+VALUE Sip-Method         Register       128
+VALUE Sip-Method         Message        256
+VALUE Sip-Method         Subscribe      512
+VALUE Sip-Method         Notify         1024
+VALUE Sip-Method         Prack          2048
+VALUE Sip-Method         Refer          4096
+VALUE Sip-Method         Other          8192
+
+

+ 10 - 1
lib/srdb1/schema/gw.xml

@@ -122,10 +122,19 @@
     </column>
 
     <index>
-        <name>lcr_id_gw_name_idx</name>
+        <name>lcr_id_grp_id_gw_name_idx</name>
         <colref linkend="lcr_id"/>
+        <colref linkend="grp_id"/>
         <colref linkend="gw_name"/>
         <unique/>
     </index>
 
+    <index>
+        <name>lcr_id_grp_id_ip_addr_idx</name>
+        <colref linkend="lcr_id"/>
+        <colref linkend="grp_id"/>
+        <colref linkend="ip_addr"/>
+        <unique/>
+    </index>
+
 </table>

+ 3 - 3
modules/avpops/avpops_impl.c

@@ -659,13 +659,13 @@ int ops_dbstore_avps (struct sip_msg* msg, struct fis_param *sp,
 	/* set uuid/(username and domain) fields */
 
 	n =0 ;
-
+	memset(&st, 0, sizeof(struct search_state));
 	if ((dbp->a.opd&AVPOPS_VAL_NONE)==0)
 	{
 		/* avp name is known ->set it and its type */
 		store_vals[1].val.str_val = dbp->sa; /*attr name*/
-		avp = search_first_avp( name_type, avp_name, &i_s, 0);
-		for( ; avp; avp=search_first_avp( name_type, avp_name, &i_s, &st))
+		avp = search_first_avp( name_type, avp_name, &i_s, &st);
+		for( ; avp; avp=search_next_avp( &st, &i_s))
 		{
 			/* don't insert avps which were loaded */
 			if (avp->flags&AVP_IS_IN_DB)

+ 3 - 3
modules/carrierroute/carrierroute.c

@@ -87,11 +87,11 @@ static void mod_destroy(void);
 /************* Module Exports **********************************************/
 static cmd_export_t cmds[]={
 	{"cr_user_carrier",  (cmd_function)cr_load_user_carrier,  3, cr_load_user_carrier_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{"cr_route",         (cmd_function)cr_route,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_route",         (cmd_function)cr_route5,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"cr_route",         (cmd_function)cr_route,              6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{"cr_prime_route",   (cmd_function)cr_prime_route,        5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_prime_route",   (cmd_function)cr_prime_route5,        5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"cr_prime_route",   (cmd_function)cr_prime_route,        6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{"cr_nofallback_route",(cmd_function)cr_nofallback_route,     5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_nofallback_route",(cmd_function)cr_nofallback_route5,     5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"cr_nofallback_route",(cmd_function)cr_nofallback_route,     6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"cr_next_domain",   (cmd_function)cr_load_next_domain,   6, cr_load_next_domain_fixup,  0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{0, 0, 0, 0, 0, 0}

+ 44 - 10
modules/carrierroute/cr_func.c

@@ -267,11 +267,10 @@ static struct route_rule * get_rule_by_hash(const struct route_flags * rf,
 /**
  * does the work for rewrite_on_rule, writes the new URI into dest
  *
- * @param rs the route rule used for rewriting
- * @param dest the returned new destination URI
- * @param msg the sip message
- * @param user the localpart of the uri to be rewritten
- * @param descavp the name of the AVP where the description is stored
+ * @param rs the route rule used for rewriting, not NULL
+ * @param dest the returned new destination URI, not NULL
+ * @param msg the sip message, not NULL
+ * @param user the localpart of the uri to be rewritten, not NULL
  *
  * @return 0 on success, -1 on failure
  *
@@ -284,11 +283,23 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
 	int_str avp_val;
 	int strip = 0;
 
+	str l_user = *user;
+	
 	strip = (rs->strip > user->len ? user->len : rs->strip);
 	strip = (strip < 0 ? 0 : strip);
 
-	len = rs->local_prefix.len + user->len + rs->local_suffix.len +
-	      AT_SIGN.len + rs->host.len - strip;
+	if ( strcmp(user->s, "<null>") == 0 || user->len == 0)
+	{
+		l_user.s = NULL;
+		l_user.len = 0;
+		len = rs->host.len;
+		strip = 0;
+	}
+	else{
+		len = rs->local_prefix.len + l_user.len + rs->local_suffix.len +
+                       AT_SIGN.len + rs->host.len - strip;
+	}
+	
 	if (msg->parsed_uri.type == SIPS_URI_T) {
 		len += SIPS_URI.len;
 	} else {
@@ -309,11 +320,11 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
 		memcpy(p, SIP_URI.s, SIP_URI.len);
 		p += SIP_URI.len;
 	}
-	if (user->len) {
+	if (l_user.len) {
 		memcpy(p, rs->local_prefix.s, rs->local_prefix.len);
 		p += rs->local_prefix.len;
-		memcpy(p, user->s + strip, user->len - strip);
-		p += user->len - strip;
+		memcpy(p, l_user.s + strip, l_user.len - strip);
+		p += l_user.len - strip;
 		memcpy(p, rs->local_suffix.s, rs->local_suffix.len);
 		p += rs->local_suffix.len;
 		memcpy(p, AT_SIGN.s, AT_SIGN.len);
@@ -666,6 +677,14 @@ int cr_route(struct sip_msg * _msg, gparam_t *_carrier,
 		_rewrite_user, _hsrc, alg_crc32, _descavp);
 }
 
+int cr_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc)
+{
+	return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+		_rewrite_user, _hsrc, alg_crc32, NULL);
+}
+
 
 /**
  * rewrites the request URI of msg after determining the
@@ -690,6 +709,13 @@ int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
 		_rewrite_user, _hsrc, alg_prime, _descavp);
 }
 
+int cr_prime_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc)
+{
+	return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+		_rewrite_user, _hsrc, alg_prime, NULL);
+}
 /**
  * rewrites the request URI of msg after determining the
  * new destination URI with the crc32 hash algorithm. The difference
@@ -715,6 +741,14 @@ int cr_nofallback_route(struct sip_msg * _msg, gparam_t *_carrier,
 		_rewrite_user, _hsrc, alg_crc32_nofallback, _dstavp);
 }
 
+int cr_nofallback_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc)
+{
+	return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+		_rewrite_user, _hsrc, alg_crc32_nofallback, NULL);
+}
+
 
 /**
  * Loads next domain from failure routing table and stores it in an AVP.

+ 9 - 0
modules/carrierroute/cr_func.h

@@ -69,6 +69,9 @@ int cr_route(struct sip_msg * _msg, gparam_t *_carrier,
 		gparam_t *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_descavp);
+int cr_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc);
 
 
 /**
@@ -89,6 +92,9 @@ int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
 		gparam_t *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_descavp);
+int cr_prime_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc);
 
 /**
  *
@@ -111,6 +117,9 @@ int cr_nofallback_route(struct sip_msg * _msg, gparam_t *_carrier,
 		gparam_t *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_dstavp);
+int cr_nofallback_route5(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc);
 
 
 /**

+ 5 - 0
modules/lcr/README

@@ -809,6 +809,11 @@ if (to_any_gw()) {
    Causes lcr module to re-read the contents of gw and lcr tables into
    memory.
 
+   Reload fails if number of gateways is larger than value of constant
+   MAX_NO_OF_GWS in file lcr_mod.h, which defaults to 128. If you have
+   more than 128 gateways, you need to increase the value of this constant
+   and recompile lcr module.
+
    Name: lcr_reload
 
    Parameters: none

+ 9 - 0
modules/lcr/doc/lcr_admin.xml

@@ -1006,6 +1006,15 @@ if (to_any_gw()) {
 			Causes lcr module to re-read the contents of
 			gw and lcr tables into memory.
 		</para>
+		<para>
+			Reload fails if number of
+			gateways is larger than value of constant
+			MAX_NO_OF_GWS in file lcr_mod.h, which
+			defaults to 128.  If you have more than 128
+			gateways, you need
+			to increase the value of this constant and recompile
+			lcr module.
+		</para>
 		<para>
 		Name: <emphasis>lcr_reload</emphasis>
 		</para>

+ 20 - 13
modules/lcr/lcr_mod.c

@@ -151,7 +151,7 @@ struct matched_gw_info {
     unsigned short prefix_len;
     unsigned short priority;
     unsigned int weight;
-    unsigned int defunct_until;
+    unsigned short duplicate;
 };
 
 /*
@@ -1412,7 +1412,7 @@ int mi_print_gws(struct mi_node* rpl)
 	    attr = add_mi_attr(node, MI_DUP_VALUE, "TAG", 3,
 			       gws[i].tag, gws[i].tag_len);
 	    if (attr == NULL) goto err;
-	    
+
 	    p = int2str((unsigned long)gws[i].weight, &len);
 	    attr = add_mi_attr(node, MI_DUP_VALUE, "WEIGHT", 6, p, len);
 	    if (attr == NULL) goto err;
@@ -1658,6 +1658,7 @@ void add_gws_into_avps(struct gw_info *gws, struct matched_gw_info *matched_gws,
     delete_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp);
 
     for (i = 0; i < gw_cnt; i++) {
+	if (matched_gws[i].duplicate == 1) continue;
 	index = matched_gws[i].gw_index;
       	hostname_len = gws[index].hostname_len;
 	strip = gws[index].strip;
@@ -1684,7 +1685,7 @@ void add_gws_into_avps(struct gw_info *gws, struct matched_gw_info *matched_gws,
 	add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val);
 
 	LM_DBG("added gw_uri_avp <%.*s> with weight <%u>\n",
-		value.len, value.s, matched_gws[i].weight);
+	       value.len, value.s, matched_gws[i].weight);
     skip:
 	continue;
     }
@@ -1697,8 +1698,8 @@ void add_gws_into_avps(struct gw_info *gws, struct matched_gw_info *matched_gws,
 static int load_gws(struct sip_msg* _m, char *_lcr_id, char *_from_uri)
 {
     str ruri_user, from_uri;
-    unsigned int j, k, gw_index, gw_count, now;
-    int lcr_id;
+    int i, j, lcr_id;
+    unsigned int gw_index, gw_count, now, ip_addr;
     int_str val;
     struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
     struct lcr_info **lcrs, *lcr_rec, *pl;
@@ -1770,18 +1771,12 @@ static int load_gws(struct sip_msg* _m, char *_lcr_id, char *_from_uri)
 		    while (j) {
                         /* If this gw is defunct, skip it */
 		        if (gws[j].defunct_until > now) goto gw_found;
-			for (k = 0; k < gw_index; k++) {
-			    if (gws[j].ip_addr ==
-				gws[matched_gws[k].gw_index].ip_addr)
-				/* Skip already existing gw */
-				goto gw_found;
-			}
-			/* This is a new gw */
 			matched_gws[gw_index].gw_index = j;
 			matched_gws[gw_index].prefix_len = pl->prefix_len;
 			matched_gws[gw_index].priority = lcr_rec->priority;
 			matched_gws[gw_index].weight = gws[j].weight *
 			    (rand() >> 8);
+			matched_gws[gw_index].duplicate = 0;
 			LM_DBG("added matched_gws[%d]=[%u, %u, %u, %u]\n",
 			       gw_index, j, pl->prefix_len, lcr_rec->priority,
 			       matched_gws[gw_index].weight);
@@ -1796,9 +1791,21 @@ static int load_gws(struct sip_msg* _m, char *_lcr_id, char *_from_uri)
 	pl = pl->next;
     }
 
-    /* Sort gateways based on prefix_len, priority, and randomized weight */
+    /* Sort gateways in reverse order based on prefix_len, priority,
+       and randomized weight */
     qsort(matched_gws, gw_index, sizeof(struct matched_gw_info), comp_matched);
 
+    /* Remove duplicate gws */
+    for (i = gw_index - 1; i >= 0; i--) {
+	if (matched_gws[i].duplicate == 1) continue;
+	ip_addr = gws[matched_gws[i].gw_index].ip_addr;
+	for (j = i - 1; j >= 0; j--) {
+	    if (gws[matched_gws[j].gw_index].ip_addr == ip_addr) {
+		matched_gws[j].duplicate = 1;
+	    }
+	}
+    }
+
     /* Add gateways into gw_uris_avp */
     add_gws_into_avps(gws, matched_gws, gw_index, &ruri_user);
 

+ 3 - 3
modules/lcr/lcr_rpc.c

@@ -74,7 +74,7 @@ static void dump_gws(rpc_t* rpc, void* c)
 		if (rpc->add(c, "{", &st) < 0) return;
 		rpc->struct_add(st, "d", "lcr_id", j);
 		rpc->struct_add(st, "d", "grp_id", gws[i].grp_id);
-		rpc->struct_printf(st,   "ip_addr", "%d.%d.%d.%d",
+		rpc->struct_printf(st, "ip_addr", "%d.%d.%d.%d",
 				   (gws[i].ip_addr << 24) >> 24,
 				   ((gws[i].ip_addr >> 8) << 24) >> 24,
 				   ((gws[i].ip_addr >> 16) << 24) >> 24,
@@ -110,9 +110,9 @@ static void dump_gws(rpc_t* rpc, void* c)
 		tag.len=gws[i].tag_len;
 		rpc->struct_add(st, "dSddd",
 				"strip",  gws[i].strip,
-				"tag",    gws[i].tag, /* FIXME */
+				"tag",    &tag,
 				"weight", gws[i].weight,
-				"flags",  &tag,
+				"flags",  gws[i].flags,
 				"defunct_until",  &gws[i].defunct_until
 				);
 	    }

+ 325 - 178
modules/tm/t_fwd.c

@@ -78,7 +78,12 @@
  *              t saved, thus, are not available. Set reparse_on_dns_failover
  *              to 0 to revert the change. (Miklos)
  * 2008-06-04  T_CANCELED is now set each time a CANCEL is received (andrei)
- * 2009-06-01  Pre- and post-script callbacks of branch route are executed (Miklos)
+ * 2009-06-01  Pre- and post-script callbacks of branch route are 
+ *             executed (Miklos)
+ * 2009-10-26  support for changing dst_uri in branch routes,
+ *             s/print_uac_request/prepare_new_uac/ (andrei)
+ * 2009-10-28  support for changing r-uris and path in branch routes; more of
+ *             add_uac() functionality moved into prepare_new_uac()  (andrei)
  */
 
 #include "defs.h"
@@ -144,8 +149,20 @@ unsigned int get_on_branch(void)
 }
 
 
-/** create a branch "buffer".
- * Creates the buffer used in the branch rb and runs the on_branch route.
+
+/* prepare_new_uac flags */
+#define UAC_DNS_FAILOVER_F 1 /**< new branch due to dns failover */
+
+
+/** prepares a new branch "buffer".
+ * Creates the buffer used in the branch rb, fills everything needed (
+   the sending information: t->uac[branch].request.dst, branch buffer, uri
+   path vector a.s.o.) and runs the on_branch route.
+ * t->uac[branch].request.dst will be filled if next_hop !=0 with the result
+ * of the DNS resolution (next_hop and fproto).
+ * If next_hop is 0 all the dst members except the send_flags are read-only
+ * (send_flags it's updated) and are supposed to be pre-filled.
+ *
  * @param t  - transaction
  * @param i_req - corresponding sip_msg, must be non-null, flags might be
  *                be modified (on_branch route)
@@ -153,64 +170,61 @@ unsigned int get_on_branch(void)
  * @param uri
  * @param path  - path vector (list of route like destination in text form,
  *                 e.g.: "<sip:1.2.3.4;lr>, <sip:5.6.7.8;lr>")
- * @param len - resul parameter, it will be filled with the created buffer 
- *              lenght.
- * @param dst - value/result parameter. It will be used to generate the
- *              message. All the values except the send_flags are read-only.
- *              send_flags it's updated.
- * @return pointer to shm alloc'ed buffer on success (*len and dst->send_flags
- *  are filled/modified), 0 on failure
+ * @param next_hop - uri of the next hop. If non 0 it will be used
+ *              for DNS resolution and the branch request.dst structure will
+ *              be filled. If 0 the branch must already have
+ *              a pre-filled valid request.dst.
+ * @param fproto - forced proto for forwarding. Used only if next_hop!=0.
+ * @param flags - 0 or UAC_DNS_FAILOVER_F for now.
+ *
+ * @return  0 on success, < 0 (ser_errror E***) on failure.
  */
-static char *print_uac_request( struct cell *t, struct sip_msg *i_req,
-	int branch, str *uri, str* path, unsigned int *len, struct dest_info* dst)
+static int prepare_new_uac( struct cell *t, struct sip_msg *i_req,
+									int branch, str *uri, str* path,
+									str* next_hop, int fproto,
+									int flags)
 {
-	char *buf, *shbuf;
-	str* msg_uri;
+	char *shbuf;
 	struct lump* add_rm_backup, *body_lumps_backup;
 	struct sip_uri parsed_uri_bak;
-	int parsed_uri_ok_bak, uri_backed_up;
+	int ret;
+	unsigned int len;
+	int parsed_uri_ok_bak, free_new_uri;
 	str msg_uri_bak;
+	str dst_uri_bak;
+	int dst_uri_backed_up;
 	str path_bak;
-	int path_backed_up;
+	int free_path;
 	int backup_route_type;
 	snd_flags_t fwd_snd_flags_bak;
 	snd_flags_t rpl_snd_flags_bak;
+	struct dest_info *dst;
 
 	shbuf=0;
+	ret=E_UNSPEC;
 	msg_uri_bak.s=0; /* kill warnings */
 	msg_uri_bak.len=0;
 	parsed_uri_ok_bak=0;
-	uri_backed_up=0;
+	free_new_uri=0;
+	dst_uri_bak.s=0;
+	dst_uri_bak.len=0;
+	dst_uri_backed_up=0;
 	path_bak.s=0;
 	path_bak.len=0;
-	path_backed_up=0;
+	free_path=0;
+	dst=&t->uac[branch].request.dst;
 
 	/* ... we calculate branch ... */	
 	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
 			&i_req->add_to_branch_len ))
 	{
 		LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
+		ret=E_UNSPEC;
 		goto error00;
 	}
 
-	/* ... update uri ... */
-	msg_uri=GET_RURI(i_req);
-	if ((msg_uri->s!=uri->s) || (msg_uri->len!=uri->len)){
-		msg_uri_bak=i_req->new_uri;
-		parsed_uri_ok_bak=i_req->parsed_uri_ok;
-		parsed_uri_bak=i_req->parsed_uri;
-		i_req->new_uri=*uri;
-		i_req->parsed_uri_ok=0;
-		uri_backed_up=1;
-	}
-	/* update path_vec */
-	if (unlikely((i_req->path_vec.s!=path->s) ||
-					(i_req->path_vec.len!=path->len))){
-		path_bak=i_req->path_vec;
-		i_req->path_vec=*path;
-		path_backed_up=1;
-	}
-
+	/* dup lumps
+	 * TODO: clone lumps only if needed */
 #ifdef POSTPONE_MSG_CLONING
 	/* lumps can be set outside of the lock, make sure that we read
 	 * the up-to-date values */
@@ -218,85 +232,242 @@ static char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 #endif
 	add_rm_backup = i_req->add_rm;
 	body_lumps_backup = i_req->body_lumps;
-	i_req->add_rm = dup_lump_list(i_req->add_rm);
-	i_req->body_lumps = dup_lump_list(i_req->body_lumps);
-
-	if (unlikely(branch_route)) {
-		/* run branch_route actions if provided */
-		backup_route_type = get_route_type();
-		set_route_type(BRANCH_ROUTE);
-		tm_ctx_set_branch_index(branch+1);
-		if (exec_pre_script_cb(i_req, BRANCH_CB_TYPE)>0) {
-			/* backup ireq msg send flags */
-			fwd_snd_flags_bak=i_req->fwd_send_flags;;
-			rpl_snd_flags_bak=i_req->rpl_send_flags;
-			i_req->fwd_send_flags=dst->send_flags /* intial value  */;
-			if (run_top_route(branch_rt.rlist[branch_route], i_req, 0) < 0) {
-				LOG(L_ERR, "Error in run_top_route\n");
-			}
-			/* update dst send_flags */
-			dst->send_flags=i_req->fwd_send_flags;
-			/* restore ireq_msg flags */
-			i_req->fwd_send_flags=fwd_snd_flags_bak;
-			i_req->rpl_send_flags=rpl_snd_flags_bak;
-			exec_post_script_cb(i_req, BRANCH_CB_TYPE);
+	i_req->add_rm=0;
+	i_req->body_lumps=0;
+	if (unlikely(i_req->add_rm)){
+		i_req->add_rm = dup_lump_list(i_req->add_rm);
+		if (unlikely(i_req->add_rm==0)){
+			ret=E_OUT_OF_MEM;
+			goto error04;
 		}
-		tm_ctx_set_branch_index(0);
-		set_route_type(backup_route_type);
 	}
+	if (unlikely(i_req->body_lumps)){
+		i_req->body_lumps = dup_lump_list(i_req->body_lumps);
+		if (unlikely(i_req->body_lumps==0)){
+			ret=E_OUT_OF_MEM;
+			goto error04;
+		}
+	}
+	/* backup uri & path: we need to change them so that build_req...()
+	   will use uri & path and not the ones in the original msg (i_req)
+	   => we must back them up so that we can restore them to the original
+	   value after building the send buffer */
+	msg_uri_bak=i_req->new_uri;
+	parsed_uri_bak=i_req->parsed_uri;
+	parsed_uri_ok_bak=i_req->parsed_uri_ok;
+	path_bak=i_req->path_vec;
+	
+	if (unlikely(branch_route || has_tran_tmcbs(t, TMCB_REQUEST_FWDED))){
+		/* dup uris, path a.s.o. if we have a branch route or callback */
+		/* ... set ruri ... */
+		/* if uri points to new_uri, it needs to be "fixed" so that we can
+		   change msg->new_uri */
+		if (uri==&i_req->new_uri)
+			uri=&msg_uri_bak;
+		i_req->parsed_uri_ok=0;
+		i_req->new_uri.s=pkg_malloc(uri->len);
+		if (unlikely(i_req->new_uri.s==0)){
+			ret=E_OUT_OF_MEM;
+			goto error03;
+		}
+		free_new_uri=1;
+		memcpy(i_req->new_uri.s, uri->s, uri->len);
+		i_req->new_uri.len=uri->len;
+	
+		/* update path_vec */
+		/* if path points to msg path_vec, it needs to be "fixed" so that we 
+		   can change/update msg->path_vec */
+		if (path==&i_req->path_vec)
+			path=&path_bak;
+		/* zero it first so that set_path_vector will work */
+		i_req->path_vec.s=0;
+		i_req->path_vec.len=0;
+		if (unlikely(path)){
+			if (unlikely(set_path_vector(i_req, path)<0)){
+				ret=E_OUT_OF_MEM;
+				goto error03;
+			}
+			free_path=1;
+		}
+	
+		/* backup dst uri  & zero it*/
+		dst_uri_bak=i_req->dst_uri;
+		dst_uri_backed_up=1;
+		/* if next_hop points to dst_uri, it needs to be "fixed" so that we
+		   can change msg->dst_uri */
+		if (next_hop==&i_req->dst_uri)
+			next_hop=&dst_uri_bak;
+		/* zero it first so that set_dst_uri will work */
+		i_req->dst_uri.s=0;
+		i_req->dst_uri.len=0;
+		if (likely(next_hop)){
+			/* set dst uri to next_hop for the on_branch route */
+			if (unlikely(set_dst_uri(i_req, next_hop)<0)){
+				ret=E_OUT_OF_MEM;
+				goto error03;
+			}
+		}
 
-	/* run the specific callbacks for this transaction */
-	if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_FWDED)))
-		run_trans_callbacks( TMCB_REQUEST_FWDED , t, i_req, 0,
-								-i_req->REQ_METHOD);
+		if (likely(branch_route)) {
+			/* run branch_route actions if provided */
+			backup_route_type = get_route_type();
+			set_route_type(BRANCH_ROUTE);
+			tm_ctx_set_branch_index(branch+1);
+			/* no need to backup/set avp lists: the on_branch route is run
+			   only in the main route context (e.g. t_relay() in the main
+			   route) or in the failure route context (e.g. append_branch &
+			   t_relay()) and in both cases the avp lists are properly set
+			   Note: the branch route is not run on delayed dns failover 
+			   (for that to work one would have to set branch_route prior to
+			   calling add_uac(...) and then reset it afterwards).
+			   (
+			 */
+			if (exec_pre_script_cb(i_req, BRANCH_CB_TYPE)>0) {
+				/* backup ireq msg send flags */
+				fwd_snd_flags_bak=i_req->fwd_send_flags;;
+				rpl_snd_flags_bak=i_req->rpl_send_flags;
+				i_req->fwd_send_flags=dst->send_flags /* intial value  */;
+				if (run_top_route(branch_rt.rlist[branch_route], i_req, 0) < 0)
+				{
+					LOG(L_ERR, "Error in run_top_route\n");
+				}
+				/* update dst send_flags */
+				dst->send_flags=i_req->fwd_send_flags;
+				/* restore ireq_msg flags */
+				i_req->fwd_send_flags=fwd_snd_flags_bak;
+				i_req->rpl_send_flags=rpl_snd_flags_bak;
+				exec_post_script_cb(i_req, BRANCH_CB_TYPE);
+			}
+			tm_ctx_set_branch_index(0);
+			set_route_type(backup_route_type);
+		}
 
+		/* run the specific callbacks for this transaction */
+		if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_FWDED)))
+			run_trans_callbacks( TMCB_REQUEST_FWDED , t, i_req, 0,
+									-i_req->REQ_METHOD);
+		
+		if (likely( !(flags & UAC_DNS_FAILOVER_F) && i_req->dst_uri.s &&
+					i_req->dst_uri.len)){
+			/* no dns failover and non-empty dst_uri => use it as dst
+			  (on dns failover dns_h will be non-empty => next_hop will be
+			   ignored) */
+			next_hop=&i_req->dst_uri;
+		}
+	}else{
+		/* no branch route and no TMCB_REQUEST_FWDED callback => set
+		   msg uri and path to the new values (if needed) */
+		if (unlikely((uri->s!=i_req->new_uri.s || uri->len!=i_req->new_uri.len)
+					&& (i_req->new_uri.s!=0 ||
+						uri->s!=i_req->first_line.u.request.uri.s ||
+						uri->len!=i_req->first_line.u.request.uri.len) )){
+			/* uri is different from i_req uri => replace i_req uri and force
+			   uri re-parsing */
+			i_req->new_uri=*uri;
+			i_req->parsed_uri_ok=0;
+		}
+		if (unlikely(path && (i_req->path_vec.s!=path->s ||
+							  i_req->path_vec.len!=path->len))){
+			i_req->path_vec=*path;
+		}else if (unlikely(path==0 && i_req->path_vec.len!=0)){
+			i_req->path_vec.s=0;
+			i_req->path_vec.len=0;
+		}
+	}
+	
+	if (likely(next_hop!=0 || (flags & UAC_DNS_FAILOVER_F))){
+		/* next_hop present => use it for dns resolution */
+#ifdef USE_DNS_FAILOVER
+		if (uri2dst(&t->uac[branch].dns_h, dst, i_req,
+							next_hop?next_hop:uri, fproto) == 0)
+#else
+		/* dst filled from the uri & request (send_socket) */
+		if (uri2dst(dst, i_req, next_hop?next_hop:uri, fproto)==0)
+#endif
+		{
+			ret=E_BAD_ADDRESS;
+			goto error01;
+		}
+	} /* else next_hop==0 =>
+		no dst_uri / empty dst_uri and initial next_hop==0 =>
+		dst is pre-filled with a valid dst => use the pre-filled dst */
+	
+	/* check if send_sock is ok */
+	if (t->uac[branch].request.dst.send_sock==0) {
+		LOG(L_ERR, "ERROR: can't fwd to af %d, proto %d "
+			" (no corresponding listening socket)\n",
+			dst->to.s.sa_family, dst->proto );
+		ret=E_NO_SOCKET;
+		goto error01;
+	}
 	/* ... and build it now */
-	buf=build_req_buf_from_sip_req( i_req, len, dst, 0);
+	shbuf=build_req_buf_from_sip_req( i_req, &len, dst, BUILD_IN_SHM);
+	if (!shbuf) {
+		LOG(L_ERR, "ERROR: print_uac_request: no shm mem\n"); 
+		ret=E_OUT_OF_MEM;
+		goto error01;
+	}
 #ifdef DBG_MSG_QA
-	if (buf[*len-1]==0) {
+	if (shbuf[len-1]==0) {
 		LOG(L_ERR, "ERROR: print_uac_request: sanity check failed\n");
 		abort();
 	}
 #endif
-	if (!buf) {
-		LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); 
-		ser_error=E_OUT_OF_MEM;
-		goto error01;
-	}
-
-	shbuf=(char *)shm_malloc(*len);
-	if (!shbuf) {
-		ser_error=E_OUT_OF_MEM;
-		LOG(L_ERR, "ERROR: print_uac_request: no shmem\n");
-		goto error02;
+	/* things went well, move ahead and install new buffer! */
+	t->uac[branch].request.buffer=shbuf;
+	t->uac[branch].request.buffer_len=len;
+	t->uac[branch].uri.s=t->uac[branch].request.buffer+
+							i_req->first_line.u.request.method.len+1;
+	t->uac[branch].uri.len=GET_RURI(i_req)->len;
+	if (unlikely(i_req->path_vec.s && i_req->path_vec.len)){
+		t->uac[branch].path.s=shm_malloc(i_req->path_vec.len+1);
+		if (unlikely(t->uac[branch].path.s==0)) {
+			shm_free(shbuf);
+			t->uac[branch].request.buffer=0;
+			t->uac[branch].request.buffer_len=0;
+			t->uac[branch].uri.s=0;
+			t->uac[branch].uri.len=0;
+			ret=E_OUT_OF_MEM;
+			goto error01;
+		}
+		t->uac[branch].path.len=i_req->path_vec.len;
+		t->uac[branch].path.s[i_req->path_vec.len]=0;
+		memcpy( t->uac[branch].path.s, i_req->path_vec.s, i_req->path_vec.len);
 	}
-	memcpy( shbuf, buf, *len );
+	ret=0;
 
-error02:
-	pkg_free( buf );
 error01:
-	     /* Delete the duplicated lump lists, this will also delete
-	      * all lumps created here, such as lumps created in per-branch
-	      * routing sections, Via, and Content-Length headers created in
-	      * build_req_buf_from_sip_req
-	      */
+error03:
+	/* restore the new_uri & path from the backup */
+	if (unlikely(free_new_uri && i_req->new_uri.s)){
+			pkg_free(i_req->new_uri.s);
+	}
+	if (unlikely(free_path)){
+		reset_path_vector(i_req);
+	}
+	if (dst_uri_backed_up){
+		reset_dst_uri(i_req); /* free dst_uri */
+		i_req->dst_uri=dst_uri_bak;
+	}
+	/* restore original new_uri and path values */
+	i_req->new_uri=msg_uri_bak;
+	i_req->parsed_uri=parsed_uri_bak;
+	i_req->parsed_uri_ok=parsed_uri_ok_bak;
+	i_req->path_vec=path_bak;
+	/* Delete the duplicated lump lists, this will also delete
+	 * all lumps created here, such as lumps created in per-branch
+	 * routing sections, Via, and Content-Length headers created in
+	 * build_req_buf_from_sip_req
+	 */
+error04:
 	free_duped_lump_list(i_req->add_rm);
 	free_duped_lump_list(i_req->body_lumps);
 	     /* Restore the lists from backups */
 	i_req->add_rm = add_rm_backup;
 	i_req->body_lumps = body_lumps_backup;
-	/* restore the new_uri from the backup */
-	if (uri_backed_up){
-		i_req->new_uri=msg_uri_bak;
-		i_req->parsed_uri=parsed_uri_bak;
-		i_req->parsed_uri_ok=parsed_uri_ok_bak;
-	}
-	if (unlikely(path_backed_up)){
-		i_req->path_vec=path_bak;
-	}
 
 error00:
-	return shbuf;
+	return ret;
 }
 
 #ifdef USE_DNS_FAILOVER
@@ -420,19 +591,36 @@ int add_blind_uac( /*struct cell *t*/ )
 	return 1; /* success */
 }
 
-/* introduce a new uac to transaction; returns its branch id (>=0)
-   or error (<0); it doesn't send a message yet -- a reply to it
-   might interfere with the processes of adding multiple branches;
-   On error returns <0 & sets ser_error to the same value
+/** introduce a new uac to transaction.
+ *  It doesn't send a message yet -- a reply to it might interfere with the
+ *  processes of adding multiple branches; On error returns <0 & sets ser_error
+ *  to the same value.
+ *  @param t - transaction
+ *  @param request - corresponding sip_mst, must be non-null, flags might be
+ *                   modified (on_branch route).
+ *  @param uri - uri used for the branch (must be non-null).
+ *  @param next_hop - next_hop in sip uri format. If null and proxy is null
+ *                    too, the uri will be used 
+ *  @param path     - path vector (list of route like destinations in sip
+ *                     uri format, e.g.: "<sip:1.2.3.4;lr>, <sip:5.6.7.8;lr>").
+ *  @param proxy    - proxy structure. If non-null it takes precedence over
+ *                    next_hop/uri and it will be used for forwarding.
+ *  @param proto    - forced protocol for forwarding (overrides the protocol
+ *                    in next_hop/uri or proxy if != PROTO_NONE).
+ *  @param flags    - special flags passed to prepare_new_uac().
+ *                    @see prepare_new_uac().
+ *  @returns branch id (>=0) or error (<0)
 */
-int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
-				str* path, struct proxy_l *proxy, int proto )
+static int add_uac( struct cell *t, struct sip_msg *request, str *uri,
+					str* next_hop, str* path, struct proxy_l *proxy,
+					int proto, int flags)
 {
 
 	int ret;
 	unsigned short branch;
-	char *shbuf;
+#ifdef TM_UAC_FLAGS
 	unsigned int len;
+#endif /* TM_UAC_FLAGS */
 
 	branch=t->nr_of_outgoings;
 	if (branch==MAX_BRANCHES) {
@@ -460,60 +648,17 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 								t->uac[branch].request.dst.proto);
 		t->uac[branch].request.dst.send_flags=request?
 												request->fwd_send_flags:0;
+		next_hop=0;
 	}else {
-#ifdef USE_DNS_FAILOVER
-		if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
-					request, next_hop?next_hop:uri, proto) == 0)
-#else
-		/* dst filled from the uri & request (send_socket) */
-		if (uri2dst(&t->uac[branch].request.dst, request,
-						next_hop ? next_hop: uri, proto)==0)
-#endif
-		{
-			ret=ser_error=E_BAD_ADDRESS;
-			goto error;
-		}
-	}
-	
-	/* check if send_sock is ok */
-	if (t->uac[branch].request.dst.send_sock==0) {
-		LOG(L_ERR, "ERROR: add_uac: can't fwd to af %d, proto %d "
-			" (no corresponding listening socket)\n",
-			t->uac[branch].request.dst.to.s.sa_family, 
-			t->uac[branch].request.dst.proto );
-		ret=ser_error=E_NO_SOCKET;
-		goto error01;
+		next_hop= next_hop?next_hop:uri;
 	}
 
 	/* now message printing starts ... */
-	shbuf=print_uac_request(t, request, branch, uri, path,
-							&len, &t->uac[branch].request.dst);
-	if (!shbuf) {
-		ret=ser_error=E_OUT_OF_MEM;
+	if (unlikely( (ret=prepare_new_uac(t, request, branch, uri, path,
+											next_hop, proto, flags)) < 0)){
+		ser_error=ret;
 		goto error01;
 	}
-
-	/* things went well, move ahead and install new buffer! */
-	t->uac[branch].request.buffer=shbuf;
-	t->uac[branch].request.buffer_len=len;
-	t->uac[branch].uri.s=t->uac[branch].request.buffer+
-		request->first_line.u.request.method.len+1;
-	t->uac[branch].uri.len=uri->len;
-	if (unlikely(path && path->s)){
-		t->uac[branch].path.s=shm_malloc(path->len+1);
-		if (unlikely(t->uac[branch].path.s==0)) {
-			shm_free(shbuf);
-			t->uac[branch].request.buffer=0;
-			t->uac[branch].request.buffer_len=0;
-			t->uac[branch].uri.s=0;
-			t->uac[branch].uri.len=0;
-			ret=ser_error=E_OUT_OF_MEM;
-			goto error01;
-		}
-		t->uac[branch].path.len=path->len;
-		t->uac[branch].path.s[path->len]=0;
-		memcpy( t->uac[branch].path.s, path->s, path->len);
-	}
 #ifdef TM_UAC_FLAGS
 	len = count_applied_lumps(request->add_rm, HDR_RECORDROUTE_T);
 	if(len==1)
@@ -692,7 +837,7 @@ int add_uac_dns_fallback(struct cell *t, struct sip_msg* msg,
 				 *  must be changed and the send_socket might be different =>
 				 *  re-create the whole uac */
 				ret=add_uac(t,  msg, &old_uac->uri, 0, &old_uac->path, 0,
-							old_uac->request.dst.proto);
+							old_uac->request.dst.proto, UAC_DNS_FAILOVER_F);
 
 			if (ret<0){
 				/* failed, delete the copied dns_h */
@@ -729,7 +874,9 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
 	/* note -- there is a gap in proxy stats -- we don't update 
 	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
 	*/
-
+	
+	/* set same dst as the invite */
+	t_cancel->uac[branch].request.dst=t_invite->uac[branch].request.dst;
 	/* print */
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
 		/* buffer is built localy from the INVITE which was sent out */
@@ -742,39 +889,39 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
 			LOG(L_WARN, "WARNING: e2e_cancel_branch: CANCEL is built locally, "
 			"thus lumps are not applied to the message!\n");
 		}
-		shbuf=build_local_reparse( t_invite, branch, &len, CANCEL, CANCEL_LEN, &t_invite->to);
-
+		shbuf=build_local_reparse( t_invite, branch, &len, CANCEL,
+									CANCEL_LEN, &t_invite->to);
+		if (unlikely(!shbuf)) {
+			LOG(L_ERR, "e2e_cancel_branch: printing e2e cancel failed\n");
+			ret=ser_error=E_OUT_OF_MEM;
+			goto error;
+		}
+		/* install buffer */
+		t_cancel->uac[branch].request.buffer=shbuf;
+		t_cancel->uac[branch].request.buffer_len=len;
+		t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer+
+			cancel_msg->first_line.u.request.method.len+1;
+		t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len;
 	} else {
-		/* buffer is constructed from the received CANCEL with applying lumps */
-		shbuf=print_uac_request( t_cancel, cancel_msg, branch,
-							&t_invite->uac[branch].uri,
-							&t_invite->uac[branch].path,
-							&len, &t_invite->uac[branch].request.dst);
-	}
-
-	if (!shbuf) {
-		LOG(L_ERR, "ERROR: e2e_cancel_branch: printing e2e cancel failed\n");
-		ret=ser_error=E_OUT_OF_MEM;
-		goto error;
+		/* buffer is constructed from the received CANCEL with lumps applied */
+		/*  t_cancel...request.dst is already filled (see above) */
+		if (unlikely((ret=prepare_new_uac( t_cancel, cancel_msg, branch,
+									&t_invite->uac[branch].uri,
+									&t_invite->uac[branch].path,
+									0, PROTO_NONE, 0)) <0)){
+			ser_error=ret;
+			goto error;
+		}
 	}
-	
-	/* install buffer */
-	t_cancel->uac[branch].request.dst=t_invite->uac[branch].request.dst;
-	t_cancel->uac[branch].request.buffer=shbuf;
-	t_cancel->uac[branch].request.buffer_len=len;
-	t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer+
-		cancel_msg->first_line.u.request.method.len+1;
-	t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len;
-	
-
 	/* success */
 	ret=1;
 
-
 error:
 	return ret;
 }
 
+
+
 void e2e_cancel( struct sip_msg *cancel_msg, 
 	struct cell *t_cancel, struct cell *t_invite )
 {
@@ -1158,7 +1305,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 #endif
 		try_new=1;
 		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
-							&p_msg->path_vec, proxy, proto );
+							&p_msg->path_vec, proxy, proto, 0);
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 		else
@@ -1174,7 +1321,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 
 		branch_ret=add_uac( t, p_msg, &current_uri,
 							(dst_uri.len) ? (&dst_uri) : &current_uri,
-							&path, proxy, proto);
+							&path, proxy, proto, 0);
 		/* pick some of the errors in case things go wrong;
 		   note that picking lowest error is just as good as
 		   any other algorithm which picks any other negative

+ 2 - 0
modules/tm/t_fwd.h

@@ -59,8 +59,10 @@ char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 */
 void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite );
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch );
+/*
 int add_uac(struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 				str* path, struct proxy_l *proxy, int proto );
+*/
 #ifdef USE_DNS_FAILOVER
 int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
 									struct ua_client* old_uac,

+ 3 - 4
modules/utils/conf.c

@@ -75,12 +75,11 @@ static int fwd_max_id = 0;
 static void remove_spaces(char *s)
 {
 	char *p, *dst;
-
-	for (p = s, dst = s; *p != '\0'; ++p, ++dst) {
-		while (isspace(*p)) ++p;
-		*dst = *p;
+	for (p = s, dst = s; *p != '\0'; ++p) {
+		if (!isspace(*p)) *dst++ = *p;
 	}
 	*dst = '\0';
+
 }
 
 

+ 1 - 0
modules_k/htable/htable.c

@@ -185,6 +185,7 @@ static int child_init(int rank)
 		fmsg = faked_msg_next();
 		rtb = get_route_type();
 		set_route_type(REQUEST_ROUTE);
+		init_run_actions_ctx(&ctx);
 		run_top_route(event_rt.rlist[rt], fmsg, &ctx);
 		if(ctx.run_flags&DROP_R_F)
 		{

+ 1 - 0
modules_k/pua_dialoginfo/pua_dialoginfo.c

@@ -75,6 +75,7 @@ int override_lifetime   = DEF_OVERRIDE_LIFETIME;
 int caller_confirmed    = DEF_CALLER_ALWAYS_CONFIRMED;
 
 
+send_publish_t pua_send_publish;
 /** module functions */
 
 static int mod_init(void);

+ 2 - 0
modules_k/pua_usrloc/pua_usrloc.c

@@ -63,6 +63,8 @@ static int mod_init(void);
 
 int pua_set_publish(struct sip_msg* , char*, char*);
 
+send_publish_t pua_send_publish;
+send_subscribe_t pua_send_subscribe;
 
 static cmd_export_t cmds[]=
 {

+ 1 - 1
modules_k/pua_usrloc/pua_usrloc.h

@@ -23,7 +23,7 @@
  */
 
 #ifndef _PUA_UL_
-#define _PUA_UL
+#define _PUA_UL_
 #include "../pua/pua_bind.h"
 
 extern send_publish_t pua_send_publish;

+ 2 - 1
modules_k/pua_xmpp/pua_xmpp.c

@@ -136,11 +136,12 @@ static int mod_init(void)
 	server_address.len= strlen(server_address.s);
 
 	/* import the TM auto-loading function */
-	if((load_tm=(load_tm_f)find_export("load_tm", 0, 0))==NULL)
+	if((load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))==NULL)
 	{
 		LM_ERR("can't import load_tm\n");
 		return -1;
 	}
+
 	/* let the auto-loading function load all TM stuff */
 
 	if(load_tm(&tmb)==-1)

+ 16 - 0
modules_k/sanity/Makefile

@@ -0,0 +1,16 @@
+# $Id$
+#
+# sanity check module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=sanity.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 196 - 0
modules_k/sanity/README

@@ -0,0 +1,196 @@
+Sanity Module
+
+Nils Ohlmeier
+
+   iptelorg GmbH
+   <[email protected]>
+
+Edited by
+
+Nils Ohlmeier
+
+   <[email protected]>
+
+   Copyright © 2006 iptelorg GmbH
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. default_checks (integer)
+              1.3.2. uri_checks (integer)
+              1.3.3. proxy_require (string)
+
+        1.4. Exported Functions
+
+              1.4.1. sanity_check([checks, [uri_checks]])
+
+   List of Examples
+
+   1.1. Set default_checks parameter
+   1.2. Set proxy_require parameter
+   1.3. sanity_check usage
+   1.4. sanity_check usage with parameter
+   1.5. sanity_check usage with two parameters
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   This module aims to implement several sanity checks on incoming
+   requests which are suggested or even required by a RFC.
+
+   This checks are not required by Kamailio itself for its
+   functionality. But on the other side it makes not much sence if
+   a broken request traverses through a SIP network if it is
+   rejected sooner or later by a SIP device any way. As every
+   sanity cost extra performance because of additional parsing and
+   evaluation it is now with this module up to the Kamailio
+   adminstrator which checks should be done on which request.
+
+   The following checks are available:
+     * ruri sip version - (1) - checks if the SIP version in the
+       request URI is supported, currently only 2.0.
+     * ruri scheme - (2) - checks if the URI scheme of the request
+       URI is supported (sip[s]|tel[s]) by Kamailio.
+     * required headers - (4) -checks if the minimum set of
+       required headers to, from, cseq, callid and via is present
+       in the request.
+     * via sip version - (8) - not working because parser fails
+       already when another version then 2.0 is present.
+     * via protocol - (16) - not working because parser fails
+       already if an unsupported transport is present.
+     * cseq method - (32) - checks if the method from the cseq
+       header is equal to the request method.
+     * cseq value - (64) - checks if the number in the cseq header
+       is a valid unsigend integer.
+     * content length - (128) - checks if the size of the body
+       matches with the value from the content length header.
+     * expires value - (256) - checks if the value of the expires
+       header is a valid unsigned integer.
+     * proxy require - (512) - checks if all items of the proxy
+       require header are present in the list of the extensions
+       from the module parameter proxy_require.
+     * parse uri's - (1024) - checks if the specified URIs are
+       present and parseable by the Kamailio parsers
+     * digest credentials (2048) Check all instances of digest
+       credentials in a message. The test checks whether there are
+       all required digest parameters and have meaningful values.
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * sl - send reply.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running Kamailio with this module loaded:
+     * None.
+
+1.3. Exported Parameters
+
+1.3.1. default_checks (integer)
+
+   This parameter determines which of the checks from the sanity
+   module are executed if no parameter was given to the
+   sanity_check function call. By default all implemented checks
+   are included in the execution of the sanity_check function. The
+   integer value is the sum of the check numbers which should be
+   executed by default.
+
+   Default value is 999. This resolves to the following list of
+   checks: ruri_sip_version (1), ruri_scheme (2), required_headers
+   (4), cseq_method (32), cseq_value (64), content_length (128),
+   expires_value (256), proxy_require (512).
+
+   Example 1.1. Set default_checks parameter
+...
+modparam("sanity", "default_checks", 1)
+...
+
+1.3.2. uri_checks (integer)
+
+   This parameter determines which URIs are going to be checked if
+   the 'parse uri' will be executed.
+
+   Default value is 7. This resolves to the following list of
+   parsed URIs: Request RUI (1), From URI (2) and To URI (4).
+
+1.3.3. proxy_require (string)
+
+   This parameter set the list of supported extensions for this
+   Kamailio. The value is expected as comma seperated list of the
+   extensions. This list is seperated into single tokens. Each
+   token from a proxy require header will be compare to the tokens
+   from this list.
+
+   Example 1.2. Set proxy_require parameter
+...
+modparam("sanity", "proxy_require", "foo, bar")
+...
+
+1.4. Exported Functions
+
+1.4.1.  sanity_check([checks, [uri_checks]])
+
+   This function makes a row of sanity checks on the given
+   request. The function returns true if one of the checks failed.
+   If one of the checks fails the module sends a precise error
+   reply via sl_send_reply. Thus their is no need to reply with a
+   generic error message.
+
+   Example 1.3. sanity_check usage
+
+...
+if (sanity_check()) {
+        xlog("malformed message from $si:$sp\n");
+        exit;
+}
+...
+
+
+   Optionally the function takes an integer argument which
+   overwrites the global module parameter default_checks. This
+   allows to make certain test from script regions. The integer
+   value is again the sum of the checks (like for the module
+   parameter) which should be executed at this function call.
+
+   Example 1.4. sanity_check usage with parameter
+
+...
+if (method=="REGISTER" && sanity_check("256")) {
+        /* the register contains an invalid expires value and is replied
+ with a 400 */
+        xlog("malformed message from $si:$sp\n");
+        exit;
+}
+...
+
+
+   Optionally the function takes a second integer argument which
+   overwrites the global module parameter uri_checks and thus
+   determines which URIs will be checked if the parse uri test
+   will be executed.
+
+   Example 1.5. sanity_check usage with two parameters
+
+...
+if (method=="INVITE" && sanity_check("1024", "6")) {
+        /* the INVITE contains an invalid From or To header and is repli
+ed with a 400 */
+        xlog("malformed message from $si:$sp\n");
+        exit;
+}
+...

+ 4 - 0
modules_k/sanity/doc/Makefile

@@ -0,0 +1,4 @@
+docs = sanity.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 41 - 0
modules_k/sanity/doc/sanity.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>Sanity Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Nils</firstname>
+		<surname>Ohlmeier</surname>
+		<affiliation><orgname>iptelorg GmbH</orgname></affiliation>
+		<email>[email protected]</email>
+		<address>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Nils</firstname>
+		<surname>Ohlmeier</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2006</year>
+	    <holder>iptelorg GmbH</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="sanity_admin.xml"/>
+    
+    
+</book>
+

+ 271 - 0
modules_k/sanity/doc/sanity_admin.xml

@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module aims to implement several sanity checks on incoming
+		requests which are suggested or even required by a RFC.
+	</para>
+	<para>
+		This checks are not required by &kamailio; itself for its functionality.
+		But on the other side it makes not much sence if a broken
+		request traverses through a SIP network if it is rejected sooner
+		or later by a SIP device any way. As every sanity cost extra
+		performance because of additional parsing and evaluation it
+		is now with this module up to the &kamailio; adminstrator which checks
+		should be done on which request.
+	</para>
+	<para>
+		The following checks are available:
+		<itemizedlist>
+		<listitem>
+			<para>
+			ruri sip version - (1) - checks if the SIP version in the request
+			URI is supported, currently only 2.0.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			ruri scheme - (2) - checks if the URI scheme of the request URI is
+			supported (sip[s]|tel[s]) by &kamailio;.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			required headers - (4) -checks if the minimum set of required headers
+			to, from, cseq, callid and via is present in the request.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			via sip version - (8) - not working because parser fails already 
+			when another version then 2.0 is present.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			via protocol - (16) - not working because parser fails already if an
+			unsupported transport is present.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			cseq method - (32) - checks if the method from the cseq header is equal
+			to the request method.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			cseq value - (64) - checks if the number in the cseq header is a valid
+			unsigend integer.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			content length - (128) - checks if the size of the body matches with the
+			value from the content length header.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			expires value - (256) - checks if the value of the expires header is a
+			valid unsigned integer.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			proxy require - (512) - checks if all items of the proxy require header
+			are present in the list of the extensions from the module 
+			parameter proxy_require.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			parse uri's - (1024) - checks if the specified URIs are present and
+			parseable by the &kamailio; parsers
+			</para>
+		</listitem>
+		<listitem>
+		    <para>
+			digest credentials (2048) Check all instances of digest credentials in a
+			message. The test checks whether there are all required
+			digest parameters and have meaningful values.
+			</para>
+		</listitem>
+		</itemizedlist>
+	</para>
+
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>sl</emphasis> - send reply.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+   <section>
+	<title><varname>default_checks</varname> (integer)</title>
+	<para>
+		This parameter determines which of the checks from the sanity
+		module are executed if no parameter was given to the sanity_check
+		function call. By default all implemented checks are included
+		in the execution of the sanity_check function. The integer value
+		is the sum of the check numbers which should be executed by default.
+	</para>
+	<para>
+	    Default value is 999. This resolves to the following list of
+		checks: ruri_sip_version (1), ruri_scheme (2), required_headers (4),
+		cseq_method (32), cseq_value (64), content_length (128), 
+		expires_value (256), proxy_require (512).
+	</para>
+	<example>
+	    <title>Set <varname>default_checks</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sanity", "default_checks", 1)
+...
+	    </programlisting>
+	</example>
+    </section>
+
+	<section>
+	<title><varname>uri_checks</varname> (integer)</title>
+	<para>
+		This parameter determines which URIs are going to be checked
+		if the 'parse uri' will be executed.
+	</para>
+	<para>
+		Default value is 7. This resolves to the following list of
+		parsed URIs: Request RUI (1), From URI (2) and To URI (4).
+	</para>
+	</section>
+
+	<section>
+	<title><varname>proxy_require</varname> (string)</title>
+	<para>
+		This parameter set the list of supported extensions for this &kamailio;.
+		The value is expected as comma seperated list of the extensions.
+		This list is seperated into single tokens. Each token from
+		a proxy require header will be compare to the tokens from this
+		list.
+	</para>
+	<example>
+	    <title>Set <varname>proxy_require</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sanity", "proxy_require", "foo, bar")
+...
+	    </programlisting>
+	</example>
+	</section>
+	</section>
+	<section>
+	<title>Exported Functions</title>
+    <section id="sanity_check">
+	    <title>
+		<function>sanity_check([checks, [uri_checks]])</function>
+	    </title>
+	<para>
+		This function makes a row of sanity checks on the given request.
+		The function returns true if one of the checks failed. If one
+		of the checks fails the module sends a precise error reply via 
+		sl_send_reply. Thus their is no need to reply with a generic 
+		error message.
+	</para>
+	<example>
+	    <title><function>sanity_check</function> usage</title>
+	    <programlisting>
+<![CDATA[
+...
+if (sanity_check()) {
+	xlog("malformed message from $si:$sp\n");
+	exit;
+}
+...
+]]>
+	    </programlisting>
+	</example>
+	<para>
+		Optionally the function takes an integer argument which overwrites
+		the global module parameter default_checks. This allows to make
+		certain test from script regions. The integer value is again the sum
+		of the checks (like for the module parameter) which should be executed
+		at this function call.
+	</para>
+	<example>
+	    <title><function>sanity_check</function> usage with parameter</title>
+	    <programlisting>
+<![CDATA[
+...
+if (method=="REGISTER" && sanity_check("256")) {
+	/* the register contains an invalid expires value and is replied with a 400 */
+	xlog("malformed message from $si:$sp\n");
+	exit;
+}
+...
+]]>
+	    </programlisting>
+	</example>
+	<para>
+		Optionally the function takes a second integer argument which
+		overwrites the global module parameter uri_checks and thus determines
+		which URIs will be checked if the parse uri test will be executed.
+	</para>
+	<example>
+		<title><function>sanity_check</function> usage with two parameters</title>
+		<programlisting>
+<![CDATA[
+...
+if (method=="INVITE" && sanity_check("1024", "6")) {
+	/* the INVITE contains an invalid From or To header and is replied with a 400 */
+	xlog("malformed message from $si:$sp\n");
+	exit;
+}
+...
+]]>
+		</programlisting>
+	</example>
+    </section>
+    </section>
+
+</chapter>
+

+ 221 - 0
modules_k/sanity/mod_sanity.c

@@ -0,0 +1,221 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ * 
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "mod_sanity.h"
+#include "sanity.h"
+#include "../../sr_module.h"
+#include "../../ut.h"
+#include "../../error.h"
+
+MODULE_VERSION
+
+#define PROXY_REQUIRE_DEF 	""
+
+str pr_str 	= {PROXY_REQUIRE_DEF, sizeof(PROXY_REQUIRE_DEF)-1 };
+
+int default_checks = SANITY_DEFAULT_CHECKS;
+int uri_checks = SANITY_DEFAULT_URI_CHECKS;
+strl* proxyrequire_list = NULL;
+
+struct sl_binds sl;
+
+static int mod_init(void);
+static int sanity_fixup(void** param, int param_no);
+static int sanity_check(struct sip_msg* _msg, char* _foo, char* _bar);
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"sanity_check", (cmd_function)sanity_check, 0, 0, 0, REQUEST_ROUTE},
+	{"sanity_check", (cmd_function)sanity_check, 1, sanity_fixup, 0,
+		REQUEST_ROUTE},
+	{"sanity_check", (cmd_function)sanity_check, 2, sanity_fixup, 0,
+		REQUEST_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"default_checks", 	INT_PARAM, 	&default_checks},
+	{"uri_checks",		INT_PARAM,  &uri_checks	},
+	{"proxy_require", 	STR_PARAM, 	&pr_str 	},
+	{0, 0, 0}
+};
+
+/*
+ * Module description
+ */
+struct module_exports exports = {
+	"sanity",        /* Module name */
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,            /* Exported functions */
+	params,          /* Exported parameters */
+	0,          /* exported statistics */
+	0,          /* exported MI functions */
+	0,          /* exported pseudo-variables */
+	0,          /* extra processes */
+	mod_init,        /* Initialization function */
+	0,               /* Response function */
+	0,               /* Destroy function */
+	0                /* Child init function */
+};
+
+/*
+ * initialize module
+ */
+static int mod_init(void) {
+	strl* ptr;
+
+	/*
+	 * We will need sl_send_reply from stateless
+	 * module for sending replies
+	 */
+	if (load_sl_api(&sl)!=0) {
+		LM_ERR("can't load SL API\n");
+		return -1;
+	}
+
+	LM_DBG("parsing proxy requires string:\n");
+	ptr = parse_str_list(&pr_str);
+
+	proxyrequire_list = ptr;
+
+	while (ptr != NULL) {
+		LM_DBG("string: '%.*s', next: %p\n", ptr->string.len,
+				ptr->string.s, ptr->next);
+		ptr = ptr->next;
+	}
+
+	return 0;
+}
+
+static int sanity_fixup(void** param, int param_no) {
+	int checks;
+	str in;
+
+	if (param_no == 1) {
+		in.s = (char*)*param;
+		in.len = strlen(in.s);
+		if (str2int(&in, (unsigned int*)&checks) < 0) {
+			LM_ERR("failed to convert input integer\n");
+			return E_UNSPEC;
+		}
+		if ((checks < 1) || (checks >= (SANITY_MAX_CHECKS))) {
+			LM_ERR("input parameter (%i) outside of valid range <1-%i)\n",
+					checks, SANITY_MAX_CHECKS);
+			return E_UNSPEC;
+		}
+		*param = (void*)(long)checks;
+	}
+	if (param_no == 2) {
+		in.s = (char*)*param;
+		in.len = strlen(in.s);
+		if (str2int(&in, (unsigned int*)&checks) < 0) {
+			LM_ERR("failed to convert second integer argument\n");
+			return E_UNSPEC;
+		}
+		if ((checks < 1) || (checks > (SANITY_DEFAULT_URI_CHECKS))) {
+			LM_ERR("second input parameter (%i) outside of valid range 1-%i\n",
+					checks, SANITY_DEFAULT_URI_CHECKS);
+			return E_UNSPEC;
+		}
+		*param = (void*)(long)checks;
+	}
+	return 0;
+}
+
+static int sanity_check(struct sip_msg* _msg, char* _number, char* _arg) {
+	int ret, check, arg;
+
+	if (_number == NULL) {
+		check = default_checks;
+	}
+	else {
+		check = (int)(long)_number;
+	}
+	if (_arg == NULL) {
+		arg = uri_checks;
+	}
+	else {
+		arg = (int)(long)_arg;
+	}
+
+	if (SANITY_RURI_SIP_VERSION & check &&
+		(ret = check_ruri_sip_version(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_RURI_SCHEME & check &&
+		(ret = check_ruri_scheme(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_REQUIRED_HEADERS & check &&
+		(ret = check_required_headers(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_VIA_SIP_VERSION & check &&
+		(ret = check_via_sip_version(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_VIA_PROTOCOL & check &&
+		(ret = check_via_protocol(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CSEQ_METHOD & check &&
+		(ret = check_cseq_method(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CSEQ_VALUE & check &&
+		(ret = check_cseq_value(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CL & check &&
+		(ret = check_cl(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_EXPIRES_VALUE & check &&
+		(ret = check_expires_value(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_PROXY_REQUIRE & check &&
+		(ret = check_proxy_require(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_PARSE_URIS & check &&
+		(ret = check_parse_uris(_msg, arg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+
+	if (SANITY_CHECK_DIGEST & check &&
+	        (ret = check_digest(_msg, arg)) != SANITY_CHECK_PASSED) {
+	        return ret;
+	}
+
+	LM_DBG("all sanity checks passed\n");
+	/* nobody complained so everything is fine */
+	return 1;
+}

+ 85 - 0
modules_k/sanity/mod_sanity.h

@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ * 
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef MOD_SANITY_CHK_H
+#define MOD_SANITY_CHK_H
+
+#include "../../str.h"
+#include "../sl/sl_api.h"
+#include "../../parser/msg_parser.h"
+
+#define SANITY_RURI_SIP_VERSION        (1<<0)
+#define SANITY_RURI_SCHEME             (1<<1)
+#define SANITY_REQUIRED_HEADERS        (1<<2)
+#define SANITY_VIA_SIP_VERSION         (1<<3)
+#define SANITY_VIA_PROTOCOL            (1<<4)
+#define SANITY_CSEQ_METHOD             (1<<5)
+#define SANITY_CSEQ_VALUE              (1<<6)
+#define SANITY_CL                      (1<<7)
+#define SANITY_EXPIRES_VALUE           (1<<8)
+#define SANITY_PROXY_REQUIRE           (1<<9)
+#define SANITY_PARSE_URIS              (1<<10)
+#define SANITY_CHECK_DIGEST            (1<<11)
+#define SANITY_MAX_CHECKS              (1<<12)  /* Make sure this is the highest value */
+
+/* VIA_SIP_VERSION and VIA_PROTOCOL do not work yet
+ * and PARSE_URIS is very expensive */
+#define SANITY_DEFAULT_CHECKS 	SANITY_RURI_SIP_VERSION | \
+								SANITY_RURI_SCHEME | \
+								SANITY_REQUIRED_HEADERS | \
+								SANITY_CSEQ_METHOD | \
+								SANITY_CSEQ_VALUE | \
+								SANITY_CL | \
+								SANITY_EXPIRES_VALUE | \
+								SANITY_PROXY_REQUIRE | \
+                                                                SANITY_CHECK_DIGEST
+
+
+#define SANITY_URI_CHECK_RURI    (1<<0)
+#define SANITY_URI_CHECK_FROM    (1<<1)
+#define SANITY_URI_CHECK_TO      (1<<2)
+#define SANITY_URI_CHECK_CONTACT (1<<3)
+
+#define SANITY_DEFAULT_URI_CHECKS	SANITY_URI_CHECK_RURI | \
+									SANITY_URI_CHECK_FROM | \
+									SANITY_URI_CHECK_TO
+
+#define SANITY_CHECK_PASSED 1
+#define SANITY_CHECK_FAILED 0
+#define SANITY_CHECK_ERROR -1
+
+struct _strlist {
+	str string;            /* the string */
+	struct _strlist* next; /* the next strlist element */
+};
+
+typedef struct _strlist strl;
+
+extern int default_checks;
+extern strl* proxyrequire_list;
+
+extern struct sl_binds sl;
+
+#endif /* MOD_SANITY_CHK_H */

+ 749 - 0
modules_k/sanity/sanity.c

@@ -0,0 +1,749 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ * 
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "mod_sanity.h"
+#include "sanity.h"
+#include "../../ut.h"
+#include "../../trim.h"
+#include "../../data_lump_rpl.h"
+#include "../../mem/mem.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_expires.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/digest/digest.h"
+#include "../../parser/contact/parse_contact.h"
+
+#define UNSUPPORTED_HEADER "Unsupported: "
+#define UNSUPPORTED_HEADER_LEN (sizeof(UNSUPPORTED_HEADER)-1)
+
+int sanity_reply(struct sip_msg* _msg, int _code, char *_reason)
+{
+	str s;
+	s.s = _reason;
+	s.len = strlen(s.s);
+	return sl.send_reply(_msg, _code, &s);
+}
+
+
+/* check if the given string is a valid unsigned int value */
+int str2valid_uint(str* _number, unsigned int* _result) {
+	int i;
+	int result= 0;
+	int equal = 1;
+	char mui[10] = "4294967296";
+
+	*_result = 0;
+	if (_number->len > 10) {
+		return -1;
+	}
+	if (_number->len < 10) {
+		equal = 0;
+	}
+	for (i=0; i < _number->len; i++) {
+		if (_number->s[i] < '0' || _number->s[i] > '9') {
+			return -1;
+		}
+		if (equal == 1) {
+			if (_number->s[i] < mui[i]) {
+				equal = 0;
+			}
+			else if (_number->s[i] > mui[i]) {
+				return -1;
+			}
+		}
+		result *= 10;
+		result += _number->s[i] - '0';
+	}
+	*_result = result;
+	return 0;
+}
+
+/* parses the given comma seperated string into a string list */
+strl* parse_str_list(str* _string) {
+	str input;
+	strl *parsed_list, *pl;
+	char *comma;
+
+	/* make a copy because we trim it */
+	input.s = _string->s;
+	input.len = _string->len;
+
+	trim(&input);
+
+	if (input.len == 0) {
+		return NULL;
+	}
+	parsed_list = pkg_malloc(sizeof(strl));
+	if (parsed_list == NULL) {
+		LM_ERR("OUT OF MEMORY for initial list element\n");
+		return NULL;
+	}
+	memset(parsed_list, 0, sizeof(strl));
+	parsed_list->string.s = input.s;
+	parsed_list->string.len = input.len;
+
+	comma = q_memchr(input.s, ',', input.len);
+	pl = parsed_list;
+	while (comma != NULL) {
+		pl->next = pkg_malloc(sizeof(strl));
+		if (pl->next == NULL) {
+			LM_ERR("parse_str_list: OUT OF MEMORY for further list element\n");
+			return parsed_list;
+		}
+		memset(pl->next, 0, sizeof(strl));
+		pl->next->string.s = comma + 1;
+		pl->next->string.len = pl->string.len - (pl->next->string.s - pl->string.s);
+		pl->string.len = comma - pl->string.s;
+		trim_trailing(&(pl->string));
+		pl = pl->next;
+		trim_leading(&(pl->string));
+		comma = q_memchr(pl->string.s, ',', pl->string.len);
+	}
+
+	return parsed_list;
+}
+
+/* free the elements of the linked str list */
+void free_str_list(strl *_list) {
+	strl *cur, *next;
+
+	if (_list != NULL) {
+		cur = _list;
+		while (cur != NULL) {
+			next = cur->next;
+			pkg_free(cur);
+			cur = next;
+		}
+	}
+}
+
+int parse_proxyrequire(struct hdr_field* _h) {
+	strl *pr_l;
+
+	if (_h->parsed) {
+		return 0; /* Already parsed */
+	}
+
+	if ((pr_l = parse_str_list(&(_h->body))) == NULL) {
+		LM_ERR("Error while parsing\n");
+		return -1;
+	}
+
+	_h->parsed = pr_l;
+	return 0;
+}
+
+/* check the SIP version in the request URI */
+int check_ruri_sip_version(struct sip_msg* _msg) {
+	char *sep;
+	str version;
+
+	if (_msg->first_line.u.request.version.len != 0) {
+		sep = q_memchr(_msg->first_line.u.request.version.s, '/',
+						_msg->first_line.u.request.version.len);
+		if (sep == NULL) {
+			LM_WARN("check_ruri_sip_version(): failed to find / in ruri version\n");
+			return SANITY_CHECK_FAILED;
+		}
+		version.s = sep + 1;
+		version.len = _msg->first_line.u.request.version.len - (version.s - _msg->first_line.u.request.version.s);
+
+		if (version.len != SIP_VERSION_TWO_POINT_ZERO_LENGTH ||
+			(memcmp(version.s, SIP_VERSION_TWO_POINT_ZERO, 
+				SIP_VERSION_TWO_POINT_ZERO_LENGTH) != 0)) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 505, "Version Not Supported (R-URI)") == -1) {
+					LM_WARN("check_ruri_sip_version(): failed to send 505 via send_reply\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+	}
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the r-uri scheme */
+int check_ruri_scheme(struct sip_msg* _msg) {
+
+	if (_msg->parsed_uri_ok == 0 &&
+			parse_sip_msg_uri(_msg) != 1) {
+		/* unsupported schemes end up here already */
+		LM_WARN("failed to parse request uri\n");
+	}
+	if (_msg->parsed_uri.type == ERROR_URI_T) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sanity_reply(_msg, 416, "Unsupported URI Scheme in Request URI") == -1) {
+				LM_WARN("failed to send 416 via send_reply\n");
+			}
+		}
+		LM_DBG("check_ruri_scheme failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check for the presence of the minimal required headers */
+int check_required_headers(struct sip_msg* _msg) {
+
+	if (!check_transaction_quadruple(_msg)) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sanity_reply(_msg, 400, "Missing Required Header in Request") == -1) {
+				LM_WARN("failed to send 400 via send_reply\n");
+			}
+		}
+		LM_DBG("check_required_headers failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the SIP version in the Via header is 2.0 */
+int check_via_sip_version(struct sip_msg* _msg) {
+
+	LM_DBG("this is a useless check for now; check the source code comments for details\n");
+	return SANITY_CHECK_PASSED;
+
+	/* FIMXE the Via parser fails already on non-2.0 versions
+	 * thus this check makes no sence yet
+	DBG("check_via_sip_version entered\n");
+
+	// FIXME via parser fails on non 2.0 number
+	if (parse_headers(_msg, HDR_VIA1_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_via_sip_version(): failed to parse the first Via header\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	if (_msg->via1->version.len != 3 ||
+			memcmp(_msg->via1->version.s, SIP_VERSION_TWO_POINT_ZERO, 
+					SIP_VERSION_TWO_POINT_ZERO_LENGTH ) != 0) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 505, "Version Not Supported (Via)") == -1) {
+				LOG(L_WARN, "sanity_check(): check_via_sip_version(): failed to send 505 via send_reply\n");
+			}
+		}
+		DBG("check_via_sip_version failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_via_sip_version passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+	*/
+}
+
+/* compare the protocol string in the Via header with the transport */
+int check_via_protocol(struct sip_msg* _msg) {
+
+	LM_DBG("this is a useless check for now; check the source code comment for details\n");
+	return SANITY_CHECK_PASSED;
+
+	/* FIXME as the Via parser fails already on unknown transports
+	 * this function makes no sence yet
+	DBG("check_via_protocol entered\n");
+
+	// FIXME via parser fails on unknown transport
+	if (parse_headers(_msg, HDR_VIA1_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to parse the first Via header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->via1->transport.len != 3 &&
+			_msg->via1->transport.len != 4) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 400, "Unsupported Transport in Topmost Via") == -1) {
+				LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 400 via send_reply\n");
+			}
+		}
+		DBG("check_via_protocol failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+	switch (_msg->rcv.proto) {
+		case PROTO_UDP:
+			if (memcmp(_msg->via1->transport.s, "UDP", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_TCP:
+			if (memcmp(_msg->via1->transport.s, "TCP", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_TLS:
+			if (memcmp(_msg->via1->transport.s, "TLS", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_SCTP:
+			if (memcmp(_msg->via1->transport.s, "SCTP", 4) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		default:
+			LOG(L_WARN, "sanity_check(): check_via_protocol(): unknown protocol in received structure\n");
+			return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_via_protocol passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+	*/
+}
+
+/* compare the method in the CSeq header with the request line value */
+int check_cseq_method(struct sip_msg* _msg) {
+
+	if (parse_headers(_msg, HDR_CSEQ_F, 0) != 0) {
+		LM_WARN("failed to parse the CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->cseq != NULL && _msg->cseq->parsed != NULL) {
+		if (((struct cseq_body*)_msg->cseq->parsed)->method.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Missing method in CSeq header") == -1) {
+					LM_WARN("failed to send 400 via send_reply\n");
+				}
+			}
+			LM_DBG("check_cseq_method failed (missing method)\n");
+			return SANITY_CHECK_FAILED;
+		}
+
+		if (((struct cseq_body*)_msg->cseq->parsed)->method.len != 
+					_msg->first_line.u.request.method.len ||
+			memcmp(((struct cseq_body*)_msg->cseq->parsed)->method.s, 
+				_msg->first_line.u.request.method.s,
+				((struct cseq_body*)_msg->cseq->parsed)->method.len) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "CSeq method does not match request method") == -1) {
+					LM_WARN("failed to send 400 via send_reply 2\n");
+				}
+			}
+			LM_DBG("check_cseq_method failed (non-equal method)\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+	else {
+		LM_WARN("missing CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the number within the CSeq header */
+int check_cseq_value(struct sip_msg* _msg) {
+	unsigned int cseq;
+
+	if (parse_headers(_msg, HDR_CSEQ_F, 0) != 0) {
+		LM_WARN("failed to parse the CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->cseq != NULL && _msg->cseq->parsed != NULL) {
+		if (((struct cseq_body*)_msg->cseq->parsed)->number.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Missing number in CSeq header") == -1) {
+					LM_WARN("failed to send 400 via send_reply\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (str2valid_uint(&((struct cseq_body*)_msg->cseq->parsed)->number, &cseq) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "CSeq number is illegal") == -1) {
+					LM_WARN("failed to send 400 via send_reply 2\n");
+				}
+			}
+			LM_DBG("check_cseq_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+	else {
+		LM_WARN("missing CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* compare the Content-Length value with the accutal body length */
+int check_cl(struct sip_msg* _msg) {
+	char *body;
+
+	if (parse_headers(_msg, HDR_CONTENTLENGTH_F, 0) != 0) {
+		LM_WARN("failed to parse content-length header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->content_length != NULL) {
+		//dump_hdr_field(_msg->content_length);
+		if ((body = get_body(_msg)) == NULL) {
+			return SANITY_CHECK_FAILED;
+		}
+		if ((_msg->len - (body - _msg->buf)) != get_content_length(_msg)) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Content-Length mis-match") == -1) {
+					LM_WARN("failed to send 400 via send_reply\n");
+				}
+			}
+			LM_DBG("check_cl failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the number within the Expires header */
+int check_expires_value(struct sip_msg* _msg) {
+	unsigned int expires;
+
+	if (parse_headers(_msg, HDR_EXPIRES_F, 0) != 0) {
+		LM_WARN("failed to parse expires header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->expires != NULL) {
+		//dump_hdr_field(_msg->expires);
+		if (_msg->expires->parsed == NULL &&
+				parse_expires(_msg->expires) < 0) {
+			LM_WARN("parse_expires failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		if (((struct exp_body*)_msg->expires->parsed)->text.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Missing number in Expires header") == -1) {
+					LM_WARN("failed to send 400 via send_reply\n");
+				}
+			}
+			LM_DBG("check_expires_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		if (str2valid_uint(&((struct exp_body*)_msg->expires->parsed)->text, &expires) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Expires value is illegal") == -1) {
+					LM_WARN("failed to send 400 via send_reply 2\n");
+				}
+			}
+			LM_DBG("check_expires_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the content of the Proxy-Require header */
+int check_proxy_require(struct sip_msg* _msg) {
+	strl *r_pr, *l_pr;
+	char *u;
+	int u_len;
+
+	if (parse_headers(_msg, HDR_PROXYREQUIRE_F, 0) != 0) {
+		LM_WARN("failed to parse proxy require header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->proxy_require != NULL) {
+		dump_hdr_field(_msg->proxy_require);
+		if (_msg->proxy_require->parsed == NULL &&
+				parse_proxyrequire(_msg->proxy_require) < 0) {
+			LM_WARN("parse_proxy_require failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		r_pr = _msg->proxy_require->parsed;
+		while (r_pr != NULL) {
+			l_pr = proxyrequire_list;
+			while (l_pr != NULL) {
+				if (l_pr->string.len == r_pr->string.len &&
+						/* FIXME tokens are case in-sensitive */
+						memcmp(l_pr->string.s, r_pr->string.s, l_pr->string.len) == 0) {
+					break;
+				}
+				l_pr = l_pr->next;
+			}
+			if (l_pr == NULL) {
+				LM_DBG("request contains unsupported extension: %.*s\n", r_pr->string.len, r_pr->string.s);
+				u_len = UNSUPPORTED_HEADER_LEN + 2 + r_pr->string.len;
+				u = pkg_malloc(u_len);
+				if (u == NULL) {
+					LM_ERR("failed to allocate memory for Unsupported header\n");
+				}
+				else {
+					memcpy(u, UNSUPPORTED_HEADER, UNSUPPORTED_HEADER_LEN);
+					memcpy(u + UNSUPPORTED_HEADER_LEN, r_pr->string.s, r_pr->string.len);
+					memcpy(u + UNSUPPORTED_HEADER_LEN + r_pr->string.len, CRLF, CRLF_LEN);
+					add_lump_rpl(_msg, u, u_len, LUMP_RPL_HDR);
+				}
+
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sanity_reply(_msg, 420, "Bad Extension") == -1) {
+						LM_WARN("failed to send 420 via send_reply\n");
+					}
+				}
+				if (u) pkg_free(u);
+				return SANITY_CHECK_FAILED;
+			}
+			else {
+				r_pr = r_pr->next;
+			}
+		}
+		if (_msg->proxy_require->parsed) {
+			/* TODO we have to free it here, because it is not automatically
+			 * freed when the message freed. Lets hope nobody needs to access
+			 * this header again later on */
+			free_str_list(_msg->proxy_require->parsed);
+		}
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the typical URI's are parseable */
+int check_parse_uris(struct sip_msg* _msg, int checks) {
+
+	struct to_body *ft_body = NULL;
+	struct sip_uri uri;
+
+	/* check R-URI */
+	if (SANITY_URI_CHECK_RURI & checks) {
+		if (_msg->parsed_uri_ok == 0 &&
+				parse_sip_msg_uri(_msg) != 1) {
+			LM_WARN("failed to parse request uri\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Bad Request URI") == -1) {
+					LM_WARN("failed to send 400 via send_reply (bad ruri)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		/* FIXME: would it make sense to check here for "mandatory"
+		 * or "requested" parts of the URI? */
+	}
+	/* check From URI */
+	if (SANITY_URI_CHECK_FROM & checks) {
+		if ((!_msg->from && parse_headers(_msg, HDR_FROM_F, 0) != 0) || !_msg->from) {
+			LM_WARN("missing from header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Missing From Header") == -1) {
+					LM_WARN("failed to send 400 via send_reply (missing From)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (!_msg->from->parsed) {
+			ft_body = pkg_malloc(sizeof(struct to_body));
+			if (!ft_body) {
+				LM_ERR("out of pkg_memory (From)\n");
+				return SANITY_CHECK_ERROR;
+			}
+			memset(ft_body, 0, sizeof(struct to_body));
+			parse_to(_msg->from->body.s, _msg->from->body.s + \
+					_msg->from->body.len + 1, ft_body);
+			if (ft_body->error == PARSE_ERROR) {
+				LM_WARN("failed to parse From header\n");
+				pkg_free(ft_body);
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sanity_reply(_msg, 400, "Bad From header") == -1) {
+						LM_WARN("failed to send 400 via send_reply (bad from header)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			_msg->from->parsed = ft_body;
+			ft_body = NULL;
+		}
+		if (((struct to_body*)_msg->from->parsed)->uri.s) {
+			if (parse_uri(((struct to_body*)_msg->from->parsed)->uri.s, 
+					((struct to_body*)_msg->from->parsed)->uri.len, &uri) != 0) {
+			    LM_WARN("failed to parse From uri\n");
+			    if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Bad From URI") == -1) {
+				    LM_WARN("failed to send 400 via send_reply (bad from uri)\n");
+				}
+			    }
+			    return SANITY_CHECK_FAILED;
+			}
+			/* FIXME: we should store this parsed struct somewhere so that
+			 * it could be re-used */
+			/* FIXME 2: would it make sense to check here for "mandatory"
+			 * or "requested" parts of the URI? */
+		}
+	}
+	/* check To URI */
+	if (SANITY_URI_CHECK_TO & checks) {
+		if ((!_msg->to && parse_headers(_msg, HDR_TO_F, 0) != 0) || !_msg->to) {
+			LM_WARN("missing to header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Missing To Header") == -1) {
+					LM_WARN("failed to send 400 via send_reply (missing To)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		/* parse_to is automatically called for HDR_TO_F */
+		if (!_msg->to->parsed) {
+			LM_WARN("failed to parse To header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sanity_reply(_msg, 400, "Bad To URI") == -1) {
+					LM_WARN("failed to send 400 via send_reply (bad to uri)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (((struct to_body*)_msg->to->parsed)->uri.s) {
+			if (parse_uri(((struct to_body*)_msg->to->parsed)->uri.s, 
+					((struct to_body*)_msg->to->parsed)->uri.len, &uri) != 0) {
+				LM_WARN("failed to parse To uri\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sanity_reply(_msg, 400, "Bad To URI") == -1) {
+						LM_WARN("failed to send 400 via send_reply (bad to uri)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			/* FIXME: we should store this parsed struct somewhere so that
+			 * it could be re-used */
+			/* FIXME 2: would it make sense to check here for "mandatory"
+			 * or "requested" parts of the URI? */
+		}
+	}
+	/* check Contact URI */
+	if (SANITY_URI_CHECK_CONTACT & checks) {
+		if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0) || !_msg->contact) {
+			LM_WARN("missing contact header\n");
+		}
+		if (_msg->contact) {
+			if (parse_contact(_msg->contact) < 0) {
+				LM_WARN("check_parse_uris(): failed to parse Contact header\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sanity_reply(_msg, 400, "Bad Contact Header") == -1) {
+						LM_WARN("failed to send 400 via send_reply (bad Contact)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			if (parse_uri(((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
+					((struct contact_body*)_msg->contact->parsed)->contacts->uri.len, &uri) != 0) {
+				LM_WARN("failed to parse Contact uri\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sanity_reply(_msg, 400, "Bad Contact URI") == -1) {
+						LM_WARN("failed to send 400 via send_reply (bad Contact uri)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+		}
+	}
+
+	return SANITY_CHECK_PASSED;
+}
+
+
+/* Make sure that username attribute in all digest credentials
+ * instances has a meaningful value
+ */
+int check_digest(struct sip_msg* msg, int checks)
+{
+    struct hdr_field* ptr;
+    dig_cred_t* cred;
+    int ret;
+    int hf_type;
+
+    if (parse_headers(msg, HDR_EOH_F, 0) != 0) {
+		LM_ERR("failed to parse proxy require header\n");
+		return SANITY_CHECK_FAILED;
+    }
+
+    if (!msg->authorization && !msg->proxy_auth) {
+		return SANITY_CHECK_PASSED;
+    }
+
+    if (msg->authorization) {
+	hf_type = HDR_AUTHORIZATION_T;
+	ptr = msg->authorization;
+    } else {
+	hf_type = HDR_PROXYAUTH_T;
+	ptr = msg->proxy_auth;
+    }
+    while(ptr) {
+	if ((ret = parse_credentials(ptr)) != 0) {
+	    LM_DBG("Cannot parse credentials: %d\n", ret);
+	    return SANITY_CHECK_FAILED;
+	}
+
+	cred = &((auth_body_t*)ptr->parsed)->digest;
+
+	if (check_dig_cred(cred) != E_DIG_OK) {
+	    return SANITY_CHECK_FAILED;
+	}
+
+	if (cred->username.whole.len == 0) {
+	    return SANITY_CHECK_FAILED;
+	}
+	
+	if (cred->nonce.len == 0) {
+	    return SANITY_CHECK_FAILED;
+	}
+
+	if (cred->response.len == 0) {
+	    return SANITY_CHECK_FAILED;
+	}
+
+	do {
+	    ptr = ptr->next;
+	} while(ptr && ptr->type != hf_type);
+
+	if (!ptr && hf_type == HDR_AUTHORIZATION_T) {
+	    hf_type = HDR_PROXYAUTH_T;
+	    ptr = msg->proxy_auth;
+	}
+    }
+
+    return SANITY_CHECK_PASSED;
+}

+ 79 - 0
modules_k/sanity/sanity.h

@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ * 
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef SANITY_CHK_H
+#define SANITY_CHK_H
+
+#include "mod_sanity.h"
+
+#define SIP_VERSION_TWO_POINT_ZERO "2.0"
+#define SIP_VERSION_TWO_POINT_ZERO_LENGTH 3
+
+/* check if the given string is a valid unsigned int value
+ * and converts it into _result. returns -1 on error and 0 on success*/
+int str2valid_uint(str* _number, unsigned int* _result);
+
+/* parses the given comma seperated string into a string list */
+strl* parse_str_list(str* _string);
+
+/* compare the protocol string in the Via header with the transport */
+int check_via_protocol(struct sip_msg* _msg);
+
+/* check if the SIP version in the Via header is 2.0 */
+int check_via_sip_version(struct sip_msg* _msg);
+
+/* compare the Content-Length value with the accutal body length */
+int check_cl(struct sip_msg* _msg);
+
+/* compare the method in the CSeq header with the request line value */
+int check_cseq_method(struct sip_msg* _msg);
+
+/* check the number within the CSeq header */
+int check_cseq_value(struct sip_msg* _msg);
+
+/* check the number within the Expires header */
+int check_expires_value(struct sip_msg* _msg);
+
+/* check for the presence of the minimal required headers */
+int check_required_headers(struct sip_msg* _msg);
+
+/* check the content of the Proxy-Require header */
+int check_proxy_require(struct sip_msg* _msg);
+
+/* check the SIP version in the request URI */
+int check_ruri_sip_version(struct sip_msg* _msg);
+
+/* check if the r-uri scheme */
+int check_ruri_scheme(struct sip_msg* _msg);
+
+/* check if the typical URIs are parseable */
+int check_parse_uris(struct sip_msg* _msg, int checks);
+
+/* Make sure that username attribute in all digest credentials
+ * instances has a meaningful value
+ */
+int check_digest(struct sip_msg* _msg, int checks);
+
+#endif /* SANITY_CHK_H */

+ 29 - 4
modules_k/userblacklist/userblacklist.c

@@ -82,6 +82,10 @@ static int check_user_blacklist_fixup(void** param, int param_no);
 /* ---- exported commands: */
 static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
 static int check_user_whitelist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
+static int check_user_blacklist2(struct sip_msg *msg, char* str1, char* str2);
+static int check_user_whitelist2(struct sip_msg *msg, char* str1, char* str2);
+static int check_user_blacklist3(struct sip_msg *msg, char* str1, char* str2, char* str3);
+static int check_user_whitelist3(struct sip_msg *msg, char* str1, char* str2, char* str3);
 static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
 
 /* ---- module init functions: */
@@ -95,10 +99,10 @@ struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param);  /* usag
 
 
 static cmd_export_t cmds[]={
-	{ "check_user_blacklist", (cmd_function)check_user_blacklist, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{ "check_user_whitelist", (cmd_function)check_user_whitelist, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{ "check_user_blacklist", (cmd_function)check_user_blacklist, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
-	{ "check_user_whitelist", (cmd_function)check_user_whitelist, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{ "check_user_blacklist", (cmd_function)check_user_blacklist2, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{ "check_user_whitelist", (cmd_function)check_user_whitelist2, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{ "check_user_blacklist", (cmd_function)check_user_blacklist3, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{ "check_user_whitelist", (cmd_function)check_user_whitelist3, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_user_blacklist", (cmd_function)check_user_blacklist, 4, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_user_whitelist", (cmd_function)check_user_whitelist, 4, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_blacklist", (cmd_function)check_blacklist, 1, check_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
@@ -313,6 +317,27 @@ static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, cha
 	return check_user_list(msg, str1, str2, str3, str4, 0);
 }
 
+static int check_user_whitelist2(struct sip_msg *msg, char* str1, char* str2)
+{
+	return check_user_list(msg, str1, str2, NULL, NULL, 1);
+}
+
+
+static int check_user_blacklist2(struct sip_msg *msg, char* str1, char* str2)
+{
+	return check_user_list(msg, str1, str2, NULL, NULL, 0);
+}
+
+static int check_user_whitelist3(struct sip_msg *msg, char* str1, char* str2, char* str3)
+{
+	return check_user_list(msg, str1, str2, str3, NULL, 1);
+}
+
+
+static int check_user_blacklist3(struct sip_msg *msg, char* str1, char* str2, char* str3)
+{
+	return check_user_list(msg, str1, str2, str3, NULL, 0);
+}
 
 
 /**

+ 2 - 0
modules_k/xcap_client/Makefile

@@ -18,4 +18,6 @@ DEFS+=-DOPENSER_MOD_INTERFACE
 
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+
 include ../../Makefile.modules

+ 1 - 1
modules_s/domain/domain_mod.c

@@ -440,7 +440,7 @@ static int lookup_domain(struct sip_msg* msg, char* flags, char* fp)
 	track = 0;
 
 	if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 0) {
-		ERR("Cannot get domain name to lookup\n");
+		DBG("lookup_domain: Cannot get the domain name to lookup\n");
 		return -1;
 	}
 

+ 21 - 17
modules_s/registrar/save.c

@@ -257,10 +257,11 @@ static int create_rcv_uri(str** uri, struct sip_msg* m)
 
 
 /*
- * Message contained some contacts, but record with same address
- * of record was not found so we have to create a new record
- * and insert all contacts from the message that have expires
- * > 0
+ * Message contained some contacts, but record with same address of record was
+ * not found so we have to create a new record and insert all contacts from
+ * the message that have expires > 0. The function returns a negative number
+ * on error, a positive number if the number of contacts would exceed
+ * max_contacts and 0 on success.
  */
 static inline int insert(struct sip_msg* _m, str* aor, contact_t* _c, udomain_t* _d, str* _u, str *ua, str* aor_filter, int sid)
 {
@@ -289,7 +290,7 @@ static inline int insert(struct sip_msg* _m, str* aor, contact_t* _c, udomain_t*
 		if (max_contacts && (num >= max_contacts)) {
 			rerrno = R_TOO_MANY;
 			ul.delete_urecord(_d, _u);
-			return -1;
+			return 1;
 		}
 		num++;
 		
@@ -394,7 +395,7 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c)
 		_c = get_next_contact(_c);
 	}
 	
-	DBG("test_max_contacts: %d contacts after commit\n", num);
+	DBG("test_max_contacts: %d contacts after commit, max_contacts=%d\n", num, max_contacts);
 	if (num > max_contacts) {
 		rerrno = R_TOO_MANY;
 		return 1;
@@ -414,6 +415,11 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c)
  *    > 0, update the contact
  * 3) If contact in usrloc exists and expires
  *    == 0, delete contact
+ *
+ * The function returns a negative number on error, a positive number if
+ * max_contacts is set and the number of contacts after the change would
+ * exceed that maximum number of allowed contacts. On success the function
+ * returns 0.
  */
 static inline int update(struct sip_msg* _m, urecord_t* _r, str* aor, contact_t* _c, str* _ua, str* aor_filter, int sid)
 {
@@ -434,8 +440,12 @@ static inline int update(struct sip_msg* _m, urecord_t* _r, str* aor, contact_t*
 	if (max_contacts) {
 		ret = test_max_contacts(_m, _r, _c);
 		if (ret != 0) {
+			/* test_max_contacts returns a negative number on error and a
+			 * positive number if the number of contacts after the update
+			 * exceeds the configured max_contacts. In both cases we return
+			 * here. */
 			build_contact(_r->contacts, aor_filter);
-			return -1;
+			return ret;
 		}
 	}
 
@@ -607,24 +617,18 @@ static inline int contacts(struct sip_msg* _m, contact_t* _c, udomain_t* _d, str
 	}
 
 	if (res == 0) { /* Contacts found */
-		if (update(_m, r, aor, _c, _ua, aor_filter, sid) < 0) {
+		if ((res = update(_m, r, aor, _c, _ua, aor_filter, sid) < 0)) {
 			LOG(L_ERR, "contacts(): Error while updating record\n");
-			build_contact(r->contacts, aor_filter);
-			ul.release_urecord(r);
-			ul.unlock_udomain(_d);
-			return -3;
 		}
 		build_contact(r->contacts, aor_filter);
 		ul.release_urecord(r);
 	} else {
-		if (insert(_m, aor, _c, _d, _u, _ua, aor_filter, sid) < 0) {
+		if ((res = insert(_m, aor, _c, _d, _u, _ua, aor_filter, sid) < 0)) {
 			LOG(L_ERR, "contacts(): Error while inserting record\n");
-			ul.unlock_udomain(_d);
-			return -4;
 		}
 	}
 	ul.unlock_udomain(_d);
-	return 0;
+	return res;
 }
 
 #define UA_DUMMY_STR "Unknown"
@@ -684,7 +688,7 @@ static inline int save_real(struct sip_msg* _m, udomain_t* _t, char* aor_filt, i
 			if (no_contacts(_t, &uid, &aor_filter) < 0) goto error;
 		}
 	} else {
-		if (contacts(_m, c, _t, &uid, &ua, &aor_filter) < 0) goto error;
+		if (contacts(_m, c, _t, &uid, &ua, &aor_filter) != 0) goto error;
 	}
 
 	if (doreply) {

+ 16 - 0
modules_s/sanity/Makefile

@@ -0,0 +1,16 @@
+# $Id$
+#
+# sanity check module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=sanity.so
+LIBS=
+
+DEFS+=-DSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 158 - 0
modules_s/sanity/README

@@ -0,0 +1,158 @@
+1. Sanity Module
+
+Nils Ohlmeier
+
+   iptelorg GmbH
+
+   Copyright © 2006 iptelorg GmbH
+   Revision History
+   Revision $Revision$ $Date$
+     __________________________________________________________________
+
+   1.1. Overview
+   1.2. Dependencies
+   1.3. Parameters
+
+        1.3.1. default_checks (integer)
+        1.3.2. uri_checks (integer)
+        1.3.3. proxy_require (string)
+
+   1.4. Functions
+
+        1.4.1. sanity_check()
+
+1.1. Overview
+
+   This module aims to implement several sanity checks on incoming
+   requests which are suggested or even required by a RFC, but are not
+   available yet in the core of SER.
+
+   This checks are not required by SER itself for its functionality. But
+   on the other side it makes not much sence if a broken request traverses
+   through a SIP network if it is rejected sooner or later by a SIP device
+   any way. As every sanity cost extra performance because of additional
+   parsing and evaluation it is now with this module up to the SER
+   adminstrator which checks should be done on which request.
+
+   The following checks are available:
+     * ruri sip version - (1) - checks if the SIP version in the request
+       URI is supported, currently only 2.0.
+     * ruri scheme - (2) - checks if the URI scheme of the request URI is
+       supported (sip[s]|tel[s]) by SER.
+     * required headers - (4) -checks if the minimum set of required
+       headers to, from, cseq, callid and via is present in the request.
+     * via sip version - (8) - not working because parser fails already
+       when another version then 2.0 is present.
+     * via protocol - (16) - not working because parser fails already if
+       an unsupported transport is present.
+     * cseq method - (32) - checks if the method from the cseq header is
+       equal to the request method.
+     * cseq value - (64) - checks if the number in the cseq header is a
+       valid unsigend integer.
+     * content length - (128) - checks if the size of the body matches
+       with the value from the content length header.
+     * expires value - (256) - checks if the value of the expires header
+       is a valid unsigned integer.
+     * proxy require - (512) - checks if all items of the proxy require
+       header are present in the list of the extensions from the module
+       parameter proxy_require.
+     * parse uri's - (1024) - checks if the specified URIs are present and
+       parseable by the SER parsers
+     * digest credentials (2048) Check all instances of digest credentials
+       in a message. The test checks whether there are all required digest
+       parameters and have meaningful values.
+
+1.2. Dependencies
+
+   The following modules must be loaded before this module:
+     * sl - Stateless replies.
+
+1.3. Parameters
+
+   Revision History
+   Revision $Revision$ $Date$
+
+1.3.1. default_checks (integer)
+
+   This parameter determines which of the checks from the sanity module
+   are executed if no parameter was given to the sanity_check function
+   call. By default all implemented checks are included in the execution
+   of the sanity_check function. The integer value is the sum of the check
+   numbers which should be executed by default.
+
+   Default value is 999. This resolves to the following list of checks:
+   ruri_sip_version (1), ruri_scheme (2), required_headers (4),
+   cseq_method (32), cseq_value (64), cotent_length (128), expires_value
+   (256), proxy_require (512).
+
+   Example 1. Set default_checks parameter
+...
+modparam("sanity", "default_checks", "1")
+...
+
+1.3.2. uri_checks (integer)
+
+   This parameter determines which URIs are going to be checked if the
+   'parse uri' will be executed.
+
+   Default value is 7. This resolves to the following list of parsed URIs:
+   Request RUI (1), From URI (2) and To URI (4).
+
+1.3.3. proxy_require (string)
+
+   This parameter set the list of supported extensions for this SER. The
+   value is expected as comma seperated list of the extensions. This list
+   is seperated into single tokens. Each token from a proxy require header
+   will be compare to the tokens from this list.
+
+   Example 2. Set proxy_require parameter
+...
+modparam("sanity", "proxy_require", "foo, bar")
+...
+
+1.4. Functions
+
+   Revision History
+   Revision $Revision$ $Date$
+
+1.4.1.  sanity_check()
+
+   This function makes a row of sanity checks on the given request. The
+   function returns true if one of the checks failed. If one of the checks
+   fails the module sends a precise error reply via sl_send_reply. Thus
+   their is no need to reply with a generic error message.
+
+   Example 3. sanity_check usage
+...
+if (sanity_check()) {
+        break;
+}
+...
+
+   Optionally the function takes an integer argument which overwrites the
+   global module parameter default_checks. This allows to make certain
+   test from script regions. The integer value is again the sum of the
+   checks (like for the module parameter) which should be executed at this
+   function call.
+
+   Example 4. sanity_check usage with parameter
+...
+if (method=="REGISTER" && sanity_check("256")) {
+        /* the register contains an invalid expires value and is replied with a
+400 */
+        break;
+}
+...
+
+   Optionally the function takes a second integer argument which
+   overwrites the global module parameter uri_checks and thus determines
+   which URIs will be checked if the parse uri test will be executed.
+
+   Example 5. sanity_check usage with two parameters
+...
+if (method=="INVITE" && sanity_check("1024", "6")) {
+        /* the INVITE contains an invalid From or To header and is replied with
+a 400 */
+        break;
+}
+...

+ 4 - 0
modules_s/sanity/doc/Makefile

@@ -0,0 +1,4 @@
+docs = sanity.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module

+ 79 - 0
modules_s/sanity/doc/functions.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="options.functions" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>Functions</title>
+
+    <section id="sanity_check">
+	    <title>
+		<function>sanity_check()</function>
+	    </title>
+	<para>
+		This function makes a row of sanity checks on the given request.
+		The function returns true if one of the checks failed. If one
+		of the checks fails the module sends a precise error reply via 
+		sl_send_reply. Thus their is no need to reply with a generic 
+		error message.
+	</para>
+	<example>
+	    <title><function>sanity_check</function> usage</title>
+	    <programlisting>
+<![CDATA[
+...
+if (sanity_check()) {
+	break;
+}
+...
+]]>
+	    </programlisting>
+	</example>
+	<para>
+		Optionally the function takes an integer argument which overwrites
+		the global module parameter default_checks. This allows to make
+		certain test from script regions. The integer value is again the sum
+		of the checks (like for the module parameter) which should be executed
+		at this function call.
+	</para>
+	<example>
+	    <title><function>sanity_check</function> usage with parameter</title>
+	    <programlisting>
+<![CDATA[
+...
+if (method=="REGISTER" && sanity_check("256")) {
+	/* the register contains an invalid expires value and is replied with a 400 */
+	break;
+}
+...
+]]>
+	    </programlisting>
+	</example>
+	<para>
+		Optionally the function takes a second integer argument which
+		overwrites the global module parameter uri_checks and thus determines
+		which URIs will be checked if the parse uri test will be executed.
+	</para>
+	<example>
+		<title><function>sanity_check</function> usage with two parameters</title>
+		<programlisting>
+<![CDATA[
+...
+if (method=="INVITE" && sanity_check("1024", "6")) {
+	/* the INVITE contains an invalid From or To header and is replied with a 400 */
+	break;
+}
+...
+]]>
+		</programlisting>
+	</example>
+    </section>
+</section>

+ 74 - 0
modules_s/sanity/doc/params.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="sanity.parameters" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>Parameters</title>
+
+    <section id="default_checks">
+	<title><varname>default_checks</varname> (integer)</title>
+	<para>
+		This parameter determines which of the checks from the sanity
+		module are executed if no parameter was given to the sanity_check
+		function call. By default all implemented checks are included
+		in the execution of the sanity_check function. The integer value
+		is the sum of the check numbers which should be executed by default.
+	</para>
+	<para>
+	    Default value is 999. This resolves to the following list of
+		checks: ruri_sip_version (1), ruri_scheme (2), required_headers (4),
+		cseq_method (32), cseq_value (64), cotent_length (128), 
+		expires_value (256), proxy_require (512).
+	</para>
+	<example>
+	    <title>Set <varname>default_checks</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sanity", "default_checks", "1")
+...
+	    </programlisting>
+	</example>
+    </section>
+
+	<section id="uri_checks">
+	<title><varname>uri_checks</varname> (integer)</title>
+	<para>
+		This parameter determines which URIs are going to be checked
+		if the 'parse uri' will be executed.
+	</para>
+	<para>
+		Default value is 7. This resolves to the following list of
+		parsed URIs: Request RUI (1), From URI (2) and To URI (4).
+	</para>
+	</section>
+
+	<section id="proxy_require">
+	<title><varname>proxy_require</varname> (string)</title>
+	<para>
+		This parameter set the list of supported extensions for this SER.
+		The value is expected as comma seperated list of the extensions.
+		This list is seperated into single tokens. Each token from
+		a proxy require header will be compare to the tokens from this
+		list.
+	</para>
+	<example>
+	    <title>Set <varname>proxy_require</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sanity", "proxy_require", "foo, bar")
+...
+	    </programlisting>
+	</example>
+	</section>
+
+
+</section>

+ 144 - 0
modules_s/sanity/doc/sanity.xml

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="options" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<authorgroup>
+	    <author>
+		<firstname>Nils</firstname>
+		<surname>Ohlmeier</surname>
+		<affiliation><orgname>iptelorg GmbH</orgname></affiliation>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </author>
+	</authorgroup>
+	<copyright>
+	    <year>2006</year>
+	    <holder>iptelorg GmbH</holder>
+	</copyright>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>Sanity Module</title>
+
+    <section id="sanity.overview">
+	<title>Overview</title>
+	<para>
+		This module aims to implement several sanity checks on incoming
+		requests which are suggested or even required by a RFC, but are
+		not available yet in the core of SER.
+	</para>
+	<para>
+		This checks are not required by SER itself for its functionality.
+		But on the other side it makes not much sence if a broken
+		request traverses through a SIP network if it is rejected sooner
+		or later by a SIP device any way. As every sanity cost extra
+		performance because of additional parsing and evaluation it
+		is now with this module up to the SER adminstrator which checks
+		should be done on which request.
+	</para>
+	<para>
+		The following checks are available:
+		<itemizedlist>
+		<listitem>
+			<para>
+			ruri sip version - (1) - checks if the SIP version in the request
+			URI is supported, currently only 2.0.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			ruri scheme - (2) - checks if the URI scheme of the request URI is
+			supported (sip[s]|tel[s]) by SER.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			required headers - (4) -checks if the minimum set of required headers
+			to, from, cseq, callid and via is present in the request.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			via sip version - (8) - not working because parser fails already 
+			when another version then 2.0 is present.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			via protocol - (16) - not working because parser fails already if an
+			unsupported transport is present.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			cseq method - (32) - checks if the method from the cseq header is equal
+			to the request method.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			cseq value - (64) - checks if the number in the cseq header is a valid
+			unsigend integer.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			content length - (128) - checks if the size of the body matches with the
+			value from the content length header.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			expires value - (256) - checks if the value of the expires header is a
+			valid unsigned integer.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			proxy require - (512) - checks if all items of the proxy require header
+			are present in the list of the extensions from the module 
+			parameter proxy_require.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			parse uri's - (1024) - checks if the specified URIs are present and
+			parseable by the SER parsers
+			</para>
+		</listitem>
+		<listitem>
+		        <para>
+			digest credentials (2048) Check all instances of digest credentials in a
+			message. The test checks whether there are all required
+			digest parameters and have meaningful values.
+			</para>
+		</listitem>
+		</itemizedlist>
+	</para>
+    </section>
+    
+    <section id="sanity.dep">
+	<title>Dependencies</title>
+	<para>
+	    The following modules must be loaded before this module:
+	    <itemizedlist>
+		<listitem>
+		    <para>
+			<emphasis>sl</emphasis> - Stateless replies.
+		    </para>
+		</listitem>
+	    </itemizedlist>
+	</para>
+    </section>
+
+    <xi:include href="params.xml"/>
+    <xi:include href="functions.xml"/>
+</section>

+ 223 - 0
modules_s/sanity/mod_sanity.c

@@ -0,0 +1,223 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ *
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "mod_sanity.h"
+#include "sanity.h"
+#include "../../sr_module.h"
+#include "../../ut.h"
+#include "../../error.h"
+
+MODULE_VERSION
+
+#define PROXY_REQUIRE_DEF 	""
+
+str pr_str 	= STR_STATIC_INIT(PROXY_REQUIRE_DEF);
+
+int default_checks = SANITY_DEFAULT_CHECKS;
+int uri_checks = SANITY_DEFAULT_URI_CHECKS;
+strl* proxyrequire_list = NULL;
+
+sl_api_t sl;
+
+static int mod_init(void);
+static int sanity_fixup(void** param, int param_no);
+static int sanity_check(struct sip_msg* _msg, char* _foo, char* _bar);
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"sanity_check", (cmd_function)sanity_check, 0, 0, REQUEST_ROUTE},
+	{"sanity_check", (cmd_function)sanity_check, 1, sanity_fixup, REQUEST_ROUTE},
+	{"sanity_check", (cmd_function)sanity_check, 2, sanity_fixup, REQUEST_ROUTE},
+	{0, 0, 0, 0}
+};
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"default_checks", 	PARAM_INT, 	&default_checks},
+	{"uri_checks",		PARAM_INT,  &uri_checks	},
+	{"proxy_require", 	PARAM_STR, 	&pr_str 	},
+	{0, 0, 0}
+};
+
+/*
+ * Module description
+ */
+struct module_exports exports = {
+	"sanity",        /* Module name */
+	cmds,            /* Exported functions */
+	0,               /* RPC methods */
+	params,          /* Exported parameters */
+	mod_init,        /* Initialization function */
+	0,               /* Response function */
+	0,               /* Destroy function */
+	0,               /* OnCancel function */
+	0                /* Child init function */
+};
+
+/*
+ * initialize module
+ */
+static int mod_init(void) {
+	bind_sl_t bind_sl;
+	strl* ptr;
+
+	DBG("sanity initializing\n");
+
+	/*
+	 * We will need sl_send_reply from stateless
+	 * module for sending replies
+	 */
+	bind_sl = (bind_sl_t)find_export("bind_sl", 0, 0);
+	if (!bind_sl) {
+		ERR("This module requires sl module\n");
+		return -1;
+	}
+	if (bind_sl(&sl) < 0) return -1;
+
+	DBG("parsing proxy requires string:\n");
+	ptr = parse_str_list(&pr_str);
+
+	proxyrequire_list = ptr;
+
+	while (ptr != NULL) {
+		DBG("string: '%.*s', next: %p\n", ptr->string.len, ptr->string.s, ptr->next);
+		ptr = ptr->next;
+	}
+
+	return 0;
+}
+
+static int sanity_fixup(void** param, int param_no) {
+	int checks;
+	str in;
+
+	if (param_no == 1) {
+		in.s = (char*)*param;
+		in.len = strlen(in.s);
+		if (str2int(&in, (unsigned int*)&checks) < 0) {
+			LOG(L_ERR, "sanity: failed to convert input integer\n");
+			return E_UNSPEC;
+		}
+		if ((checks < 1) || (checks >= (SANITY_MAX_CHECKS))) {
+			LOG(L_ERR, "sanity: input parameter (%i) outside of valid range <1-%i)\n", checks, SANITY_MAX_CHECKS);
+			return E_UNSPEC;
+		}
+		*param = (void*)(long)checks;
+	}
+	if (param_no == 2) {
+		in.s = (char*)*param;
+		in.len = strlen(in.s);
+		if (str2int(&in, (unsigned int*)&checks) < 0) {
+			LOG(L_ERR, "sanity: failed to convert second integer argument\n");
+			return E_UNSPEC;
+		}
+		if ((checks < 1) || (checks > (SANITY_DEFAULT_URI_CHECKS))) {
+			LOG(L_ERR, "sanity: second input parameter (%i) outside of valid range 1-%i\n", checks, SANITY_DEFAULT_URI_CHECKS);
+			return E_UNSPEC;
+		}
+		*param = (void*)(long)checks;
+	}
+	return 0;
+}
+
+static int sanity_check(struct sip_msg* _msg, char* _number, char* _arg) {
+	int ret, check, arg;
+
+	if (_number == NULL) {
+		check = default_checks;
+	}
+	else {
+		check = (int)(long)_number;
+	}
+	if (_arg == NULL) {
+		arg = uri_checks;
+	}
+	else {
+		arg = (int)(long)_arg;
+	}
+
+	if (SANITY_RURI_SIP_VERSION & check &&
+		(ret = check_ruri_sip_version(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_RURI_SCHEME & check &&
+		(ret = check_ruri_scheme(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_REQUIRED_HEADERS & check &&
+		(ret = check_required_headers(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_VIA_SIP_VERSION & check &&
+		(ret = check_via_sip_version(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_VIA_PROTOCOL & check &&
+		(ret = check_via_protocol(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CSEQ_METHOD & check &&
+		(ret = check_cseq_method(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CSEQ_VALUE & check &&
+		(ret = check_cseq_value(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_CL & check &&
+		(ret = check_cl(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_EXPIRES_VALUE & check &&
+		(ret = check_expires_value(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_PROXY_REQUIRE & check &&
+		(ret = check_proxy_require(_msg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+	if (SANITY_PARSE_URIS & check &&
+		(ret = check_parse_uris(_msg, arg)) != SANITY_CHECK_PASSED) {
+		return ret;
+	}
+
+	if (SANITY_CHECK_DIGEST & check &&
+	        (ret = check_digest(_msg, arg)) != SANITY_CHECK_PASSED) {
+	        return ret;
+	}
+
+	DBG("all sanity checks passed\n");
+	/* nobody complained so everything is fine */
+	return 1;
+}

+ 90 - 0
modules_s/sanity/mod_sanity.h

@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ *
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef MOD_SANITY_CHK_H
+#define MOD_SANITY_CHK_H
+
+#include "../../str.h"
+#include "../sl/sl.h"
+#include "../../parser/msg_parser.h"
+
+#define SANITY_RURI_SIP_VERSION        (1<<0)
+#define SANITY_RURI_SCHEME             (1<<1)
+#define SANITY_REQUIRED_HEADERS        (1<<2)
+#define SANITY_VIA_SIP_VERSION         (1<<3)
+#define SANITY_VIA_PROTOCOL            (1<<4)
+#define SANITY_CSEQ_METHOD             (1<<5)
+#define SANITY_CSEQ_VALUE              (1<<6)
+#define SANITY_CL                      (1<<7)
+#define SANITY_EXPIRES_VALUE           (1<<8)
+#define SANITY_PROXY_REQUIRE           (1<<9)
+#define SANITY_PARSE_URIS              (1<<10)
+#define SANITY_CHECK_DIGEST            (1<<11)
+#define SANITY_MAX_CHECKS              (1<<12)  /* Make sure this is the highest value */
+
+/* VIA_SIP_VERSION and VIA_PROTOCOL do not work yet
+ * and PARSE_URIS is very expensive */
+#define SANITY_DEFAULT_CHECKS 	SANITY_RURI_SIP_VERSION | \
+								SANITY_RURI_SCHEME | \
+								SANITY_REQUIRED_HEADERS | \
+								SANITY_CSEQ_METHOD | \
+								SANITY_CSEQ_VALUE | \
+								SANITY_CL | \
+								SANITY_EXPIRES_VALUE | \
+								SANITY_PROXY_REQUIRE | \
+                                                                SANITY_CHECK_DIGEST
+
+
+#define SANITY_URI_CHECK_RURI    (1<<0)
+#define SANITY_URI_CHECK_FROM    (1<<1)
+#define SANITY_URI_CHECK_TO      (1<<2)
+#define SANITY_URI_CHECK_CONTACT (1<<3)
+
+#define SANITY_DEFAULT_URI_CHECKS	SANITY_URI_CHECK_RURI | \
+									SANITY_URI_CHECK_FROM | \
+									SANITY_URI_CHECK_TO
+
+#define SANITY_CHECK_PASSED 1
+#define SANITY_CHECK_FAILED 0
+#define SANITY_CHECK_ERROR -1
+
+struct _strlist {
+	str string;            /* the string */
+	struct _strlist* next; /* the next strlist element */
+};
+
+typedef struct _strlist strl;
+
+extern int default_checks;
+extern strl* proxyrequire_list;
+
+extern sl_api_t sl;
+
+#endif /* MOD_SANITY_CHK_H */

+ 887 - 0
modules_s/sanity/sanity.c

@@ -0,0 +1,887 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ *
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "mod_sanity.h"
+#include "sanity.h"
+#include "../../ut.h"
+#include "../../trim.h"
+#include "../../data_lump_rpl.h"
+#include "../../mem/mem.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_expires.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/digest/digest.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/parse_to.h"
+
+#define UNSUPPORTED_HEADER "Unsupported: "
+#define UNSUPPORTED_HEADER_LEN (sizeof(UNSUPPORTED_HEADER)-1)
+
+/* check if the given string is a valid unsigned int value */
+int str2valid_uint(str* _number, unsigned int* _result) {
+	int i;
+	int result= 0;
+	int equal = 1;
+	char mui[10] = "4294967296";
+
+	*_result = 0;
+	if (_number->len > 10) {
+#ifdef EXTRA_DEBUG
+		DBG("valid_uint(): number is too long\n");
+#endif
+		return -1;
+	}
+	if (_number->len < 10) {
+		equal = 0;
+	}
+	for (i=0; i < _number->len; i++) {
+		if (_number->s[i] < '0' || _number->s[i] > '9') {
+#ifdef EXTRA_DEBUG
+			DBG("valid_uint(): number contains non-number char\n");
+#endif
+			return -1;
+		}
+		if (equal == 1) {
+			if (_number->s[i] < mui[i]) {
+				equal = 0;
+			}
+			else if (_number->s[i] > mui[i]) {
+#ifdef EXTRA_DEBUG
+				DBG("valid_uint(): number exceeds uint\n");
+#endif
+				return -1;
+			}
+		}
+		result *= 10;
+		result += _number->s[i] - '0';
+	}
+	*_result = result;
+	return 0;
+}
+
+/* parses the given comma seperated string into a string list */
+strl* parse_str_list(str* _string) {
+	str input;
+	strl *parsed_list, *pl;
+	char *comma;
+
+	/* make a copy because we trim it */
+	input.s = _string->s;
+	input.len = _string->len;
+
+	trim(&input);
+
+	if (input.len == 0) {
+#ifdef EXTRA_DEBUG
+		DBG("parse_str_list: list is empty\n");
+#endif
+		return NULL;
+	}
+	parsed_list = pkg_malloc(sizeof(strl));
+	if (parsed_list == NULL) {
+		LOG(L_ERR, "parse_str_list: OUT OF MEMORY for initial list element\n");
+		return NULL;
+	}
+	memset(parsed_list, 0, sizeof(strl));
+	parsed_list->string.s = input.s;
+	parsed_list->string.len = input.len;
+
+	comma = q_memchr(input.s, ',', input.len);
+	pl = parsed_list;
+	while (comma != NULL) {
+		pl->next = pkg_malloc(sizeof(strl));
+		if (pl->next == NULL) {
+			LOG(L_ERR, "parse_str_list: OUT OF MEMORY for further list element\n");
+			return parsed_list;
+		}
+		memset(pl->next, 0, sizeof(strl));
+		pl->next->string.s = comma + 1;
+		pl->next->string.len = pl->string.len - (pl->next->string.s - pl->string.s);
+		pl->string.len = comma - pl->string.s;
+		trim_trailing(&(pl->string));
+		pl = pl->next;
+		trim_leading(&(pl->string));
+		comma = q_memchr(pl->string.s, ',', pl->string.len);
+	}
+
+	return parsed_list;
+}
+
+/* free the elements of the linked str list */
+void free_str_list(strl *_list) {
+	strl *cur, *next;
+
+	if (_list != NULL) {
+		cur = _list;
+		while (cur != NULL) {
+			next = cur->next;
+			pkg_free(cur);
+			cur = next;
+		}
+	}
+}
+
+int parse_proxyrequire(struct hdr_field* _h) {
+	strl *pr_l;
+
+	if (_h->parsed) {
+		return 0; /* Already parsed */
+	}
+
+	if ((pr_l = parse_str_list(&(_h->body))) == NULL) {
+		LOG(L_ERR, "parse_proxy_require(): Error while parsing\n");
+		return -1;
+	}
+
+	_h->parsed = pr_l;
+	return 0;
+}
+
+/* check the SIP version in the request URI */
+int check_ruri_sip_version(struct sip_msg* _msg) {
+	char *sep;
+	str version;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_ruri_sip_version entered\n");
+#endif
+
+	if (_msg->first_line.u.request.version.len != 0) {
+		sep = q_memchr(_msg->first_line.u.request.version.s, '/',
+						_msg->first_line.u.request.version.len);
+		if (sep == NULL) {
+			LOG(L_WARN, "sanity_check(): check_ruri_sip_version(): failed to find / in ruri version\n");
+			return SANITY_CHECK_FAILED;
+		}
+		version.s = sep + 1;
+		version.len = _msg->first_line.u.request.version.len - (version.s - _msg->first_line.u.request.version.s);
+
+		if (version.len != SIP_VERSION_TWO_POINT_ZERO_LENGTH ||
+			(memcmp(version.s, SIP_VERSION_TWO_POINT_ZERO, 
+				SIP_VERSION_TWO_POINT_ZERO_LENGTH) != 0)) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 505, "Version Not Supported (R-URI)") == -1) {
+					LOG(L_WARN, "sanity_check(): check_ruri_sip_version(): failed to send 505 via send_reply\n");
+				}
+			}
+#ifdef EXTRA_DEBUG
+			DBG("check_ruri_sip_version failed\n");
+#endif
+			return SANITY_CHECK_FAILED;
+		}
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_ruri_sip_version passed\n");
+#endif
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the r-uri scheme */
+int check_ruri_scheme(struct sip_msg* _msg) {
+
+#ifdef EXTRA_DEBUG
+	DBG("check_ruri_scheme entered\n");
+#endif
+
+	if (_msg->parsed_uri_ok == 0 &&
+			parse_sip_msg_uri(_msg) != 1) {
+		/* unsupported schemes end up here already */
+		LOG(L_WARN, "sanity_check(): check_ruri_scheme(): failed to parse request uri\n");
+	}
+	if (_msg->parsed_uri.type == ERROR_URI_T) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 416, "Unsupported URI Scheme in Request URI") == -1) {
+				LOG(L_WARN, "sanity_check(): check_ruri_scheme(): failed to send 416 via send_reply\n");
+			}
+		}
+		DBG("check_ruri_scheme failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_ruri_scheme passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check for the presence of the minimal required headers */
+int check_required_headers(struct sip_msg* _msg) {
+
+#ifdef EXTRA_DEBUG
+	DBG("check_required_headers entered\n");
+#endif
+
+	if (!check_transaction_quadruple(_msg)) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 400, "Missing Required Header in Request") == -1) {
+				LOG(L_WARN, "sanity_check(): check_required_headers(): failed to send 400 via send_reply\n");
+			}
+		}
+		DBG("check_required_headers failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+	/* TODO: check for other required headers according to request type */
+#ifdef EXTRA_DEBUG
+	DBG("check_required_headers passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the SIP version in the Via header is 2.0 */
+int check_via_sip_version(struct sip_msg* _msg) {
+
+	DBG("sanity_check(): check_via_sip_version(): this is a useless check for now; check the source code comments for details\n");
+	return SANITY_CHECK_PASSED;
+
+	/* FIMXE the Via parser fails already on non-2.0 versions
+	 * thus this check makes no sence yet
+	DBG("check_via_sip_version entered\n");
+
+	// FIXME via parser fails on non 2.0 number
+	if (parse_headers(_msg, HDR_VIA1_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_via_sip_version(): failed to parse the first Via header\n");
+		return SANITY_CHECK_FAILED;
+	}
+
+	if (_msg->via1->version.len != 3 ||
+			memcmp(_msg->via1->version.s, SIP_VERSION_TWO_POINT_ZERO, 
+					SIP_VERSION_TWO_POINT_ZERO_LENGTH ) != 0) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 505, "Version Not Supported (Via)") == -1) {
+				LOG(L_WARN, "sanity_check(): check_via_sip_version(): failed to send 505 via send_reply\n");
+			}
+		}
+		DBG("check_via_sip_version failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_via_sip_version passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+	*/
+}
+
+/* compare the protocol string in the Via header with the transport */
+int check_via_protocol(struct sip_msg* _msg) {
+
+	DBG("sanity_check(): check_via_protocol(): this is a useless check for now; check the source code comment for details\n");
+	return SANITY_CHECK_PASSED;
+
+	/* FIXME as the Via parser fails already on unknown transports
+	 * this function makes no sence yet
+	DBG("check_via_protocol entered\n");
+
+	// FIXME via parser fails on unknown transport
+	if (parse_headers(_msg, HDR_VIA1_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to parse the first Via header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->via1->transport.len != 3 &&
+			_msg->via1->transport.len != 4) {
+		if (_msg->REQ_METHOD != METHOD_ACK) {
+			if (sl.reply(_msg, 400, "Unsupported Transport in Topmost Via") == -1) {
+				LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 400 via send_reply\n");
+			}
+		}
+		DBG("check_via_protocol failed\n");
+		return SANITY_CHECK_FAILED;
+	}
+	switch (_msg->rcv.proto) {
+		case PROTO_UDP:
+			if (memcmp(_msg->via1->transport.s, "UDP", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_TCP:
+			if (memcmp(_msg->via1->transport.s, "TCP", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_TLS:
+			if (memcmp(_msg->via1->transport.s, "TLS", 3) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		case PROTO_SCTP:
+			if (memcmp(_msg->via1->transport.s, "SCTP", 4) != 0) {
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Transport Missmatch in Topmost Via") == -1) {
+						LOG(L_WARN, "sanity_check(): check_via_protocol(): failed to send 505 via send_reply\n");
+					}
+				}
+				DBG("check_via_protocol failed\n");
+				return SANITY_CHECK_FAILED;
+			}
+			break;
+		default:
+			LOG(L_WARN, "sanity_check(): check_via_protocol(): unknown protocol in received structure\n");
+			return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_via_protocol passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+	*/
+}
+
+/* compare the method in the CSeq header with the request line value */
+int check_cseq_method(struct sip_msg* _msg) {
+
+#ifdef EXTRA_DEBUG
+	DBG("check_cseq_method entered\n");
+#endif
+
+	if (parse_headers(_msg, HDR_CSEQ_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_cseq_method(): failed to parse the CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->cseq != NULL && _msg->cseq->parsed != NULL) {
+		if (((struct cseq_body*)_msg->cseq->parsed)->method.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Missing method in CSeq header") == -1) {
+					LOG(L_WARN, "sanity_check(): check_cseq_method(): failed to send 400 via send_reply\n");
+				}
+			}
+			DBG("check_cseq_method failed (missing method)\n");
+			return SANITY_CHECK_FAILED;
+		}
+
+		if (((struct cseq_body*)_msg->cseq->parsed)->method.len != 
+					_msg->first_line.u.request.method.len ||
+			memcmp(((struct cseq_body*)_msg->cseq->parsed)->method.s, 
+				_msg->first_line.u.request.method.s,
+				((struct cseq_body*)_msg->cseq->parsed)->method.len) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "CSeq method does not match request method") == -1) {
+					LOG(L_WARN, "sanity_check(): check_cseq_method(): failed to send 400 via send_reply 2\n");
+				}
+			}
+			DBG("check_cseq_method failed (non-equal method)\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+	else {
+		LOG(L_WARN, "sanity_check(): check_cseq_method(): missing CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_cseq_method passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the number within the CSeq header */
+int check_cseq_value(struct sip_msg* _msg) {
+	unsigned int cseq;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_cseq_value entered\n");
+#endif
+
+	if (parse_headers(_msg, HDR_CSEQ_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_cseq_value(): failed to parse the CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->cseq != NULL && _msg->cseq->parsed != NULL) {
+		if (((struct cseq_body*)_msg->cseq->parsed)->number.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Missing number in CSeq header") == -1) {
+					LOG(L_WARN, "sanity_check(): check_cseq_value(): failed to send 400 via send_reply\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (str2valid_uint(&((struct cseq_body*)_msg->cseq->parsed)->number, &cseq) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "CSeq number is illegal") == -1) {
+					LOG(L_WARN, "sanity_check(): check_cseq_value(): failed to send 400 via send_reply 2\n");
+				}
+			}
+			DBG("check_cseq_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+	}
+	else {
+		LOG(L_WARN, "sanity_check(): check_cseq_method(): missing CSeq header\n");
+		return SANITY_CHECK_FAILED;
+	}
+#ifdef EXTRA_DEBUG
+	DBG("check_cseq_value passed\n");
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* compare the Content-Length value with the accutal body length */
+int check_cl(struct sip_msg* _msg) {
+	char *body;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_cl entered\n");
+#endif
+
+	if (parse_headers(_msg, HDR_CONTENTLENGTH_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_cl(): failed to parse content-length header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->content_length != NULL) {
+		//dump_hdr_field(_msg->content_length);
+		if ((body = get_body(_msg)) == NULL) {
+#ifdef EXTRA_DEBUG
+			DBG("check_cl(): no body\n");
+#endif
+			return SANITY_CHECK_FAILED;
+		}
+		if ((_msg->len - (body - _msg->buf)) != get_content_length(_msg)) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Content-Length mis-match") == -1) {
+					LOG(L_WARN, "sanity_check(): check_cl(): failed to send 400 via send_reply\n");
+				}
+			}
+			DBG("check_cl failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+#ifdef EXTRA_DEBUG
+		DBG("check_cl passed\n");
+#endif
+	}
+#ifdef EXTRA_DEBUG
+	else {
+		WARN("check_cl(): content length header missing in request\n");
+	}
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the number within the Expires header */
+int check_expires_value(struct sip_msg* _msg) {
+	unsigned int expires;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_expires_value entered\n");
+#endif
+
+	if (parse_headers(_msg, HDR_EXPIRES_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_expires_value(): failed to parse expires header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->expires != NULL) {
+		//dump_hdr_field(_msg->expires);
+		if (_msg->expires->parsed == NULL &&
+				parse_expires(_msg->expires) < 0) {
+			LOG(L_WARN, "sanity_check(): check_expires_value(): parse_expires failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		if (((struct exp_body*)_msg->expires->parsed)->text.len == 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Missing number in Expires header") == -1) {
+					LOG(L_WARN, "sanity_check(): check_expires_value(): failed to send 400 via send_reply\n");
+				}
+			}
+			DBG("check_expires_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		if (str2valid_uint(&((struct exp_body*)_msg->expires->parsed)->text, &expires) != 0) {
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Expires value is illegal") == -1) {
+					LOG(L_WARN, "sanity_check(): check_expires_value(): failed to send 400 via send_reply 2\n");
+				}
+			}
+			DBG("check_expires_value failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+#ifdef EXTRA_DEBUG
+		DBG("check_expires_value passed\n");
+#endif
+	}
+#ifdef EXTRA_DEBUG
+	else {
+		DBG("check_expires_value(): no expires header found\n");
+	}
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check the content of the Proxy-Require header */
+int check_proxy_require(struct sip_msg* _msg) {
+	strl *r_pr, *l_pr;
+	char *u;
+	int u_len;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_proxy_require entered\n");
+#endif
+
+	if (parse_headers(_msg, HDR_PROXYREQUIRE_F, 0) != 0) {
+		LOG(L_WARN, "sanity_check(): check_proxy_require(): failed to parse proxy require header\n");
+		return SANITY_CHECK_FAILED;
+	}
+	if (_msg->proxy_require != NULL) {
+		dump_hdr_field(_msg->proxy_require);
+		if (_msg->proxy_require->parsed == NULL &&
+				parse_proxyrequire(_msg->proxy_require) < 0) {
+			LOG(L_WARN, "sanity_check(): check_proxy_require(): parse_proxy_require failed\n");
+			return SANITY_CHECK_FAILED;
+		}
+		r_pr = _msg->proxy_require->parsed;
+		while (r_pr != NULL) {
+			l_pr = proxyrequire_list;
+			while (l_pr != NULL) {
+#ifdef EXTRA_DEBUG
+				DBG("check_proxy_require(): comparing r='%.*s' l='%.*s'\n", r_pr->string.len, r_pr->string.s, l_pr->string.len, l_pr->string.s);
+#endif
+				if (l_pr->string.len == r_pr->string.len &&
+						/* FIXME tokens are case in-sensitive */
+						memcmp(l_pr->string.s, r_pr->string.s, l_pr->string.len) == 0) {
+					break;
+				}
+				l_pr = l_pr->next;
+			}
+			if (l_pr == NULL) {
+				DBG("sanit_check(): check_proxy_require(): request contains unsupported extension: %.*s\n", r_pr->string.len, r_pr->string.s);
+				u_len = UNSUPPORTED_HEADER_LEN + 2 + r_pr->string.len;
+				u = pkg_malloc(u_len);
+				if (u == NULL) {
+					LOG(L_ERR, "sanity_check(): check_proxy_require(): failed to allocate memory for Unsupported header\n");
+				}
+				else {
+					memcpy(u, UNSUPPORTED_HEADER, UNSUPPORTED_HEADER_LEN);
+					memcpy(u + UNSUPPORTED_HEADER_LEN, r_pr->string.s, r_pr->string.len);
+					memcpy(u + UNSUPPORTED_HEADER_LEN + r_pr->string.len, CRLF, CRLF_LEN);
+					add_lump_rpl(_msg, u, u_len, LUMP_RPL_HDR);
+				}
+
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 420, "Bad Extension") == -1) {
+						LOG(L_WARN, "sanity_check(): check_proxy_require(): failed to send 420 via send_reply\n");
+					}
+				}
+#ifdef EXTRA_DEBUG
+				DBG("check_proxy_require failed\n");
+#endif
+				if (u) pkg_free(u);
+				return SANITY_CHECK_FAILED;
+			}
+			else {
+				r_pr = r_pr->next;
+			}
+		}
+#ifdef EXTRA_DEBUG
+		DBG("check_proxy_require passed\n");
+#endif
+		if (_msg->proxy_require->parsed) {
+			/* TODO we have to free it here, because it is not automatically
+			 * freed when the message freed. Lets hope nobody needs to access
+			 * this header again later on */
+			free_str_list(_msg->proxy_require->parsed);
+		}
+	}
+#ifdef EXTRA_DEBUG
+	else {
+		DBG("check_proxy_require(): no proxy-require header found\n");
+	}
+#endif
+
+	return SANITY_CHECK_PASSED;
+}
+
+/* check if the typical URI's are parseable */
+int check_parse_uris(struct sip_msg* _msg, int checks) {
+
+	struct to_body *ft_body = NULL;
+	struct sip_uri uri;
+
+#ifdef EXTRA_DEBUG
+	DBG("check_parse_uris entered\n");
+#endif
+
+	/* check R-URI */
+	if (SANITY_URI_CHECK_RURI & checks) {
+#ifdef EXTRA_DEBUG
+		DBG("check_parse_uris(): parsing ruri\n");
+#endif
+		if (_msg->parsed_uri_ok == 0 &&
+				parse_sip_msg_uri(_msg) != 1) {
+			LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse request uri\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Bad Request URI") == -1) {
+					LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad ruri)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		/* FIXME: would it make sense to check here for "mandatory"
+		 * or "requested" parts of the URI? */
+	}
+	/* check From URI */
+	if (SANITY_URI_CHECK_FROM & checks) {
+#ifdef EXTRA_DEBUG
+		DBG("check_parse_uris(): looking up From header\n");
+#endif
+		if ((!_msg->from && parse_headers(_msg, HDR_FROM_F, 0) != 0) || !_msg->from) {
+			LOG(L_WARN, "sanity_check(): check_parse_uris(): missing from header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Missing From Header") == -1) {
+					LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (missing From)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (!_msg->from->parsed) {
+#ifdef EXTRA_DEBUG
+			DBG("check_parse_uris(): parsing From header\n");
+#endif
+			ft_body = pkg_malloc(sizeof(struct to_body));
+			if (!ft_body) {
+				LOG(L_ERR, "sanity_check(): check_parse_uris(): out of pkg_memory (From)\n");
+				return SANITY_CHECK_ERROR;
+			}
+			memset(ft_body, 0, sizeof(struct to_body));
+			parse_to(_msg->from->body.s, _msg->from->body.s + \
+					_msg->from->body.len + 1, ft_body);
+			if (ft_body->error == PARSE_ERROR) {
+				LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse From header\n");
+				pkg_free(ft_body);
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Bad From header") == -1) {
+						LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad from header)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			_msg->from->parsed = ft_body;
+			ft_body = NULL;
+		}
+		if (((struct to_body*)_msg->from->parsed)->uri.s) {
+#ifdef EXTRA_DEBUG
+			DBG("check_parse_uris(): parsing From URI\n");
+#endif
+			if (parse_uri(((struct to_body*)_msg->from->parsed)->uri.s, 
+					((struct to_body*)_msg->from->parsed)->uri.len, &uri) != 0) {
+			    LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse From uri\n");
+			    if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Bad From URI") == -1) {
+				    LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad from uri)\n");
+				}
+			    }
+			    return SANITY_CHECK_FAILED;
+			}
+			/* FIXME: we should store this parsed struct somewhere so that
+			 * it could be re-used */
+			/* FIXME 2: would it make sense to check here for "mandatory"
+			 * or "requested" parts of the URI? */
+		}
+	}
+	/* check To URI */
+	if (SANITY_URI_CHECK_TO & checks) {
+#ifdef EXTRA_DEBUG
+		DBG("check_parse_uris(): looking up To header\n");
+#endif
+		if ((!_msg->to && parse_headers(_msg, HDR_TO_F, 0) != 0) || !_msg->to) {
+			LOG(L_WARN, "sanity_check(): check_parse_uris(): missing to header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Missing To Header") == -1) {
+					LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (missing To)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		/* parse_to is automatically called for HDR_TO_F */
+		if (!_msg->to->parsed) {
+			LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse To header\n");
+			if (_msg->REQ_METHOD != METHOD_ACK) {
+				if (sl.reply(_msg, 400, "Bad To URI") == -1) {
+					LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad to uri)\n");
+				}
+			}
+			return SANITY_CHECK_FAILED;
+		}
+		if (((struct to_body*)_msg->to->parsed)->uri.s) {
+#ifdef EXTRA_DEBUG
+			DBG("check_parse_uris(): parsing To URI\n");
+#endif
+			if (parse_uri(((struct to_body*)_msg->to->parsed)->uri.s, 
+					((struct to_body*)_msg->to->parsed)->uri.len, &uri) != 0) {
+				LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse To uri\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Bad To URI") == -1) {
+						LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad to uri)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			/* FIXME: we should store this parsed struct somewhere so that
+			 * it could be re-used */
+			/* FIXME 2: would it make sense to check here for "mandatory"
+			 * or "requested" parts of the URI? */
+		}
+	}
+	/* check Contact URI */
+	if (SANITY_URI_CHECK_CONTACT & checks) {
+#ifdef EXTRA_DEBUG
+		DBG("check_parse_uris(): looking up Contact header\n");
+#endif
+		if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0) || !_msg->contact) {
+			LOG(L_WARN, "sanity_check(): check_parse_uris(): missing contact header\n");
+		}
+		if (_msg->contact) {
+#ifdef EXTRA_DEBUG
+			DBG("check_parse_uris(): parsing Contact header\n");
+#endif
+			if (parse_contact(_msg->contact) < 0) {
+				LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse Contact header\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Bad Contact Header") == -1) {
+						LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad Contact)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+			if (parse_uri(((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
+					((struct contact_body*)_msg->contact->parsed)->contacts->uri.len, &uri) != 0) {
+				LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to parse Contact uri\n");
+				if (_msg->REQ_METHOD != METHOD_ACK) {
+					if (sl.reply(_msg, 400, "Bad Contact URI") == -1) {
+						LOG(L_WARN, "sanity_check(): check_parse_uris(): failed to send 400 via send_reply (bad Contact uri)\n");
+					}
+				}
+				return SANITY_CHECK_FAILED;
+			}
+		}
+	}
+
+#ifdef EXTRA_DEBUG
+	DBG("check_parse_uris passed\n");
+#endif
+	return SANITY_CHECK_PASSED;
+}
+
+
+/* Make sure that username attribute in all digest credentials
+ * instances has a meaningful value
+ */
+int check_digest(struct sip_msg* msg, int checks)
+{
+    struct hdr_field* ptr;
+    dig_cred_t* cred;
+    int ret;
+    int hf_type;
+
+    if (parse_headers(msg, HDR_EOH_F, 0) != 0) {
+	LOG(L_ERR, "sanity_check(): check_digest: failed to parse proxy require header\n");
+	return SANITY_CHECK_FAILED;
+    }
+
+    if (!msg->authorization && !msg->proxy_auth) {
+#ifdef EXTRA_DEBUG
+	DBG("sanity_check(): check_digest: Nothing to check\n");
+#endif
+	return SANITY_CHECK_PASSED;
+    }
+
+    if (msg->authorization) {
+	hf_type = HDR_AUTHORIZATION_T;
+	ptr = msg->authorization;
+    } else {
+	hf_type = HDR_PROXYAUTH_T;
+	ptr = msg->proxy_auth;
+    }
+    while(ptr) {
+	if ((ret = parse_credentials(ptr)) != 0) {
+	    DBG("sanity_check(): check_digest: Cannot parse credentials: %d\n", ret);
+	    return SANITY_CHECK_FAILED;
+	}
+
+	cred = &((auth_body_t*)ptr->parsed)->digest;
+
+	if (check_dig_cred(cred) != E_DIG_OK) {
+#ifdef EXTRA_DEBUG
+	    DBG("sanity_check(): check_digest: Digest credentials malformed\n");
+#endif
+	    return SANITY_CHECK_FAILED;
+	}
+
+	if (cred->username.whole.len == 0) {
+#ifdef EXTRA_DEBUG
+	    DBG("sanity_check(): check_digest: Empty username\n");
+#endif
+	    return SANITY_CHECK_FAILED;
+	}
+	
+	if (cred->nonce.len == 0) {
+#ifdef EXTRA_DEBUG
+	    DBG("sanity_check(): check_digest: Empty nonce attribute\n");
+#endif
+	    return SANITY_CHECK_FAILED;
+	}
+
+	if (cred->response.len == 0) {
+#ifdef EXTRA_DEBUG
+	    DBG("sanity_check(): check_digest: Empty response attribute\n");
+#endif
+	    return SANITY_CHECK_FAILED;
+	}
+
+	do {
+	    ptr = ptr->next;
+	} while(ptr && ptr->type != hf_type);
+
+	if (!ptr && hf_type == HDR_AUTHORIZATION_T) {
+	    hf_type = HDR_PROXYAUTH_T;
+	    ptr = msg->proxy_auth;
+	}
+    }
+
+    return SANITY_CHECK_PASSED;
+}

+ 84 - 0
modules_s/sanity/sanity.h

@@ -0,0 +1,84 @@
+/*
+ * $Id$
+ *
+ * Sanity Checks Module
+ *
+ * Copyright (C) 2006 iptelorg GbmH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SANITY_CHK_H
+#define SANITY_CHK_H
+
+#include "mod_sanity.h"
+
+#define SIP_VERSION_TWO_POINT_ZERO "2.0"
+#define SIP_VERSION_TWO_POINT_ZERO_LENGTH 3
+
+/* check if the given string is a valid unsigned int value
+ * and converts it into _result. returns -1 on error and 0 on success*/
+int str2valid_uint(str* _number, unsigned int* _result);
+
+/* parses the given comma seperated string into a string list */
+strl* parse_str_list(str* _string);
+
+/* compare the protocol string in the Via header with the transport */
+int check_via_protocol(struct sip_msg* _msg);
+
+/* check if the SIP version in the Via header is 2.0 */
+int check_via_sip_version(struct sip_msg* _msg);
+
+/* compare the Content-Length value with the accutal body length */
+int check_cl(struct sip_msg* _msg);
+
+/* compare the method in the CSeq header with the request line value */
+int check_cseq_method(struct sip_msg* _msg);
+
+/* check the number within the CSeq header */
+int check_cseq_value(struct sip_msg* _msg);
+
+/* check the number within the Expires header */
+int check_expires_value(struct sip_msg* _msg);
+
+/* check for the presence of the minimal required headers */
+int check_required_headers(struct sip_msg* _msg);
+
+/* check the content of the Proxy-Require header */
+int check_proxy_require(struct sip_msg* _msg);
+
+/* check the SIP version in the request URI */
+int check_ruri_sip_version(struct sip_msg* _msg);
+
+/* check if the r-uri scheme */
+int check_ruri_scheme(struct sip_msg* _msg);
+
+/* check if the typical URIs are parseable */
+int check_parse_uris(struct sip_msg* _msg, int checks);
+
+/* Make sure that username attribute in all digest credentials
+ * instances has a meaningful value
+ */
+int check_digest(struct sip_msg* _msg, int checks);
+
+#endif /* SANITY_CHK_H */

+ 2 - 2
parser/parse_uri.c

@@ -1399,8 +1399,8 @@ int parse_sip_msg_uri(struct sip_msg* msg)
 		tmp_len=msg->first_line.u.request.uri.len;
 	}
 	if (parse_uri(tmp, tmp_len, &msg->parsed_uri)<0){
-		LOG(L_ERR, "ERROR: parse_sip_msg_uri: bad uri <%.*s>\n",
-					tmp_len, tmp);
+		DBG("ERROR: parse_sip_msg_uri: bad uri <%.*s>\n",
+			tmp_len, tmp);
 		msg->parsed_uri_ok=0;
 		return -1;
 	}