parse_fline.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  * 
00004  * sip first line parsing automaton
00005  * 
00006  *
00007  * Copyright (C) 2001-2003 FhG Fokus
00008  *
00009  * This file is part of ser, a free SIP server.
00010  *
00011  * ser is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * For a license to use the ser software under conditions
00017  * other than those described here, or to purchase support for this
00018  * software, please contact iptel.org by e-mail at the following addresses:
00019  *    info@iptel.org
00020  *
00021  * ser is distributed in the hope that it will be useful,
00022  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  * GNU General Public License for more details.
00025  *
00026  * You should have received a copy of the GNU General Public License 
00027  * along with this program; if not, write to the Free Software 
00028  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00029  *
00030  * History:
00031  * ---------
00032  * 2003-02-28 scratchpad compatibility abandoned (jiri)
00033  * 2003-01-28: removed 0-terminators from first line (jiri)
00034  * 2003-04-26 ZSW (jiri)
00035  */
00036 
00044 #include "../comp_defs.h"
00045 #include "../dprint.h"
00046 #include "msg_parser.h"
00047 #include "parser_f.h"
00048 #include "../mem/mem.h"
00049 #include "../ut.h"
00050 
00051 int http_reply_hack = 0;
00052 
00053 /* grammar:
00054         request  =  method SP uri SP version CRLF
00055         response =  version SP status  SP reason  CRLF
00056         (version = "SIP/2.0")
00057 */
00058 
00059 
00060 /* parses the first line, returns pointer to  next line  & fills fl;
00061    also  modifies buffer (to avoid extra copy ops) */
00062 char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
00063 {
00064         
00065         char *tmp;
00066         char* second;
00067         char* third;
00068         char* nl;
00069         int offset;
00070         /* int l; */
00071         char* end;
00072         char s1,s2,s3;
00073         char *prn;
00074         unsigned int t;
00075 
00076         /* grammar:
00077                 request  =  method SP uri SP version CRLF
00078                 response =  version SP status  SP reason  CRLF
00079                 (version = "SIP/2.0")
00080         */
00081         
00082 
00083         end=buffer+len;
00084         /* see if it's a reply (status) */
00085 
00086         /* jku  -- parse well-known methods */
00087 
00088         /* drop messages which are so short they are for sure useless;
00089            utilize knowledge of minimum size in parsing the first
00090            token 
00091         */
00092         if (len <=16 ) {
00093                 LOG(L_INFO, "ERROR: parse_first_line: message too short: %d\n", len);
00094                 goto error1;
00095         }
00096         tmp=buffer;
00097         /* is it perhaps a reply, ie does it start with "SIP...." ? */
00098         if (    (*tmp=='S' || *tmp=='s') && 
00099                 strncasecmp( tmp+1, SIP_VERSION+1, SIP_VERSION_LEN-1)==0 &&
00100                 (*(tmp+SIP_VERSION_LEN)==' ')) {
00101                         fl->type=SIP_REPLY;
00102                         fl->u.reply.version.len=SIP_VERSION_LEN;
00103                         tmp=buffer+SIP_VERSION_LEN;
00104         } else if (http_reply_hack != 0 && 
00105                         (*tmp=='H' || *tmp=='h') &&
00106                         /* 'HTTP/1.' */
00107                         strncasecmp( tmp+1, HTTP_VERSION+1, HTTP_VERSION_LEN-1)==0 &&
00108                         /* [0|1] */
00109                         ((*(tmp+HTTP_VERSION_LEN)=='0') || (*(tmp+HTTP_VERSION_LEN)=='1')) &&
00110                         (*(tmp+HTTP_VERSION_LEN+1)==' ')  ){ 
00111                         /* ugly hack to be able to route http replies
00112                          * Note: - the http reply must have a via
00113                          *       - the message is marked as SIP_REPLY (ugly)
00114                          */
00115                                 fl->type=SIP_REPLY;
00116                                 fl->u.reply.version.len=HTTP_VERSION_LEN+1 /*include last digit*/;
00117                                 tmp=buffer+HTTP_VERSION_LEN+1 /* last digit */;
00118         } else IFISMETHOD( INVITE, 'I' )
00119         else IFISMETHOD( CANCEL, 'C')
00120         else IFISMETHOD( ACK, 'A' )
00121         else IFISMETHOD( BYE, 'B' ) 
00122         else IFISMETHOD( INFO, 'I' )
00123         else IFISMETHOD( REGISTER, 'R')
00124         else IFISMETHOD( SUBSCRIBE, 'S')
00125         else IFISMETHOD( NOTIFY, 'N')
00126         else IFISMETHOD( MESSAGE, 'M')
00127         else IFISMETHOD( OPTIONS, 'O')
00128         else IFISMETHOD( PRACK, 'P')
00129         else IFISMETHOD( UPDATE, 'U')
00130         else IFISMETHOD( REFER, 'R')
00131         else IFISMETHOD( PUBLISH, 'P')
00132         /* if you want to add another method XXX, include METHOD_XXX in
00133            H-file (this is the value which you will take later in
00134            processing and define XXX_LEN as length of method name;
00135            then just call IFISMETHOD( XXX, 'X' ) ... 'X' is the first
00136            latter; everything must be capitals
00137         */
00138         else {
00139                 /* neither reply, nor any of known method requests, 
00140                    let's believe it is an unknown method request
00141                 */
00142                 tmp=eat_token_end(buffer,buffer+len);
00143                 if ((tmp==buffer)||(tmp>=end)){
00144                         LOG(L_INFO, "ERROR:parse_first_line: empty  or bad first line\n");
00145                         goto error1;
00146                 }
00147                 if (*tmp!=' ') {
00148                         LOG(L_INFO, "ERROR:parse_first_line: method not followed by SP\n");
00149                         goto error1;
00150                 }
00151                 fl->type=SIP_REQUEST;
00152                 fl->u.request.method_value=METHOD_OTHER;
00153                 fl->u.request.method.len=tmp-buffer;
00154         }
00155 
00156 
00157         /* identifying type of message over now; 
00158            tmp points at space after; go ahead */
00159 
00160         fl->u.request.method.s=buffer;  /* store ptr to first token */
00161         second=tmp+1;                   /* jump to second token */
00162         offset=second-buffer;
00163 
00164 /* EoJku */
00165         
00166         /* next element */
00167         tmp=eat_token_end(second, second+len-offset);
00168         if (tmp>=end){
00169                 goto error;
00170         }
00171         offset+=tmp-second;
00172         third=eat_space_end(tmp, tmp+len-offset);
00173         offset+=third-tmp;
00174         if ((third==tmp)||(tmp>=end)){
00175                 goto error;
00176         }
00177         fl->u.request.uri.s=second;
00178         fl->u.request.uri.len=tmp-second;
00179 
00180         /* jku: parse status code */
00181         if (fl->type==SIP_REPLY) {
00182                 if (fl->u.request.uri.len!=3) {
00183                         LOG(L_INFO, "ERROR:parse_first_line: len(status code)!=3: %.*s\n",
00184                                 fl->u.request.uri.len, ZSW(second) );
00185                         goto error;
00186                 }
00187                 s1=*second; s2=*(second+1);s3=*(second+2);
00188                 if (s1>='0' && s1<='9' && 
00189                     s2>='0' && s2<='9' &&
00190                     s3>='0' && s3<='9' ) {
00191                         fl->u.reply.statuscode=(s1-'0')*100+10*(s2-'0')+(s3-'0');
00192                 } else {
00193                         LOG(L_INFO, "ERROR:parse_first_line: status_code non-numerical: %.*s\n",
00194                                 fl->u.request.uri.len, ZSW(second) );
00195                         goto error;
00196                 }
00197         }
00198         /* EoJku */
00199 
00200         /*  last part: for a request it must be the version, for a reply
00201          *  it can contain almost anything, including spaces, so we don't care
00202          *  about it*/
00203         if (fl->type==SIP_REQUEST){
00204                 tmp=eat_token_end(third,third+len-offset);
00205                 offset+=tmp-third;
00206                 if ((tmp==third)||(tmp>=end)){
00207                         goto error;
00208                 }
00209                 if (! is_empty_end(tmp, tmp+len-offset)){
00210                         goto error;
00211                 }
00212         }else{
00213                 tmp=eat_token2_end(third,third+len-offset,'\r'); /* find end of line 
00214                                                                                                   ('\n' or '\r') */
00215                 if (tmp>=end){ /* no crlf in packet => invalid */
00216                         goto error;
00217                 }
00218                 offset+=tmp-third;
00219         }
00220         nl=eat_line(tmp,len-offset);
00221         if (nl>=end){ /* no crlf in packet or only 1 line > invalid */
00222                 goto error;
00223         }
00224         fl->u.request.version.s=third;
00225         fl->u.request.version.len=tmp-third;
00226         fl->len=nl-buffer;
00227 
00228         return nl;
00229 
00230 error:
00231         LOG(L_INFO, "ERROR:parse_first_line: bad %s first line\n",
00232                 (fl->type==SIP_REPLY)?"reply(status)":"request");
00233 
00234         LOG(L_INFO, "ERROR: at line 0 char %d: \n", offset );
00235         prn=pkg_malloc( offset );
00236         if (prn) {
00237                 for (t=0; t<offset; t++)
00238                         if (*(buffer+t)) *(prn+t)=*(buffer+t);
00239                         else *(prn+t)='°';
00240                 LOG(L_INFO, "ERROR: parsed so far: %.*s\n", offset, ZSW(prn) );
00241                 pkg_free( prn );
00242         };
00243 error1:
00244         fl->type=SIP_INVALID;
00245         LOG(L_INFO, "ERROR:parse_first_line: bad message\n");
00246         /* skip  line */
00247         nl=eat_line(buffer,len);
00248         return nl;
00249 }