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