parser/parse_fline.c
b6f4c1df
 /*
  * $Id$
  * 
  * sip first line parsing automaton
  * 
7dd0b342
  *
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * This file is part of ser, a free SIP server.
  *
  * ser 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
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
049f64c2
  *
  * History:
  * ---------
18dbc018
  * 2003-02-28 scratchpad compatibility abandoned (jiri)
049f64c2
  * 2003-01-28: removed 0-terminators from first line (jiri)
6b6f226f
  * 2003-04-26 ZSW (jiri)
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
 
7b4461e2
 
 #ifdef NO_HTTP_REPLY_HACK
 #undef HTTP_REPLY_HACK
 #else
 /* #define HTTP_REPLY_HACK */ /* allow HTTP replies */
 #endif
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) */
 char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 {
 	
 	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")
 	*/
 	
 
 	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;
 			fl->u.reply.version.len=SIP_VERSION_LEN;
 			tmp=buffer+SIP_VERSION_LEN;
7b4461e2
 #ifdef HTTP_REPLY_HACK
 	}else if ( 	(*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;
 			fl->u.reply.version.len=HTTP_VERSION_LEN+1 /*include last digit*/;
 			tmp=buffer+HTTP_VERSION_LEN+1 /* last digit */;
 #endif
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
 
 	return nl;
 
 error:
 	LOG(L_INFO, "ERROR:parse_first_line: bad %s first line\n",
 		(fl->type==SIP_REPLY)?"reply(status)":"request");
 
 	LOG(L_INFO, "ERROR: at line 0 char %d: \n", offset );
 	prn=pkg_malloc( offset );
 	if (prn) {
 		for (t=0; t<offset; t++)
 			if (*(buffer+t)) *(prn+t)=*(buffer+t);
 			else *(prn+t)='�';
6b6f226f
 		LOG(L_INFO, "ERROR: parsed so far: %.*s\n", offset, ZSW(prn) );
2bb60634
 		pkg_free( prn );
 	};
 error1:
 	fl->type=SIP_INVALID;
 	LOG(L_INFO, "ERROR:parse_first_line: bad message\n");
 	/* skip  line */
 	nl=eat_line(buffer,len);
 	return nl;
 }