parser seems to work
[sip-router] / msg_parser.c
1 /*
2  * $Id$
3  *
4  * sip msg. header proxy parser 
5  *
6  */
7
8 #include <string.h>
9
10 #include "msg_parser.h"
11 #include "parser_f.h"
12 #include "dprint.h"
13
14
15
16 #define DEBUG
17
18
19
20 /* parses the first line, returns pointer to  next line  & fills fl;
21    also  modifies buffer (to avoid extra copy ops) */
22 char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
23 {
24         
25         char *tmp;
26         char* second;
27         char* third;
28         char* nl;
29         int offset;
30         
31         /* grammar:
32                 request  =  method SP uri SP version CRLF
33                 response =  version SP status  SP reason  CRLF
34                 (version = "SIP/2.0")
35         */
36         
37
38         /* see if it's a reply (status) */
39         tmp=eat_token(buffer, len);
40         if (tmp==buffer){
41                 DPrint("ERROR: empty  or bad first line\n");
42                 goto error1;
43         }
44         if ((strlen(SIP_VERSION)==(tmp-buffer)) &&
45                 (memcmp(buffer,SIP_VERSION,tmp-buffer)==0)){
46                 
47                 fl->type=SIP_REPLY;
48         }else{
49                 fl->type=SIP_REQUEST;
50         }
51         
52         offset=tmp-buffer;
53         second=eat_space(tmp, len-offset);
54         offset+=second-tmp;
55         if (second==tmp){
56                 goto error;
57         }
58         *tmp=0; /* mark the end of the token */
59         fl->u.request.method=buffer;
60         
61         /* next element */
62         tmp=eat_token(second, len-offset);
63         offset+=tmp-second;
64         third=eat_space(tmp, len-offset);
65         offset+=third-tmp;
66         if(third==tmp){
67                 goto error;
68         }
69         *tmp=0; /* mark the end of the token */
70         fl->u.request.uri=second;
71         /*  last part */
72         tmp=eat_token(third,len-offset);
73         offset+=tmp-third;
74         if (tmp==third){
75                 goto error;
76         }
77         if (! is_empty(tmp, len-offset)){               
78                 goto error;
79         }
80         nl=eat_line(tmp,len-offset);
81         *tmp=0;
82         fl->u.request.version=third;
83         
84         return nl;
85
86 error:
87         DPrint("ERROR: bad %s first line\n", 
88                 (fl->type==SIP_REPLY)?"reply(status)":"request");
89 error1:
90         fl->type=SIP_INVALID;
91         DPrint("ERROR: at line 0 char %d\n", offset);
92         /* skip  line */
93         nl=eat_line(buffer,len);
94         return nl;
95 }
96
97
98 /* returns integer field name type */
99 int field_name(char *s)
100 {
101         if ((strcmp(s, "Via")==0 )||(strcmp(s,"v")==0))
102                 return  HDR_VIA;
103         if ((strcmp(s, "To")==0)||(strcmp(s,"t")==0))
104                 return HDR_TO;
105         return HDR_OTHER;
106 }
107
108
109
110
111 /* returns pointer to next header line, and fill hdr_f */
112 char* get_hdr_field(char *buffer, unsigned int len, struct hdr_field*  hdr_f)
113 {
114         /* grammar (rfc822):
115                 field = field-name ":" field-body CRLF
116                 field-body = text [ CRLF SP field-body ]
117            (CRLF in the field body must be removed)
118         */
119
120         char* tmp;
121         char* nl;
122         char* body;
123         int offset;
124
125         if ((*buffer=='\n')||(*buffer=='\r')){
126                 /* double crlf */
127                 tmp=eat_line(buffer,len);
128                 hdr_f->type=HDR_EOH;
129                 return tmp;
130         }
131         
132         tmp=eat_token2(buffer, len, ':');
133         if ((tmp==buffer) || (tmp-buffer==len) ||
134                 (is_empty(buffer, tmp-buffer-1))|| (*tmp!=':')){
135                 hdr_f->type=HDR_ERROR;
136                 goto error;
137         }
138         *tmp=0;
139         hdr_f->type=field_name(buffer);
140         body= ++tmp;
141         hdr_f->name=buffer;
142         offset=tmp-buffer;
143         /* get all the lines in this field  body */
144         do{
145                 nl=eat_line(tmp, len-offset);
146                 offset+=nl-tmp;
147                 tmp=nl;
148         
149         }while( (*tmp==' ' ||  *tmp=='\t') && (offset<len) );
150         if (offset==len){
151                 hdr_f->type=HDR_ERROR;
152                 DPrint("ERROR: field body too  long\n");
153                 goto error;
154         }
155         *(tmp-1)=0; /* should be an LF */
156         hdr_f->body=body;
157 error:
158         return tmp;
159 }
160
161
162
163 char* parse_hostport(char* buf, char** host, short int* port)
164 {
165         char *tmp;
166         char *invalid;
167         
168         *host=buf;
169         for(tmp=buf;(*tmp)&&(*tmp!=':');tmp++);
170         if (*tmp==0){
171                 *port=0;
172         }else{
173                 *tmp=0;
174                 invalid=0;
175                 *port=strtol(tmp+1, &invalid, 10);
176                 if ((invalid!=0)&&(*invalid)){
177                         DPrint("ERROR: hostport: trailing chars in port number: %s(%x)\n",
178                                         invalid, invalid);
179                         /* report error? */
180                 }
181         }
182         return *host;
183 }
184
185
186
187 /* parses a via body, returns next via (for compact vias) & fills vb,
188  * the buffer should be null terminated! */
189 char* parse_via_body(char* buffer,unsigned int len, struct via_body * vb)
190 {
191         /* format: sent-proto sent-by  *(";" params) [comment] 
192
193                    sent-proto = name"/"version"/"transport
194                    sent-by    = host [":" port]
195                    
196         */
197
198         char* tmp;
199         char *name,*version, *transport, *comment, *params, *hostport;
200         char * next_via;
201         char * host;
202         short int port;
203         int offset;
204         
205
206         name=version=transport=comment=params=hostport=next_via=host=0;
207         name=eat_space(buffer, len);
208         if (name-buffer==len) goto error;
209         offset=name-buffer;
210         tmp=name;
211
212         version=eat_token2(tmp,len-offset,'/');
213         if (version+1-buffer>=len) goto error;
214         *version=0;
215         version++;
216         offset+=version-tmp;
217         
218         transport=eat_token2(tmp,len-offset,'/');
219         if (transport+1-buffer>=len) goto error;
220         *transport=0;
221         transport++;
222         offset+=transport-tmp;
223         
224         tmp=eat_token(transport,len-offset);
225         if (tmp+1-buffer>=len) goto error;
226         *tmp=0;
227         tmp++;
228         offset+=tmp-transport;
229         
230         hostport=eat_space(tmp,len-offset);
231         if (hostport+1-buffer>=len) goto error;
232         offset+=hostport-tmp;
233
234         /* find end of hostport */
235         for(tmp=hostport; (tmp-buffer)<len &&
236                         (*tmp!=' ')&&(*tmp!=';')&&(*tmp!=','); tmp++);
237         if (tmp-buffer<len){
238                 switch (*tmp){
239                         case ' ':
240                                 *tmp=0;
241                                 /*the rest is comment? */
242                                 if (tmp+1-buffer<len){
243                                         tmp++;
244                                         comment=tmp;
245                                         /* eat the comment */
246                                         for(;((tmp-buffer)<len)&&
247                                                 (*tmp!=',');tmp++);
248                                         /* mark end of compact via (also end of comment)*/
249                                         if (tmp-buffer<len){
250                                                 *tmp=0;
251                                         }else break;
252                                         /* eat space & ',' */
253                                         for(tmp=tmp+1;((tmp-buffer)<len)&&
254                                                 (*tmp==' '|| *tmp==',');tmp++);
255                                         
256                                 }
257                                 break;
258
259                         case ';':
260                                 *tmp=0;
261                                 if (tmp+1-buffer>=len) goto error;
262                                 tmp++;
263                                 params=tmp;
264                                 /* eat till end, first space  or ',' */
265                                 for(;((tmp-buffer)<len)&&
266                                         (*tmp!=' '&& *tmp!=',');tmp++);
267                                 if (tmp-buffer==len)  break;
268                                 if (*tmp==' '){
269                                         /* eat comment */
270                                         *tmp=0;
271                                         tmp++;
272                                         comment=tmp;
273                                         for(;((tmp-buffer)<len)&&
274                                                 (*tmp!=',');tmp++);
275                                         if (tmp-buffer==len)  break;
276                                 }
277                                 /* mark end of via*/
278                                 *tmp=0;
279                                 /* eat space & ',' */
280                                 for(tmp=tmp+1;((tmp-buffer)<len)&&
281                                         (*tmp==' '|| *tmp==',');tmp++);
282                                 break;
283
284                         case ',':
285                                 *tmp=0;
286                                 if (tmp+1-buffer<len){
287                                         /* eat space and ',' */
288                                         for(tmp=tmp+1; 
289                                                 ((tmp-buffer)<len)&&
290                                                 (*tmp==' '|| *tmp==',');
291                                            tmp++);
292                                 }
293                 }
294         }
295         /* if we are not at the end of the body => we found another compact via */
296         if (tmp-buffer<len) next_via=tmp;
297         
298         /* parse hostport */
299         parse_hostport(hostport, &host, &port);
300         vb->name=name;
301         vb->version=version;
302         vb->transport=transport;
303         vb->host=host;
304         vb->port=port;
305         vb->params=params;
306         vb->comment=comment;
307         vb->next=next_via;
308         vb->error=VIA_PARSE_OK;
309
310         
311         /* tmp points to end of body or to next via (if compact)*/
312         
313         return tmp;
314
315 error:
316         vb->error=VIA_PARSE_ERROR;
317         return tmp;
318 }
319
320
321
322 /* returns 0 if ok, -1 for errors */
323 int parse_msg(char* buf, unsigned int len, struct sip_msg* msg)
324 {
325
326         char *tmp, *bar;
327         char* rest;
328         char* first_via;
329         char* second_via;
330         struct msg_start fl;
331         struct hdr_field hf;
332         struct via_body vb1, vb2;
333         int offset;
334         int r;
335
336         
337         /* eat crlf from the beginning */
338         for (tmp=buf; (*tmp=='\n' || *tmp=='\r')&&
339                         tmp-buf < len ; tmp++);
340         offset=tmp-buf;
341         rest=parse_first_line(tmp, len-offset, &fl);
342         offset+=rest-tmp;
343         tmp=rest;
344         switch(fl.type){
345                 case SIP_INVALID:
346                         DPrint("invalid message\n");
347                         goto error;
348                         break;
349                 case SIP_REQUEST:
350                         DPrint("SIP Request:\n");
351                         DPrint(" method:  <%s>\n",fl.u.request.method);
352                         DPrint(" uri:     <%s>\n",fl.u.request.uri);
353                         DPrint(" version: <%s>\n",fl.u.request.version);
354                         break;
355                 case SIP_REPLY:
356                         DPrint("SIP Reply  (status):\n");
357                         DPrint(" version: <%s>\n",fl.u.reply.version);
358                         DPrint(" status:  <%s>\n",fl.u.reply.status);
359                         DPrint(" reason:  <%s>\n",fl.u.reply.reason);
360                         break;
361                 default:
362                         DPrint("unknown type %d\n",fl.type);
363         }
364         
365         /*find first Via: */
366         hf.type=HDR_ERROR;
367         first_via=0;
368         second_via=0;
369         do{
370                 rest=get_hdr_field(tmp, len-offset, &hf);
371                 offset+=rest-tmp;
372                 switch (hf.type){
373                         case HDR_ERROR:
374                                 DPrint("ERROR: bad header  field\n");
375                                 goto  error;
376                         case HDR_EOH: 
377                                 goto skip;
378                         case HDR_VIA:
379                                 if (first_via==0){
380                                                 first_via=hf.body;
381                                                 vb1.hdr=hf.name;
382                                                 /* replace cr/lf with space in first via */
383                                                 for (bar=first_via;(first_via) && (*bar);bar++)
384                                                         if ((*bar=='\r')||(*bar=='\n')) *bar=' ';
385                                 #ifdef DEBUG
386                                                 printf("first via: <%s>\n", first_via);
387                                 #endif
388                                                 bar=parse_via_body(first_via, strlen(first_via), &vb1);
389                                                 if (vb1.error!=VIA_PARSE_OK){
390                                                         DPrint("ERROR: parsing via body: %s\n", first_via);
391                                                         goto error;
392                                                 }
393                                                 vb1.size=bar-first_via+first_via-vb1.hdr;
394                                                 
395                                                 /* compact via */
396                                                 if (vb1.next) {
397                                                         second_via=vb1.next;
398                                                         /* not interested in the rest of the header */
399                                                         goto skip;
400                                                 }
401                                 }else if (second_via==0){
402                                                         second_via=hf.body;
403                                                         vb2.hdr=hf.name;
404                                                         goto skip;
405                                 }
406                                 break;
407                 }
408         #ifdef DEBUG
409                 printf("header field type %d, name=<%s>, body=<%s>\n",
410                         hf.type, hf.name, hf.body);
411         #endif
412                 tmp=rest;
413         }while(hf.type!=HDR_EOH && rest-buf < len);
414
415 skip:
416         /* replace cr/lf with space in the second via */
417         for (tmp=second_via;(second_via) && (*tmp);tmp++)
418                 if ((*tmp=='\r')||(*tmp=='\n')) *tmp=' ';
419
420         if (second_via) {
421                 tmp=parse_via_body(second_via, strlen(second_via), &vb2);
422                 if (vb2.error!=VIA_PARSE_OK){
423                         DPrint("ERROR: parsing via body: %s\n", second_via);
424                         goto error;
425                 }
426                 vb2.size=tmp-second_via;
427                 if (vb2.hdr) vb2.size+=second_via-vb2.hdr;
428         }
429         
430
431 #ifdef DEBUG
432         /* dump parsed data */
433         printf(" first  via: <%s/%s/%s> <%s:%d>",
434                         vb1.name, vb1.version, vb1.transport, vb1.host, vb1.port);
435         if (vb1.params) printf(";<%s>", vb1.params);
436         if (vb1.comment) printf(" <%s>", vb1.comment);
437         printf ("\n");
438         if (second_via){
439                 printf(" second via: <%s/%s/%s> <%s:%d>",
440                                 vb2.name, vb2.version, vb2.transport, vb2.host, vb2.port);
441                 if (vb2.params) printf(";<%s>", vb2.params);
442                 if (vb2.comment) printf(" <%s>", vb2.comment);
443                 printf ("\n");
444         }
445 #endif
446         
447         /* copy data into msg */
448         memcpy(&(msg->first_line), &fl, sizeof(struct msg_start));
449         memcpy(&(msg->via1), &vb1, sizeof(struct via_body));
450         memcpy(&(msg->via2), &vb2, sizeof(struct via_body));
451
452 #ifdef DEBUG
453         printf ("exiting parse_msg\n");
454 #endif
455
456         return 0;
457         
458 error:
459         return -1;
460 }
461