Jelajahi Sumber

- new parse_uri function added (better + parses transport, maddr, lr, method, user, ttl )
- sip_uri structure updated
- PROTO_SCTP added :-)

Andrei Pelinescu-Onciul 22 tahun lalu
induk
melakukan
4b50480eed
7 mengubah file dengan 1571 tambahan dan 63 penghapusan
  1. 1 1
      Makefile.defs
  2. 3 0
      TODO
  3. 1 1
      ip_addr.h
  4. 79 7
      msg_translator.c
  5. 30 3
      parser/msg_parser.h
  6. 901 1
      parser/parse_uri.c
  7. 556 50
      test/p_uri.c

+ 1 - 1
Makefile.defs

@@ -21,7 +21,7 @@
 VERSION = 0
 PATCHLEVEL = 8
 SUBLEVEL =   11
-EXTRAVERSION = pre14
+EXTRAVERSION = pre15-new_parse_uri
 
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 OS = $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z]" "[a-z]")

+ 3 - 0
TODO

@@ -19,6 +19,9 @@ x (different way) add request header bitmap field for the modules
 - add conflict in debs/rpms/etc (conflict w/ older ser-mysql, ser-jabber)
 - new packages ser-radius etc
 - tcp_destroy (called on ser exit)
+- BUG:?? ipv6 only and try to send to ipv4 => getsendsocket=>0 (send_ipv6=0)
+  the reverse is also true
+- resolver should resolve [ipv6]
 
 
 High priority:

+ 1 - 1
ip_addr.h

@@ -45,7 +45,7 @@
 
 #include "dprint.h"
 
-enum sip_protos { PROTO_NONE, PROTO_UDP, PROTO_TCP, PROTO_TLS };
+enum sip_protos { PROTO_NONE, PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_SCTP };
 
 
 struct ip_addr{

+ 79 - 7
msg_translator.c

@@ -455,7 +455,20 @@ static inline int lumps_len(struct sip_msg* msg, struct socket_info* send_sock)
 				break; \
 			case SUBST_RCV_PROTO: \
 				if (msg->rcv.bind_address){ \
-					new_len+=3; \
+					switch(msg->rcv.bind_address->proto){ \
+						case PROTO_NONE: \
+						case PROTO_UDP: \
+						case PROTO_TCP: \
+						case PROTO_TLS: \
+								new_len+=3; \
+								break; \
+						case PROTO_SCTP: \
+								new_len+=4; \
+								break; \
+						default: \
+						LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
+								msg->rcv.bind_address->proto); \
+					}\
 				}else{ \
 					/* FIXME */ \
 					LOG(L_CRIT, "FIXME: null bind_address\n"); \
@@ -470,9 +483,21 @@ static inline int lumps_len(struct sip_msg* msg, struct socket_info* send_sock)
 						/* add :port_no */ \
 						new_len+=1+msg->rcv.bind_address->port_no_str.len; \
 					}\
-					if(msg->rcv.bind_address->proto!=PROTO_UDP) {\
 						/*add;transport=xxx*/ \
-							new_len+=TRANSPORT_PARAM_LEN+3; \
+					switch(msg->rcv.bind_address->proto){ \
+						case PROTO_NONE: \
+						case PROTO_UDP: \
+								break; /* udp is the default */ \
+						case PROTO_TCP: \
+						case PROTO_TLS: \
+								new_len+=TRANSPORT_PARAM_LEN+3; \
+								break; \
+						case PROTO_SCTP: \
+								new_len+=TRANSPORT_PARAM_LEN+4; \
+								break; \
+						default: \
+						LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
+								msg->rcv.bind_address->proto); \
 					}\
 				}else{ \
 					/* FIXME */ \
@@ -499,7 +524,20 @@ static inline int lumps_len(struct sip_msg* msg, struct socket_info* send_sock)
 				break; \
 			case SUBST_SND_PROTO: \
 				if (send_sock){ \
-					new_len+=3; /* tcp, udp or tls*/ \
+					switch(send_sock->proto){ \
+						case PROTO_NONE: \
+						case PROTO_UDP: \
+						case PROTO_TCP: \
+						case PROTO_TLS: \
+								new_len+=3; \
+								break; \
+						case PROTO_SCTP: \
+								new_len+=4; \
+								break; \
+						default: \
+						LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
+								send_sock->proto); \
+					}\
 				}else{ \
 					LOG(L_CRIT, "FIXME: lumps_len called with" \
 							" null send_sock\n"); \
@@ -514,9 +552,21 @@ static inline int lumps_len(struct sip_msg* msg, struct socket_info* send_sock)
 						/* add :port_no */ \
 						new_len+=1+send_sock->port_no_str.len; \
 					}\
-					if(send_sock->proto!=PROTO_UDP) {\
-						/*add;transport=xxx*/ \
-							new_len+=TRANSPORT_PARAM_LEN+3; \
+					/*add;transport=xxx*/ \
+					switch(send_sock->proto){ \
+						case PROTO_NONE: \
+						case PROTO_UDP: \
+								break; /* udp is the default */ \
+						case PROTO_TCP: \
+						case PROTO_TLS: \
+								new_len+=TRANSPORT_PARAM_LEN+3; \
+								break; \
+						case PROTO_SCTP: \
+								new_len+=TRANSPORT_PARAM_LEN+4; \
+								break; \
+						default: \
+						LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
+								send_sock->proto); \
 					}\
 				}else{ \
 					/* FIXME */ \
@@ -703,6 +753,13 @@ static inline void process_lumps(	struct sip_msg* msg,
 						memcpy(new_buf+offset, "tls", 3); \
 						offset+=3; \
 						break; \
+					case PROTO_SCTP: \
+						memcpy(new_buf+offset, TRANSPORT_PARAM, \
+								TRANSPORT_PARAM_LEN); \
+						offset+=TRANSPORT_PARAM_LEN; \
+						memcpy(new_buf+offset, "sctp", 4); \
+						offset+=4; \
+						break; \
 					default: \
 						LOG(L_CRIT, "BUG: process_lumps: unknown proto %d\n", \
 								msg->rcv.bind_address->proto); \
@@ -777,6 +834,13 @@ static inline void process_lumps(	struct sip_msg* msg,
 						memcpy(new_buf+offset, "tls", 3); \
 						offset+=3; \
 						break; \
+					case PROTO_SCTP: \
+						memcpy(new_buf+offset, TRANSPORT_PARAM, \
+								TRANSPORT_PARAM_LEN); \
+						offset+=TRANSPORT_PARAM_LEN; \
+						memcpy(new_buf+offset, "sctp", 4); \
+						offset+=4; \
+						break; \
 					default: \
 						LOG(L_CRIT, "BUG: process_lumps: unknown proto %d\n", \
 								send_sock->proto); \
@@ -802,6 +866,10 @@ static inline void process_lumps(	struct sip_msg* msg,
 						memcpy(new_buf+offset, "tls", 3); \
 						offset+=3; \
 						break; \
+					case PROTO_SCTP: \
+						memcpy(new_buf+offset, "sctp", 4); \
+						offset+=4; \
+						break; \
 					default: \
 						LOG(L_CRIT, "BUG: process_lumps: unknown proto %d\n", \
 								msg->rcv.bind_address->proto); \
@@ -828,6 +896,10 @@ static inline void process_lumps(	struct sip_msg* msg,
 						memcpy(new_buf+offset, "tls", 3); \
 						offset+=3; \
 						break; \
+					case PROTO_SCTP: \
+						memcpy(new_buf+offset, "sctp", 4); \
+						offset+=4; \
+						break; \
 					default: \
 						LOG(L_CRIT, "BUG: process_lumps: unknown proto %d\n", \
 								send_sock->proto); \

+ 30 - 3
parser/msg_parser.h

@@ -26,13 +26,14 @@
  *
  * History
  * -------
- *  2003-03-06  enum_request_method changed to begin with 1;
- *             0 reserved for invalid values; (jiri)
- *  2003-02-28  scratchpad compatibility abandoned (jiri)
  *  2003-01-28  removed scratchpad (jiri)
+ *  2003-02-28  scratchpad compatibility abandoned (jiri)
+ *  2003-03-06  enum_request_method changed to begin with 1;
+ *               0 reserved for invalid values; (jiri)
  *  2003-03-31  removed sip_msg->repl_add_rm (andrei)
  *  2003-04-01  2 macros added: GET_NEXT_HOP and GET_RURI (janakj)
  *  2003-04-04  structure for parsed inbound uri added (jiri)
+ *  2003-04-11  updated the  sip_uri structure (lots of fields added) (andrei)
  */
 
 
@@ -98,6 +99,8 @@ if (  (*tmp==(firstchar) || *tmp==((firstchar) | 32)) &&                  \
 (((m)->new_uri.s && (m)->new_uri.len) ? ((m)->new_uri) : ((m)->first_line.u.request.uri))
 
 
+#if 0
+	/* old version */
 struct sip_uri {
 	str user;     /* Username */
 	str passwd;   /* Password */
@@ -107,7 +110,31 @@ struct sip_uri {
 	str headers;  
 	unsigned short port_no;
 };
+#endif
 
+struct sip_uri {
+	str user;     /* Username */
+	str passwd;   /* Password */
+	str host;     /* Host name */
+	str port;     /* Port number */
+	str params;   /* Parameters */
+	str headers;  
+	unsigned short port_no;
+	unsigned short proto; /* from transport */
+	/* parameters */
+	str transport;
+	str ttl;
+	str user_param;
+	str maddr;
+	str method;
+	str lr;
+	/* values */
+	str transport_val;
+	str ttl_val;
+	str user_param_val;
+	str maddr_val;
+	str method_val;
+};
 
 
 struct sip_msg {

+ 901 - 1
parser/parse_uri.c

@@ -28,19 +28,916 @@
  * --------
  * 2003-04-04  convenience inbound-uri parser parse_orig_ruri
  *             introduced (jiri)
+ * 2003-04-11  new parse_uri introduced (better, parses also some parameters,
+ *              works in one pass) (andrei)
  */
 
 
 #include "parse_uri.h"
 #include <string.h>
 #include "../dprint.h"
-#include "../ut.h"    /* q_memchr */
+#ifdef PARSE_URI_OLD
+#include "../ut.h"   /* q_memchr */
+#endif
 #include "../error.h"
 
 /* buf= pointer to begining of uri (sip:[email protected]:5060;a=b?h=i)
  * len= len of uri
  * returns: fills uri & returns <0 on error or 0 if ok 
  */
+#ifndef PARSE_URI_OLD
+int parse_uri(char* buf, int len, struct sip_uri* uri)
+{
+	enum states  {	URI_INIT, URI_USER, URI_PASSWORD, URI_PASSWORD_ALPHA,
+					URI_HOST, URI_HOST_P,
+					URI_HOST6_P, URI_HOST6_END, URI_PORT, 
+					URI_PARAM, URI_PARAM_P, URI_VAL_P, URI_HEADERS,
+					/* param states */
+					/* transport */
+					PT_T, PT_R, PT_A, PT_N, PT_S, PT_P, PT_O, PT_R2, PT_T2,
+					PT_eq,
+					/* ttl */
+					      PTTL_T2, PTTL_L, PTTL_eq,
+					/* user */
+					PU_U, PU_S, PU_E, PU_R, PU_eq,
+					/* method */
+					PM_M, PM_E, PM_T, PM_H, PM_O, PM_D, PM_eq,
+					/* maddr */
+					      PMA_A, PMA_D, PMA_D2, PMA_R, PMA_eq,
+					/* lr */
+					PLR_L, PLR_R_FIN,
+					/* transport values */
+					/* udp */
+					VU_U, VU_D, VU_P_FIN,
+					/* tcp */
+					VT_T, VT_C, VT_P_FIN,
+					/* tls */
+					      VTLS_L, VTLS_S_FIN,
+					/* sctp */
+					VS_S, VS_C, VS_T, VS_P_FIN
+	};
+	enum states state;
+	char* s;
+	char* b; /* param start */
+	char *v; /* value start */
+	str* param; /* current param */
+	str* param_val; /* current param val */
+	str user;
+	str password;
+	int port_no;
+	char* p;
+	char* end;
+	char* pass;
+	int found_user;
+	int error_headers;
+	
+#define case_port( ch, var) \
+	case ch: \
+			 (var)=(var)*10+ch-'0'; \
+			 break
+			 
+#define still_at_user  \
+						if (found_user==0){ \
+							user.s=uri->host.s; \
+							if (pass){\
+								user.len=pass-user.s; \
+								password.s=pass+1; \
+								password.len=p-password.s; \
+							}else{ \
+								user.len=p-user.s; \
+							}\
+							/* everything else is 0 */ \
+							memset(uri, 0, sizeof(struct sip_uri)); \
+							/* copy user & pass */ \
+							uri->user=user; \
+							if (pass)	uri->passwd=password;  \
+							s=p+1; \
+							found_user=1;\
+							error_headers=0; \
+							state=URI_HOST; \
+						}else goto error_bad_char 
+
+#define check_host_end \
+					case ':': \
+						/* found the host */ \
+						uri->host.s=s; \
+						uri->host.len=p-s; \
+						state=URI_PORT; \
+						s=p+1; \
+						break; \
+					case ';': \
+						uri->host.s=s; \
+						uri->host.len=p-s; \
+						state=URI_PARAM; \
+						s=p+1; \
+						break; \
+					case '?': \
+						uri->host.s=s; \
+						uri->host.len=p-s; \
+						state=URI_HEADERS; \
+						s=p+1; \
+						break; \
+					case '&': \
+					case '@': \
+						goto error_bad_char 
+
+
+#define param_set(t_start, v_start) \
+					param->s=(t_start);\
+					param->len=(p-(t_start));\
+					param_val->s=(v_start); \
+					param_val->len=(p-(v_start)) 
+
+#define semicolon_case \
+					case';': \
+						if (pass){ \
+							found_user=1;/* no user, pass cannot contain ';'*/ \
+							pass=0; \
+						} \
+						state=URI_PARAM   /* new param */ 
+
+#define question_case \
+					case '?': \
+						uri->params.s=s; \
+						uri->params.len=p-s; \
+						state=URI_HEADERS; \
+						s=p+1; \
+						if (pass){ \
+							found_user=1;/* no user, pass cannot contain '?'*/ \
+							pass=0; \
+						}
+
+#define colon_case \
+					case ':': \
+						if (found_user==0){ \
+							/*might be pass but only if user not found yet*/ \
+							if (pass){ \
+								found_user=1; /* no user */ \
+								pass=0; \
+							}else{ \
+								pass=p; \
+							} \
+						} \
+						state=URI_PARAM_P /* generic param */
+
+#define param_common_cases \
+					case '@': \
+						/* ughhh, this is still the user */ \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						break; \
+					question_case; \
+						break; \
+					colon_case; \
+						break
+
+#define value_common_cases \
+					case '@': \
+						/* ughhh, this is still the user */ \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						param_set(b, v); \
+						break; \
+					question_case; \
+						param_set(b, v); \
+						break; \
+					colon_case; \
+						state=URI_VAL_P; \
+						break
+
+#define param_switch(old_state, c1, c2, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define param_switch1(old_state, c1, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+						state=(new_state); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define param_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
+			case old_state : \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state_c); \
+						break; \
+					case d1: \
+					case d2: \
+						state=(new_state_d); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define value_switch(old_state, c1, c2, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state); \
+						break; \
+					value_common_cases; \
+					default: \
+						state=URI_VAL_P; \
+				} \
+				break
+#define value_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state_c); \
+						break; \
+					case d1: \
+					case d2: \
+						state=(new_state_d); \
+						break; \
+					value_common_cases; \
+					default: \
+						state=URI_VAL_P; \
+				} \
+				break
+
+#define transport_fin(c_state, proto_no) \
+			case c_state: \
+				switch(*p){ \
+					case '@': \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						param_set(b, v); \
+						uri->proto=(proto_no); \
+						break; \
+					question_case; \
+						param_set(b, v); \
+						uri->proto=(proto_no); \
+						break; \
+					colon_case;  \
+					default: \
+						state=URI_VAL_P; \
+						break; \
+				} \
+				break
+			
+	
+
+	/* init */
+	end=buf+len;
+	p=buf+4;
+	found_user=0;
+	error_headers=0;
+	b=v=0;
+	param=param_val=0;
+	pass=0;
+	port_no=0;
+	state=URI_INIT;
+	memset(uri, 0, sizeof(struct sip_uri)); /* zero it all, just to be sure*/
+	/*look for sip:*/
+	if (len<4) goto error_too_short;
+	if (! ( ((buf[0]|0x20)=='s')&&((buf[1]|0x20)=='i')&&((buf[2]|0x20)=='p')&&
+		     (buf[3]==':') ) ) goto error_bad_uri;
+	
+	s=p;
+	for(;p<end; p++){
+		switch(state){
+			case URI_INIT:
+				switch(*p){
+					case '[':
+						/* uri =  [ipv6address]... */
+						state=URI_HOST6_P;
+						s=p;
+						break;
+					case ']':
+						/* invalid, no uri can start with ']' */
+					case ':':
+						/* the same as above for ':' */
+						goto error_bad_char;
+					case '@': /* error no user part, or
+								 be forgiving and accept it ? */
+					default:
+						state=URI_USER;
+				}
+				break; 
+			case URI_USER:
+				switch(*p){
+					case '@':
+						/* found the user*/
+						uri->user.s=s;
+						uri->user.len=p-s;
+						state=URI_HOST;
+						found_user=1;
+						s=p+1; /* skip '@' */
+						break;
+					case ':':
+						/* found the user, or the host? */
+						uri->user.s=s;
+						uri->user.len=p-s;
+						state=URI_PASSWORD;
+						s=p+1; /* skip ':' */
+						break;
+					case ';':
+						/* this could be still the user or
+						 * params?*/
+						uri->host.s=s;
+						uri->host.len=p-s;
+						state=URI_PARAM;
+						s=p+1;
+						break;
+					case '?': /* still user or headers? */
+						uri->host.s=s;
+						uri->host.len=p-s;
+						state=URI_HEADERS;
+						s=p+1;
+						break;
+						/* almost anything permitted in the user part */
+					case '[':
+					case ']': /* the user part cannot contain "[]" */
+						goto error_bad_char;
+				}
+				break;
+			case URI_PASSWORD: /* this can also be the port (missing user)*/
+				switch(*p){
+					case '@':
+						/* found the password*/
+						uri->passwd.s=s;
+						uri->passwd.len=p-s;
+						port_no=0;
+						state=URI_HOST;
+						found_user=1;
+						s=p+1; /* skip '@' */
+						break;
+					case ';':
+						/* upps this is the port */
+						uri->port.s=s;
+						uri->port.len=p-s;
+						uri->port_no=port_no;
+						/* user contains in fact the host */
+						uri->host.s=uri->user.s;
+						uri->host.len=uri->user.len;
+						uri->user.s=0;
+						uri->user.len=0;
+						state=URI_PARAM;
+						found_user=1; /*  there is no user part */
+						s=p+1;
+						break;
+					case '?':
+						/* upps this is the port */
+						uri->port.s=s;
+						uri->port.len=p-s;
+						uri->port_no=port_no;
+						/* user contains in fact the host */
+						uri->host.s=uri->user.s;
+						uri->host.len=uri->user.len;
+						uri->user.s=0;
+						uri->user.len=0;
+						state=URI_HEADERS;
+						found_user=1; /*  there is no user part */
+						s=p+1;
+						break;
+					case_port('0', port_no);
+					case_port('1', port_no);
+					case_port('2', port_no);
+					case_port('3', port_no);
+					case_port('4', port_no);
+					case_port('5', port_no);
+					case_port('6', port_no);
+					case_port('7', port_no);
+					case_port('8', port_no);
+					case_port('9', port_no);
+					case '[':
+					case ']':
+					case ':':
+						goto error_bad_char;
+					default:
+						/* it can't be the port, non number found */
+						port_no=0;
+						state=URI_PASSWORD_ALPHA;
+				}
+				break;
+			case URI_PASSWORD_ALPHA:
+				switch(*p){
+					case '@':
+						/* found the password*/
+						uri->passwd.s=s;
+						uri->passwd.len=p-s;
+						state=URI_HOST;
+						found_user=1;
+						s=p+1; /* skip '@' */
+						break;
+					case ';': /* contains non-numbers => cannot be port no*/
+					case '?':
+						goto error_bad_port;
+					case '[':
+					case ']':
+					case ':':
+						goto error_bad_char;
+				}
+				break;
+			case URI_HOST:
+				switch(*p){
+					case '[':
+						state=URI_HOST6_P;
+						break;
+					case ':': 
+					case ';':
+					case '?': /* null host name ->invalid */
+					case '&':
+					case '@': /*chars not allowed in hosts names */
+						goto error_bad_host;
+					default:
+						state=URI_HOST_P;
+				}
+				break;
+			case URI_HOST_P:
+				switch(*p){
+					check_host_end;
+				}
+				break;
+			case URI_HOST6_END:
+				switch(*p){
+					check_host_end;
+					default: /*no chars allowed after [ipv6] */
+						goto error_bad_host;
+				}
+				break;
+			case URI_HOST6_P:
+				switch(*p){
+					case ']':
+						state=URI_HOST6_END;
+						break;
+					case '[':
+					case '&':
+					case '@':
+					case ';':
+					case '?':
+						goto error_bad_host;
+				}
+				break;
+			case URI_PORT:
+				switch(*p){
+					case ';':
+						uri->port.s=s;
+						uri->port.len=p-s;
+						uri->port_no=port_no;
+						state=URI_PARAM;
+						s=p+1;
+						break;
+					case '?':
+						uri->port.s=s;
+						uri->port.len=p-s;
+						uri->port_no=port_no;
+						state=URI_HEADERS;
+						s=p+1;
+						break;
+					case_port('0', port_no);
+					case_port('1', port_no);
+					case_port('2', port_no);
+					case_port('3', port_no);
+					case_port('4', port_no);
+					case_port('5', port_no);
+					case_port('6', port_no);
+					case_port('7', port_no);
+					case_port('8', port_no);
+					case_port('9', port_no);
+					case '&':
+					case '@':
+					case ':':
+					default:
+						goto error_bad_port;
+				}
+				break;
+			case URI_PARAM: /* beginning of a new param */
+				switch(*p){
+					param_common_cases;
+					/* recognized params */
+					case 't':
+					case 'T':
+						b=p;
+						state=PT_T;
+						break;
+					case 'u':
+					case 'U':
+						b=p;
+						state=PU_U;
+						break;
+					case 'm':
+					case 'M':
+						b=p;
+						state=PM_M;
+						break;
+					case 'l':
+					case 'L':
+						b=p;
+						state=PLR_L;
+						break;
+					default:
+						state=URI_PARAM_P;
+				}
+				break;
+			case URI_PARAM_P: /* ignore current param */
+				/* supported params:
+				 *  maddr, transport, ttl, lr, user, method  */
+				switch(*p){
+					param_common_cases;
+				};
+				break;
+			/* ugly but fast param names parsing */
+			/*transport */
+			param_switch_big(PT_T,  'r', 'R', 't', 'T', PT_R, PTTL_T2);
+			param_switch(PT_R,  'a', 'A', PT_A);
+			param_switch(PT_A,  'n', 'N', PT_N);
+			param_switch(PT_N,  's', 'S', PT_S);
+			param_switch(PT_S,  'p', 'P', PT_P);
+			param_switch(PT_P,  'o', 'O', PT_O);
+			param_switch(PT_O,  'r', 'R', PT_R2);
+			param_switch(PT_R2, 't', 'T', PT_T2);
+			param_switch1(PT_T2, '=',  PT_eq);
+			/* value parsing */
+			case PT_eq:
+				param=&uri->transport;
+				param_val=&uri->transport_val;
+				switch (*p){
+					param_common_cases;
+					case 'u':
+					case 'U':
+						v=p;
+						state=VU_U;
+						break;
+					case 't':
+					case 'T':
+						v=p;
+						state=VT_T;
+						break;
+					case 's':
+					case 'S':
+						v=p;
+						state=VS_S;
+						break;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+				/* generic value */
+			case URI_VAL_P:
+				switch(*p){
+					value_common_cases;
+				}
+				break;
+			/* udp */
+			value_switch(VU_U,  'd', 'D', VU_D);
+			value_switch(VU_D,  'p', 'P', VU_P_FIN);
+			transport_fin(VU_P_FIN, PROTO_UDP);
+			/* tcp */
+			value_switch_big(VT_T,  'c', 'C', 'l', 'L', VT_C, VTLS_L);
+			value_switch(VT_C,  'p', 'P', VT_P_FIN);
+			transport_fin(VT_P_FIN, PROTO_TCP);
+			/* tls */
+			value_switch(VTLS_L, 's', 'S', VTLS_S_FIN);
+			transport_fin(VTLS_S_FIN, PROTO_TLS);
+			/* sctp */
+			value_switch(VS_S, 'c', 'C', VS_C);
+			value_switch(VS_C, 't', 'T', VS_T);
+			value_switch(VS_T, 'p', 'P', VS_P_FIN);
+			transport_fin(VS_P_FIN, PROTO_SCTP);
+			
+			/* ttl */
+			param_switch(PTTL_T2,  'l', 'L', PTTL_L);
+			param_switch1(PTTL_L,  '=', PTTL_eq);
+			case PTTL_eq:
+				param=&uri->ttl;
+				param_val=&uri->ttl_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* user param */
+			param_switch(PU_U, 's', 'S', PU_S);
+			param_switch(PU_S, 'e', 'E', PU_E);
+			param_switch(PU_E, 'r', 'R', PU_R);
+			param_switch1(PU_R, '=', PU_eq);
+			case PU_eq:
+				param=&uri->user_param;
+				param_val=&uri->user_param_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* method*/
+			param_switch_big(PM_M, 'e', 'E', 'a', 'A', PM_E, PMA_A);
+			param_switch(PM_E, 't', 'T', PM_T);
+			param_switch(PM_T, 'h', 'H', PM_H);
+			param_switch(PM_H, 'o', 'O', PM_O);
+			param_switch(PM_O, 'd', 'D', PM_D);
+			param_switch1(PM_D, '=', PM_eq);
+			case PM_eq:
+				param=&uri->method;
+				param_val=&uri->method_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+
+			/*maddr*/
+			param_switch(PMA_A,  'd', 'D', PMA_D);
+			param_switch(PMA_D,  'd', 'D', PMA_D2);
+			param_switch(PMA_D2, 'r', 'R', PMA_R);
+			param_switch1(PMA_R, '=', PMA_eq);
+			case PMA_eq:
+				param=&uri->maddr;
+				param_val=&uri->maddr_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* lr */
+			param_switch(PLR_L,  'r', 'R', PLR_R_FIN);
+			case PLR_R_FIN:
+				switch(*p){
+					case '@':
+						still_at_user; 
+						break;
+					semicolon_case; 
+						uri->lr.s=b;
+						uri->lr.len=(p-b);
+						break;
+					question_case; 
+						uri->lr.s=b;
+						uri->lr.len=(p-b);
+						break;
+					colon_case;
+						break;
+					default:
+						state=URI_PARAM_P;
+				}
+				break;
+						
+				
+				
+			case URI_HEADERS:
+				/* for now nobody needs them so we completely ignore the 
+				 * headers (they are not allowed in request uri) --andrei */
+				switch(*p){
+					case '@':
+						/* yak, we are still at user */
+						still_at_user;
+						break;
+					case ';':
+						/* we might be still parsing user, try it */
+						if (found_user) goto error_bad_char;
+						error_headers=1; /* if this is not the user
+											we have an error */
+						/* if pass is set => it cannot be user:pass
+						 * => error (';') is illegal in a header */
+						if (pass) goto error_headers;
+						break;
+					case ':':
+						if (found_user==0){
+							/*might be pass but only if user not found yet*/
+							if (pass){
+								found_user=1; /* no user */
+								pass=0;
+							}else{
+								pass=p;
+							}
+						}
+						break;
+					case '?':
+						if (pass){
+							found_user=1; /* no user, pass cannot conaint '?'*/
+							pass=0;
+						}
+						break;
+				}
+				break;
+			default:
+				goto error_bug;
+		}
+	}
+	/*end of uri */
+	switch (state){
+		case URI_INIT: /* error empy uri */
+			goto error_too_short;
+		case URI_USER:
+			/* this is the host, it can't be the user */
+			if (found_user) goto error_bad_uri;
+			uri->host.s=s;
+			uri->host.len=p-s;
+			state=URI_HOST;
+			break;
+		case URI_PASSWORD:
+			/* this is the port, it can't be the passwd */
+			if (found_user) goto error_bad_port;
+			uri->port.s=s;
+			uri->port.len=p-s;
+			uri->port_no=port_no;
+			uri->host=uri->user;
+			uri->user.s=0;
+			uri->user.len=0;
+			break;
+		case URI_PASSWORD_ALPHA:
+			/* this is the port, it can't be the passwd */
+			goto error_bad_port;
+		case URI_HOST_P:
+		case URI_HOST6_END:
+			uri->host.s=s;
+			uri->host.len=p-s;
+			break;
+		case URI_HOST: /* error: null host */
+		case URI_HOST6_P: /* error: unterminated ipv6 reference*/
+			goto error_bad_host;
+		case URI_PORT:
+			uri->port.s=s;
+			uri->port.len=p-s;
+			uri->port_no=port_no;
+			break;
+		case URI_PARAM:
+		case URI_PARAM_P:
+		/* intermediate param states */
+		case PT_T: /* transport */
+		case PT_R:
+		case PT_A:
+		case PT_N:
+		case PT_S:
+		case PT_P:
+		case PT_O:
+		case PT_R2:
+		case PT_T2:
+		case PT_eq: /* ignore empty transport params */
+		case PTTL_T2: /* ttl */
+		case PTTL_L:
+		case PTTL_eq:
+		case PU_U:  /* user */
+		case PU_S:
+		case PU_E:
+		case PU_R:
+		case PU_eq:
+		case PM_M: /* method */
+		case PM_E:
+		case PM_T:
+		case PM_H:
+		case PM_O:
+		case PM_D:
+		case PM_eq:
+		case PLR_L: /* lr */
+			uri->params.s=s;
+			uri->params.len=p-s;
+			break;
+		/* fin param states */
+		case PLR_R_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			uri->lr.s=b;
+			uri->lr.len=p-b;
+			break;
+		case URI_VAL_P:
+		/* intermediate value states */
+		case VU_U:
+		case VU_D:
+		case VT_T:
+		case VT_C:
+		case VTLS_L:
+		case VS_S:
+		case VS_C:
+		case VS_T:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			break;
+		/* fin value states */
+		case VU_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_UDP;
+			break;
+		case VT_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_TCP;
+			break;
+		case VTLS_S_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_TLS;
+			break;
+		case VS_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_SCTP;
+			break;
+		/* headers */
+		case URI_HEADERS:
+			uri->headers.s=s;
+			uri->headers.len=p-s;
+			if (error_headers) goto error_headers;
+			break;
+		default:
+			goto error_bug;
+	}
+	
+	/* do stuff */
+	DBG("parsed uri:\n user=<%.*s>(%d)\n passwd=<%.*s>(%d)\n host=<%.*s>(%d)\n"
+			" port=<%.*s>(%d): %d\n params=<%.*s>(%d)\n headers=<%.*s>(%d)\n",
+			uri->user.len, uri->user.s, uri->user.len,
+			uri->passwd.len, uri->passwd.s, uri->passwd.len,
+			uri->host.len, uri->host.s, uri->host.len,
+			uri->port.len, uri->port.s, uri->port.len, uri->port_no,
+			uri->params.len, uri->params.s, uri->params.len,
+			uri->headers.len, uri->headers.s, uri->headers.len
+		);
+	DBG(" uri params:\n   transport=<%.*s>, val=<%.*s>, proto=%d\n",
+			uri->transport.len, uri->transport.s, uri->transport_val.len,
+			uri->transport_val.s, uri->proto);
+	DBG("   user-param=<%.*s>, val=<%.*s>\n",
+			uri->user_param.len, uri->user_param.s, uri->user_param_val.len,
+			uri->user_param_val.s);
+	DBG("   method=<%.*s>, val=<%.*s>\n",
+			uri->method.len, uri->method.s, uri->method_val.len,
+			uri->method_val.s);
+	DBG("   ttl=<%.*s>, val=<%.*s>\n",
+			uri->ttl.len, uri->ttl.s, uri->ttl_val.len,
+			uri->ttl_val.s);
+	DBG("   maddr=<%.*s>, val=<%.*s>\n",
+			uri->maddr.len, uri->maddr.s, uri->maddr_val.len,
+			uri->maddr_val.s);
+	DBG("   lr=<%.*s>\n", uri->lr.len, uri->lr.s); 
+	return 0;
+	
+error_too_short:
+	LOG(L_ERR, "ERROR: parse_uri: uri too short: <%.*s> (%d)\n",
+			len, buf, len);
+	return -1;
+error_bad_char:
+	LOG(L_ERR, "ERROR: parse_uri: bad char '%c' in state %d"
+			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
+			*p, state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
+error_bad_host:
+	LOG(L_ERR, "ERROR: parse_uri: bad host in uri (error at char %c in"
+			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
+			*p, state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
+error_bad_port:
+	LOG(L_ERR, "ERROR: parse_uri: bad port in uri (error at char %c in"
+			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
+			*p, state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
+error_bad_uri:
+	LOG(L_ERR, "ERROR: parse_uri: bad uri,  state %d"
+			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
+			 state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
+error_headers:
+	LOG(L_ERR, "ERROR: parse_uri: bad uri headers: <%.*s>(%d)"
+			" / <%.*s>(%d)\n",
+			uri->headers.len, uri->headers.s, uri->headers.len,
+			len, buf, len);
+	return -1;
+error_bug:
+	LOG(L_CRIT, "BUG: parse_uri: bad  state %d"
+			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
+			 state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
+}
+
+#else /* PARSE_URI_OLD */
+
 int parse_uri(char *buf, int len, struct sip_uri* uri)
 {
 	char* next, *end;
@@ -176,6 +1073,9 @@ int parse_uri(char *buf, int len, struct sip_uri* uri)
 
 	return ret;
 }
+#endif
+
+
 
 
 static inline int _parse_ruri(str *uri,

+ 556 - 50
test/p_uri.c

@@ -6,6 +6,8 @@
 #define DBG printf
 #define LOG(lev, fmt, args...) printf(fmt, ## args)
 
+enum {PROTO_NONE, PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_SCTP };
+
 struct sip_uri {
 	str user;     /* Username */
 	str passwd;   /* Password */
@@ -14,48 +16,95 @@ struct sip_uri {
 	str params;   /* Parameters */
 	str headers;  
 	unsigned short port_no;
+	unsigned short proto; /* from transport */
+	/* parameters */
+	str transport;
+	str ttl;
+	str user_param;
+	str maddr;
+	str method;
+	str lr;
+	/* values */
+	str transport_val;
+	str ttl_val;
+	str user_param_val;
+	str maddr_val;
+	str method_val;
 };
 
 
 
 int parse_uri(char* buf, int len, struct sip_uri* uri)
 {
-	enum states  {	URI_INIT, URI_USER, URI_PASSWORD, URI_HOST, URI_HOST_P,
-					URI_HOST6_P, URI_HOST6_END, URI_PORT,
-					URI_PARAM, URI_HEADERS };
+	enum states  {	URI_INIT, URI_USER, URI_PASSWORD, URI_PASSWORD_ALPHA,
+					URI_HOST, URI_HOST_P,
+					URI_HOST6_P, URI_HOST6_END, URI_PORT, 
+					URI_PARAM, URI_PARAM_P, URI_VAL_P, URI_HEADERS,
+					/* param states */
+					/* transport */
+					PT_T, PT_R, PT_A, PT_N, PT_S, PT_P, PT_O, PT_R2, PT_T2,
+					PT_eq,
+					/* ttl */
+					      PTTL_T2, PTTL_L, PTTL_eq,
+					/* user */
+					PU_U, PU_S, PU_E, PU_R, PU_eq,
+					/* method */
+					PM_M, PM_E, PM_T, PM_H, PM_O, PM_D, PM_eq,
+					/* maddr */
+					      PMA_A, PMA_D, PMA_D2, PMA_R, PMA_eq,
+					/* lr */
+					PLR_L, PLR_R_FIN,
+					/* transport values */
+					/* udp */
+					VU_U, VU_D, VU_P_FIN,
+					/* tcp */
+					VT_T, VT_C, VT_P_FIN,
+					/* tls */
+					      VTLS_L, VTLS_S_FIN,
+					/* sctp */
+					VS_S, VS_C, VS_T, VS_P_FIN
+	};
 	enum states state;
 	char* s;
+	char* b; /* param start */
+	char *v; /* value start */
+	str* param; /* current param */
+	str* param_val; /* current param val */
+	str user;
+	str password;
+	int port_no;
 	char* p;
 	char* end;
 	char* pass;
 	int found_user;
 	int error_headers;
 	
+#define case_port( ch, var) \
+	case ch: \
+			 (var)=(var)*10+ch-'0'; \
+			 break
+			 
 #define still_at_user  \
 						if (found_user==0){ \
-							uri->user.s=uri->host.s; \
+							user.s=uri->host.s; \
 							if (pass){\
-								uri->user.len=pass-uri->user.s; \
-								uri->passwd.s=pass+1; \
-								uri->passwd.len=p-uri->passwd.s; \
-								pass=0; \
+								user.len=pass-user.s; \
+								password.s=pass+1; \
+								password.len=p-password.s; \
 							}else{ \
-								uri->user.len=p-uri->user.s; \
+								user.len=p-user.s; \
 							}\
 							/* everything else is 0 */ \
-							uri->host.len=0; \
-							uri->port.s=0; \
-							uri->port.len=0; \
-							uri->params.s=0; \
-							uri->params.len=0; \
-							uri->headers.s=0; \
-							uri->headers.len=0; \
-							/* more zeroing */ \
+							memset(uri, 0, sizeof(struct sip_uri)); \
+							/* copy user & pass */ \
+							uri->user=user; \
+							if (pass)	uri->passwd=password;  \
 							s=p+1; \
 							found_user=1;\
 							error_headers=0; \
 							state=URI_HOST; \
 						}else goto error_bad_char 
+
 #define check_host_end \
 					case ':': \
 						/* found the host */ \
@@ -79,12 +128,173 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 					case '&': \
 					case '@': \
 						goto error_bad_char 
+
+
+#define param_set(t_start, v_start) \
+					param->s=(t_start);\
+					param->len=(p-(t_start));\
+					param_val->s=(v_start); \
+					param_val->len=(p-(v_start)) 
+
+#define semicolon_case \
+					case';': \
+						if (pass){ \
+							found_user=1;/* no user, pass cannot contain ';'*/ \
+							pass=0; \
+						} \
+						state=URI_PARAM   /* new param */ 
+
+#define question_case \
+					case '?': \
+						uri->params.s=s; \
+						uri->params.len=p-s; \
+						state=URI_HEADERS; \
+						s=p+1; \
+						if (pass){ \
+							found_user=1;/* no user, pass cannot contain '?'*/ \
+							pass=0; \
+						}
+
+#define colon_case \
+					case ':': \
+						if (found_user==0){ \
+							/*might be pass but only if user not found yet*/ \
+							if (pass){ \
+								found_user=1; /* no user */ \
+								pass=0; \
+							}else{ \
+								pass=p; \
+							} \
+						} \
+						state=URI_PARAM_P /* generic param */
+
+#define param_common_cases \
+					case '@': \
+						/* ughhh, this is still the user */ \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						break; \
+					question_case; \
+						break; \
+					colon_case; \
+						break
+
+#define value_common_cases \
+					case '@': \
+						/* ughhh, this is still the user */ \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						param_set(b, v); \
+						break; \
+					question_case; \
+						param_set(b, v); \
+						break; \
+					colon_case; \
+						state=URI_VAL_P; \
+						break
+
+#define param_switch(old_state, c1, c2, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define param_switch1(old_state, c1, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+						state=(new_state); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define param_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
+			case old_state : \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state_c); \
+						break; \
+					case d1: \
+					case d2: \
+						state=(new_state_d); \
+						break; \
+					param_common_cases; \
+					default: \
+						state=URI_PARAM_P; \
+				} \
+				break
+#define value_switch(old_state, c1, c2, new_state) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state); \
+						break; \
+					value_common_cases; \
+					default: \
+						state=URI_VAL_P; \
+				} \
+				break
+#define value_switch_big(old_state, c1, c2, d1, d2, new_state_c, new_state_d) \
+			case old_state: \
+				switch(*p){ \
+					case c1: \
+					case c2: \
+						state=(new_state_c); \
+						break; \
+					case d1: \
+					case d2: \
+						state=(new_state_d); \
+						break; \
+					value_common_cases; \
+					default: \
+						state=URI_VAL_P; \
+				} \
+				break
+
+#define transport_fin(c_state, proto_no) \
+			case c_state: \
+				switch(*p){ \
+					case '@': \
+						still_at_user; \
+						break; \
+					semicolon_case; \
+						param_set(b, v); \
+						uri->proto=(proto_no); \
+						break; \
+					question_case; \
+						param_set(b, v); \
+						uri->proto=(proto_no); \
+						break; \
+					colon_case;  \
+					default: \
+						state=URI_VAL_P; \
+						break; \
+				} \
+				break
+			
 	
+
+	/* init */
 	end=buf+len;
 	p=buf+4;
 	found_user=0;
 	error_headers=0;
+	b=v=0;
+	param=param_val=0;
 	pass=0;
+	port_no=0;
 	state=URI_INIT;
 	memset(uri, 0, sizeof(struct sip_uri)); /* zero it all, just to be sure*/
 	/*look for sip:*/
@@ -156,6 +366,7 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 						/* found the password*/
 						uri->passwd.s=s;
 						uri->passwd.len=p-s;
+						port_no=0;
 						state=URI_HOST;
 						found_user=1;
 						s=p+1; /* skip '@' */
@@ -164,6 +375,7 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 						/* upps this is the port */
 						uri->port.s=s;
 						uri->port.len=p-s;
+						uri->port_no=port_no;
 						/* user contains in fact the host */
 						uri->host.s=uri->user.s;
 						uri->host.len=uri->user.len;
@@ -177,6 +389,7 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 						/* upps this is the port */
 						uri->port.s=s;
 						uri->port.len=p-s;
+						uri->port_no=port_no;
 						/* user contains in fact the host */
 						uri->host.s=uri->user.s;
 						uri->host.len=uri->user.len;
@@ -186,6 +399,39 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 						found_user=1; /*  there is no user part */
 						s=p+1;
 						break;
+					case_port('0', port_no);
+					case_port('1', port_no);
+					case_port('2', port_no);
+					case_port('3', port_no);
+					case_port('4', port_no);
+					case_port('5', port_no);
+					case_port('6', port_no);
+					case_port('7', port_no);
+					case_port('8', port_no);
+					case_port('9', port_no);
+					case '[':
+					case ']':
+					case ':':
+						goto error_bad_char;
+					default:
+						/* it can't be the port, non number found */
+						port_no=0;
+						state=URI_PASSWORD_ALPHA;
+				}
+				break;
+			case URI_PASSWORD_ALPHA:
+				switch(*p){
+					case '@':
+						/* found the password*/
+						uri->passwd.s=s;
+						uri->passwd.len=p-s;
+						state=URI_HOST;
+						found_user=1;
+						s=p+1; /* skip '@' */
+						break;
+					case ';': /* contains non-numbers => cannot be port no*/
+					case '?':
+						goto error_bad_port;
 					case '[':
 					case ']':
 					case ':':
@@ -237,58 +483,217 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 					case ';':
 						uri->port.s=s;
 						uri->port.len=p-s;
+						uri->port_no=port_no;
 						state=URI_PARAM;
 						s=p+1;
 						break;
 					case '?':
 						uri->port.s=s;
 						uri->port.len=p-s;
+						uri->port_no=port_no;
 						state=URI_HEADERS;
 						s=p+1;
 						break;
+					case_port('0', port_no);
+					case_port('1', port_no);
+					case_port('2', port_no);
+					case_port('3', port_no);
+					case_port('4', port_no);
+					case_port('5', port_no);
+					case_port('6', port_no);
+					case_port('7', port_no);
+					case_port('8', port_no);
+					case_port('9', port_no);
 					case '&':
 					case '@':
 					case ':':
-						goto error_bad_char;
+					default:
+						goto error_bad_port;
+				}
+				break;
+			case URI_PARAM: /* beginning of a new param */
+				switch(*p){
+					param_common_cases;
+					/* recognized params */
+					case 't':
+					case 'T':
+						b=p;
+						state=PT_T;
+						break;
+					case 'u':
+					case 'U':
+						b=p;
+						state=PU_U;
+						break;
+					case 'm':
+					case 'M':
+						b=p;
+						state=PM_M;
+						break;
+					case 'l':
+					case 'L':
+						b=p;
+						state=PLR_L;
+						break;
+					default:
+						state=URI_PARAM_P;
 				}
 				break;
-			case URI_PARAM:
+			case URI_PARAM_P: /* ignore current param */
 				/* supported params:
-				 *  maddr, transport, ttl, lr  */
+				 *  maddr, transport, ttl, lr, user, method  */
+				switch(*p){
+					param_common_cases;
+				};
+				break;
+			/* ugly but fast param names parsing */
+			/*transport */
+			param_switch_big(PT_T,  'r', 'R', 't', 'T', PT_R, PTTL_T2);
+			param_switch(PT_R,  'a', 'A', PT_A);
+			param_switch(PT_A,  'n', 'N', PT_N);
+			param_switch(PT_N,  's', 'S', PT_S);
+			param_switch(PT_S,  'p', 'P', PT_P);
+			param_switch(PT_P,  'o', 'O', PT_O);
+			param_switch(PT_O,  'r', 'R', PT_R2);
+			param_switch(PT_R2, 't', 'T', PT_T2);
+			param_switch1(PT_T2, '=',  PT_eq);
+			/* value parsing */
+			case PT_eq:
+				param=&uri->transport;
+				param_val=&uri->transport_val;
+				switch (*p){
+					param_common_cases;
+					case 'u':
+					case 'U':
+						v=p;
+						state=VU_U;
+						break;
+					case 't':
+					case 'T':
+						v=p;
+						state=VT_T;
+						break;
+					case 's':
+					case 'S':
+						v=p;
+						state=VS_S;
+						break;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+				/* generic value */
+			case URI_VAL_P:
+				switch(*p){
+					value_common_cases;
+				}
+				break;
+			/* udp */
+			value_switch(VU_U,  'd', 'D', VU_D);
+			value_switch(VU_D,  'p', 'P', VU_P_FIN);
+			transport_fin(VU_P_FIN, PROTO_UDP);
+			/* tcp */
+			value_switch_big(VT_T,  'c', 'C', 'l', 'L', VT_C, VTLS_L);
+			value_switch(VT_C,  'p', 'P', VT_P_FIN);
+			transport_fin(VT_P_FIN, PROTO_TCP);
+			/* tls */
+			value_switch(VTLS_L, 's', 'S', VTLS_S_FIN);
+			transport_fin(VTLS_S_FIN, PROTO_TLS);
+			/* sctp */
+			value_switch(VS_S, 'c', 'C', VS_C);
+			value_switch(VS_C, 't', 'T', VS_T);
+			value_switch(VS_T, 'p', 'P', VS_P_FIN);
+			transport_fin(VS_P_FIN, PROTO_SCTP);
+			
+			/* ttl */
+			param_switch(PTTL_T2,  'l', 'L', PTTL_L);
+			param_switch1(PTTL_L,  '=', PTTL_eq);
+			case PTTL_eq:
+				param=&uri->ttl;
+				param_val=&uri->ttl_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* user param */
+			param_switch(PU_U, 's', 'S', PU_S);
+			param_switch(PU_S, 'e', 'E', PU_E);
+			param_switch(PU_E, 'r', 'R', PU_R);
+			param_switch1(PU_R, '=', PU_eq);
+			case PU_eq:
+				param=&uri->user_param;
+				param_val=&uri->user_param_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* method*/
+			param_switch_big(PM_M, 'e', 'E', 'a', 'A', PM_E, PMA_A);
+			param_switch(PM_E, 't', 'T', PM_T);
+			param_switch(PM_T, 'h', 'H', PM_H);
+			param_switch(PM_H, 'o', 'O', PM_O);
+			param_switch(PM_O, 'd', 'D', PM_D);
+			param_switch1(PM_D, '=', PM_eq);
+			case PM_eq:
+				param=&uri->method;
+				param_val=&uri->method_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+
+			/*maddr*/
+			param_switch(PMA_A,  'd', 'D', PMA_D);
+			param_switch(PMA_D,  'd', 'D', PMA_D2);
+			param_switch(PMA_D2, 'r', 'R', PMA_R);
+			param_switch1(PMA_R, '=', PMA_eq);
+			case PMA_eq:
+				param=&uri->maddr;
+				param_val=&uri->maddr_val;
+				switch(*p){
+					param_common_cases;
+					default:
+						v=p;
+						state=URI_VAL_P;
+				}
+				break;
+			
+			/* lr */
+			param_switch(PLR_L,  'r', 'R', PLR_R_FIN);
+			case PLR_R_FIN:
 				switch(*p){
 					case '@':
-						/* ughhh, this is still the user */
-						still_at_user;
+						still_at_user; 
 						break;
-					case ';':
-						if (pass){
-							found_user=1; /* no user, pass cannot contain ';'*/
-							pass=0;
-						}
+					semicolon_case; 
+						uri->lr.s=b;
+						uri->lr.len=(p-b);
 						break;
-					case '?':
-						uri->params.s=s;
-						uri->params.len=p-s;
-						state=URI_HEADERS;
-						s=p+1;
-						if (pass){
-							found_user=1; /* no user, pass cannot contain '?'*/
-							pass=0;
-						}
+					question_case; 
+						uri->lr.s=b;
+						uri->lr.len=(p-b);
 						break;
-					case ':':
-						if (found_user==0){
-							/*might be pass but only if user not found yet*/
-							if (pass){
-								found_user=1; /* no user */
-								pass=0;
-							}else{
-								pass=p;
-							}
-						}
+					colon_case;
 						break;
-				};
+					default:
+						state=URI_PARAM_P;
+				}
 				break;
+						
+				
+				
 			case URI_HEADERS:
 				/* for now nobody needs them so we completely ignore the 
 				 * headers (they are not allowed in request uri) --andrei */
@@ -342,13 +747,17 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 			break;
 		case URI_PASSWORD:
 			/* this is the port, it can't be the passwd */
-			if (found_user) goto error_bad_uri;
+			if (found_user) goto error_bad_port;
 			uri->port.s=s;
 			uri->port.len=p-s;
+			uri->port_no=port_no;
 			uri->host=uri->user;
 			uri->user.s=0;
 			uri->user.len=0;
 			break;
+		case URI_PASSWORD_ALPHA:
+			/* this is the port, it can't be the passwd */
+			goto error_bad_port;
 		case URI_HOST_P:
 		case URI_HOST6_END:
 			uri->host.s=s;
@@ -360,11 +769,87 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 		case URI_PORT:
 			uri->port.s=s;
 			uri->port.len=p-s;
+			uri->port_no=port_no;
 			break;
 		case URI_PARAM:
+		case URI_PARAM_P:
+		/* intermediate param states */
+		case PT_T: /* transport */
+		case PT_R:
+		case PT_A:
+		case PT_N:
+		case PT_S:
+		case PT_P:
+		case PT_O:
+		case PT_R2:
+		case PT_T2:
+		case PT_eq: /* ignore empty transport params */
+		case PTTL_T2: /* ttl */
+		case PTTL_L:
+		case PTTL_eq:
+		case PU_U:  /* user */
+		case PU_S:
+		case PU_E:
+		case PU_R:
+		case PU_eq:
+		case PM_M: /* method */
+		case PM_E:
+		case PM_T:
+		case PM_H:
+		case PM_O:
+		case PM_D:
+		case PM_eq:
+		case PLR_L: /* lr */
+			uri->params.s=s;
+			uri->params.len=p-s;
+			break;
+		/* fin param states */
+		case PLR_R_FIN:
 			uri->params.s=s;
 			uri->params.len=p-s;
+			uri->lr.s=b;
+			uri->lr.len=p-b;
 			break;
+		case URI_VAL_P:
+		/* intermediate value states */
+		case VU_U:
+		case VU_D:
+		case VT_T:
+		case VT_C:
+		case VTLS_L:
+		case VS_S:
+		case VS_C:
+		case VS_T:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			break;
+		/* fin value states */
+		case VU_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_UDP;
+			break;
+		case VT_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_TCP;
+			break;
+		case VTLS_S_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_TLS;
+			break;
+		case VS_P_FIN:
+			uri->params.s=s;
+			uri->params.len=p-s;
+			param_set(b, v);
+			uri->proto=PROTO_SCTP;
+			break;
+		/* headers */
 		case URI_HEADERS:
 			uri->headers.s=s;
 			uri->headers.len=p-s;
@@ -376,14 +861,30 @@ int parse_uri(char* buf, int len, struct sip_uri* uri)
 	
 	/* do stuff */
 	DBG("parsed uri:\n user=<%.*s>(%d)\n passwd=<%.*s>(%d)\n host=<%.*s>(%d)\n"
-			" port=<%.*s>(%d)\n params=<%.*s>(%d)\n headers=<%.*s>(%d)\n",
+			" port=<%.*s>(%d): %d\n params=<%.*s>(%d)\n headers=<%.*s>(%d)\n",
 			uri->user.len, uri->user.s, uri->user.len,
 			uri->passwd.len, uri->passwd.s, uri->passwd.len,
 			uri->host.len, uri->host.s, uri->host.len,
-			uri->port.len, uri->port.s, uri->port.len,
+			uri->port.len, uri->port.s, uri->port.len, uri->port_no,
 			uri->params.len, uri->params.s, uri->params.len,
 			uri->headers.len, uri->headers.s, uri->headers.len
 		);
+	DBG(" uri params:\n   transport=<%.*s>, val=<%.*s>, proto=%d\n",
+			uri->transport.len, uri->transport.s, uri->transport_val.len,
+			uri->transport_val.s, uri->proto);
+	DBG("   user-param=<%.*s>, val=<%.*s>\n",
+			uri->user_param.len, uri->user_param.s, uri->user_param_val.len,
+			uri->user_param_val.s);
+	DBG("   method=<%.*s>, val=<%.*s>\n",
+			uri->method.len, uri->method.s, uri->method_val.len,
+			uri->method_val.s);
+	DBG("   ttl=<%.*s>, val=<%.*s>\n",
+			uri->ttl.len, uri->ttl.s, uri->ttl_val.len,
+			uri->ttl_val.s);
+	DBG("   maddr=<%.*s>, val=<%.*s>\n",
+			uri->maddr.len, uri->maddr.s, uri->maddr_val.len,
+			uri->maddr_val.s);
+	DBG("   lr=<%.*s>\n", uri->lr.len, uri->lr.s); 
 	return 0;
 	
 error_too_short:
@@ -400,6 +901,11 @@ error_bad_host:
 			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
 			*p, state, (p-buf), buf, (p-buf), len, buf, len);
 	return -1;
+error_bad_port:
+	LOG(L_ERR, "ERROR: parse_uri: bad port in uri (error at char %c in"
+			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
+			*p, state, (p-buf), buf, (p-buf), len, buf, len);
+	return -1;
 error_bad_uri:
 	LOG(L_ERR, "ERROR: parse_uri: bad uri,  state %d"
 			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",