소스 검색

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 16 년 전
부모
커밋
39ce77440b
42개의 변경된 파일3968개의 추가작업 그리고 241개의 파일을 삭제
  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>
     </column>
 
 
     <index>
     <index>
-        <name>lcr_id_gw_name_idx</name>
+        <name>lcr_id_grp_id_gw_name_idx</name>
         <colref linkend="lcr_id"/>
         <colref linkend="lcr_id"/>
+        <colref linkend="grp_id"/>
         <colref linkend="gw_name"/>
         <colref linkend="gw_name"/>
         <unique/>
         <unique/>
     </index>
     </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>
 </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 */
 	/* set uuid/(username and domain) fields */
 
 
 	n =0 ;
 	n =0 ;
-
+	memset(&st, 0, sizeof(struct search_state));
 	if ((dbp->a.opd&AVPOPS_VAL_NONE)==0)
 	if ((dbp->a.opd&AVPOPS_VAL_NONE)==0)
 	{
 	{
 		/* avp name is known ->set it and its type */
 		/* avp name is known ->set it and its type */
 		store_vals[1].val.str_val = dbp->sa; /*attr name*/
 		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 */
 			/* don't insert avps which were loaded */
 			if (avp->flags&AVP_IS_IN_DB)
 			if (avp->flags&AVP_IS_IN_DB)

+ 3 - 3
modules/carrierroute/carrierroute.c

@@ -87,11 +87,11 @@ static void mod_destroy(void);
 /************* Module Exports **********************************************/
 /************* Module Exports **********************************************/
 static cmd_export_t cmds[]={
 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_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_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_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_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 },
 	{"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}
 	{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
  * 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
  * @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_str avp_val;
 	int strip = 0;
 	int strip = 0;
 
 
+	str l_user = *user;
+	
 	strip = (rs->strip > user->len ? user->len : rs->strip);
 	strip = (rs->strip > user->len ? user->len : rs->strip);
 	strip = (strip < 0 ? 0 : 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) {
 	if (msg->parsed_uri.type == SIPS_URI_T) {
 		len += SIPS_URI.len;
 		len += SIPS_URI.len;
 	} else {
 	} else {
@@ -309,11 +320,11 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
 		memcpy(p, SIP_URI.s, SIP_URI.len);
 		memcpy(p, SIP_URI.s, SIP_URI.len);
 		p += SIP_URI.len;
 		p += SIP_URI.len;
 	}
 	}
-	if (user->len) {
+	if (l_user.len) {
 		memcpy(p, rs->local_prefix.s, rs->local_prefix.len);
 		memcpy(p, rs->local_prefix.s, rs->local_prefix.len);
 		p += 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);
 		memcpy(p, rs->local_suffix.s, rs->local_suffix.len);
 		p += rs->local_suffix.len;
 		p += rs->local_suffix.len;
 		memcpy(p, AT_SIGN.s, AT_SIGN.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);
 		_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
  * 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);
 		_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
  * rewrites the request URI of msg after determining the
  * new destination URI with the crc32 hash algorithm. The difference
  * 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);
 		_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.
  * 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 *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_descavp);
 		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 *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_descavp);
 		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 *_domain, gparam_t *_prefix_matching,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_rewrite_user, enum hash_source _hsrc,
 		gparam_t *_dstavp);
 		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
    Causes lcr module to re-read the contents of gw and lcr tables into
    memory.
    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
    Name: lcr_reload
 
 
    Parameters: none
    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
 			Causes lcr module to re-read the contents of
 			gw and lcr tables into memory.
 			gw and lcr tables into memory.
 		</para>
 		</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>
 		<para>
 		Name: <emphasis>lcr_reload</emphasis>
 		Name: <emphasis>lcr_reload</emphasis>
 		</para>
 		</para>

+ 20 - 13
modules/lcr/lcr_mod.c

@@ -151,7 +151,7 @@ struct matched_gw_info {
     unsigned short prefix_len;
     unsigned short prefix_len;
     unsigned short priority;
     unsigned short priority;
     unsigned int weight;
     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,
 	    attr = add_mi_attr(node, MI_DUP_VALUE, "TAG", 3,
 			       gws[i].tag, gws[i].tag_len);
 			       gws[i].tag, gws[i].tag_len);
 	    if (attr == NULL) goto err;
 	    if (attr == NULL) goto err;
-	    
+
 	    p = int2str((unsigned long)gws[i].weight, &len);
 	    p = int2str((unsigned long)gws[i].weight, &len);
 	    attr = add_mi_attr(node, MI_DUP_VALUE, "WEIGHT", 6, p, len);
 	    attr = add_mi_attr(node, MI_DUP_VALUE, "WEIGHT", 6, p, len);
 	    if (attr == NULL) goto err;
 	    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);
     delete_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp);
 
 
     for (i = 0; i < gw_cnt; i++) {
     for (i = 0; i < gw_cnt; i++) {
+	if (matched_gws[i].duplicate == 1) continue;
 	index = matched_gws[i].gw_index;
 	index = matched_gws[i].gw_index;
       	hostname_len = gws[index].hostname_len;
       	hostname_len = gws[index].hostname_len;
 	strip = gws[index].strip;
 	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);
 	add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val);
 
 
 	LM_DBG("added gw_uri_avp <%.*s> with weight <%u>\n",
 	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:
     skip:
 	continue;
 	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)
 static int load_gws(struct sip_msg* _m, char *_lcr_id, char *_from_uri)
 {
 {
     str ruri_user, 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;
     int_str val;
     struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
     struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
     struct lcr_info **lcrs, *lcr_rec, *pl;
     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) {
 		    while (j) {
                         /* If this gw is defunct, skip it */
                         /* If this gw is defunct, skip it */
 		        if (gws[j].defunct_until > now) goto gw_found;
 		        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].gw_index = j;
 			matched_gws[gw_index].prefix_len = pl->prefix_len;
 			matched_gws[gw_index].prefix_len = pl->prefix_len;
 			matched_gws[gw_index].priority = lcr_rec->priority;
 			matched_gws[gw_index].priority = lcr_rec->priority;
 			matched_gws[gw_index].weight = gws[j].weight *
 			matched_gws[gw_index].weight = gws[j].weight *
 			    (rand() >> 8);
 			    (rand() >> 8);
+			matched_gws[gw_index].duplicate = 0;
 			LM_DBG("added matched_gws[%d]=[%u, %u, %u, %u]\n",
 			LM_DBG("added matched_gws[%d]=[%u, %u, %u, %u]\n",
 			       gw_index, j, pl->prefix_len, lcr_rec->priority,
 			       gw_index, j, pl->prefix_len, lcr_rec->priority,
 			       matched_gws[gw_index].weight);
 			       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;
 	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);
     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 gateways into gw_uris_avp */
     add_gws_into_avps(gws, matched_gws, gw_index, &ruri_user);
     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;
 		if (rpc->add(c, "{", &st) < 0) return;
 		rpc->struct_add(st, "d", "lcr_id", j);
 		rpc->struct_add(st, "d", "lcr_id", j);
 		rpc->struct_add(st, "d", "grp_id", gws[i].grp_id);
 		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 << 24) >> 24,
 				   ((gws[i].ip_addr >> 8) << 24) >> 24,
 				   ((gws[i].ip_addr >> 8) << 24) >> 24,
 				   ((gws[i].ip_addr >> 16) << 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;
 		tag.len=gws[i].tag_len;
 		rpc->struct_add(st, "dSddd",
 		rpc->struct_add(st, "dSddd",
 				"strip",  gws[i].strip,
 				"strip",  gws[i].strip,
-				"tag",    gws[i].tag, /* FIXME */
+				"tag",    &tag,
 				"weight", gws[i].weight,
 				"weight", gws[i].weight,
-				"flags",  &tag,
+				"flags",  gws[i].flags,
 				"defunct_until",  &gws[i].defunct_until
 				"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
  *              t saved, thus, are not available. Set reparse_on_dns_failover
  *              to 0 to revert the change. (Miklos)
  *              to 0 to revert the change. (Miklos)
  * 2008-06-04  T_CANCELED is now set each time a CANCEL is received (andrei)
  * 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"
 #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 t  - transaction
  * @param i_req - corresponding sip_msg, must be non-null, flags might be
  * @param i_req - corresponding sip_msg, must be non-null, flags might be
  *                be modified (on_branch route)
  *                be modified (on_branch route)
@@ -153,64 +170,61 @@ unsigned int get_on_branch(void)
  * @param uri
  * @param uri
  * @param path  - path vector (list of route like destination in text form,
  * @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>")
  *                 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 lump* add_rm_backup, *body_lumps_backup;
 	struct sip_uri parsed_uri_bak;
 	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 msg_uri_bak;
+	str dst_uri_bak;
+	int dst_uri_backed_up;
 	str path_bak;
 	str path_bak;
-	int path_backed_up;
+	int free_path;
 	int backup_route_type;
 	int backup_route_type;
 	snd_flags_t fwd_snd_flags_bak;
 	snd_flags_t fwd_snd_flags_bak;
 	snd_flags_t rpl_snd_flags_bak;
 	snd_flags_t rpl_snd_flags_bak;
+	struct dest_info *dst;
 
 
 	shbuf=0;
 	shbuf=0;
+	ret=E_UNSPEC;
 	msg_uri_bak.s=0; /* kill warnings */
 	msg_uri_bak.s=0; /* kill warnings */
 	msg_uri_bak.len=0;
 	msg_uri_bak.len=0;
 	parsed_uri_ok_bak=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.s=0;
 	path_bak.len=0;
 	path_bak.len=0;
-	path_backed_up=0;
+	free_path=0;
+	dst=&t->uac[branch].request.dst;
 
 
 	/* ... we calculate branch ... */	
 	/* ... we calculate branch ... */	
 	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
 	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
 			&i_req->add_to_branch_len ))
 			&i_req->add_to_branch_len ))
 	{
 	{
 		LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
 		LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
+		ret=E_UNSPEC;
 		goto error00;
 		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
 #ifdef POSTPONE_MSG_CLONING
 	/* lumps can be set outside of the lock, make sure that we read
 	/* lumps can be set outside of the lock, make sure that we read
 	 * the up-to-date values */
 	 * the up-to-date values */
@@ -218,85 +232,242 @@ static char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 #endif
 #endif
 	add_rm_backup = i_req->add_rm;
 	add_rm_backup = i_req->add_rm;
 	body_lumps_backup = i_req->body_lumps;
 	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 */
 	/* ... 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
 #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");
 		LOG(L_ERR, "ERROR: print_uac_request: sanity check failed\n");
 		abort();
 		abort();
 	}
 	}
 #endif
 #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:
 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->add_rm);
 	free_duped_lump_list(i_req->body_lumps);
 	free_duped_lump_list(i_req->body_lumps);
 	     /* Restore the lists from backups */
 	     /* Restore the lists from backups */
 	i_req->add_rm = add_rm_backup;
 	i_req->add_rm = add_rm_backup;
 	i_req->body_lumps = body_lumps_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:
 error00:
-	return shbuf;
+	return ret;
 }
 }
 
 
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
@@ -420,19 +591,36 @@ int add_blind_uac( /*struct cell *t*/ )
 	return 1; /* success */
 	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;
 	int ret;
 	unsigned short branch;
 	unsigned short branch;
-	char *shbuf;
+#ifdef TM_UAC_FLAGS
 	unsigned int len;
 	unsigned int len;
+#endif /* TM_UAC_FLAGS */
 
 
 	branch=t->nr_of_outgoings;
 	branch=t->nr_of_outgoings;
 	if (branch==MAX_BRANCHES) {
 	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.proto);
 		t->uac[branch].request.dst.send_flags=request?
 		t->uac[branch].request.dst.send_flags=request?
 												request->fwd_send_flags:0;
 												request->fwd_send_flags:0;
+		next_hop=0;
 	}else {
 	}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 ... */
 	/* 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;
 		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
 #ifdef TM_UAC_FLAGS
 	len = count_applied_lumps(request->add_rm, HDR_RECORDROUTE_T);
 	len = count_applied_lumps(request->add_rm, HDR_RECORDROUTE_T);
 	if(len==1)
 	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 =>
 				 *  must be changed and the send_socket might be different =>
 				 *  re-create the whole uac */
 				 *  re-create the whole uac */
 				ret=add_uac(t,  msg, &old_uac->uri, 0, &old_uac->path, 0,
 				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){
 			if (ret<0){
 				/* failed, delete the copied dns_h */
 				/* 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 
 	/* note -- there is a gap in proxy stats -- we don't update 
 	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
 	   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 */
 	/* print */
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
 		/* buffer is built localy from the INVITE which was sent out */
 		/* 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, "
 			LOG(L_WARN, "WARNING: e2e_cancel_branch: CANCEL is built locally, "
 			"thus lumps are not applied to the message!\n");
 			"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 {
 	} 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 */
 	/* success */
 	ret=1;
 	ret=1;
 
 
-
 error:
 error:
 	return ret;
 	return ret;
 }
 }
 
 
+
+
 void e2e_cancel( struct sip_msg *cancel_msg, 
 void e2e_cancel( struct sip_msg *cancel_msg, 
 	struct cell *t_cancel, struct cell *t_invite )
 	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
 #endif
 		try_new=1;
 		try_new=1;
 		branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
 		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) 
 		if (branch_ret>=0) 
 			added_branches |= 1<<branch_ret;
 			added_branches |= 1<<branch_ret;
 		else
 		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,
 		branch_ret=add_uac( t, p_msg, &current_uri,
 							(dst_uri.len) ? (&dst_uri) : &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;
 		/* pick some of the errors in case things go wrong;
 		   note that picking lowest error is just as good as
 		   note that picking lowest error is just as good as
 		   any other algorithm which picks any other negative
 		   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 );
 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 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,
 int add_uac(struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 				str* path, struct proxy_l *proxy, int proto );
 				str* path, struct proxy_l *proxy, int proto );
+*/
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
 int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
 int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
 									struct ua_client* old_uac,
 									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)
 static void remove_spaces(char *s)
 {
 {
 	char *p, *dst;
 	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';
 	*dst = '\0';
+
 }
 }
 
 
 
 

+ 1 - 0
modules_k/htable/htable.c

@@ -185,6 +185,7 @@ static int child_init(int rank)
 		fmsg = faked_msg_next();
 		fmsg = faked_msg_next();
 		rtb = get_route_type();
 		rtb = get_route_type();
 		set_route_type(REQUEST_ROUTE);
 		set_route_type(REQUEST_ROUTE);
+		init_run_actions_ctx(&ctx);
 		run_top_route(event_rt.rlist[rt], fmsg, &ctx);
 		run_top_route(event_rt.rlist[rt], fmsg, &ctx);
 		if(ctx.run_flags&DROP_R_F)
 		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;
 int caller_confirmed    = DEF_CALLER_ALWAYS_CONFIRMED;
 
 
 
 
+send_publish_t pua_send_publish;
 /** module functions */
 /** module functions */
 
 
 static int mod_init(void);
 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*);
 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[]=
 static cmd_export_t cmds[]=
 {
 {

+ 1 - 1
modules_k/pua_usrloc/pua_usrloc.h

@@ -23,7 +23,7 @@
  */
  */
 
 
 #ifndef _PUA_UL_
 #ifndef _PUA_UL_
-#define _PUA_UL
+#define _PUA_UL_
 #include "../pua/pua_bind.h"
 #include "../pua/pua_bind.h"
 
 
 extern send_publish_t pua_send_publish;
 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);
 	server_address.len= strlen(server_address.s);
 
 
 	/* import the TM auto-loading function */
 	/* 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");
 		LM_ERR("can't import load_tm\n");
 		return -1;
 		return -1;
 	}
 	}
+
 	/* let the auto-loading function load all TM stuff */
 	/* let the auto-loading function load all TM stuff */
 
 
 	if(load_tm(&tmb)==-1)
 	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: */
 /* ---- exported commands: */
 static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
 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_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);
 static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
 
 
 /* ---- module init functions: */
 /* ---- 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[]={
 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_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_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 },
 	{ "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);
 	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
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
 SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+
 include ../../Makefile.modules
 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;
 	track = 0;
 
 
 	if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 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;
 		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)
 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)) {
 		if (max_contacts && (num >= max_contacts)) {
 			rerrno = R_TOO_MANY;
 			rerrno = R_TOO_MANY;
 			ul.delete_urecord(_d, _u);
 			ul.delete_urecord(_d, _u);
-			return -1;
+			return 1;
 		}
 		}
 		num++;
 		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);
 		_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) {
 	if (num > max_contacts) {
 		rerrno = R_TOO_MANY;
 		rerrno = R_TOO_MANY;
 		return 1;
 		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
  *    > 0, update the contact
  * 3) If contact in usrloc exists and expires
  * 3) If contact in usrloc exists and expires
  *    == 0, delete contact
  *    == 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)
 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) {
 	if (max_contacts) {
 		ret = test_max_contacts(_m, _r, _c);
 		ret = test_max_contacts(_m, _r, _c);
 		if (ret != 0) {
 		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);
 			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 (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");
 			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);
 		build_contact(r->contacts, aor_filter);
 		ul.release_urecord(r);
 		ul.release_urecord(r);
 	} else {
 	} 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");
 			LOG(L_ERR, "contacts(): Error while inserting record\n");
-			ul.unlock_udomain(_d);
-			return -4;
 		}
 		}
 	}
 	}
 	ul.unlock_udomain(_d);
 	ul.unlock_udomain(_d);
-	return 0;
+	return res;
 }
 }
 
 
 #define UA_DUMMY_STR "Unknown"
 #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;
 			if (no_contacts(_t, &uid, &aor_filter) < 0) goto error;
 		}
 		}
 	} else {
 	} 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) {
 	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;
 		tmp_len=msg->first_line.u.request.uri.len;
 	}
 	}
 	if (parse_uri(tmp, tmp_len, &msg->parsed_uri)<0){
 	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;
 		msg->parsed_uri_ok=0;
 		return -1;
 		return -1;
 	}
 	}