parser/parse_fline.c
b6f4c1df
 /*
  * sip first line parsing automaton
  * 
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
07ca8c05
  * This file is part of Kamailio, a free SIP server.
7dd0b342
  *
07ca8c05
  * Kamailio is free software; you can redistribute it and/or modify
7dd0b342
  * 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
  *
07ca8c05
  * Kamailio is distributed in the hope that it will be useful,
7dd0b342
  * 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 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
049f64c2
  *
b6f4c1df
  */
 
7b7456b0
 /*! \file
  * \brief Parser :: SIP first line parsing automaton
  *
  * \ingroup parser
  */
 
7dd0b342
 
049f64c2
 #include "../comp_defs.h"
3881f12c
 #include "../dprint.h"
b6f4c1df
 #include "msg_parser.h"
2bb60634
 #include "parser_f.h"
 #include "../mem/mem.h"
6b6f226f
 #include "../ut.h"
b6f4c1df
 
93289188
 /* flags for first line
  * - stored on a short field (16 flags) */
 #define FLINE_FLAG_PROTO_SIP	(1<<0)
 #define FLINE_FLAG_PROTO_HTTP	(1<<1)
 
adf32ec8
 int http_reply_parse = 0;
ac34f9f4
 
b6f4c1df
 /* grammar:
 	request  =  method SP uri SP version CRLF
 	response =  version SP status  SP reason  CRLF
 	(version = "SIP/2.0")
 */
 
2bb60634
 
 /* parses the first line, returns pointer to  next line  & fills fl;
    also  modifies buffer (to avoid extra copy ops) */
93289188
 char* parse_first_line(char* buffer, unsigned int len, struct msg_start* fl)
2bb60634
 {
 	
 	char *tmp;
 	char* second;
 	char* third;
 	char* nl;
 	int offset;
 	/* int l; */
 	char* end;
 	char s1,s2,s3;
 	char *prn;
 	unsigned int t;
 
 	/* grammar:
 		request  =  method SP uri SP version CRLF
 		response =  version SP status  SP reason  CRLF
 		(version = "SIP/2.0")
 	*/
 	
 
93289188
 	memset(fl, 0, sizeof(struct msg_start));
a111b33a
 	offset = 0;
2bb60634
 	end=buffer+len;
 	/* see if it's a reply (status) */
 
 	/* jku  -- parse well-known methods */
 
 	/* drop messages which are so short they are for sure useless;
            utilize knowledge of minimum size in parsing the first
 	   token 
         */
 	if (len <=16 ) {
 		LOG(L_INFO, "ERROR: parse_first_line: message too short: %d\n", len);
 		goto error1;
 	}
 	tmp=buffer;
   	/* is it perhaps a reply, ie does it start with "SIP...." ? */
 	if ( 	(*tmp=='S' || *tmp=='s') && 
 		strncasecmp( tmp+1, SIP_VERSION+1, SIP_VERSION_LEN-1)==0 &&
 		(*(tmp+SIP_VERSION_LEN)==' ')) {
 			fl->type=SIP_REPLY;
93289188
 			fl->flags|=FLINE_FLAG_PROTO_SIP;
2bb60634
 			fl->u.reply.version.len=SIP_VERSION_LEN;
 			tmp=buffer+SIP_VERSION_LEN;
adf32ec8
 	} else if (http_reply_parse != 0 &&
c7151212
 		 	(*tmp=='H' || *tmp=='h') &&
 			/* 'HTTP/1.' */
 			strncasecmp( tmp+1, HTTP_VERSION+1, HTTP_VERSION_LEN-1)==0 &&
 			/* [0|1] */
 			((*(tmp+HTTP_VERSION_LEN)=='0') || (*(tmp+HTTP_VERSION_LEN)=='1')) &&
 			(*(tmp+HTTP_VERSION_LEN+1)==' ')  ){ 
 			/* ugly hack to be able to route http replies
 			 * Note: - the http reply must have a via
 			 *       - the message is marked as SIP_REPLY (ugly)
 			 */
 				fl->type=SIP_REPLY;
93289188
 				fl->flags|=FLINE_FLAG_PROTO_HTTP;
c7151212
 				fl->u.reply.version.len=HTTP_VERSION_LEN+1 /*include last digit*/;
 				tmp=buffer+HTTP_VERSION_LEN+1 /* last digit */;
2bb60634
 	} else IFISMETHOD( INVITE, 'I' )
 	else IFISMETHOD( CANCEL, 'C')
 	else IFISMETHOD( ACK, 'A' )
 	else IFISMETHOD( BYE, 'B' ) 
0f0f44c4
 	else IFISMETHOD( INFO, 'I' )
394abcfd
 	else IFISMETHOD( REGISTER, 'R')
 	else IFISMETHOD( SUBSCRIBE, 'S')
 	else IFISMETHOD( NOTIFY, 'N')
19af811d
 	else IFISMETHOD( MESSAGE, 'M')
 	else IFISMETHOD( OPTIONS, 'O')
74ca8909
 	else IFISMETHOD( PRACK, 'P')
 	else IFISMETHOD( UPDATE, 'U')
8b521263
 	else IFISMETHOD( REFER, 'R')
 	else IFISMETHOD( PUBLISH, 'P')
2bb60634
 	/* if you want to add another method XXX, include METHOD_XXX in
            H-file (this is the value which you will take later in
            processing and define XXX_LEN as length of method name;
 	   then just call IFISMETHOD( XXX, 'X' ) ... 'X' is the first
 	   latter; everything must be capitals
 	*/
 	else {
 		/* neither reply, nor any of known method requests, 
 		   let's believe it is an unknown method request
         	*/
 		tmp=eat_token_end(buffer,buffer+len);
 		if ((tmp==buffer)||(tmp>=end)){
 			LOG(L_INFO, "ERROR:parse_first_line: empty  or bad first line\n");
 			goto error1;
 		}
 		if (*tmp!=' ') {
 			LOG(L_INFO, "ERROR:parse_first_line: method not followed by SP\n");
 			goto error1;
 		}
 		fl->type=SIP_REQUEST;
 		fl->u.request.method_value=METHOD_OTHER;
 		fl->u.request.method.len=tmp-buffer;
 	}
 
 
 	/* identifying type of message over now; 
 	   tmp points at space after; go ahead */
 
 	fl->u.request.method.s=buffer;  /* store ptr to first token */
 	second=tmp+1;			/* jump to second token */
 	offset=second-buffer;
 
 /* EoJku */
 	
 	/* next element */
 	tmp=eat_token_end(second, second+len-offset);
 	if (tmp>=end){
 		goto error;
 	}
 	offset+=tmp-second;
 	third=eat_space_end(tmp, tmp+len-offset);
 	offset+=third-tmp;
 	if ((third==tmp)||(tmp>=end)){
 		goto error;
 	}
 	fl->u.request.uri.s=second;
 	fl->u.request.uri.len=tmp-second;
 
 	/* jku: parse status code */
 	if (fl->type==SIP_REPLY) {
 		if (fl->u.request.uri.len!=3) {
049f64c2
 			LOG(L_INFO, "ERROR:parse_first_line: len(status code)!=3: %.*s\n",
6b6f226f
 				fl->u.request.uri.len, ZSW(second) );
2bb60634
 			goto error;
 		}
 		s1=*second; s2=*(second+1);s3=*(second+2);
 		if (s1>='0' && s1<='9' && 
 		    s2>='0' && s2<='9' &&
 		    s3>='0' && s3<='9' ) {
 			fl->u.reply.statuscode=(s1-'0')*100+10*(s2-'0')+(s3-'0');
 		} else {
049f64c2
 			LOG(L_INFO, "ERROR:parse_first_line: status_code non-numerical: %.*s\n",
6b6f226f
 				fl->u.request.uri.len, ZSW(second) );
2bb60634
 			goto error;
 		}
 	}
 	/* EoJku */
 
 	/*  last part: for a request it must be the version, for a reply
 	 *  it can contain almost anything, including spaces, so we don't care
 	 *  about it*/
 	if (fl->type==SIP_REQUEST){
 		tmp=eat_token_end(third,third+len-offset);
 		offset+=tmp-third;
 		if ((tmp==third)||(tmp>=end)){
 			goto error;
 		}
 		if (! is_empty_end(tmp, tmp+len-offset)){
 			goto error;
 		}
 	}else{
 		tmp=eat_token2_end(third,third+len-offset,'\r'); /* find end of line 
 												  ('\n' or '\r') */
 		if (tmp>=end){ /* no crlf in packet => invalid */
 			goto error;
 		}
 		offset+=tmp-third;
 	}
 	nl=eat_line(tmp,len-offset);
 	if (nl>=end){ /* no crlf in packet or only 1 line > invalid */
 		goto error;
 	}
 	fl->u.request.version.s=third;
 	fl->u.request.version.len=tmp-third;
049f64c2
 	fl->len=nl-buffer;
2bb60634
 
93289188
 	if (fl->type==SIP_REQUEST) {
 		if(fl->u.request.version.len >= SIP_VERSION_LEN
 				&& (fl->u.request.version.s[0]=='S'
 					|| fl->u.request.version.s[0]=='s')
 				&& !strncasecmp(fl->u.request.version.s+1,
 					SIP_VERSION+1, SIP_VERSION_LEN-1)) {
 			fl->flags|=FLINE_FLAG_PROTO_SIP;
 		} else if(fl->u.request.version.len >= HTTP_VERSION_LEN
 				&& (fl->u.request.version.s[0]=='H'
 					|| fl->u.request.version.s[0]=='h')
 				&& !strncasecmp(fl->u.request.version.s+1,
 					HTTP_VERSION+1, HTTP_VERSION_LEN-1)) {
 			fl->flags|=FLINE_FLAG_PROTO_HTTP;
 		}
 	}
 
2bb60634
 	return nl;
 
 error:
2685cd9b
 	LOG(L_DBG, "parse_first_line: bad %s first line\n",
2bb60634
 		(fl->type==SIP_REPLY)?"reply(status)":"request");
 
2685cd9b
 	LOG(L_DBG, "at line 0 char %d: \n", offset );
2bb60634
 	prn=pkg_malloc( offset );
 	if (prn) {
 		for (t=0; t<offset; t++)
 			if (*(buffer+t)) *(prn+t)=*(buffer+t);
f0fb6f1e
 			else *(prn+t)=176; /* '�' */
2685cd9b
 		LOG(L_DBG, "parsed so far: %.*s\n", offset, ZSW(prn) );
2bb60634
 		pkg_free( prn );
 	};
 error1:
 	fl->type=SIP_INVALID;
a111b33a
 	LOG(L_ERR, "parse_first_line: bad message (offset: %d)\n", offset);
2bb60634
 	/* skip  line */
 	nl=eat_line(buffer,len);
 	return nl;
 }
93289188
 
 char* parse_fline(char* buffer, char* end, struct msg_start* fl)
 {
 	if(end<=buffer) {
 		/* make it throw error via parse_first_line() for consistency */
 		return parse_first_line(buffer, 0, fl);
 	}
 	return parse_first_line(buffer, (unsigned int)(end-buffer), fl);
 }