86f9431299e0cb385426184e04c6199439e5d36f
[sip-router] / parser / parse_fline.c
1 /*
2  * $Id$
3  * 
4  * sip first line parsing automaton
5  * 
6  *
7  * Copyright (C) 2001-2003 FhG Fokus
8  *
9  * This file is part of ser, a free SIP server.
10  *
11  * ser is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version
15  *
16  * For a license to use the ser software under conditions
17  * other than those described here, or to purchase support for this
18  * software, please contact iptel.org by e-mail at the following addresses:
19  *    info@iptel.org
20  *
21  * ser is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License 
27  * along with this program; if not, write to the Free Software 
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  *
30  * History:
31  * ---------
32  * 2003-02-28 scratchpad compatibility abandoned (jiri)
33  * 2003-01-28: removed 0-terminators from first line (jiri)
34  * 2003-04-26 ZSW (jiri)
35  */
36
37
38 #include "../comp_defs.h"
39 #include "../dprint.h"
40 #include "msg_parser.h"
41 #include "parser_f.h"
42 #include "../mem/mem.h"
43 #include "../ut.h"
44
45 /* grammar:
46         request  =  method SP uri SP version CRLF
47         response =  version SP status  SP reason  CRLF
48         (version = "SIP/2.0")
49 */
50
51
52 /* parses the first line, returns pointer to  next line  & fills fl;
53    also  modifies buffer (to avoid extra copy ops) */
54 char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
55 {
56         
57         char *tmp;
58         char* second;
59         char* third;
60         char* nl;
61         int offset;
62         /* int l; */
63         char* end;
64         char s1,s2,s3;
65         char *prn;
66         unsigned int t;
67
68         /* grammar:
69                 request  =  method SP uri SP version CRLF
70                 response =  version SP status  SP reason  CRLF
71                 (version = "SIP/2.0")
72         */
73         
74
75         end=buffer+len;
76         /* see if it's a reply (status) */
77
78         /* jku  -- parse well-known methods */
79
80         /* drop messages which are so short they are for sure useless;
81            utilize knowledge of minimum size in parsing the first
82            token 
83         */
84         if (len <=16 ) {
85                 LOG(L_INFO, "ERROR: parse_first_line: message too short: %d\n", len);
86                 goto error1;
87         }
88
89         tmp=buffer;
90         /* is it perhaps a reply, ie does it start with "SIP...." ? */
91         if (    (*tmp=='S' || *tmp=='s') && 
92                 strncasecmp( tmp+1, SIP_VERSION+1, SIP_VERSION_LEN-1)==0 &&
93                 (*(tmp+SIP_VERSION_LEN)==' ')) {
94                         fl->type=SIP_REPLY;
95                         fl->u.reply.version.len=SIP_VERSION_LEN;
96                         tmp=buffer+SIP_VERSION_LEN;
97         } else IFISMETHOD( INVITE, 'I' )
98         else IFISMETHOD( CANCEL, 'C')
99         else IFISMETHOD( ACK, 'A' )
100         else IFISMETHOD( BYE, 'B' ) 
101         else IFISMETHOD( INFO, 'I' )
102         else IFISMETHOD( REGISTER, 'R')
103         else IFISMETHOD( SUBSCRIBE, 'S')
104         else IFISMETHOD( NOTIFY, 'N')
105         /* if you want to add another method XXX, include METHOD_XXX in
106            H-file (this is the value which you will take later in
107            processing and define XXX_LEN as length of method name;
108            then just call IFISMETHOD( XXX, 'X' ) ... 'X' is the first
109            latter; everything must be capitals
110         */
111         else {
112                 /* neither reply, nor any of known method requests, 
113                    let's believe it is an unknown method request
114                 */
115                 tmp=eat_token_end(buffer,buffer+len);
116                 if ((tmp==buffer)||(tmp>=end)){
117                         LOG(L_INFO, "ERROR:parse_first_line: empty  or bad first line\n");
118                         goto error1;
119                 }
120                 if (*tmp!=' ') {
121                         LOG(L_INFO, "ERROR:parse_first_line: method not followed by SP\n");
122                         goto error1;
123                 }
124                 fl->type=SIP_REQUEST;
125                 fl->u.request.method_value=METHOD_OTHER;
126                 fl->u.request.method.len=tmp-buffer;
127         }
128
129
130         /* identifying type of message over now; 
131            tmp points at space after; go ahead */
132
133         fl->u.request.method.s=buffer;  /* store ptr to first token */
134         second=tmp+1;                   /* jump to second token */
135         offset=second-buffer;
136
137 /* EoJku */
138         
139         /* next element */
140         tmp=eat_token_end(second, second+len-offset);
141         if (tmp>=end){
142                 goto error;
143         }
144         offset+=tmp-second;
145         third=eat_space_end(tmp, tmp+len-offset);
146         offset+=third-tmp;
147         if ((third==tmp)||(tmp>=end)){
148                 goto error;
149         }
150         fl->u.request.uri.s=second;
151         fl->u.request.uri.len=tmp-second;
152
153         /* jku: parse status code */
154         if (fl->type==SIP_REPLY) {
155                 if (fl->u.request.uri.len!=3) {
156                         LOG(L_INFO, "ERROR:parse_first_line: len(status code)!=3: %.*s\n",
157                                 fl->u.request.uri.len, ZSW(second) );
158                         goto error;
159                 }
160                 s1=*second; s2=*(second+1);s3=*(second+2);
161                 if (s1>='0' && s1<='9' && 
162                     s2>='0' && s2<='9' &&
163                     s3>='0' && s3<='9' ) {
164                         fl->u.reply.statuscode=(s1-'0')*100+10*(s2-'0')+(s3-'0');
165                 } else {
166                         LOG(L_INFO, "ERROR:parse_first_line: status_code non-numerical: %.*s\n",
167                                 fl->u.request.uri.len, ZSW(second) );
168                         goto error;
169                 }
170         }
171         /* EoJku */
172
173         /*  last part: for a request it must be the version, for a reply
174          *  it can contain almost anything, including spaces, so we don't care
175          *  about it*/
176         if (fl->type==SIP_REQUEST){
177                 tmp=eat_token_end(third,third+len-offset);
178                 offset+=tmp-third;
179                 if ((tmp==third)||(tmp>=end)){
180                         goto error;
181                 }
182                 if (! is_empty_end(tmp, tmp+len-offset)){
183                         goto error;
184                 }
185         }else{
186                 tmp=eat_token2_end(third,third+len-offset,'\r'); /* find end of line 
187                                                                                                   ('\n' or '\r') */
188                 if (tmp>=end){ /* no crlf in packet => invalid */
189                         goto error;
190                 }
191                 offset+=tmp-third;
192         }
193         nl=eat_line(tmp,len-offset);
194         if (nl>=end){ /* no crlf in packet or only 1 line > invalid */
195                 goto error;
196         }
197         fl->u.request.version.s=third;
198         fl->u.request.version.len=tmp-third;
199         fl->len=nl-buffer;
200
201         return nl;
202
203 error:
204         LOG(L_INFO, "ERROR:parse_first_line: bad %s first line\n",
205                 (fl->type==SIP_REPLY)?"reply(status)":"request");
206
207         LOG(L_INFO, "ERROR: at line 0 char %d: \n", offset );
208         prn=pkg_malloc( offset );
209         if (prn) {
210                 for (t=0; t<offset; t++)
211                         if (*(buffer+t)) *(prn+t)=*(buffer+t);
212                         else *(prn+t)='°';
213                 LOG(L_INFO, "ERROR: parsed so far: %.*s\n", offset, ZSW(prn) );
214                 pkg_free( prn );
215         };
216 error1:
217         fl->type=SIP_INVALID;
218         LOG(L_INFO, "ERROR:parse_first_line: bad message\n");
219         /* skip  line */
220         nl=eat_line(buffer,len);
221         return nl;
222 }