/*
 * via parsing automaton
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/** @file
 * @brief Parser :: Via parsing automation
 *
 * @ingroup parser
 */

#include "../comp_defs.h"
#include <stdlib.h>
#include <string.h>
#include "../dprint.h"
#include "../ut.h"
#include "../mem/mem.h"
#include "../ip_addr.h"
#include "parse_via.h"
#include "parse_def.h"
#include "msg_parser.h"


/** \brief main via states (uri:port ...) */
enum
{
	F_HOST,
	P_HOST,
	L_PORT,
	F_PORT,
	P_PORT,
	L_PARAM,
	F_PARAM,
	P_PARAM,
	L_VIA,
	F_VIA,
	F_COMMENT,
	P_COMMENT,
	F_IP6HOST,
	P_IP6HOST,
	F_CRLF,
	F_LF,
	F_CR,
	END_OF_HEADER
};


/** \brief first via part state */
enum
{
	F_SIP = 100,
	SIP1,
	SIP2,
	FIN_SIP,
	L_VER,
	F_VER,
	VER1,
	VER2,
	FIN_VER,
	UDP1,
	UDP2,
	FIN_UDP,
	TCP_TLS1,
	TCP2,
	FIN_TCP,
	TLS2,
	FIN_TLS,
	SCTP1,
	SCTP2,
	SCTP3,
	FIN_SCTP,
	WS_WSS1,
	WS_WSS2,
	FIN_WSS,
	OTHER_PROTO,
	L_PROTO,
	F_PROTO
};


/** \brief param related states
 * WARNING: keep in sync with parse_via.h, PARAM_HIDDEN, ...
 */
enum
{
	L_VALUE = 200,
	F_VALUE,
	P_VALUE,
	P_STRING,
	HIDDEN1,
	HIDDEN2,
	HIDDEN3,
	HIDDEN4,
	HIDDEN5,
	TTL1,
	TTL2,
	BRANCH1,
	BRANCH2,
	BRANCH3,
	BRANCH4,
	BRANCH5,
	MADDR1,
	MADDR2,
	MADDR3,
	MADDR4,
	RECEIVED1,
	RECEIVED2,
	RECEIVED3,
	RECEIVED4,
	RECEIVED5,
	RECEIVED6,
	RECEIVED7,
	RPORT1,
	RPORT2,
	RPORT3,
	ALIAS1,
	ALIAS2,
	ALIAS3,
	ALIAS4,
#ifdef USE_COMP
	COMP1,
	COMP2,
	COMP3,
	/* values */
	L_COMP_VALUE,
	F_COMP_VALUE,
	V_COMP_S,
	V_SIGCOMP_I,
	V_SIGCOMP_G,
	V_SIGCOMP_C,
	V_SIGCOMP_O,
	V_SIGCOMP_M,
	FIN_V_SIGCOMP_P,
	V_SERGZ_E,
	V_SERGZ_R,
	V_SERGZ_G,
	FIN_V_SERGZ_Z,
#endif
	/* fin states (227-...)*/
	FIN_HIDDEN = 230,
	FIN_TTL,
	FIN_BRANCH,
	FIN_MADDR,
	FIN_RECEIVED,
	FIN_RPORT,
	FIN_I,
	FIN_ALIAS
#ifdef USE_COMP
	,
	FIN_COMP
#endif
	/*GEN_PARAM,
	PARAM_ERROR*/ /* declared in msg_parser.h*/
};


/* entry state must be F_PARAM, or saved_state=F_PARAM and
 * state=F_{LF,CR,CRLF}!
 * output state = L_PARAM or F_PARAM or END_OF_HEADER
 * (and saved_state= last state); everything else => error
 * WARNING: param->start must be filled before, it's used in param->size
 * computation.
 */
static /*inline*/ char *parse_via_param(char *const p, const char *const end,
		unsigned char *const pstate, unsigned char *const psaved_state,
		struct via_param *const param, const struct via_body *vb)
{
	char *tmp;
	register unsigned char state;
	unsigned char saved_state;

#define value_case(c, C, oldstate, newstate, start_of_value) \
	case(c):                                                 \
	case(C):                                                 \
		switch(state) {                                      \
			case(oldstate):                                  \
				state = (newstate);                          \
				if((start_of_value))                         \
					param->value.s = tmp;                    \
				break;                                       \
				default_value_cases                          \
		}                                                    \
		break

#define value_case_double(c, C, oldstate1, newstate1, oldstate2, newstate2) \
	case(c):                                                                \
	case(C):                                                                \
		switch(state) {                                                     \
			case(oldstate1):                                                \
				state = (newstate1);                                        \
				break;                                                      \
			case(oldstate2):                                                \
				state = (newstate2);                                        \
				break;                                                      \
				default_value_cases                                         \
		}                                                                   \
		break

#define default_value_cases                                                   \
	case F_VALUE:                                                             \
		state = P_VALUE;                                                      \
		param->value.s = tmp;                                                 \
		break;                                                                \
	case P_VALUE:                                                             \
	case P_STRING:                                                            \
		break;                                                                \
	case F_LF:                                                                \
	case F_CR:                                                                \
	case F_CRLF:                                                              \
		state = END_OF_HEADER;                                                \
		goto end_via;                                                         \
	default:                                                                  \
		switch(state) {                                                       \
			case F_COMP_VALUE:                                                \
				comp_unexpected_char;                                         \
				state = P_VALUE;                                              \
				param->value.s = tmp;                                         \
				break;                                                        \
				comp_states_cases comp_fin_states_cases comp_unexpected_char; \
				state = P_VALUE;                                              \
				break;                                                        \
			default:                                                          \
				LM_ERR("invalid "                                             \
					   "char <%c> in state %d\n",                             \
						*tmp, state);                                         \
				goto error;                                                   \
		}

#define comp_states_cases \
	case V_COMP_S:        \
	case V_SIGCOMP_I:     \
	case V_SIGCOMP_G:     \
	case V_SIGCOMP_C:     \
	case V_SIGCOMP_O:     \
	case V_SIGCOMP_M:     \
	case V_SERGZ_E:       \
	case V_SERGZ_R:       \
	case V_SERGZ_G:

#define comp_fin_states_cases \
	case FIN_V_SIGCOMP_P:     \
	case FIN_V_SERGZ_Z:


/* if unrecognized/bad comp, don't return error, just ignore comp */
#define comp_unexpected_char                  \
	LM_ERR("bad/unrecognized comp method\n"); \
	vb->comp_no = 0


	state = *pstate;
	saved_state = *psaved_state;
	param->type = PARAM_ERROR;

	for(tmp = p; tmp < end; tmp++) {
		switch(*tmp) {
			case ' ':
			case '\t':
				switch(state) {
					case FIN_HIDDEN:
					case FIN_ALIAS:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = L_PARAM;
						goto endofparam;
					case FIN_BRANCH:
					case FIN_TTL:
					case FIN_MADDR:
					case FIN_RECEIVED:
					case FIN_RPORT:
					case FIN_I:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = L_VALUE;
						goto find_value;
#ifdef USE_COMP
					case FIN_COMP:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = L_COMP_VALUE;
						goto find_value;
#endif
					case F_PARAM:
						break;
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = saved_state;
						break;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						param->name.len = tmp - param->name.s;
						state = L_VALUE;
						goto find_value;
				}
				break;
			/* \n and \r*/
			case '\n':
				switch(state) {
					case FIN_HIDDEN:
					case FIN_ALIAS:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_PARAM;
						state = F_LF;
						goto endofparam;
					case FIN_BRANCH:
					case FIN_TTL:
					case FIN_MADDR:
					case FIN_RECEIVED:
					case FIN_I:
					case FIN_RPORT:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_VALUE;
						state = F_LF;
						goto find_value;
#ifdef USE_COMP
					case FIN_COMP:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_COMP_VALUE;
						state = F_LF;
						goto find_value;
#endif
					case F_PARAM:
						saved_state = state;
						state = F_LF;
						break;
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case F_CR:
						state = F_CRLF;
						break;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						saved_state = L_VALUE;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						state = F_LF;
						goto find_value;
				}
				break;
			case '\r':
				switch(state) {
					case FIN_HIDDEN:
					case FIN_ALIAS:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_PARAM;
						state = F_CR;
						goto endofparam;
					case FIN_BRANCH:
					case FIN_TTL:
					case FIN_MADDR:
					case FIN_RECEIVED:
					case FIN_I:
					case FIN_RPORT:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_VALUE;
						state = F_CR;
						goto find_value;
#ifdef USE_COMP
					case FIN_COMP:
						param->type = state;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_COMP_VALUE;
						state = F_LF;
						goto find_value;
#endif
					case F_PARAM:
						saved_state = state;
						state = F_CR;
						break;
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						param->name.len = tmp - param->name.s;
						param->size = tmp - param->start;
						saved_state = L_VALUE;
						state = F_CR;
						goto find_value;
				}
				break;

			case '=':
				switch(state) {
					case FIN_BRANCH:
					case FIN_TTL:
					case FIN_MADDR:
					case FIN_RECEIVED:
					case FIN_RPORT:
					case FIN_I:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = F_VALUE;
						goto find_value;
#ifdef USE_COMP
					case FIN_COMP:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = F_COMP_VALUE;
						goto find_value;
#endif
					case F_PARAM:
					case FIN_HIDDEN:
					case FIN_ALIAS:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						param->name.len = tmp - param->name.s;
						state = F_VALUE;
						goto find_value;
				}
				break;
			case ';':
				switch(state) {
					case FIN_HIDDEN:
					case FIN_RPORT: /* rport can appear w/o a value */
					case FIN_ALIAS:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = F_PARAM;
						goto endofparam;
					case FIN_BRANCH:
					case FIN_MADDR:
					case FIN_TTL:
					case FIN_RECEIVED:
					case FIN_I:
#ifdef USE_COMP
					case FIN_COMP:
#endif
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						param->name.len = tmp - param->name.s;
						state = F_PARAM;
						goto endofparam;
				}
				break;
			case ',':
				switch(state) {
					case FIN_HIDDEN:
					case FIN_RPORT:
					case FIN_ALIAS:
						param->type = state;
						param->name.len = tmp - param->name.s;
						state = F_VIA;
						goto endofvalue;
					case FIN_BRANCH:
					case FIN_MADDR:
					case FIN_TTL:
					case FIN_RECEIVED:
					case FIN_I:
#ifdef USE_COMP
					case FIN_COMP:
#endif
						LM_ERR("new via found"
							   "(',') when '=' expected (state %d=)\n",
								state);
						goto error; /* or we could ignore this bad param*/
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case GEN_PARAM:
					default:
						param->type = GEN_PARAM;
						param->name.len = tmp - param->name.s;
						state = F_VIA;
						goto endofvalue;
				}
				break;

			/* param names */
			case 'h':
			case 'H':
				switch(state) {
					case F_PARAM:
						state = HIDDEN1;
						param->name.s = tmp;
						break;
					case BRANCH5:
						state = FIN_BRANCH;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'i':
			case 'I':
				switch(state) {
					case F_PARAM:
						state = FIN_I;
						param->name.s = tmp;
						break;
					case HIDDEN1:
						state = HIDDEN2;
						break;
					case RECEIVED4:
						state = RECEIVED5;
						break;
					case ALIAS2:
						state = ALIAS3;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'd':
			case 'D':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case HIDDEN2:
						state = HIDDEN3;
						break;
					case HIDDEN3:
						state = HIDDEN4;
						break;
					case MADDR2:
						state = MADDR3;
						break;
					case MADDR3:
						state = MADDR4;
						break;
					case RECEIVED7:
						state = FIN_RECEIVED;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'e':
			case 'E':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case HIDDEN4:
						state = HIDDEN5;
						break;
					case RECEIVED1:
						state = RECEIVED2;
						break;
					case RECEIVED3:
						state = RECEIVED4;
						break;
					case RECEIVED6:
						state = RECEIVED7;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'n':
			case 'N':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case HIDDEN5:
						state = FIN_HIDDEN;
						break;
					case BRANCH3:
						state = BRANCH4;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 't':
			case 'T':
				switch(state) {
					case F_PARAM:
						state = TTL1;
						param->name.s = tmp;
						break;
					case TTL1:
						state = TTL2;
						break;
					case RPORT3:
						state = FIN_RPORT;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'l':
			case 'L':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case TTL2:
						state = FIN_TTL;
						break;
					case ALIAS1:
						state = ALIAS2;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'm':
			case 'M':
				switch(state) {
					case F_PARAM:
						state = MADDR1;
						param->name.s = tmp;
						break;
#ifdef USE_COMP
					case COMP2:
						state = COMP3;
						break;
#endif
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'a':
			case 'A':
				switch(state) {
					case F_PARAM:
						state = ALIAS1;
						param->name.s = tmp;
						break;
					case MADDR1:
						state = MADDR2;
						break;
					case BRANCH2:
						state = BRANCH3;
						break;
					case ALIAS3:
						state = ALIAS4;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'r':
			case 'R':
				switch(state) {
					case MADDR4:
						state = FIN_MADDR;
						break;
					case F_PARAM:
						state = RECEIVED1;
						param->name.s = tmp;
						break;
					case BRANCH1:
						state = BRANCH2;
						break;
					case RPORT2:
						state = RPORT3;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'c':
			case 'C':
				switch(state) {
					case F_PARAM:
#ifdef USE_COMP
						state = COMP1;
#else
						state = GEN_PARAM;
#endif
						param->name.s = tmp;
						break;
					case RECEIVED2:
						state = RECEIVED3;
						break;
					case BRANCH4:
						state = BRANCH5;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'v':
			case 'V':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case RECEIVED5:
						state = RECEIVED6;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'b':
			case 'B':
				switch(state) {
					case F_PARAM:
						state = BRANCH1;
						param->name.s = tmp;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'p':
			case 'P':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case RECEIVED1:
						state = RPORT1;
						break;
#ifdef USE_COMP
					case COMP3:
						state = FIN_COMP;
						break;
#endif
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 'o':
			case 'O':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case RPORT1:
						state = RPORT2;
						break;
#ifdef USE_COMP
					case COMP1:
						state = COMP2;
						break;
#endif
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			case 's':
			case 'S':
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case ALIAS4:
						state = FIN_ALIAS;
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
				break;
			default:
				switch(state) {
					case F_PARAM:
						state = GEN_PARAM;
						param->name.s = tmp;
						break;
					case GEN_PARAM:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						state = GEN_PARAM;
				}
		}
	} /* for tmp*/

	/* end of packet? => error, no cr/lf,',' found!!!*/
	saved_state = state;
	state = END_OF_HEADER;
	goto error;

find_value:
	tmp++;
	for(; tmp < end && *tmp; tmp++) {
		switch(*tmp) {
			case ' ':
			case '\t':
				switch(state) {
					case L_VALUE:
					case F_VALUE: /*eat space*/
						break;
					case P_VALUE:
						state = L_PARAM;
						param->value.len = tmp - param->value.s;
						goto endofvalue;
#ifdef USE_COMP
					case L_COMP_VALUE:
					case F_COMP_VALUE:
						break; /* eat space */
					case FIN_V_SIGCOMP_P:
						state = L_PARAM;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
					case FIN_V_SERGZ_Z:
						state = L_PARAM;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
						comp_states_cases state = L_PARAM;
						param->value.len = tmp - param->value.s;
						comp_unexpected_char;
						goto endofvalue;
#endif
					case P_STRING:
						break;
					case F_CR:
					case F_LF:
					case F_CRLF:
						state = saved_state;
						break;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '\n':
				switch(state) {
					case L_VALUE:
					case F_VALUE: /*eat space*/
#ifdef USE_COMP
					case L_COMP_VALUE:
					case F_COMP_VALUE:
#endif
					case P_STRING:
						saved_state = state;
						param->size = tmp - param->start;
						state = F_LF;
						break;
					case P_VALUE:
						saved_state = L_PARAM;
						state = F_LF;
						param->value.len = tmp - param->value.s;
						goto endofvalue;
#ifdef USE_COMP
					case FIN_V_SIGCOMP_P:
						saved_state = L_PARAM;
						state = F_LF;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
					case FIN_V_SERGZ_Z:
						saved_state = L_PARAM;
						state = F_LF;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
						comp_states_cases saved_state = L_PARAM;
						state = F_LF;
						param->value.len = tmp - param->value.s;
						comp_unexpected_char;
						goto endofvalue;
#endif
					case F_LF:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					case F_CR:
						state = F_CRLF;
						break;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '\r':
				switch(state) {
					case L_VALUE:
					case F_VALUE: /*eat space*/
#ifdef USE_COMP
					case L_COMP_VALUE:
					case F_COMP_VALUE:
#endif
					case P_STRING:
						saved_state = state;
						param->size = tmp - param->start;
						state = F_CR;
						break;
					case P_VALUE:
						param->value.len = tmp - param->value.s;
						saved_state = L_PARAM;
						state = F_CR;
						goto endofvalue;
#ifdef USE_COMP
					case FIN_V_SIGCOMP_P:
						saved_state = L_PARAM;
						state = F_CR;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
					case FIN_V_SERGZ_Z:
						saved_state = L_PARAM;
						state = F_CR;
						param->value.len = tmp - param->value.s;
						vb->comp_no = COMP_SIGCOMP;
						goto endofvalue;
						comp_states_cases saved_state = L_PARAM;
						state = F_CR;
						param->value.len = tmp - param->value.s;
						comp_unexpected_char;
						goto endofvalue;
#endif
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;

			case '=':
				switch(state) {
					case L_VALUE:
						state = F_VALUE;
						break;
#ifdef USE_COMP
					case L_COMP_VALUE:
						state = F_COMP_VALUE;
						break;
/* '=' in any other COMP value state is an error,
					 * and it will be caught by the default branch */
#endif
					case P_STRING:
						break;
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;
			case ';':
				switch(state) {
					case P_VALUE:
						param->value.len = tmp - param->value.s;
						state = F_PARAM;
						goto endofvalue;
					case F_VALUE:
						param->value.len = 0;
						state = F_PARAM;
						goto endofvalue;
					case P_STRING:
						break; /* what to do? */
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
#ifdef USE_COMP
					case L_COMP_VALUE:
						comp_unexpected_char;
						/* we want to continue with no comp */
						state = F_PARAM;
						param->value.len = 0;
						param->value.s = 0;
						goto endofvalue;
					case F_COMP_VALUE:
						comp_unexpected_char;
						param->value.len = 0;
						state = F_PARAM;
						goto endofvalue;
						comp_states_cases comp_unexpected_char;
						param->value.len = tmp - param->value.s;
						state = F_PARAM;
						goto endofvalue;
					case FIN_V_SIGCOMP_P:
						vb->comp_no = COMP_SIGCOMP;
						param->value.len = tmp - param->value.s;
						state = F_PARAM;
						goto endofvalue;
					case FIN_V_SERGZ_Z:
						vb->comp_no = COMP_SIGCOMP;
						param->value.len = tmp - param->value.s;
						state = F_PARAM;
						goto endofvalue;
#endif
					case L_VALUE:
						param->value.len = 0;
						param->value.s = 0; /* null value */
						state = F_PARAM;
						goto endofvalue;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;
			case ',':
				switch(state) {
					case P_VALUE:
						if(param->flags & VIA_PARAM_F_QUOTED) {
							/* inside a quoted value */
							break;
						}
						param->value.len = tmp - param->value.s;
						state = F_VIA;
						goto endofvalue;
					case P_STRING:
						if(param->flags & VIA_PARAM_F_QUOTED) {
							/* inside a quoted value */
							break;
						}
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
#ifdef USE_COMP
					case L_COMP_VALUE:
						comp_unexpected_char;
						/* we want to continue with no comp */
						state = F_VIA;
						param->value.len = 0;
						param->value.s = 0;
						goto endofvalue;
					case F_COMP_VALUE:
						comp_unexpected_char;
						param->value.len = 0;
						state = F_VIA;
						goto endofvalue;
						comp_states_cases comp_unexpected_char;
						param->value.len = tmp - param->value.s;
						state = F_VIA;
						goto endofvalue;
					case FIN_V_SIGCOMP_P:
						vb->comp_no = COMP_SIGCOMP;
						param->value.len = tmp - param->value.s;
						state = F_VIA;
						goto endofvalue;
					case FIN_V_SERGZ_Z:
						vb->comp_no = COMP_SIGCOMP;
						param->value.len = tmp - param->value.s;
						state = F_VIA;
						goto endofvalue;
#endif
					case L_VALUE:
						if(param->type == FIN_RPORT) {
							param->value.len = 0;
							param->value.s = 0; /* null value */
							state = F_VIA;
							goto endofvalue;
						};
					/* no break */
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break; /* what to do? */
			case '"':
				switch(state) {
					case F_VALUE:
						state = P_STRING;
						param->value.s = tmp + 1;
						param->flags |= VIA_PARAM_F_QUOTED;
						break;
					case P_STRING:
						state = L_PARAM;
						param->value.len = tmp - param->value.s;
						goto endofvalue;
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
				}
				break;
#ifdef USE_COMP
				value_case('s', 'S', F_COMP_VALUE, V_COMP_S, 1);
				value_case('i', 'I', V_COMP_S, V_SIGCOMP_I, 0);
				value_case_double('g', 'G', V_SIGCOMP_I, V_SIGCOMP_G, V_SERGZ_R,
						V_SERGZ_G);
				value_case('c', 'C', V_SIGCOMP_G, V_SIGCOMP_C, 0);
				value_case('o', 'O', V_SIGCOMP_C, V_SIGCOMP_O, 0);
				value_case('m', 'M', V_SIGCOMP_O, V_SIGCOMP_M, 0);
				value_case('p', 'P', V_SIGCOMP_M, FIN_V_SIGCOMP_P, 0);

				value_case('e', 'E', V_COMP_S, V_SERGZ_E, 0);
				value_case('r', 'R', V_SERGZ_E, V_SERGZ_R, 0);
				value_case('z', 'Z', V_SERGZ_G, FIN_V_SERGZ_Z, 0);
#endif /* USE_COMP */

			default:
				switch(state) {
					case F_VALUE:
						state = P_VALUE;
						param->value.s = tmp;
						break;
					case P_VALUE:
					case P_STRING:
						break;
					case F_LF:
					case F_CR:
					case F_CRLF:
						state = END_OF_HEADER;
						goto end_via;
					default:
#ifdef USE_COMP
						switch(state) {
							case F_COMP_VALUE:
								comp_unexpected_char;
								state = P_VALUE;
								param->value.s = tmp;
								break;
								comp_states_cases comp_fin_states_cases
										comp_unexpected_char;
								state = P_VALUE;
								break;
							default:
								LM_ERR("invalid char <%c> in state %d\n", *tmp,
										state);
								goto error;
						}
#else
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
#endif /* USE_COMP */
				}
		}
	} /* for2 tmp*/

	/* end of buff and no CR/LF =>error*/
	saved_state = state;
	state = END_OF_HEADER;
	goto error;

endofparam:
endofvalue:
	param->size = tmp - param->start;
normal_exit:
	*pstate = state;
	*psaved_state = saved_state;
	DBG("Found param type %d, <%.*s> = <%.*s>; state=%d\n", param->type,
			param->name.len, ZSW(param->name.s),
			(param->value.len ? param->value.len : 3),
			(param->value.len ? param->value.s : "n/a"), state);
	return tmp;

end_via:
	/* if we are here we found an "unexpected" end of via
	 * (cr/lf). This is valid only if the param type is GEN_PARAM or
	 *  RPORT (the only ones which can miss the value; HIDDEN is a
	 *  special case )*/
	if((param->type == GEN_PARAM) || (param->type == PARAM_RPORT)) {
		saved_state = L_PARAM; /* change the saved_state, we have an unknown
								* param. w/o a value */
		/* param->size should be computed before */
		goto normal_exit;
	}
	*pstate = state;
	*psaved_state = saved_state;
	DBG("Error on  param type %d, <%.*s>, state=%d, saved_state=%d\n",
			param->type, param->name.len, ZSW(param->name.s), state,
			saved_state);

error:
	LM_ERR("failure parsing via param\n");
	param->type = PARAM_ERROR;
	*pstate = PARAM_ERROR;
	*psaved_state = state;
	return tmp;
}


/*
 * call it with a vb initialized to 0
 * returns: pointer after the parsed parts and sets vb->error
 * WARNING: don't forget to cleanup on error with free_via_list(vb)!
 */
char *parse_via(
		char *buffer, const char *const end, struct via_body *const vbody)
{
	char *tmp;
	char *param_start;
	unsigned char state;
	unsigned char saved_state;
	int c_nest;
	int err;
	struct via_body *vb;
	struct via_param *param;

	vb = vbody; /* keep original vbody value, needed to set the error member
				* in case of multiple via bodies in the same header */
parse_again:
	vb->error = PARSE_ERROR;
	/* parse start of via ( SIP/2.0/UDP    )*/
	state = F_SIP;
	saved_state = F_SIP; /* fixes gcc 4.0 warning */
	param_start = 0;
	for(tmp = buffer; tmp < end; tmp++) {
		switch(*tmp) {
			case ' ':
			case '\t':
				switch(state) {
					case L_VER: /* eat space */
					case L_PROTO:
					case F_SIP:
					case F_VER:
					case F_PROTO:
						break;
					case FIN_UDP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_UDP;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_TCP:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TCP;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_TLS:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TLS;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_SCTP:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_SCTP;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case WS_WSS2:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WS;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_WSS:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WSS;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case OTHER_PROTO:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case UDP1:
					case UDP2:
					case TCP_TLS1:
					case TCP2:
					case TLS2:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case WS_WSS1:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_SIP:
						vb->name.len = tmp - vb->name.s;
						state = L_VER;
						break;
					case FIN_VER:
						vb->version.len = tmp - vb->version.s;
						state = L_PROTO;
						break;
					case F_LF:
					case F_CRLF:
					case F_CR: /* header continues on this line */
						state = saved_state;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '\n':
				switch(state) {
					case L_VER:
					case F_SIP:
					case F_VER:
					case F_PROTO:
					case L_PROTO:
						saved_state = state;
						state = F_LF;
						break;
					case FIN_UDP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_UDP;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_TCP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TCP;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_TLS:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TLS;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_SCTP:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_SCTP;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case WS_WSS2:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WS;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_WSS:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WSS;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case OTHER_PROTO:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case UDP1:
					case UDP2:
					case TCP_TLS1:
					case TCP2:
					case TLS2:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case WS_WSS1:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_LF;
						saved_state = F_HOST; /* start looking for host*/
						goto main_via;
					case FIN_SIP:
						vb->name.len = tmp - vb->name.s;
						state = F_LF;
						saved_state = L_VER;
						break;
					case FIN_VER:
						vb->version.len = tmp - vb->version.s;
						state = F_LF;
						saved_state = L_PROTO;
						break;
					case F_CR:
						state = F_CRLF;
						break;
					case F_LF:
					case F_CRLF:
						state = saved_state;
						goto endofheader;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '\r':
				switch(state) {
					case L_VER:
					case F_SIP:
					case F_VER:
					case F_PROTO:
					case L_PROTO:
						saved_state = state;
						state = F_CR;
						break;
					case FIN_UDP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_UDP;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case FIN_TCP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TCP;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case FIN_TLS:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_TLS;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case FIN_SCTP:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_SCTP;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case WS_WSS2:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WS;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case FIN_WSS:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_WSS;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case OTHER_PROTO:
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case UDP1:
					case UDP2:
					case TCP_TLS1:
					case TCP2:
					case TLS2:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case WS_WSS1:
						/* finished proto parsing */
						vb->transport.len = tmp - vb->transport.s;
						vb->proto = PROTO_OTHER;
						state = F_CR;
						saved_state = F_HOST;
						goto main_via;
					case FIN_SIP:
						vb->name.len = tmp - vb->name.s;
						state = F_CR;
						saved_state = L_VER;
						break;
					case FIN_VER:
						vb->version.len = tmp - vb->version.s;
						state = F_CR;
						saved_state = L_PROTO;
						break;
					case F_LF: /*end of line ?next header?*/
					case F_CR:
					case F_CRLF:
						state = saved_state;
						goto endofheader;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;

			case '/':
				switch(state) {
					case FIN_SIP:
						vb->name.len = tmp - vb->name.s;
						state = F_VER;
						break;
					case FIN_VER:
						vb->version.len = tmp - vb->version.s;
						state = F_PROTO;
						break;
					case L_VER:
						state = F_VER;
						break;
					case L_PROTO:
						state = F_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			/* match SIP*/
			case 'S':
			case 's':
				switch(state) {
					case F_SIP:
						state = SIP1;
						vb->name.s = tmp;
						break;
					case TLS2:
						state = FIN_TLS;
						break;
					case WS_WSS1:
						state = WS_WSS2;
						break;
					case WS_WSS2:
						state = FIN_WSS;
						break;
					case F_PROTO:
						state = SCTP1;
						vb->transport.s = tmp;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_WSS:
					case FIN_SCTP:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'I':
			case 'i':
				switch(state) {
					case SIP1:
						state = SIP2;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'p':
			case 'P':
				switch(state) {
					case SIP2:
						state = FIN_SIP;
						break;
					/* allow p in PROTO */
					case UDP2:
						state = FIN_UDP;
						break;
					case TCP2:
						state = FIN_TCP;
						break;
					case SCTP3:
						state = FIN_SCTP;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case FIN_UDP:
					case TCP_TLS1:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'U':
			case 'u':
				switch(state) {
					case F_PROTO:
						state = UDP1;
						vb->transport.s = tmp;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'D':
			case 'd':
				switch(state) {
					case UDP1:
						state = UDP2;
						break;
					case OTHER_PROTO:
						break;
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'T':
			case 't':
				switch(state) {
					case F_PROTO:
						state = TCP_TLS1;
						vb->transport.s = tmp;
						break;
					case SCTP2:
						state = SCTP3;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'C':
			case 'c':
				switch(state) {
					case TCP_TLS1:
						state = TCP2;
						break;
					case SCTP1:
						state = SCTP2;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'L':
			case 'l':
				switch(state) {
					case TCP_TLS1:
						state = TLS2;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case 'W':
			case 'w':
				switch(state) {
					case F_PROTO:
						state = WS_WSS1;
						vb->transport.s = tmp;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			/*match 2.0*/
			case '2':
				switch(state) {
					case F_VER:
						state = VER1;
						vb->version.s = tmp;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '.':
				switch(state) {
					case VER1:
						state = VER2;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			case '0':
				switch(state) {
					case VER2:
						state = FIN_VER;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
			default:
				switch(state) {
					case F_PROTO:
						state = OTHER_PROTO;
						vb->transport.s = tmp;
						break;
					case OTHER_PROTO:
						break;
					case UDP1:
					case UDP2:
					case FIN_UDP:
					case TCP_TLS1:
					case TCP2:
					case FIN_TCP:
					case TLS2:
					case FIN_TLS:
					case SCTP1:
					case SCTP2:
					case SCTP3:
					case FIN_SCTP:
					case WS_WSS1:
					case WS_WSS2:
					case FIN_WSS:
						state = OTHER_PROTO;
						break;
					default:
						LM_ERR("bad char <%c> on state %d\n", *tmp, state);
						goto error;
				}
				break;
		}
	} /* for tmp*/

	/* we should not be here! if everything is ok > main_via*/
	LM_ERR("bad via: end of packet on state=%d\n", state);
	goto error;

main_via:
	/* inc tmp to point to the next char*/
	tmp++;
	c_nest = 0;
	/*state should always be F_HOST here*/;
	for(; tmp < end && *tmp; tmp++) {
		switch(*tmp) {
			case ' ':
			case '\t':
				switch(state) {
					case F_HOST: /*eat the spaces*/
						break;
					case P_HOST:
						/*mark end of host*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						state = L_PORT;
						break;
					case L_PORT: /*eat the spaces*/
					case F_PORT:
						break;
					case P_PORT:
						/*end of port */
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
						state = L_PARAM;
						break;
					case L_PARAM: /* eat the space */
					case F_PARAM:
						break;
					case P_PARAM:
						/*	*tmp=0;*/ /*!?end of param*/
						state = L_PARAM;
						break;
					case L_VIA:
					case F_VIA: /* eat the space */
						break;
					case F_COMMENT:
					case P_COMMENT:
						break;
					case F_IP6HOST: /*no spaces allowed*/
					case P_IP6HOST:
						LM_ERR("bad ipv6 reference\n");
						goto error;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now =' '*/
						state = saved_state;
						break;
					default:
						LM_CRIT("BUG: parsing via on <%c>, state=%d\n", *tmp,
								state);
						goto error;
				}
				break;
			case '\n':
				switch(state) {
					case F_HOST: /*eat the spaces*/
					case L_PORT: /*eat the spaces*/
					case F_PORT:
					case L_PARAM: /* eat the space */
					case F_PARAM:
					case F_VIA: /* eat the space */
					case L_VIA:
					case F_COMMENT:
					case P_COMMENT:
					case F_IP6HOST:
					case P_IP6HOST:
						saved_state = state;
						state = F_LF;
						break;
					case P_HOST:
						/*mark end of host*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						saved_state = L_PORT;
						state = F_LF;
						break;
					case P_PORT:
						/*end of port */
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
						saved_state = L_PARAM;
						state = F_LF;
						break;
					case P_PARAM:
						/*	*tmp=0;*/ /*!?end of param*/
						saved_state = L_PARAM;
						state = F_LF;
						break;
					case F_CR:
						state = F_CRLF;
						break;
					case F_CRLF:
					case F_LF:
						state = saved_state;
						goto endofheader;
					default:
						LM_CRIT("BUG: parsing via on <%c>\n", *tmp);
						goto error;
				}
				break;
			case '\r':
				switch(state) {
					case F_HOST: /*eat the spaces*/
					case L_PORT: /*eat the spaces*/
					case F_PORT:
					case L_PARAM: /* eat the space */
					case F_PARAM:
					case F_VIA: /* eat the space */
					case L_VIA:
					case F_COMMENT:
					case P_COMMENT:
					case F_IP6HOST:
					case P_IP6HOST:
						saved_state = state;
						state = F_CR;
						break;
					case P_HOST:
						/*mark end of host*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						saved_state = L_PORT;
						state = F_CR;
						break;
					case P_PORT:
						/*end of port */
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
						saved_state = L_PARAM;
						state = F_CR;
						break;
					case P_PARAM:
						/*	*tmp=0;*/ /*!?end of param*/
						saved_state = L_PARAM;
						state = F_CR;
						break;
					case F_CRLF:
					case F_CR:
					case F_LF:
						state = saved_state;
						goto endofheader;
					default:
						LM_CRIT("BUG: parsing via on <%c>\n", *tmp);
						goto error;
				}
				break;

			case ':':
				switch(state) {
					case F_HOST:
					case F_IP6HOST:
						state = P_IP6HOST;
						break;
					case P_IP6HOST:
						break;
					case P_HOST:
						/*mark  end of host*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						state = F_PORT;
						break;
					case L_PORT:
						state = F_PORT;
						break;
					case P_PORT:
						LM_ERR("bad port\n");
						goto error;
					case L_PARAM:
					case F_PARAM:
					case F_PORT:
					case P_PARAM:
						LM_ERR("bad char <%c> in state %d\n", *tmp, state);
						goto error;
					case L_VIA:
					case F_VIA:
						LM_ERR("bad char in compact via\n");
						goto error;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					case F_COMMENT: /*everything is allowed in a comment*/
						vb->comment.s = tmp;
						state = P_COMMENT;
						break;
					case P_COMMENT: /*everything is allowed in a comment*/
						break;
					default:
						LM_CRIT("on <%c> state %d\n", *tmp, state);
						goto error;
				}
				break;
			case ';':
				switch(state) {
					case F_HOST:
					case F_IP6HOST:
						LM_ERR("no host found\n");
						goto error;
					case P_IP6HOST:
						LM_ERR("bad ipv6 reference\n");
						goto error;
					case P_HOST:
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						state = F_PARAM;
						param_start = tmp + 1;
						break;
					case P_PORT:
						/*mark the end*/
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
					case L_PORT:
					case L_PARAM:
						state = F_PARAM;
						param_start = tmp + 1;
						break;
					case F_PORT:
						LM_ERR("bad char <%c> in state %d\n", *tmp, state);
						goto error;
					case F_PARAM:
						LM_ERR("null param?\n");
						goto error;
					case P_PARAM:
						/*hmm next, param?*/
						state = F_PARAM;
						param_start = tmp + 1;
						break;
					case L_VIA:
					case F_VIA:
						LM_ERR("bad char <%c> in next via\n", *tmp);
						goto error;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					case F_COMMENT: /*everything is allowed in a comment*/
						vb->comment.s = tmp;
						state = P_COMMENT;
						break;
					case P_COMMENT: /*everything is allowed in a comment*/
						break;

					default:
						LM_CRIT("BUG: parsing via on <%c> state %d\n", *tmp,
								state);
						goto error;
				}
				break;
			case ',':
				switch(state) {
					case F_HOST:
					case F_IP6HOST:
						LM_ERR("no host found\n");
						goto error;
					case P_IP6HOST:
						LM_ERR("bad ipv6 reference\n");
						goto error;
					case P_HOST:
						/*mark the end*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						state = F_VIA;
						break;
					case P_PORT:
						/*mark the end*/
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
						state = F_VIA;
						break;
					case L_PORT:
					case L_PARAM:
					case P_PARAM:
					case L_VIA:
						state = F_VIA;
						break;
					case F_PORT:
					case F_PARAM:
						LM_ERR("invalid char <%c> in state %d\n", *tmp, state);
						goto error;
					case F_VIA:
						/* do  nothing,  eat ","*/
						break;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					case F_COMMENT: /*everything is allowed in a comment*/
						vb->comment.s = tmp;
						state = P_COMMENT;
						break;
					case P_COMMENT: /*everything is allowed in a comment*/
						break;
					default:
						LM_CRIT("BUG: parsing via on <%c> state %d\n", *tmp,
								state);
						goto error;
				}
				break;
			case '(':
				switch(state) {
					case F_HOST:
					case F_PORT:
					case F_PARAM:
					case F_VIA:
					case F_IP6HOST:
					case P_IP6HOST: /*must be terminated in ']'*/
						LM_ERR("parsing via on <%c> state %d\n", *tmp, state);
						goto error;
					case P_HOST:
						/*mark the end*/
						if(vb->host.s)
							vb->host.len = tmp - vb->host.s;
						state = F_COMMENT;
						c_nest++;
						break;
					case P_PORT:
						/*mark the end*/
						if(vb->port_str.s)
							vb->port_str.len = tmp - vb->port_str.s;
						state = F_COMMENT;
						c_nest++;
						break;
					case P_PARAM:
						/*mark the end*/
						vb->params.len = tmp - vb->params.s;
						state = F_COMMENT;
						c_nest++;
						break;
					case L_PORT:
					case L_PARAM:
					case L_VIA:
						state = F_COMMENT;
						vb->params.len = tmp - vb->params.s;
						c_nest++;
						break;
					case P_COMMENT:
					case F_COMMENT:
						c_nest++;
						break;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					default:
						LM_CRIT("BUG: parsing via on <%c> state %d\n", *tmp,
								state);
						goto error;
				}
				break;
			case ')':
				switch(state) {
					case F_COMMENT:
					case P_COMMENT:
						if(c_nest) {
							c_nest--;
							if(c_nest == 0) {
								state = L_VIA;
								vb->comment.len = tmp - vb->comment.s;
								break;
							}
						} else {
							LM_ERR("parsing via: missing '(' - nesting = %d\n",
									c_nest);
							goto error;
						}
						break;
					case F_HOST:
					case F_PORT:
					case F_PARAM:
					case F_VIA:
					case P_HOST:
					case P_PORT:
					case P_PARAM:
					case L_PORT:
					case L_PARAM:
					case L_VIA:
					case F_IP6HOST:
					case P_IP6HOST:
						LM_ERR("parsing via on <%c> state %d\n", *tmp, state);
						goto error;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					default:
						LM_CRIT("BUG: parsing via on <%c> state %d\n", *tmp,
								state);
						goto error;
				}
				break;
			case '[':
				switch(state) {
					case F_HOST:
						vb->host.s = tmp; /* mark start here (include [])*/
						state = F_IP6HOST;
						break;
					case F_COMMENT: /*everything is allowed in a comment*/
						vb->comment.s = tmp;
						state = P_COMMENT;
						break;
					case P_COMMENT:
						break;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					default:
						LM_ERR("parsing via on <%c> state %d\n", *tmp, state);
						goto error;
				}
				break;
			case ']':
				switch(state) {
					case P_IP6HOST:
						/*mark the end*/
						if(vb->host.s)
							vb->host.len =
									(tmp - vb->host.s) + 1; /* include "]" */
						state = L_PORT;
						break;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					case F_COMMENT: /*everything is allowed in a comment*/
						vb->comment.s = tmp;
						state = P_COMMENT;
						break;
					case P_COMMENT:
						break;
					default:
						LM_ERR("parsing via on <%c> state %d\n", *tmp, state);
						goto error;
				}
				break;

			default:
				switch(state) {
					case F_HOST:
						state = P_HOST;
						vb->host.s = tmp;
						break;
					case P_HOST:
						break;
					case F_PORT:
						state = P_PORT;
						vb->port_str.s = tmp;
						break;
					case P_PORT:
						/*check if number?*/
						break;
					case F_PARAM:
							/*state=P_PARAM*/;
						if(vb->params.s == 0)
							vb->params.s = param_start;
						param = pkg_malloc(sizeof(struct via_param));
						if(param == 0) {
							PKG_MEM_ERROR;
							goto error;
						}
						memset(param, 0, sizeof(struct via_param));
						param->start = param_start;
						tmp = parse_via_param(
								tmp, end, &state, &saved_state, param, vb);

						switch(state) {
							case F_PARAM:
								param_start = tmp + 1;
							case L_PARAM:
							case F_LF:
							case F_CR:
								break;
							case F_VIA:
								vb->params.len = param->start + param->size
												 - vb->params.s;
								break;
							case END_OF_HEADER:
								vb->params.len = param->start + param->size
												 - vb->params.s;
								break;
							case PARAM_ERROR:
								pkg_free(param);
								goto error;
							default:
								pkg_free(param);
								LM_ERR("parsing via after parse_via_param:"
									   " invalid char <%c> on state %d\n",
										*tmp, state);
								goto error;
						}
						/*add param to the list*/
						if(vb->last_param)
							vb->last_param->next = param;
						else
							vb->param_lst = param;
						vb->last_param = param;
						/* update param. shortcuts */
						switch(param->type) {
							case PARAM_BRANCH:
								vb->branch = param;
								break;
							case PARAM_RECEIVED:
								vb->received = param;
								break;
							case PARAM_RPORT:
								vb->rport = param;
								break;
							case PARAM_I:
								vb->i = param;
								break;
							case PARAM_ALIAS:
								vb->alias = param;
								break;
#ifdef USE_COMP
							case PARAM_COMP:
								vb->comp = param;
								/*  moved comp value parsing in via_param */
								/*
								if  ((param->value.len==SIGCOMP_NAME_LEN) &&
									(strncasecmp(param->value.s, SIGCOMP_NAME,
												SIGCOMP_NAME_LEN)==0)){
									vb->comp_no=COMP_SIGCOMP;
								}else if ((param->value.len==SERGZ_NAME_LEN) &&
										(strncasecmp(param->value.s,
													SERGZ_NAME,
													SERGZ_NAME_LEN)==0)){
									vb->comp_no=COMP_SERGZ;
								}else{
									LM_ERR("unrecognized"
										" compression method in comp=%.*s\n",
										param->value.len, param->value.s);
								}
								*/
								break;
#endif
						}

						if(state == END_OF_HEADER) {
							state = saved_state;
							goto endofheader;
						}
						break;
					case P_PARAM:
						break;
					case F_VIA:
						/*vb->next=tmp;*/ /*???*/
						goto nextvia;
					case L_PORT:
					case L_PARAM:
					case L_VIA:
						LM_ERR("parsing via on <%c> state %d (default)\n", *tmp,
								state);
						goto error;
					case F_COMMENT:
						state = P_COMMENT;
						vb->comment.s = tmp;
						break;
					case P_COMMENT:
						break;
					case F_IP6HOST:
						state = P_IP6HOST;
						break;
					case P_IP6HOST:
						break;
					case F_CRLF:
					case F_LF:
					case F_CR:
						/*previous=crlf and now !=' '*/
						goto endofheader;
					default:
						LM_ERR("BUG: parsing via - invalid char <%c>"
							   " in state %d\n",
								*tmp, state);
						goto error;
				}
		}
	}

	DBG("end of packet reached, state=%d\n", state);
	goto endofpacket; /*end of packet, probably should be goto error*/

endofheader:
	state = saved_state;
	DBG("end of header reached, state=%d\n", state);
endofpacket:
	/* check if error*/
	switch(state) {
		case P_HOST:
		case L_PORT:
		case P_PORT:
		case L_PARAM:
		case P_PARAM:
		case P_VALUE:
		case GEN_PARAM:
		case FIN_HIDDEN:
		case L_VIA:
			break;
		default:
			LM_ERR("invalid via - end of header in state %d\n", state);
			goto error;
	}


	/*
	if (proto) printf("<SIP/2.0/%s>\n", proto);
	if (host) printf("host= <%s>\n", host);
	if (port_str) printf("port= <%s>\n", port_str);
	if (param) printf("params= <%s>\n", param);
	if (comment) printf("comment= <%s>\n", comment);
	if(next_via) printf("next_via= <%s>\n", next_via);
	*/
	/*DBG("parse_via: rest=<%s>\n", tmp);*/

	vb->error = PARSE_OK;
	vb->bstart = buffer;
	vb->bsize = tmp - buffer;
	if(vb->port_str.s) {
		vb->port = str2s(vb->port_str.s, vb->port_str.len, &err);
		if(err) {
			LM_ERR("invalid port number <%.*s>\n", vb->port_str.len,
					ZSW(vb->port_str.s));
			goto error;
		}
	}
	if(vb->params.s != NULL && vb->params.len == 0 && vb->last_param != NULL) {
		if(vb->last_param->name.len > 0) {
			if(vb->last_param->value.len > 0) {
				vb->params.len = vb->last_param->value.s + vb->last_param->value.len
					- vb->params.s;
			} else {
				vb->params.len = vb->last_param->name.s + vb->last_param->name.len
					- vb->params.s;
			}
		}
	}
	return tmp;
nextvia:
	DBG("parsing via: next via\n");
	vb->error = PARSE_OK;
	vb->bstart = buffer;
	vb->bsize = tmp - buffer;
	if(vb->port_str.s) {
		vb->port = str2s(vb->port_str.s, vb->port_str.len, &err);
		if(err) {
			LM_ERR("invalid port number <%.*s>\n", vb->port_str.len,
					ZSW(vb->port_str.s));
			goto error;
		}
	}
	if(vb->params.s != NULL && vb->params.len == 0 && vb->last_param != NULL) {
		if(vb->last_param->name.len > 0) {
			if(vb->last_param->value.len > 0) {
				vb->params.len = vb->last_param->value.s + vb->last_param->value.len
					- vb->params.s;
			} else {
				vb->params.len = vb->last_param->name.s + vb->last_param->name.len
					- vb->params.s;
			}
		}
	}

	vb->next = pkg_malloc(sizeof(struct via_body));
	if(vb->next == 0) {
		PKG_MEM_ERROR;
		goto error;
	}
	vb = vb->next;
	memset(vb, 0, sizeof(struct via_body));
	buffer = tmp;
	goto parse_again;

error:
	if(end > buffer) {
		LM_ERR("parsing via on: <%.*s>\n", (int)(end - buffer),
				ZSW(ksr_buf_oneline(buffer, (int)(end - buffer))));
	}
	if((tmp > buffer) && (tmp < end)) {
		LM_ERR("parse error, parsed so far:<%.*s>\n", (int)(tmp - buffer),
				ZSW(ksr_buf_oneline(buffer, (int)(tmp - buffer))));
	} else {
		LM_ERR("via parse error\n");
	}
	vb->error = PARSE_ERROR;
	vbody->error = PARSE_ERROR; /* make sure the first via body is marked
								 * as bad also */
	return tmp;
}


static inline void free_via_param_list(struct via_param *vp)
{
	struct via_param *foo;
	while(vp) {
		foo = vp;
		vp = vp->next;
		pkg_free(foo);
	}
}


void free_via_list(struct via_body *vb)
{
	struct via_body *foo;
	while(vb) {
		foo = vb;
		vb = vb->next;
		if(foo->param_lst)
			free_via_param_list(foo->param_lst);
		pkg_free(foo);
	}
}

int parse_via_header(struct sip_msg *msg, int n, struct via_body **q)
{
	struct hdr_field *p;
	struct via_body *pp = NULL;
	int i;

	switch(n) {
		case 1:
		case 2:
			if(!msg->h_via1
					&& (parse_headers(msg, HDR_VIA_F, 0) == -1
							|| !msg->h_via1)) {
				DBG("bad msg or missing VIA1 header \n");
				return -1;
			}
			pp = msg->h_via1->parsed;
			if(n == 1)
				break;
			pp = pp->next;
			if(pp)
				break;

			if(!msg->h_via2
					&& (parse_headers(msg, HDR_VIA2_F, 0) == -1
							|| !msg->h_via2)) {
				DBG("bad msg or missing VIA2 header \n");
				return -1;
			}
			pp = msg->h_via2->parsed;
			break;
		default:
			if(!msg->eoh
					&& (parse_headers(msg, HDR_EOH_F, 0) == -1 || !msg->eoh)) {
				ERR("bad msg while parsing to EOH \n");
				return -1;
			}
			p = msg->h_via1;
			i = n;
			while(i && p) {
				if(p->type == HDR_VIA_T) {
					i--;
					pp = p->parsed;
					while(i && (pp->next)) {
						i--;
						pp = pp->next;
					}
				}
				p = p->next;
			}
			if(i > 0) {
				DBG("missing VIA[%d] header\n", n);
				return -1;
			}
	}
	if(pp) {
		*q = pp;
		return 0;
	} else
		return -1;
}


/*
 * Parse/link Via overload-control parameters
 */
int parse_via_oc(struct sip_msg *msg, struct via_body *vbp, via_oc_t *ocp)
{
	via_param_t *vp;

	if(vbp == NULL || ocp == NULL) {
		return -1;
	}
	memset(ocp, 0, sizeof(via_oc_t));

	for(vp = vbp->param_lst; vp != NULL; vp = vp->next) {
		if(vp->name.len == 2 && strncasecmp(vp->name.s, "oc", 2) == 0) {
			if(vp->value.len > 0) {
				ocp->oc = 2;
				ocp->ocval.len = vp->value.len;
				ocp->ocval.s = vp->value.s;
			} else {
				ocp->oc = 1;
			}
		} else if(vp->name.len == 7
				  && strncasecmp(vp->name.s, "oc-algo", 7) == 0) {
			if(vp->value.len > 0) {
				ocp->algo.len = vp->value.len;
				ocp->algo.s = vp->value.s;
			}
		} else if(vp->name.len == 11
				  && strncasecmp(vp->name.s, "oc-validity", 11) == 0) {
			if(vp->value.len > 0) {
				str2ulong(&vp->value, &ocp->validity);
			}
		} else if(vp->name.len == 6
				  && strncasecmp(vp->name.s, "oc-seq", 6) == 0) {
			if(vp->value.len > 0) {
				str2int(&vp->value, &ocp->seq);
			}
		}
	}
	return 0;
}
