/* 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_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; #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 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; 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, 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; case 'r': case 'R': b=p; state=PR2_R; default: state=URI_PARAM_P; } break; case URI_PARAM_P: /* ignore current param */ /* supported params: * maddr, transport, ttl, lr, user, method, r2 */ 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; 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: /* 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) && (strncmp(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) && (strncmp(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>\n", uri->lr.len, ZSW(uri->lr.s)); #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; }
/* * 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; }