/src/opensips/parser/parse_param.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Generic Parameter Parser |
3 | | * |
4 | | * Copyright (C) 2001-2003 FhG Fokus |
5 | | * |
6 | | * This file is part of opensips, a free SIP server. |
7 | | * |
8 | | * opensips is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version |
12 | | * |
13 | | * opensips is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | * |
22 | | * History: |
23 | | * ------- |
24 | | * 2003-03-24 Created by janakj |
25 | | * 2003-04-07 shm duplication added (janakj) |
26 | | * 2003-04-07 URI class added (janakj) |
27 | | */ |
28 | | |
29 | | #include <string.h> |
30 | | #include "../str.h" |
31 | | #include "../ut.h" |
32 | | #include "../dprint.h" |
33 | | #include "../trim.h" |
34 | | #include "../mem/mem.h" |
35 | | #include "../mem/shm_mem.h" |
36 | | #include "parse_param.h" |
37 | | |
38 | | |
39 | | /* |
40 | | * Try to find out parameter name, recognized parameters |
41 | | * are q, expires and methods |
42 | | */ |
43 | | static inline void parse_contact_class(param_hooks_t* _h, param_t* _p) |
44 | 0 | { |
45 | |
|
46 | 0 | if (!_p->name.s) { |
47 | 0 | LM_ERR("empty value\n"); |
48 | 0 | return; |
49 | 0 | } |
50 | 0 | switch(_p->name.s[0]) { |
51 | 0 | case 'q': |
52 | 0 | case 'Q': |
53 | 0 | if (_p->name.len == 1) { |
54 | 0 | _p->type = P_Q; |
55 | 0 | _h->contact.q = _p; |
56 | 0 | } |
57 | 0 | break; |
58 | | |
59 | 0 | case 'e': |
60 | 0 | case 'E': |
61 | 0 | if ((_p->name.len == 7) && |
62 | 0 | (!strncasecmp(_p->name.s + 1, "xpires", 6))) { |
63 | 0 | _p->type = P_EXPIRES; |
64 | 0 | _h->contact.expires = _p; |
65 | 0 | } |
66 | 0 | break; |
67 | | |
68 | 0 | case 'm': |
69 | 0 | case 'M': |
70 | 0 | if ((_p->name.len == 7) && |
71 | 0 | (!strncasecmp(_p->name.s + 1, "ethods", 6))) { |
72 | 0 | _p->type = P_METHODS; |
73 | 0 | _h->contact.methods = _p; |
74 | 0 | } |
75 | 0 | break; |
76 | | |
77 | 0 | case 'r': |
78 | 0 | case 'R': |
79 | 0 | if ((_p->name.len == 8) && |
80 | 0 | (!strncasecmp(_p->name.s + 1, "eceived", 7))) { |
81 | 0 | _p->type = P_RECEIVED; |
82 | 0 | _h->contact.received = _p; |
83 | 0 | } |
84 | 0 | break; |
85 | 0 | case '+': |
86 | 0 | if ((_p->name.len == 13) && |
87 | 0 | (!strncasecmp(_p->name.s + 1, "sip.instance", 12))) { |
88 | 0 | _p->type = P_INSTANCE; |
89 | 0 | _h->contact.instance = _p; |
90 | 0 | } |
91 | 0 | break; |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | |
96 | | /* |
97 | | * Try to find out parameter name, recognized parameters |
98 | | * are transport, lr, r2, maddr |
99 | | */ |
100 | | static inline void parse_uri_class(param_hooks_t* _h, param_t* _p) |
101 | 0 | { |
102 | |
|
103 | 0 | if (!_p->name.s) { |
104 | 0 | LM_ERR("empty value\n"); |
105 | 0 | return; |
106 | 0 | } |
107 | 0 | switch(_p->name.s[0]) { |
108 | 0 | case 't': |
109 | 0 | case 'T': |
110 | 0 | if ((_p->name.len == 9) && |
111 | 0 | (!strncasecmp(_p->name.s + 1, "ransport", 8))) { |
112 | 0 | _p->type = P_TRANSPORT; |
113 | 0 | _h->uri.transport = _p; |
114 | 0 | } else if (_p->name.len == 2) { |
115 | 0 | if (((_p->name.s[1] == 't') || (_p->name.s[1] == 'T')) && |
116 | 0 | ((_p->name.s[2] == 'l') || (_p->name.s[2] == 'L'))) { |
117 | 0 | _p->type = P_TTL; |
118 | 0 | _h->uri.ttl = _p; |
119 | 0 | } |
120 | 0 | } |
121 | 0 | break; |
122 | | |
123 | 0 | case 'l': |
124 | 0 | case 'L': |
125 | 0 | if ((_p->name.len == 2) && ((_p->name.s[1] == 'r') || (_p->name.s[1] == 'R'))) { |
126 | 0 | _p->type = P_LR; |
127 | 0 | _h->uri.lr = _p; |
128 | 0 | } |
129 | 0 | break; |
130 | | |
131 | 0 | case 'r': |
132 | 0 | case 'R': |
133 | 0 | if ((_p->name.len == 2) && (_p->name.s[1] == '2')) { |
134 | 0 | _p->type = P_R2; |
135 | 0 | _h->uri.r2 = _p; |
136 | 0 | } |
137 | 0 | break; |
138 | | |
139 | 0 | case 'm': |
140 | 0 | case 'M': |
141 | 0 | if ((_p->name.len == 5) && |
142 | 0 | (!strncasecmp(_p->name.s + 1, "addr", 4))) { |
143 | 0 | _p->type = P_MADDR; |
144 | 0 | _h->uri.maddr = _p; |
145 | 0 | } |
146 | 0 | break; |
147 | | |
148 | 0 | case 'd': |
149 | 0 | case 'D': |
150 | 0 | if ((_p->name.len == 5) && |
151 | 0 | (!strncasecmp(_p->name.s + 1, "stip", 4))) { |
152 | 0 | _p->type = P_DSTIP; |
153 | 0 | _h->uri.dstip = _p; |
154 | 0 | } else if ((_p->name.len == 7) && |
155 | 0 | (!strncasecmp(_p->name.s + 1, "stport", 6))) { |
156 | 0 | _p->type = P_DSTPORT; |
157 | 0 | _h->uri.dstport = _p; |
158 | 0 | } |
159 | 0 | break; |
160 | 0 | } |
161 | |
|
162 | 0 | } |
163 | | |
164 | | |
165 | | /* |
166 | | * Parse quoted string in a parameter body |
167 | | * return the string without quotes in _r |
168 | | * parameter and update _s to point behind the |
169 | | * closing quote |
170 | | */ |
171 | | static inline int parse_quoted_param(str* _s, str* _r) |
172 | 0 | { |
173 | 0 | char* end_quote; |
174 | | |
175 | | /* The string must have at least |
176 | | * surrounding quotes |
177 | | */ |
178 | 0 | if (_s->len < 2) { |
179 | 0 | return -1; |
180 | 0 | } |
181 | | |
182 | | /* Skip opening quote */ |
183 | 0 | _s->s++; |
184 | 0 | _s->len--; |
185 | | |
186 | | |
187 | | /* Find closing quote */ |
188 | 0 | end_quote = q_memchr(_s->s, '\"', _s->len); |
189 | | |
190 | | /* Not found, return error */ |
191 | 0 | if (!end_quote) { |
192 | 0 | return -2; |
193 | 0 | } |
194 | | |
195 | | /* Let _r point to the string without |
196 | | * surrounding quotes |
197 | | */ |
198 | 0 | _r->s = _s->s; |
199 | 0 | _r->len = end_quote - _s->s; |
200 | | |
201 | | /* Update _s parameter to point |
202 | | * behind the closing quote |
203 | | */ |
204 | 0 | _s->len -= (end_quote - _s->s + 1); |
205 | 0 | _s->s = end_quote + 1; |
206 | | |
207 | | /* Everything went OK */ |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | |
212 | | /* |
213 | | * Parse unquoted token in a parameter body |
214 | | * let _r point to the token and update _s |
215 | | * to point right behind the token |
216 | | */ |
217 | | static inline int parse_token_param(str* _s, str* _r) |
218 | 0 | { |
219 | 0 | int i; |
220 | | |
221 | | /* There is nothing to parse, |
222 | | * return error |
223 | | */ |
224 | 0 | if (_s->len == 0) { |
225 | 0 | return -1; |
226 | 0 | } |
227 | | |
228 | | /* Save the beginning of the |
229 | | * token in _r->s |
230 | | */ |
231 | 0 | _r->s = _s->s; |
232 | | |
233 | | /* Iterate through the |
234 | | * token body |
235 | | */ |
236 | 0 | for(i = 0; i < _s->len; i++) { |
237 | | |
238 | | /* All these characters |
239 | | * mark end of the token |
240 | | */ |
241 | 0 | switch(_s->s[i]) { |
242 | 0 | case ' ': |
243 | 0 | case '\t': |
244 | 0 | case '\r': |
245 | 0 | case '\n': |
246 | 0 | case ',': |
247 | 0 | case ';': |
248 | | /* So if you find |
249 | | * any of them |
250 | | * stop iterating |
251 | | */ |
252 | 0 | goto out; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | out: |
256 | 0 | if (i == 0) { |
257 | 0 | return -1; |
258 | 0 | } |
259 | | |
260 | | /* Save length of the token */ |
261 | 0 | _r->len = i; |
262 | | |
263 | | /* Update _s parameter so it points |
264 | | * right behind the end of the token |
265 | | */ |
266 | 0 | _s->s = _s->s + i; |
267 | 0 | _s->len -= i; |
268 | | |
269 | | /* Everything went OK */ |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | | |
274 | | /* |
275 | | * Parse a parameter name |
276 | | */ |
277 | | static inline void parse_param_name(str* _s, pclass_t _c, param_hooks_t* _h, param_t* _p) |
278 | 0 | { |
279 | |
|
280 | 0 | if (!_s->s) { |
281 | 0 | LM_DBG("empty parameter\n"); |
282 | 0 | return; |
283 | 0 | } |
284 | | |
285 | 0 | _p->name.s = _s->s; |
286 | |
|
287 | 0 | while(_s->len) { |
288 | 0 | switch(_s->s[0]) { |
289 | 0 | case ' ': |
290 | 0 | case '\t': |
291 | 0 | case '\r': |
292 | 0 | case '\n': |
293 | 0 | case ';': |
294 | 0 | case ',': |
295 | 0 | case '=': |
296 | 0 | goto out; |
297 | 0 | } |
298 | 0 | _s->s++; |
299 | 0 | _s->len--; |
300 | 0 | } |
301 | | |
302 | 0 | out: |
303 | 0 | _p->name.len = _s->s - _p->name.s; |
304 | |
|
305 | 0 | switch(_c) { |
306 | 0 | case CLASS_CONTACT: parse_contact_class(_h, _p); break; |
307 | 0 | case CLASS_URI: parse_uri_class(_h, _p); break; |
308 | 0 | default: break; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | |
313 | | |
314 | | |
315 | | |
316 | | /* |
317 | | * Parse body of a parameter. It can be quoted string or |
318 | | * a single token. |
319 | | */ |
320 | | static inline int parse_param_body(str* _s, param_t* _c) |
321 | 0 | { |
322 | 0 | if (_s->s[0] == '\"') { |
323 | 0 | if (parse_quoted_param(_s, &(_c->body)) < 0) { |
324 | 0 | LM_ERR("failed to parse quoted string\n"); |
325 | 0 | return -2; |
326 | 0 | } |
327 | 0 | } else { |
328 | 0 | if (parse_token_param(_s, &(_c->body)) < 0) { |
329 | 0 | LM_ERR("failed to parse token\n"); |
330 | 0 | return -3; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | 0 | return 0; |
335 | 0 | } |
336 | | |
337 | | |
338 | | /* |
339 | | * Parse parameters |
340 | | * _s is string containing parameters, it will be updated to point behind the parameters |
341 | | * _c is class of parameters |
342 | | * _h is pointer to structure that will be filled with pointer to well known parameters |
343 | | * linked list of parsed parameters will be stored in |
344 | | * the variable _p is pointing to |
345 | | * The function returns 0 on success and negative number |
346 | | * on an error |
347 | | */ |
348 | | int parse_params(str* _s, pclass_t _c, param_hooks_t* _h, param_t** _p) |
349 | 0 | { |
350 | 0 | param_t* t; |
351 | 0 | param_t* last; |
352 | |
|
353 | 0 | if (!_s || !_h || !_p) { |
354 | 0 | LM_ERR("invalid parameter value\n"); |
355 | 0 | return -1; |
356 | 0 | } |
357 | | |
358 | 0 | memset(_h, 0, sizeof(param_hooks_t)); |
359 | 0 | last = NULL; |
360 | 0 | *_p = 0; |
361 | |
|
362 | 0 | if (!_s->s) { /* no parameters at all -- we're done */ |
363 | 0 | LM_DBG("empty uri params, skipping\n"); |
364 | 0 | return 0; |
365 | 0 | } |
366 | | |
367 | 0 | LM_DBG("Parsing params for:[%.*s]\n",_s->len,_s->s); |
368 | |
|
369 | 0 | while(1) { |
370 | 0 | t = (param_t*)pkg_malloc(sizeof(param_t)); |
371 | 0 | if (t == 0) { |
372 | 0 | LM_ERR("no pkg memory left\n"); |
373 | 0 | goto error; |
374 | 0 | } |
375 | 0 | memset(t, 0, sizeof(param_t)); |
376 | |
|
377 | 0 | parse_param_name(_s, _c, _h, t); |
378 | 0 | trim_leading(_s); |
379 | |
|
380 | 0 | if (_s->len == 0) { /* The last parameter without body */ |
381 | 0 | t->len = t->name.len; |
382 | 0 | goto ok; |
383 | 0 | } |
384 | | |
385 | 0 | if (_s->s[0] == '=') { |
386 | 0 | _s->s++; |
387 | 0 | _s->len--; |
388 | 0 | trim_leading(_s); |
389 | |
|
390 | 0 | if (_s->len == 0) { |
391 | 0 | LM_ERR("body missing\n"); |
392 | 0 | goto error; |
393 | 0 | } |
394 | | |
395 | 0 | if (parse_param_body(_s, t) < 0) { |
396 | 0 | LM_ERR("failed to parse param body\n"); |
397 | 0 | goto error; |
398 | 0 | } |
399 | | |
400 | 0 | t->len = _s->s - t->name.s; |
401 | |
|
402 | 0 | trim_leading(_s); |
403 | 0 | if (_s->len == 0) { |
404 | 0 | goto ok; |
405 | 0 | } |
406 | 0 | } else { |
407 | 0 | t->len = t->name.len; |
408 | 0 | } |
409 | | |
410 | 0 | if (_s->s[0]==',') goto ok; /* To be able to parse header parameters */ |
411 | 0 | if (_s->s[0]=='>') goto ok; /* To be able to parse URI parameters */ |
412 | | |
413 | 0 | if (_s->s[0] != ';') { |
414 | 0 | LM_ERR("invalid character, ; expected, found %c \n",_s->s[0]); |
415 | 0 | goto error; |
416 | 0 | } |
417 | | |
418 | 0 | _s->s++; |
419 | 0 | _s->len--; |
420 | 0 | trim_leading(_s); |
421 | |
|
422 | 0 | if (_s->len == 0) { |
423 | 0 | LM_ERR("param name missing after ;\n"); |
424 | 0 | goto error; |
425 | 0 | } |
426 | | |
427 | 0 | if (last) {last->next=t;} else {*_p = t;} |
428 | 0 | last = t; |
429 | 0 | } |
430 | | |
431 | 0 | error: |
432 | 0 | if (t) pkg_free(t); |
433 | 0 | free_params(*_p); |
434 | 0 | *_p = 0; |
435 | 0 | return -2; |
436 | | |
437 | 0 | ok: |
438 | 0 | if (last) {last->next=t;} else {*_p = t;} |
439 | 0 | _h->last_param = last = t; |
440 | 0 | return 0; |
441 | 0 | } |
442 | | |
443 | | |
444 | | /* |
445 | | * Free linked list of parameters |
446 | | */ |
447 | | static inline void do_free_params(param_t* _p, int _shm) |
448 | 0 | { |
449 | 0 | param_t* ptr; |
450 | |
|
451 | 0 | while(_p) { |
452 | 0 | ptr = _p; |
453 | 0 | _p = _p->next; |
454 | 0 | if (_shm) shm_free(ptr); |
455 | 0 | else pkg_free(ptr); |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | |
460 | | /* |
461 | | * Free linked list of parameters |
462 | | */ |
463 | | void free_params(param_t* _p) |
464 | 0 | { |
465 | 0 | do_free_params(_p, 0); |
466 | 0 | } |
467 | | |
468 | | |
469 | | /* |
470 | | * Free linked list of parameters |
471 | | */ |
472 | | void shm_free_params(param_t* _p) |
473 | 0 | { |
474 | 0 | do_free_params(_p, 1); |
475 | 0 | } |
476 | | |
477 | | |
478 | | /* |
479 | | * Print a parameter structure, just for debugging |
480 | | */ |
481 | | static inline void print_param(param_t* _p) |
482 | 0 | { |
483 | 0 | char* type; |
484 | |
|
485 | 0 | LM_DBG("---param(%p)---\n", _p); |
486 | |
|
487 | 0 | switch(_p->type) { |
488 | 0 | case P_OTHER: type = "P_OTHER"; break; |
489 | 0 | case P_Q: type = "P_Q"; break; |
490 | 0 | case P_EXPIRES: type = "P_EXPIRES"; break; |
491 | 0 | case P_METHODS: type = "P_METHODS"; break; |
492 | 0 | case P_TRANSPORT: type = "P_TRANSPORT"; break; |
493 | 0 | case P_LR: type = "P_LR"; break; |
494 | 0 | case P_R2: type = "P_R2"; break; |
495 | 0 | case P_MADDR: type = "P_MADDR"; break; |
496 | 0 | case P_TTL: type = "P_TTL"; break; |
497 | 0 | case P_RECEIVED: type = "P_RECEIVED"; break; |
498 | 0 | case P_DSTIP: type = "P_DSTIP"; break; |
499 | 0 | case P_DSTPORT: type = "P_DSTPORT"; break; |
500 | 0 | default: type = "UNKNOWN"; break; |
501 | 0 | } |
502 | | |
503 | 0 | LM_DBG("type: %s\n", type); |
504 | 0 | LM_DBG("name: \'%.*s\'\n", _p->name.len, _p->name.s); |
505 | 0 | LM_DBG("body: \'%.*s\'\n", _p->body.len, _p->body.s); |
506 | 0 | LM_DBG("len : %d\n", _p->len); |
507 | 0 | LM_DBG("---/param---\n"); |
508 | 0 | } |
509 | | |
510 | | |
511 | | /* |
512 | | * Print linked list of parameters, just for debugging |
513 | | */ |
514 | | void print_params(param_t* _p) |
515 | 0 | { |
516 | 0 | param_t* ptr; |
517 | |
|
518 | 0 | ptr = _p; |
519 | 0 | while(ptr) { |
520 | 0 | print_param(ptr); |
521 | 0 | ptr = ptr->next; |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | |
526 | | /* |
527 | | * Duplicate linked list of parameters |
528 | | */ |
529 | | static inline int do_duplicate_params(param_t** _n, param_t* _p, int _shm) |
530 | 0 | { |
531 | 0 | param_t* last, *ptr, *t; |
532 | |
|
533 | 0 | if (!_n) { |
534 | 0 | LM_ERR("invalid parameter value\n"); |
535 | 0 | return -1; |
536 | 0 | } |
537 | | |
538 | 0 | last = 0; |
539 | 0 | *_n = 0; |
540 | 0 | ptr = _p; |
541 | 0 | while(ptr) { |
542 | 0 | if (_shm) { |
543 | 0 | t = (param_t*)shm_malloc(sizeof(param_t)); |
544 | 0 | } else { |
545 | 0 | t = (param_t*)pkg_malloc(sizeof(param_t)); |
546 | 0 | } |
547 | 0 | if (!t) { |
548 | 0 | LM_ERR("no more pkg memory\n"); |
549 | 0 | goto err; |
550 | 0 | } |
551 | 0 | memcpy(t, ptr, sizeof(param_t)); |
552 | 0 | t->next = 0; |
553 | |
|
554 | 0 | if (!*_n) *_n = t; |
555 | 0 | if (last) last->next = t; |
556 | 0 | last = t; |
557 | |
|
558 | 0 | ptr = ptr->next; |
559 | 0 | } |
560 | 0 | return 0; |
561 | | |
562 | 0 | err: |
563 | 0 | do_free_params(*_n, _shm); |
564 | 0 | return -2; |
565 | 0 | } |
566 | | |
567 | | |
568 | | /* |
569 | | * Duplicate linked list of parameters |
570 | | */ |
571 | | int duplicate_params(param_t** _n, param_t* _p) |
572 | 0 | { |
573 | 0 | return do_duplicate_params(_n, _p, 0); |
574 | 0 | } |
575 | | |
576 | | |
577 | | /* |
578 | | * Duplicate linked list of parameters |
579 | | */ |
580 | | int shm_duplicate_params(param_t** _n, param_t* _p) |
581 | 0 | { |
582 | 0 | return do_duplicate_params(_n, _p, 1); |
583 | 0 | } |