Exemplo n.º 1
0
/*
 * This method is used to parse Replaces header body.
 *
 * params: buf : pointer to Replaces body
 *         buf_len : Replaces body length
 *         replaces_b : pointer to parsing structure
 * returns 0 on success,
 *        -1 on failure.
 */
int parse_replaces_body(char* buf, int buf_len, struct replaces_body* replaces_b)
{
	enum states {CLID, PARAM, PARAM_P, PARAM_VAL_P, VAL_P,
			/* param states */
			/* to-tag */
			/* 5 - 11 */
			TT_T, TT_O, TT__, TT_T2, TT_A, TT_G, TT_eq,
			/* from-tag */
			/* 12 - 20 */
			FT_F, FT_R, FT_O, FT_M, FT__, FT_T, FT_A, FT_G, FT_eq,
			/* early-only */
			/* 21 - 31 */
			EO_E, EO_A, EO_R, EO_L, EO_Y, EO__, EO_O, EO_N, EO_L2, EO_Y2_FIN, EO_eq
	};
	register enum states state;
	char* b = NULL;	/* current param val */
	char* v = NULL;	/* current param val */
	str* param = NULL;
	str* param_val = NULL;
	register char* p;
	char* end;


#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 u_param_set(t_start, v_start)			\
			/* FIXME: save unknown params */


#define semicolon_case					\
			case';':			\
				state=PARAM /* new param */


#define param_common_cases				\
			semicolon_case;			\
				break

#define u_param_common_cases				\
			semicolon_case;			\
				u_param_set(b, v);	\
				break


#define param_single_switch(old_state, c1, new_state)	\
		case old_state:				\
			switch(*p){			\
			case c1:			\
				state=(new_state);	\
				break;			\
			u_param_common_cases;		\
			default:			\
				state=PARAM_P;		\
			}				\
			break

#define param_switch(old_state, c1, c2, new_state)	\
		case old_state:				\
			switch(*p){			\
			case c1:			\
			case c2:			\
				state=(new_state);	\
				break;			\
			u_param_common_cases;		\
			default:			\
				state=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=PARAM_P;		\
			}				\
			break


#define value_common_cases				\
			semicolon_case;			\
				param_set(b, v);	\
				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=VAL_P;		\
			}				\
			break


	/* init */
	p = buf;
	end = buf + buf_len;
	state = CLID;
	memset(replaces_b, 0, sizeof(struct replaces_body));
	for(;p<end; p++){
		//LM_DBG("got[%c] in state[%d]\n",*p,state);
		switch((unsigned char)state){
		case CLID:
			switch(*p){
			case ';':
				replaces_b->callid_val.s=buf;
				replaces_b->callid_val.len=p-buf;
				state = PARAM;
				break;
			}
			break;
		case PARAM: /* beginning of a new param */
			switch(*p){
			param_common_cases;
			/* recognized params */
			case 't':
			case 'T':
				b = p;
				state=TT_T;
				break;
			case 'f':
			case 'F':
				b = p;
				state=FT_F;
				break;
			case 'e':
			case 'E':
				b = p;
				state=EO_E;
				break;
			default:
				b = p;
				state=PARAM;
			}
			break;
		case PARAM_P: /* ignore current param */
			/* supported params: to-tag, from-tag, early-only */
			switch(*p){
			u_param_common_cases;
			case '=':
				v=p+1;
				state=PARAM_VAL_P;
				break;
			};
			break;
		case PARAM_VAL_P: /* value of the ignored current param */
			switch(*p){
			u_param_common_cases;
			};
			break;
		case VAL_P:
			switch(*p){
			value_common_cases;
			}
			break;

		/* early-only param */
		param_switch(EO_E, 'a', 'A', EO_A);
		param_switch(EO_A, 'r', 'R', EO_R);
		param_switch(EO_R, 'l', 'L', EO_L);
		param_switch(EO_L, 'y', 'Y', EO_Y);
		param_single_switch(EO_Y, '-', EO__);
		param_switch(EO__, 'o', 'O', EO_O);
		param_switch(EO_O, 'n', 'N', EO_N);
		param_switch(EO_N, 'l', 'L', EO_L2);
		param_switch(EO_L2, 'y', 'Y', EO_Y2_FIN);
		case EO_Y2_FIN:
			switch(*p){
			case '=':
				state = EO_eq;
				break;
			semicolon_case;
				replaces_b->early_only.s=b;
				replaces_b->early_only.len=(p-b);
				break;
			default:
				state=PARAM_P;
			}
			break;
			/* handle early-only=something case */
		case EO_eq:
			param = &replaces_b->early_only;
			param_val = &replaces_b->early_only_val;
			switch(*p){
			param_common_cases;
			default:
				v=p;
				state = VAL_P;
			}
			break;

		/* from-tag param */
		param_switch(FT_F, 'r', 'R', FT_R);
		param_switch(FT_R, 'o', 'O', FT_O);
		param_switch(FT_O, 'm', 'M', FT_M);
		param_single_switch(FT_M, '-', FT__);
		param_switch(FT__, 't', 'T', FT_T);
		param_switch(FT_T, 'a', 'A', FT_A);
		param_switch(FT_A, 'g', 'G', FT_G);
		param_switch1(FT_G, '=', FT_eq);
		case FT_eq:
			param = &replaces_b->from_tag;
			param_val = &replaces_b->from_tag_val;
			switch(*p){
			param_common_cases;
			default:
				v=p;
				state = VAL_P;
			}
			break;

		/* to-tag param */
		param_switch(TT_T, 'o', 'O', TT_O);
		param_single_switch(TT_O, '-', TT__);
		param_switch(TT__, 't', 'T', TT_T2);
		param_switch(TT_T2, 'a', 'A', TT_A);
		param_switch(TT_A, 'g', 'G', TT_G);
		param_switch1(TT_G, '=', TT_eq);
		case TT_eq:
			param = &replaces_b->to_tag;
			param_val = &replaces_b->to_tag_val;
			switch(*p){
			param_common_cases;
			default:
				v=p;
				state = VAL_P;
			}
			break;

		default:
			LM_CRIT("bad state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n",
			state, (int)(p-buf), ZSW(buf), (int)(p-buf), buf_len, ZSW(buf), buf_len);
			return -1;
		}
	}

	switch(state){
	case CLID:
		replaces_b->callid_val.s=buf;
		replaces_b->callid_val.len=p-buf;
		break;
	case PARAM:
	case PARAM_P:
	case PARAM_VAL_P:
		u_param_set(b, v);
	/* intermediate param states */
	case EO_E: /* early-only */
	case EO_A:
	case EO_R:
	case EO_L:
	case EO_Y:
	case EO__:
	case EO_O:
	case EO_N:
	case EO_L2:
	case FT_F: /* from-tag */
	case FT_R:
	case FT_O:
	case FT_M:
	case FT__:
	case FT_T:
	case FT_A:
	case FT_G:
	case FT_eq: /* ignore empty from-tag params */
	case TT_T: /* to-tag */
	case TT_O:
	case TT__:
	case TT_T2:
	case TT_A:
	case TT_G:
	case TT_eq: /* ignore empty to-tag params */
		break;
	/* fin param states */
	case EO_Y2_FIN:
	case EO_eq:
		replaces_b->early_only.s=b;
		replaces_b->early_only.len=p-b;
		break;
	case VAL_P:
		param_set(b, v);
		break;

	default:
		LM_CRIT("bad state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n",
			state, (int)(p-buf), ZSW(buf), (int)(p-buf), buf_len, ZSW(buf), buf_len);
		return -1;
	}

	return 0;
}
Exemplo n.º 2
0
/* 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 
 */
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_PARAM_VAL_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, PLR_eq,
					/* r2 */
					PR2_R, PR2_2_FIN, PR2_eq,
					
					/* 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
	};
	register 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;
	register char* p;
	char* end;
	char* pass;
	int found_user;
	int error_headers;
	unsigned int scheme;
	uri_type backup;
#ifdef EXTRA_DEBUG
	int i;
#endif
	
#define SIP_SCH		0x3a706973
#define SIPS_SCH	0x73706973
#define TEL_SCH		0x3a6c6574
	
#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; \
							}\
							/* save the uri type/scheme */ \
							backup=uri->type; \
							/* everything else is 0 */ \
							memset(uri, 0, sizeof(struct sip_uri)); \
							/* restore the scheme, copy user & pass */ \
							uri->type=backup; \
							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 u_param_set(t_start, v_start) \
			if (uri->u_params_no < URI_MAX_U_PARAMS){ \
				if((v_start)>(t_start)){ \
					uri->u_name[uri->u_params_no].s=(t_start); \
					uri->u_name[uri->u_params_no].len=((v_start)-(t_start)-1); \
					if(p>(v_start)) { \
						uri->u_val[uri->u_params_no].s=(v_start); \
						uri->u_val[uri->u_params_no].len=(p-(v_start)); \
					} \
				} else { \
					uri->u_name[uri->u_params_no].s=(t_start); \
					uri->u_name[uri->u_params_no].len=(p-(t_start)); \
				} \
				uri->u_params_no++; \
			} else { \
				LM_ERR("unknown URI param list excedeed\n"); \
			}

#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 u_param_common_cases \
					case '@': \
						/* ughhh, this is still the user */ \
						still_at_user; \
						break; \
					semicolon_case; \
						u_param_set(b, v); \
						break; \
					question_case; \
						u_param_set(b, v); \
						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; \
					u_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; \
					u_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;
	password.s = 0;
	password.len = 0;
	port_no=0;
	state=URI_INIT;
	memset(uri, 0, sizeof(struct sip_uri)); /* zero it all, just to be sure*/
	/*look for sip:, sips: or tel:*/
	if (len<5) goto error_too_short;
	scheme=buf[0]+(buf[1]<<8)+(buf[2]<<16)+(buf[3]<<24);
	scheme|=0x20202020;
	if (scheme==SIP_SCH){
		uri->type=SIP_URI_T;
	}else if(scheme==SIPS_SCH){
		if(buf[4]==':'){ p++; uri->type=SIPS_URI_T;}
		else goto error_bad_uri;
	}else if (scheme==TEL_SCH){
		uri->type=TEL_URI_T;
	}else goto error_bad_uri;
	
	s=p;
	for(;p<end; p++){
		switch((unsigned char)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 */
						goto error_bad_char;
					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;
					case 'r':
					case 'R':
						b=p;
						state=PR2_R;
						break;
					default:
						b=p;
						state=URI_PARAM_P;
				}
				break;
			case URI_PARAM_P: /* ignore current param */
				/* supported params:
				 *  maddr, transport, ttl, lr, user, method, r2  */
				switch(*p){
					u_param_common_cases;
					case '=':
						v=p + 1;
						state=URI_PARAM_VAL_P;
						break;
				};
				break;
			case URI_PARAM_VAL_P: /* value of the ignored current param */
				switch(*p){
					u_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;
					case '=':
						state=PLR_eq;
						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;
				/* handle lr=something case */
			case PLR_eq:
				param=&uri->lr;
				param_val=&uri->lr_val;
				switch(*p){
					param_common_cases;
					default:
						v=p;
						state=URI_VAL_P;
				}
				break;
			/* r2 */
			param_switch1(PR2_R,  '2', PR2_2_FIN);
			case PR2_2_FIN:
				switch(*p){
					case '@':
						still_at_user; 
						break;
					case '=':
						state=PR2_eq;
						break;
					semicolon_case; 
						uri->r2.s=b;
						uri->r2.len=(p-b);
						break;
					question_case; 
						uri->r2.s=b;
						uri->r2.len=(p-b);
						break;
					colon_case;
						break;
					default:
						state=URI_PARAM_P;
				}
				break;
				/* handle lr=something case */
			case PR2_eq:
				param=&uri->r2;
				param_val=&uri->r2_val;
				switch(*p){
					param_common_cases;
					default:
						v=p;
						state=URI_VAL_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 contain '?'*/
							pass=0;
						}
						break;
				}
				break;
			default:
				goto error_bug;
		}
	}
	/*end of uri */
	switch (state){
		case URI_INIT: /* error empty 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:
		case URI_PARAM_VAL_P:
			u_param_set(b, v);
		/* 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 */
		case PR2_R:  /* r2 */
			uri->params.s=s;
			uri->params.len=p-s;
			break;
		/* fin param states */
		case PLR_R_FIN:
		case PLR_eq:
			uri->params.s=s;
			uri->params.len=p-s;
			uri->lr.s=b;
			uri->lr.len=p-b;
			break;
		case PR2_2_FIN:
		case PR2_eq:
			uri->params.s=s;
			uri->params.len=p-s;
			uri->r2.s=b;
			uri->r2.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;
	}
	switch(uri->type){
		case SIP_URI_T:
			if ((uri->user_param_val.len == 5) &&
				(strncasecmp(uri->user_param_val.s, "phone", 5) == 0)) {
				uri->type = TEL_URI_T;
				/* move params from user into uri->params */
				p=q_memchr(uri->user.s, ';', uri->user.len);
				if (p){
					uri->params.s=p+1;
					uri->params.len=uri->user.s+uri->user.len-uri->params.s;
					uri->user.len=p-uri->user.s;
				}
			}
			break;
		case SIPS_URI_T:
			if ((uri->user_param_val.len == 5) &&
				(strncasecmp(uri->user_param_val.s, "phone", 5) == 0)) {
				uri->type = TELS_URI_T;
				p=q_memchr(uri->user.s, ';', uri->user.len);
				if (p){
					uri->params.s=p+1;
					uri->params.len=uri->user.s+uri->user.len-uri->params.s;
					uri->user.len=p-uri->user.s;
				}
			}
			break;
		case TEL_URI_T:
		case TELS_URI_T:
			/* fix tel uris, move the number in uri and empty the host */
			uri->user=uri->host;
			uri->host.s="";
			uri->host.len=0;
			break;
		case ERROR_URI_T:
			LM_ERR("unexpected error (BUG?)\n"); 
			goto error_bad_uri;
			break; /* do nothing, avoids a compilation warning */
	}
#ifdef EXTRA_DEBUG
	/* do stuff */
	LM_DBG("parsed uri:\n type=%d 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->type,
			uri->user.len, ZSW(uri->user.s), uri->user.len,
			uri->passwd.len, ZSW(uri->passwd.s), uri->passwd.len,
			uri->host.len, ZSW(uri->host.s), uri->host.len,
			uri->port.len, ZSW(uri->port.s), uri->port.len, uri->port_no,
			uri->params.len, ZSW(uri->params.s), uri->params.len,
			uri->headers.len, ZSW(uri->headers.s), uri->headers.len
		);
	LM_DBG(" uri params:\n   transport=<%.*s>, val=<%.*s>, proto=%d\n",
			uri->transport.len, ZSW(uri->transport.s), uri->transport_val.len,
			ZSW(uri->transport_val.s), uri->proto);
	LM_DBG("   user-param=<%.*s>, val=<%.*s>\n",
			uri->user_param.len, ZSW(uri->user_param.s), 
			uri->user_param_val.len, ZSW(uri->user_param_val.s));
	LM_DBG("   method=<%.*s>, val=<%.*s>\n",
			uri->method.len, ZSW(uri->method.s), 
			uri->method_val.len, ZSW(uri->method_val.s));
	LM_DBG("   ttl=<%.*s>, val=<%.*s>\n",
			uri->ttl.len, ZSW(uri->ttl.s), 
			uri->ttl_val.len, ZSW(uri->ttl_val.s));
	LM_DBG("   maddr=<%.*s>, val=<%.*s>\n",
			uri->maddr.len, ZSW(uri->maddr.s), 
			uri->maddr_val.len, ZSW(uri->maddr_val.s));
	LM_DBG("   lr=<%.*s>, val=<%.*s>\n", uri->lr.len, ZSW(uri->lr.s),
			uri->lr_val.len, ZSW(uri->lr_val.s)); 
	LM_DBG("   r2=<%.*s>, val=<%.*s>\n", uri->r2.len, ZSW(uri->r2.s),
			uri->r2_val.len, ZSW(uri->r2_val.s)); 
	for(i=0; i<URI_MAX_U_PARAMS && uri->u_name[i].s; i++)
		LM_DBG("uname=[%p]-><%.*s> uval=[%p]-><%.*s>\n",
			uri->u_name[i].s, uri->u_name[i].len, uri->u_name[i].s,
			uri->u_val[i].s, uri->u_val[i].len, uri->u_val[i].s);
	if (i!=uri->u_params_no)
		LM_ERR("inconsisten # of u_name:[%d]!=[%d]\n", i, uri->u_params_no);
#endif
	return 0;
	
error_too_short:
	LM_ERR("uri too short: <%.*s> (%d)\n",
			len, ZSW(buf), len);
	goto error_exit;
error_bad_char:
	LM_ERR("bad char '%c' in state %d"
			" parsed: <%.*s> (%d) / <%.*s> (%d)\n",
			*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
			len, ZSW(buf), len);
	goto error_exit;
error_bad_host:
	LM_ERR("bad host in uri (error at char %c in"
			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
			*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
			len, ZSW(buf), len);
	goto error_exit;
error_bad_port:
	LM_ERR("bad port in uri (error at char %c in"
			" state %d) parsed: <%.*s>(%d) /<%.*s> (%d)\n",
			*p, state, (int)(p-buf), ZSW(buf), (int)(p-buf),
			len, ZSW(buf), len);
	goto error_exit;
error_bad_uri:
	LM_ERR("bad uri,  state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n",
			 state, (int)(p-buf), ZSW(buf), (int)(p-buf), len,
			 ZSW(buf), len);
	goto error_exit;
error_headers:
	LM_ERR("bad uri headers: <%.*s>(%d) / <%.*s>(%d)\n",
			uri->headers.len, ZSW(uri->headers.s), uri->headers.len,
			len, ZSW(buf), len);
	goto error_exit;
error_bug:
	LM_CRIT("bad  state %d parsed: <%.*s> (%d) / <%.*s> (%d)\n",
			 state, (int)(p-buf), ZSW(buf), (int)(p-buf), len, ZSW(buf), len);
error_exit:
	ser_error=E_BAD_URI;
	uri->type=ERROR_URI_T;
	update_stat(bad_URIs, 1);
	return E_BAD_URI;
}