/src/kamailio/src/core/parser/sdp/sdp_helpr_funcs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SDP parser helpers |
3 | | * |
4 | | * Copyright (C) 2008-2009 SOMA Networks, INC. |
5 | | * Copyright (C) 2010 VoIP Embedded, Inc |
6 | | * |
7 | | * Redistribution and use in source and binary forms, with or without |
8 | | * modification, are permitted provided that the following conditions are met: |
9 | | * |
10 | | * 1. Redistributions of source code must retain the above copyright notice, |
11 | | * this list of conditions and the following disclaimer. |
12 | | * 2. Redistributions in binary form must reproduce the above copyright |
13 | | * notice, this list of conditions and the following disclaimer in the |
14 | | * documentation and/or other materials provided with the distribution. |
15 | | * |
16 | | * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR |
17 | | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
18 | | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
19 | | * EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
20 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
21 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
23 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
24 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
25 | | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | | * |
27 | | */ |
28 | | |
29 | | |
30 | | #include <stdlib.h> |
31 | | #include "../../ut.h" |
32 | | #include "../msg_parser.h" |
33 | | #include "../parser_f.h" |
34 | | #include "../parse_hname2.h" |
35 | | #include "sdp.h" |
36 | | |
37 | | |
38 | | static struct |
39 | | { |
40 | | const char *s; |
41 | | int len; |
42 | | int is_rtp; |
43 | | } sup_ptypes[] = {{.s = "rtp/avp", .len = 7, .is_rtp = 1}, |
44 | | {.s = "udptl", .len = 5, .is_rtp = 0}, |
45 | | {.s = "rtp/avpf", .len = 8, .is_rtp = 1}, |
46 | | {.s = "rtp/savp", .len = 8, .is_rtp = 1}, |
47 | | {.s = "rtp/savpf", .len = 9, .is_rtp = 1}, |
48 | | {.s = "udp", .len = 3, .is_rtp = 0}, |
49 | | {.s = "udp/bfcp", .len = 8, .is_rtp = 0}, |
50 | | {.s = NULL, .len = 0, .is_rtp = 0}}; |
51 | | |
52 | | |
53 | | #define READ(val) \ |
54 | 2.09k | ((unsigned int)(*(val + 0)) + ((unsigned int)(*(val + 1)) << 8) \ |
55 | 2.09k | + ((unsigned int)(*(val + 2)) << 16) \ |
56 | 2.09k | + ((unsigned int)(*(val + 3)) << 24)) |
57 | | #define advance(_ptr, _n, _str, _error) \ |
58 | 23.3k | do { \ |
59 | 23.3k | if((_ptr) + (_n) > (_str).s + (_str).len) \ |
60 | 23.3k | goto _error; \ |
61 | 23.3k | (_ptr) = (_ptr) + (_n); \ |
62 | 23.2k | } while(0); |
63 | | #define one_of_16(_x, _t) \ |
64 | 2.09k | (_x == _t[0] || _x == _t[15] || _x == _t[8] || _x == _t[2] || _x == _t[3] \ |
65 | 2.09k | || _x == _t[4] || _x == _t[5] || _x == _t[6] || _x == _t[7] \ |
66 | 2.09k | || _x == _t[1] || _x == _t[9] || _x == _t[10] || _x == _t[11] \ |
67 | 2.09k | || _x == _t[12] || _x == _t[13] || _x == _t[14]) |
68 | | #define one_of_8(_x, _t) \ |
69 | | (_x == _t[0] || _x == _t[7] || _x == _t[1] || _x == _t[2] || _x == _t[3] \ |
70 | | || _x == _t[4] || _x == _t[5] || _x == _t[6]) |
71 | | |
72 | | int get_mixed_part_delimiter(str *body, str *mp_delimiter) |
73 | 1.18k | { |
74 | 1.18k | static unsigned int boun[16] = {0x6e756f62, 0x4e756f62, 0x6e556f62, |
75 | 1.18k | 0x4e556f62, 0x6e754f62, 0x4e754f62, 0x6e554f62, 0x4e554f62, |
76 | 1.18k | 0x6e756f42, 0x4e756f42, 0x6e556f42, 0x4e556f42, 0x6e754f42, |
77 | 1.18k | 0x4e754f42, 0x6e554f42, 0x4e554f42}; |
78 | 1.18k | static unsigned int dary[16] = {0x79726164, 0x59726164, 0x79526164, |
79 | 1.18k | 0x59526164, 0x79724164, 0x59724164, 0x79524164, 0x59524164, |
80 | 1.18k | 0x79726144, 0x59726144, 0x79526144, 0x59526144, 0x79724144, |
81 | 1.18k | 0x59724144, 0x79524144, 0x59524144}; |
82 | 1.18k | str str_type; |
83 | 1.18k | unsigned int x; |
84 | 1.18k | char *p; |
85 | | |
86 | | |
87 | | /* LM_DBG("<%.*s>\n",body->len,body->s); */ |
88 | 1.18k | p = str_type.s = body->s; |
89 | 1.18k | str_type.len = body->len; |
90 | 20.3k | while(*p != ';' && p < (body->s + body->len)) |
91 | 19.1k | advance(p, 1, str_type, error); |
92 | 1.18k | p++; |
93 | 1.18k | str_type.s = p; |
94 | 1.18k | str_type.len = body->len - (p - body->s); |
95 | | /* LM_DBG("string to parse: <%.*s>\n",str_type.len,str_type.s); */ |
96 | | /* skip spaces and tabs if any */ |
97 | 1.57k | while(*p == ' ' || *p == '\t') |
98 | 1.18k | advance(p, 1, str_type, error); |
99 | 1.18k | advance(p, 4, str_type, error); |
100 | 1.15k | x = READ(p - 4); |
101 | 1.15k | if(!one_of_16(x, boun)) |
102 | 199 | goto other; |
103 | 956 | advance(p, 4, str_type, error); |
104 | 940 | x = READ(p - 4); |
105 | 940 | if(!one_of_16(x, dary)) |
106 | 202 | goto other; |
107 | | |
108 | | /* skip spaces and tabs if any */ |
109 | 1.12k | while(*p == ' ' || *p == '\t') |
110 | 738 | advance(p, 1, str_type, error); |
111 | 738 | if(*p != '=') { |
112 | 39 | LM_ERR("parse error: no = found after boundary field\n"); |
113 | 39 | goto error; |
114 | 39 | } |
115 | 699 | advance(p, 1, str_type, error); |
116 | 1.26k | while((*p == ' ' || *p == '\t') && p + 1 < str_type.s + str_type.len) |
117 | 699 | advance(p, 1, str_type, error); |
118 | 699 | mp_delimiter->len = str_type.len - (int)(p - str_type.s); |
119 | 699 | mp_delimiter->s = p; |
120 | | /* check if the boundary value is enclosed in quotes */ |
121 | 699 | if(*p == '"' || *p == '\'') { |
122 | 28 | if(mp_delimiter->s[mp_delimiter->len - 1] == *p) { |
123 | 19 | mp_delimiter->s = p + 1; |
124 | 19 | mp_delimiter->len -= 2; |
125 | 19 | if(mp_delimiter->len <= 0) { |
126 | 3 | LM_ERR("invalid boundary field value\n"); |
127 | 3 | goto error; |
128 | 3 | } |
129 | 19 | } else { |
130 | 9 | LM_ERR("missing closing quote in boundary field value\n"); |
131 | 9 | goto error; |
132 | 9 | } |
133 | 28 | } |
134 | 687 | return 1; |
135 | | |
136 | 100 | error: |
137 | 100 | return -1; |
138 | 401 | other: |
139 | 401 | LM_DBG("'boundary' parsing error\n"); |
140 | 401 | return -1; |
141 | 699 | } |
142 | | |
143 | | |
144 | | /** |
145 | | * rfc4566: |
146 | | * a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>] |
147 | | */ |
148 | | int extract_rtpmap(str *body, str *rtpmap_payload, str *rtpmap_encoding, |
149 | | str *rtpmap_clockrate, str *rtpmap_parmas) |
150 | 0 | { |
151 | 0 | char *cp, *cp1; |
152 | 0 | int len; |
153 | |
|
154 | 0 | if(strncasecmp(body->s, "a=rtpmap:", 9) != 0) { |
155 | | /*LM_DBG("We are not pointing to an a=rtpmap: attribute =>`%.*s'\n", body->len, body->s); */ |
156 | 0 | return -1; |
157 | 0 | } |
158 | | |
159 | 0 | cp1 = body->s; |
160 | |
|
161 | 0 | rtpmap_payload->s = cp1 + 9; /* skip `a=rtpmap:' */ |
162 | 0 | rtpmap_payload->len = |
163 | 0 | eat_line(rtpmap_payload->s, body->s + body->len - rtpmap_payload->s) |
164 | 0 | - rtpmap_payload->s; |
165 | 0 | trim_len(rtpmap_payload->len, rtpmap_payload->s, *rtpmap_payload); |
166 | 0 | len = rtpmap_payload->len; |
167 | | |
168 | | /* */ |
169 | 0 | cp = eat_token_end( |
170 | 0 | rtpmap_payload->s, rtpmap_payload->s + rtpmap_payload->len); |
171 | 0 | rtpmap_payload->len = cp - rtpmap_payload->s; |
172 | 0 | if(rtpmap_payload->len <= 0 || cp == rtpmap_payload->s) { |
173 | 0 | LM_ERR("no encoding in `a=rtpmap'\n"); |
174 | 0 | return -1; |
175 | 0 | } |
176 | 0 | len -= rtpmap_payload->len; |
177 | 0 | rtpmap_encoding->s = cp; |
178 | 0 | cp = eat_space_end(rtpmap_encoding->s, rtpmap_encoding->s + len); |
179 | 0 | len -= cp - rtpmap_encoding->s; |
180 | 0 | if(len <= 0 || cp == rtpmap_encoding->s) { |
181 | 0 | LM_ERR("no encoding in `a=rtpmap:'\n"); |
182 | 0 | return -1; |
183 | 0 | } |
184 | | |
185 | 0 | rtpmap_encoding->s = cp; |
186 | 0 | cp1 = (char *)ser_memmem(cp, "/", len, 1); |
187 | 0 | if(cp1 == NULL) { |
188 | 0 | LM_ERR("invalid encoding in `a=rtpmap' at [%.*s]\n", len, cp); |
189 | 0 | return -1; |
190 | 0 | } |
191 | 0 | len -= cp1 - cp; |
192 | 0 | rtpmap_encoding->len = cp1 - cp; |
193 | |
|
194 | 0 | cp = cp1 + 1; /* skip '/' */ |
195 | 0 | len--; |
196 | 0 | rtpmap_clockrate->s = cp; |
197 | 0 | cp1 = (char *)ser_memmem(cp, "/", len, 1); |
198 | 0 | if(cp1 == NULL) { |
199 | | /* no encoding parameters */ |
200 | 0 | rtpmap_clockrate->len = len; |
201 | 0 | rtpmap_parmas->s = NULL; |
202 | 0 | rtpmap_parmas->len = 0; |
203 | 0 | return 0; |
204 | 0 | } |
205 | 0 | rtpmap_clockrate->len = cp1 - cp; |
206 | 0 | len -= cp1 - cp; |
207 | 0 | rtpmap_parmas->s = cp1 + 1; /* skip '/' */ |
208 | 0 | rtpmap_parmas->len = len - 1; |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | int extract_fmtp(str *body, str *fmtp_payload, str *fmtp_string) |
213 | 0 | { |
214 | 0 | char *cp, *cp1; |
215 | 0 | int len; |
216 | |
|
217 | 0 | if(strncasecmp(body->s, "a=fmtp:", 7) != 0) { |
218 | | /*LM_DBG("We are not pointing to an a=fmtp: attribute =>`%.*s'\n", body->len, body->s); */ |
219 | 0 | return -1; |
220 | 0 | } |
221 | | |
222 | 0 | cp1 = body->s; |
223 | |
|
224 | 0 | fmtp_payload->s = cp1 + 7; /* skip `a=fmtp:' */ |
225 | 0 | fmtp_payload->len = |
226 | 0 | eat_line(fmtp_payload->s, body->s + body->len - fmtp_payload->s) |
227 | 0 | - fmtp_payload->s; |
228 | 0 | trim_len(fmtp_payload->len, fmtp_payload->s, *fmtp_payload); |
229 | 0 | len = fmtp_payload->len; |
230 | | |
231 | | /* */ |
232 | 0 | cp = eat_token_end(fmtp_payload->s, fmtp_payload->s + fmtp_payload->len); |
233 | 0 | fmtp_payload->len = cp - fmtp_payload->s; |
234 | 0 | if(fmtp_payload->len <= 0 || cp == fmtp_payload->s) { |
235 | 0 | LM_ERR("no encoding in `a=fmtp:'\n"); |
236 | 0 | return -1; |
237 | 0 | } |
238 | 0 | len -= fmtp_payload->len; |
239 | 0 | fmtp_string->s = cp; |
240 | 0 | cp = eat_space_end(fmtp_string->s, fmtp_string->s + len); |
241 | 0 | len -= cp - fmtp_string->s; |
242 | 0 | if(len <= 0 || cp == fmtp_string->s) { |
243 | 0 | LM_ERR("no encoding in `a=fmtp:'\n"); |
244 | 0 | return -1; |
245 | 0 | } |
246 | | |
247 | 0 | fmtp_string->s = cp; |
248 | |
|
249 | 0 | fmtp_string->len = |
250 | 0 | eat_line(fmtp_string->s, body->s + body->len - fmtp_string->s) |
251 | 0 | - fmtp_string->s; |
252 | 0 | trim_len(fmtp_string->len, fmtp_string->s, *fmtp_string); |
253 | |
|
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | | |
258 | | /** |
259 | | * Allocate a new ice attribute |
260 | | */ |
261 | | static inline sdp_ice_attr_t *add_sdp_ice(sdp_stream_cell_t *_stream) |
262 | 0 | { |
263 | 0 | sdp_ice_attr_t *ice_attr; |
264 | 0 | int len; |
265 | |
|
266 | 0 | len = sizeof(sdp_ice_attr_t); |
267 | 0 | ice_attr = (sdp_ice_attr_t *)pkg_malloc(len); |
268 | 0 | if(ice_attr == NULL) { |
269 | 0 | PKG_MEM_ERROR; |
270 | 0 | return NULL; |
271 | 0 | } |
272 | 0 | memset(ice_attr, 0, len); |
273 | | |
274 | | /* Insert the new ice attribute */ |
275 | 0 | ice_attr->next = _stream->ice_attr; |
276 | 0 | _stream->ice_attr = ice_attr; |
277 | 0 | _stream->ice_attrs_num++; |
278 | |
|
279 | 0 | return ice_attr; |
280 | 0 | } |
281 | | |
282 | | static inline sdp_ice_opt_t *add_sdp_ice_opt(sdp_stream_cell_t *_stream) |
283 | 0 | { |
284 | 0 | sdp_ice_opt_t *ice_opt; |
285 | 0 | int len; |
286 | |
|
287 | 0 | len = sizeof(sdp_ice_opt_t); |
288 | 0 | ice_opt = (sdp_ice_opt_t *)pkg_malloc(len); |
289 | 0 | if(ice_opt == NULL) { |
290 | 0 | PKG_MEM_ERROR; |
291 | 0 | return NULL; |
292 | 0 | } |
293 | 0 | memset(ice_opt, 0, len); |
294 | | |
295 | | /* Insert the new ice option */ |
296 | 0 | ice_opt->next = _stream->ice_opt; |
297 | 0 | _stream->ice_opt = ice_opt; |
298 | 0 | _stream->ice_opt_num++; |
299 | |
|
300 | 0 | return ice_opt; |
301 | 0 | } |
302 | | |
303 | | int extract_candidate(str *body, sdp_stream_cell_t *stream) |
304 | 0 | { |
305 | 0 | char *space, *start; |
306 | 0 | int len, fl; |
307 | 0 | sdp_ice_attr_t *ice_attr; |
308 | |
|
309 | 0 | if((body->len < 12) || (strncasecmp(body->s, "a=candidate:", 12) != 0)) { |
310 | | /*LM_DBG("We are not pointing to an a=candidate: attribute =>`%.*s'\n", body->len, body->s); */ |
311 | 0 | return -1; |
312 | 0 | } |
313 | | |
314 | 0 | start = body->s + 12; |
315 | 0 | len = body->len - 12; |
316 | |
|
317 | 0 | space = memchr(start, 32, len); |
318 | 0 | if((space == NULL) || (space - start + 3 > len) || !isdigit(*(space + 1))) { |
319 | 0 | LM_ERR("no component in `a=candidate'\n"); |
320 | 0 | return -1; |
321 | 0 | } |
322 | | |
323 | 0 | fl = space - start; |
324 | |
|
325 | 0 | start = space + 1; |
326 | 0 | len = len - (space - start + 1); |
327 | 0 | space = memchr(start, 32, len); |
328 | 0 | if(space == NULL) { |
329 | 0 | LM_ERR("no component in `a=candidate'\n"); |
330 | 0 | return -1; |
331 | 0 | } |
332 | | |
333 | 0 | ice_attr = add_sdp_ice(stream); |
334 | 0 | if(ice_attr == NULL) { |
335 | 0 | LM_ERR("failed to add ice attribute\n"); |
336 | 0 | return -1; |
337 | 0 | } |
338 | | |
339 | | /* currently only foundation and component-id are parsed */ |
340 | | /* if needed, parse more */ |
341 | 0 | ice_attr->foundation.s = body->s + 12; |
342 | 0 | ice_attr->foundation.len = fl; |
343 | 0 | ice_attr->component_id = strtol(start, (char **)NULL, 10); |
344 | |
|
345 | 0 | return 0; |
346 | 0 | } |
347 | | |
348 | | |
349 | | /* generic method for attribute extraction |
350 | | * field must has format "a=attrname:" */ |
351 | | int extract_field(str *body, str *value, str field) |
352 | 0 | { |
353 | 0 | if(strncmp(body->s, field.s, field.len < body->len ? field.len : body->len) |
354 | 0 | != 0) { |
355 | | /*LM_DBG("We are not pointing to an %.* attribute =>`%.*s'\n", field.len, field.s, body->len, body->s); */ |
356 | 0 | return -1; |
357 | 0 | } |
358 | | |
359 | 0 | value->s = body->s + field.len; /* skip `a=attrname:' */ |
360 | 0 | value->len = eat_line(value->s, body->s + body->len - value->s) - value->s; |
361 | 0 | trim_len(value->len, value->s, *value); |
362 | |
|
363 | 0 | return 0; |
364 | 0 | } |
365 | | |
366 | | int extract_ice_option(str *body, sdp_stream_cell_t *stream) |
367 | 0 | { |
368 | 0 | sdp_ice_opt_t *ice_opt; |
369 | |
|
370 | 0 | char *ptr_src; |
371 | 0 | int max_options = |
372 | 0 | 10; /* protection - max options can be listed in one line */ |
373 | 0 | int length = 0; /* each option length */ |
374 | | |
375 | | /* a=ice-options: */ |
376 | 0 | if((body->len < 14) || (strncasecmp(body->s, ICE_OPTIONS, 14) != 0)) |
377 | 0 | return -1; |
378 | | |
379 | 0 | ptr_src = body->s + 14; |
380 | 0 | if(*ptr_src == 32) |
381 | 0 | ptr_src++; /* if starts with a space, skip it */ |
382 | | |
383 | | /* identify all existing ICE options, if they are listed in one row */ |
384 | 0 | while(*ptr_src && *ptr_src != '\r' && *ptr_src != '\n' |
385 | 0 | && max_options-- > 0) { |
386 | 0 | while(*ptr_src != 32 && *ptr_src && *ptr_src != '\r' |
387 | 0 | && *ptr_src != '\n') { |
388 | 0 | length++; |
389 | 0 | ptr_src++; |
390 | 0 | } |
391 | |
|
392 | 0 | ice_opt = add_sdp_ice_opt(stream); |
393 | 0 | if(ice_opt == NULL) { |
394 | 0 | LM_ERR("failed to add ice option\n"); |
395 | 0 | return -1; |
396 | 0 | } |
397 | | |
398 | 0 | ice_opt->option.s = ptr_src - length; |
399 | 0 | ice_opt->option.len = length; |
400 | 0 | trim_len(ice_opt->option.len, ice_opt->option.s, ice_opt->option); |
401 | |
|
402 | 0 | length = 0; |
403 | 0 | if(*ptr_src == 32) |
404 | 0 | ptr_src++; /* skip space */ |
405 | 0 | } |
406 | | |
407 | 0 | return 0; |
408 | 0 | } |
409 | | |
410 | | int extract_ptime(str *body, str *ptime) |
411 | 0 | { |
412 | 0 | static const str field = str_init("a=ptime:"); |
413 | 0 | return extract_field(body, ptime, field); |
414 | 0 | } |
415 | | |
416 | | int extract_accept_types(str *body, str *accept_types) |
417 | 0 | { |
418 | 0 | static const str field = str_init("a=accept-types:"); |
419 | 0 | return extract_field(body, accept_types, field); |
420 | 0 | } |
421 | | |
422 | | int extract_accept_wrapped_types(str *body, str *accept_wrapped_types) |
423 | 0 | { |
424 | 0 | static const str field = str_init("a=accept-wrapped-types:"); |
425 | 0 | return extract_field(body, accept_wrapped_types, field); |
426 | 0 | } |
427 | | |
428 | | int extract_max_size(str *body, str *max_size) |
429 | 0 | { |
430 | 0 | static const str field = str_init("a=max-size:"); |
431 | 0 | return extract_field(body, max_size, field); |
432 | 0 | } |
433 | | |
434 | | int extract_path(str *body, str *path) |
435 | 0 | { |
436 | 0 | static const str field = str_init("a=path:"); |
437 | 0 | return extract_field(body, path, field); |
438 | 0 | } |
439 | | |
440 | | int extract_rtcp(str *body, str *rtcp) |
441 | 0 | { |
442 | 0 | static const str field = str_init("a=rtcp:"); |
443 | 0 | return extract_field(body, rtcp, field); |
444 | 0 | } |
445 | | |
446 | | int extract_sendrecv_mode(str *body, str *sendrecv_mode, int *is_on_hold) |
447 | 0 | { |
448 | 0 | char *cp1; |
449 | |
|
450 | 0 | cp1 = body->s; |
451 | 0 | if(!((strncasecmp(cp1, "a=sendrecv", 10) == 0) |
452 | 0 | || (strncasecmp(cp1, "a=recvonly", 10) == 0))) { |
453 | 0 | if((strncasecmp(cp1, "a=inactive", 10) == 0) |
454 | 0 | || (strncasecmp(cp1, "a=sendonly", 10) == 0)) { |
455 | 0 | *is_on_hold = RFC3264_HOLD; |
456 | 0 | } else { |
457 | 0 | return -1; |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | 0 | sendrecv_mode->s = body->s + 2; /* skip `a=' */ |
462 | 0 | sendrecv_mode->len = |
463 | 0 | 8; /* we know the length and therefore we don't need to overkill */ |
464 | | /* |
465 | | sendrecv_mode->len = eat_line(sendrecv_mode->s, body->s + body->len - |
466 | | sendrecv_mode->s) - sendrecv_mode->s; |
467 | | trim_len(sendrecv_mode->len, sendrecv_mode->s, *sendrecv_mode); |
468 | | */ |
469 | |
|
470 | 0 | return 0; |
471 | 0 | } |
472 | | |
473 | | int extract_bwidth(str *body, str *bwtype, str *bwwitdth) |
474 | 0 | { |
475 | 0 | char *cp, *cp1; |
476 | 0 | int len; |
477 | |
|
478 | 0 | cp1 = NULL; |
479 | 0 | for(cp = body->s; (len = body->s + body->len - cp) > 0;) { |
480 | 0 | cp1 = (char *)ser_memmem(cp, "b=", len, 2); |
481 | 0 | if(cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') |
482 | 0 | break; |
483 | 0 | cp = cp1 + 2; |
484 | 0 | } |
485 | 0 | if(cp1 == NULL) |
486 | 0 | return -1; |
487 | | |
488 | 0 | bwtype->s = cp1 + 2; |
489 | 0 | bwtype->len = |
490 | 0 | eat_line(bwtype->s, body->s + body->len - bwtype->s) - bwtype->s; |
491 | 0 | trim_len(bwtype->len, bwtype->s, *bwtype); |
492 | |
|
493 | 0 | cp = bwtype->s; |
494 | 0 | len = bwtype->len; |
495 | 0 | cp1 = (char *)ser_memmem(cp, ":", len, 1); |
496 | 0 | len -= cp1 - cp; |
497 | 0 | if(len <= 0) { |
498 | 0 | LM_ERR("invalid encoding in `b=%.*s'\n", bwtype->len, bwtype->s); |
499 | 0 | return -1; |
500 | 0 | } |
501 | 0 | bwtype->len = cp1 - cp; |
502 | | |
503 | | /* skip ':' */ |
504 | 0 | bwwitdth->s = cp1 + 1; |
505 | 0 | bwwitdth->len = len - 1; |
506 | |
|
507 | 0 | return 0; |
508 | 0 | } |
509 | | |
510 | | int extract_mediaip(str *body, str *mediaip, int *pf, char *line) |
511 | 0 | { |
512 | 0 | char *cp, *cp1; |
513 | 0 | int len; |
514 | |
|
515 | 0 | cp1 = NULL; |
516 | 0 | for(cp = body->s; (len = body->s + body->len - cp) > 0;) { |
517 | 0 | cp1 = (char *)ser_memmem(cp, line, len, 2); |
518 | 0 | if(cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') |
519 | 0 | break; |
520 | 0 | cp = cp1 + 2; |
521 | 0 | } |
522 | 0 | if(cp1 == NULL) |
523 | 0 | return -1; |
524 | | |
525 | 0 | mediaip->s = cp1 + 2; |
526 | 0 | mediaip->len = |
527 | 0 | eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s; |
528 | 0 | trim_len(mediaip->len, mediaip->s, *mediaip); |
529 | 0 | if(mediaip->len == 0) { |
530 | 0 | LM_ERR("no [%s] line in SDP\n", line); |
531 | 0 | return -1; |
532 | 0 | } |
533 | | |
534 | | /* search reverse for IP[4|6] in c=/o= line */ |
535 | 0 | cp = (char *)ser_memrmem(mediaip->s, " IP", mediaip->len, 3); |
536 | 0 | if(cp == NULL) { |
537 | 0 | LM_ERR("no `IP[4|6]' in `%s' field\n", line); |
538 | 0 | return -1; |
539 | 0 | } |
540 | | /* safety checks: |
541 | | * - for length, at least 6: ' IP[4|6] x...' |
542 | | * - white space after |
543 | | */ |
544 | 0 | if(cp + 6 > mediaip->s + mediaip->len && cp[4] != ' ') { |
545 | 0 | LM_ERR("invalid content for `%s' line\n", line); |
546 | 0 | return -1; |
547 | 0 | } |
548 | 0 | switch(cp[3]) { |
549 | 0 | case '4': |
550 | 0 | *pf = AF_INET; |
551 | 0 | break; |
552 | 0 | case '6': |
553 | 0 | *pf = AF_INET6; |
554 | 0 | break; |
555 | 0 | default: |
556 | 0 | LM_ERR("invalid addrtype IPx for `%s' line\n", line); |
557 | 0 | return -1; |
558 | 0 | } |
559 | 0 | cp += 5; |
560 | | |
561 | | /* next token is the IP address */ |
562 | 0 | cp = eat_space_end(cp, mediaip->s + mediaip->len); |
563 | 0 | len = eat_token_end(cp, mediaip->s + mediaip->len) - cp; |
564 | 0 | mediaip->s = cp; |
565 | 0 | mediaip->len = len; |
566 | |
|
567 | 0 | if(mediaip->len == 0) { |
568 | 0 | LM_ERR("no `IP[4|6]' address in `%s' field\n", line); |
569 | 0 | return -1; |
570 | 0 | } |
571 | | |
572 | 0 | LM_DBG("located IP address [%.*s] in `%s' field\n", mediaip->len, |
573 | 0 | mediaip->s, line); |
574 | 0 | return 1; |
575 | 0 | } |
576 | | |
577 | | int extract_media_attr(str *body, str *mediamedia, str *mediaport, |
578 | | str *mediatransport, str *mediapayload, int *is_rtp) |
579 | 0 | { |
580 | 0 | char *cp, *cp1; |
581 | 0 | int len, i; |
582 | |
|
583 | 0 | cp1 = NULL; |
584 | 0 | for(cp = body->s; (len = body->s + body->len - cp) > 0;) { |
585 | 0 | cp1 = (char *)ser_memmem(cp, "m=", len, 2); |
586 | 0 | if(cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r') |
587 | 0 | break; |
588 | 0 | cp = cp1 + 2; |
589 | 0 | } |
590 | 0 | if(cp1 == NULL) { |
591 | 0 | LM_ERR("no `m=' in SDP\n"); |
592 | 0 | return -1; |
593 | 0 | } |
594 | 0 | mediaport->s = cp1 + 2; /* skip `m=' */ |
595 | 0 | mediaport->len = eat_line(mediaport->s, body->s + body->len - mediaport->s) |
596 | 0 | - mediaport->s; |
597 | 0 | trim_len(mediaport->len, mediaport->s, *mediaport); |
598 | |
|
599 | 0 | mediapayload->len = mediaport->len; |
600 | 0 | mediamedia->s = mediaport->s; |
601 | | |
602 | | |
603 | | /* Skip media supertype and spaces after it */ |
604 | 0 | cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); |
605 | 0 | mediaport->len -= cp - mediaport->s; |
606 | 0 | mediamedia->len = mediapayload->len - mediaport->len; |
607 | 0 | if(mediaport->len <= 0 || cp == mediaport->s) { |
608 | 0 | LM_ERR("no port in `m='\n"); |
609 | 0 | return -1; |
610 | 0 | } |
611 | 0 | mediaport->s = cp; |
612 | |
|
613 | 0 | cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len); |
614 | 0 | mediaport->len -= cp - mediaport->s; |
615 | 0 | if(mediaport->len <= 0 || cp == mediaport->s) { |
616 | 0 | LM_ERR("no port in `m='\n"); |
617 | 0 | return -1; |
618 | 0 | } |
619 | | |
620 | | /* Extract port */ |
621 | 0 | mediaport->s = cp; |
622 | 0 | cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); |
623 | 0 | mediatransport->len = mediaport->len - (cp - mediaport->s); |
624 | 0 | if(mediatransport->len <= 0 || cp == mediaport->s) { |
625 | 0 | LM_ERR("no port in `m='\n"); |
626 | 0 | return -1; |
627 | 0 | } |
628 | 0 | mediatransport->s = cp; |
629 | 0 | mediaport->len = cp - mediaport->s; |
630 | | |
631 | | /* Skip spaces after port */ |
632 | 0 | cp = eat_space_end( |
633 | 0 | mediatransport->s, mediatransport->s + mediatransport->len); |
634 | 0 | mediatransport->len -= cp - mediatransport->s; |
635 | 0 | if(mediatransport->len <= 0 || cp == mediatransport->s) { |
636 | 0 | LM_ERR("no protocol type in `m='\n"); |
637 | 0 | return -1; |
638 | 0 | } |
639 | | /* Extract protocol type */ |
640 | 0 | mediatransport->s = cp; |
641 | 0 | cp = eat_token_end( |
642 | 0 | mediatransport->s, mediatransport->s + mediatransport->len); |
643 | 0 | if(cp == mediatransport->s) { |
644 | 0 | LM_ERR("no protocol type in `m='\n"); |
645 | 0 | return -1; |
646 | 0 | } |
647 | 0 | mediatransport->len = cp - mediatransport->s; |
648 | |
|
649 | 0 | mediapayload->s = mediatransport->s + mediatransport->len; |
650 | 0 | mediapayload->len -= mediapayload->s - mediamedia->s; |
651 | 0 | cp = eat_space_end(mediapayload->s, mediapayload->s + mediapayload->len); |
652 | 0 | mediapayload->len -= cp - mediapayload->s; |
653 | 0 | mediapayload->s = cp; |
654 | |
|
655 | 0 | for(i = 0; sup_ptypes[i].s != NULL; i++) |
656 | 0 | if(mediatransport->len == sup_ptypes[i].len |
657 | 0 | && strncasecmp(mediatransport->s, sup_ptypes[i].s, |
658 | 0 | mediatransport->len) |
659 | 0 | == 0) { |
660 | 0 | *is_rtp = sup_ptypes[i].is_rtp; |
661 | 0 | return 0; |
662 | 0 | } |
663 | | /* Unproxyable protocol type. Generally it isn't error. */ |
664 | 0 | return 0; |
665 | 0 | } |
666 | | |
667 | | int extract_sess_version(str *oline, str *sess_version) |
668 | 0 | { |
669 | |
|
670 | 0 | char *cp0 = NULL; |
671 | 0 | char *cp = NULL; |
672 | 0 | int len = 0; |
673 | 0 | char ws = ' '; |
674 | 0 | int i = 0; |
675 | | |
676 | | /* |
677 | | "o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5" CR LF |
678 | | "o=W 1 2 IN IP4 0.0.0.0" CR LF -> 24 |
679 | | "o=W 1 2 IN IP6 ::1" CR LF -> 20 |
680 | | "o=W 1 2 X Y Z" CR LF -> 15 |
681 | | */ |
682 | 0 | if(oline->s == NULL || oline->len < 15) { |
683 | 0 | LM_ERR("invalid o -line\n"); |
684 | 0 | return -1; |
685 | 0 | } |
686 | | |
687 | 0 | if(sess_version == NULL) { |
688 | 0 | LM_ERR("invalid result pointer\n"); |
689 | 0 | return -1; |
690 | 0 | } |
691 | | |
692 | 0 | LM_DBG("oline(%d): >%.*s<\n", oline->len, oline->len, oline->s); |
693 | | |
694 | | // jump over o= |
695 | 0 | cp = oline->s + 2; |
696 | 0 | len = oline->len - 2; |
697 | | |
698 | | // find whitespace 3 times |
699 | 0 | do { |
700 | 0 | cp0 = cp; |
701 | | //LM_DBG("loop %d: >%.*s<\n", len, len, cp0); |
702 | |
|
703 | 0 | cp = (char *)ser_memmem(cp0, &ws, len, 1); |
704 | 0 | if(cp == NULL) { |
705 | 0 | break; |
706 | 0 | } |
707 | | |
708 | | //LM_DBG("cp0: %p cp: %p (%ld)\n", cp0, cp, cp-cp0); |
709 | 0 | len -= cp - cp0; |
710 | | |
711 | | // step over whitespace |
712 | 0 | if(len > 0) { |
713 | 0 | cp++; |
714 | 0 | len--; |
715 | 0 | } |
716 | |
|
717 | 0 | i++; |
718 | 0 | } while(len < oline->len && i < 3); |
719 | | |
720 | 0 | len = cp - cp0 - 1; |
721 | 0 | LM_DBG("end %d: >%.*s<\n", len, len, cp0); |
722 | |
|
723 | 0 | sess_version->s = cp0; |
724 | 0 | sess_version->len = len; |
725 | |
|
726 | 0 | if(!isdigit(*cp0)) { |
727 | 0 | LM_WARN("not digit >%.*s<\n", len, cp0); |
728 | 0 | } |
729 | |
|
730 | 0 | return 1; |
731 | 0 | } |
732 | | |
733 | | |
734 | | /* |
735 | | * Auxiliary for some functions. |
736 | | * Returns pointer to first character of found line, or NULL if no such line. |
737 | | */ |
738 | | |
739 | | char *find_sdp_line(char *p, char *plimit, char linechar) |
740 | 0 | { |
741 | 0 | static char linehead[3] = "x="; |
742 | 0 | char *cp, *cp1; |
743 | 0 | linehead[0] = linechar; |
744 | | /* Iterate through body */ |
745 | 0 | cp = p; |
746 | 0 | for(;;) { |
747 | 0 | if(cp >= plimit) |
748 | 0 | return NULL; |
749 | 0 | cp1 = ser_memmem(cp, linehead, plimit - cp, 2); |
750 | 0 | if(cp1 == NULL) |
751 | 0 | return NULL; |
752 | | /* |
753 | | * As it is body, we assume it has previous line and we can |
754 | | * lookup previous character. |
755 | | */ |
756 | 0 | if(cp1[-1] == '\n' || cp1[-1] == '\r') |
757 | 0 | return cp1; |
758 | | /* |
759 | | * Having such data, but not at line beginning. |
760 | | * Skip them and reiterate. ser_memmem() will find next |
761 | | * occurrence. |
762 | | */ |
763 | 0 | if(plimit - cp1 < 2) |
764 | 0 | return NULL; |
765 | 0 | cp = cp1 + 2; |
766 | 0 | } |
767 | 0 | } |
768 | | |
769 | | |
770 | | /* This function assumes p points to a line of requested type. */ |
771 | | char *find_next_sdp_line(char *p, char *plimit, char linechar, char *defptr) |
772 | 0 | { |
773 | 0 | char *t; |
774 | 0 | if(p >= plimit || plimit - p < 3) |
775 | 0 | return defptr; |
776 | 0 | t = find_sdp_line(p + 2, plimit, linechar); |
777 | 0 | return t ? t : defptr; |
778 | 0 | } |
779 | | |
780 | | |
781 | | /* Find first SDP line starting with linechar. Return defptr if not found */ |
782 | | char *find_first_sdp_line( |
783 | | char *pstart, char *plimit, char linechar, char *defptr) |
784 | 0 | { |
785 | 0 | char *t; |
786 | 0 | if(pstart >= plimit || plimit - pstart < 3) |
787 | 0 | return defptr; |
788 | 0 | t = find_sdp_line(pstart, plimit, linechar); |
789 | 0 | return t ? t : defptr; |
790 | 0 | } |
791 | | |
792 | | |
793 | | /* returns pointer to next header line, and fill hdr_f ; |
794 | | * if at end of header returns pointer to the last crlf (always buf)*/ |
795 | | char *get_sdp_hdr_field(char *buf, char *end, struct hdr_field *hdr) |
796 | 4.42k | { |
797 | | |
798 | 4.42k | char *tmp; |
799 | 4.42k | char *match; |
800 | | |
801 | 4.42k | if((*buf) == '\n' || (*buf) == '\r') { |
802 | | /* double crlf or lflf or crcr */ |
803 | 1.20k | hdr->type = HDR_EOH_T; |
804 | 1.20k | return buf; |
805 | 1.20k | } |
806 | | |
807 | 3.22k | tmp = parse_hname2(buf, end, hdr); |
808 | 3.22k | if(hdr->type == HDR_ERROR_T) { |
809 | 94 | LM_ERR("bad header\n"); |
810 | 94 | goto error; |
811 | 94 | } |
812 | | |
813 | | /* eliminate leading whitespace */ |
814 | 3.13k | tmp = eat_lws_end(tmp, end); |
815 | 3.13k | if(tmp >= end) { |
816 | 34 | LM_ERR("hf empty\n"); |
817 | 34 | goto error; |
818 | 34 | } |
819 | | |
820 | | /* just skip over it */ |
821 | 3.09k | hdr->body.s = tmp; |
822 | | /* find end of header */ |
823 | | /* find lf */ |
824 | 4.20k | do { |
825 | 4.20k | match = memchr(tmp, '\n', end - tmp); |
826 | 4.20k | if(match) { |
827 | 4.16k | match++; |
828 | 4.16k | } else { |
829 | 44 | LM_ERR("bad body for <%.*s>(%d)\n", hdr->name.len, hdr->name.s, |
830 | 44 | hdr->type); |
831 | 44 | tmp = end; |
832 | 44 | goto error; |
833 | 44 | } |
834 | 4.16k | tmp = match; |
835 | 4.16k | } while(match < end && ((*match == ' ') || (*match == '\t'))); |
836 | 3.05k | tmp = match; |
837 | 3.05k | hdr->body.len = match - hdr->body.s; |
838 | | |
839 | | /* jku: if \r covered by current length, shrink it */ |
840 | 3.05k | trim_r(hdr->body); |
841 | 3.05k | hdr->len = tmp - hdr->name.s; |
842 | 3.05k | return tmp; |
843 | 172 | error: |
844 | 172 | LM_DBG("error exit\n"); |
845 | 172 | hdr->type = HDR_ERROR_T; |
846 | 172 | hdr->len = tmp - hdr->name.s; |
847 | 172 | return tmp; |
848 | 3.09k | } |
849 | | |
850 | | |
851 | | char *find_sdp_line_delimiter(char *p, char *plimit, str delimiter) |
852 | 3.15k | { |
853 | 3.15k | static char delimiterhead[3] = "--"; |
854 | 3.15k | char *cp, *cp1; |
855 | | /* Iterate through body */ |
856 | 3.15k | cp = p; |
857 | 14.7k | for(;;) { |
858 | 14.7k | if(cp >= plimit) |
859 | 0 | return NULL; |
860 | 15.9k | for(;;) { |
861 | 15.9k | cp1 = ser_memmem(cp, delimiterhead, plimit - cp, 2); |
862 | 15.9k | if(cp1 == NULL) |
863 | 614 | return NULL; |
864 | | /* We matched '--', |
865 | | * now let's match the boundary delimiter */ |
866 | 15.3k | if(cp1 + 2 + delimiter.len >= plimit) |
867 | 56 | return NULL; |
868 | 15.3k | if(strncmp(cp1 + 2, delimiter.s, delimiter.len) == 0) |
869 | 14.0k | break; |
870 | 1.25k | else |
871 | 1.25k | cp = cp1 + 2 + delimiter.len; |
872 | 15.3k | } |
873 | 14.0k | if(cp1[-1] == '\n' || cp1[-1] == '\r') |
874 | 2.48k | return cp1; |
875 | 11.5k | if(plimit - cp1 < 2 + delimiter.len) |
876 | 0 | return NULL; |
877 | 11.5k | cp = cp1 + 2 + delimiter.len; |
878 | 11.5k | } |
879 | 3.15k | } |
880 | | |
881 | | |
882 | | /* |
883 | | * This function assumes p points to a delimiter type line. |
884 | | */ |
885 | | char *find_next_sdp_line_delimiter( |
886 | | char *p, char *plimit, str delimiter, char *defptr) |
887 | 2.46k | { |
888 | 2.46k | char *t; |
889 | 2.46k | if(p >= plimit || plimit - p < 3) |
890 | 0 | return defptr; |
891 | 2.46k | t = find_sdp_line_delimiter(p + 2, plimit, delimiter); |
892 | 2.46k | return t ? t : defptr; |
893 | 2.46k | } |