1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Parses the SDP description as per the SDP grammar defined in Section 9 of
29 * RFC 4566
30 */
31
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <sdp.h>
37
38 #include "sdp_parse.h"
39 #include "commp_util.h"
40
41 /*
42 * proto-version-field (v=)
43 * %x76 "=" 1*DIGIT CRLF
44 */
45 static void
sdp_parse_version(int * version,const char * begin,const char * end,uint_t * p_error)46 sdp_parse_version(int *version, const char *begin, const char *end,
47 uint_t *p_error)
48 {
49 if (*begin++ != COMMP_EQUALS || commp_atoi(begin, end, version) != 0)
50 *p_error |= SDP_VERSION_ERROR;
51 }
52
53 /*
54 * session-name-field (s=)
55 * %x73 "=" text CRLF
56 * text = byte-string
57 * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF)
58 * ;any byte except NUL, CR, or LF
59 */
60 static void
sdp_parse_name(char ** name,const char * begin,const char * end,uint_t * p_error)61 sdp_parse_name(char **name, const char *begin, const char *end,
62 uint_t *p_error)
63 {
64 int len;
65
66 if (*begin++ != COMMP_EQUALS) {
67 *p_error |= SDP_NAME_ERROR;
68 return;
69 }
70 /* there can be only one name field */
71 if (*name != NULL)
72 return;
73 len = end - begin;
74 if (len < 1) {
75 *p_error |= SDP_NAME_ERROR;
76 } else {
77 COMMP_COPY_STR(*name, begin, len);
78 if (*name == NULL) {
79 *p_error |= SDP_MEMORY_ERROR;
80 return;
81 }
82 }
83 }
84
85 /*
86 * information-field (i=)
87 * [%x69 "=" text CRLF]
88 * text = byte-string
89 * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF)
90 * any byte except NUL, CR, or LF
91 */
92 static void
sdp_parse_info(char ** info,const char * begin,const char * end,uint_t * p_error)93 sdp_parse_info(char **info, const char *begin, const char *end,
94 uint_t *p_error)
95 {
96 int len;
97
98 if (*begin++ != COMMP_EQUALS) {
99 *p_error |= SDP_INFO_ERROR;
100 return;
101 }
102 /* There can be only one info field */
103 if (*info != NULL)
104 return;
105 len = end - begin;
106 if (len < 1) {
107 *p_error |= SDP_INFO_ERROR;
108 } else {
109 COMMP_COPY_STR(*info, begin, len);
110 if (*info == NULL) {
111 *p_error |= SDP_MEMORY_ERROR;
112 return;
113 }
114 }
115 }
116
117 /*
118 * uri-field (u=)
119 * [%x75 "=" uri CRLF]
120 * anything between "=" and "CRLF" is considered to be URI.
121 */
122 static void
sdp_parse_uri(char ** uri,const char * begin,const char * end,uint_t * p_error)123 sdp_parse_uri(char **uri, const char *begin, const char *end, uint_t *p_error)
124 {
125 int len;
126
127 if (*begin++ != COMMP_EQUALS) {
128 *p_error |= SDP_URI_ERROR;
129 return;
130 }
131 /* There can be only one uri field */
132 if (*uri != NULL)
133 return;
134 len = end - begin;
135 if (len < 1 || isspace(*begin) || isspace (*(end - 1))) {
136 *p_error |= SDP_URI_ERROR;
137 } else {
138 COMMP_COPY_STR(*uri, begin, len);
139 if (*uri == NULL) {
140 *p_error |= SDP_MEMORY_ERROR;
141 return;
142 }
143 }
144 }
145
146 /*
147 * phone-fields (p=)
148 * *(%x70 "=" phone-number CRLF)
149 * anything between "=" and "CRLF" is considered to be phone-number
150 */
151 static void
sdp_parse_phone(sdp_list_t ** phone,const char * begin,const char * end,uint_t * p_error)152 sdp_parse_phone(sdp_list_t **phone, const char *begin, const char *end,
153 uint_t *p_error)
154 {
155 int len;
156 sdp_list_t *new_phone = NULL;
157 sdp_list_t *tmp = NULL;
158
159 if (*begin++ != COMMP_EQUALS) {
160 *p_error |= SDP_PHONE_ERROR;
161 return;
162 }
163 len = end - begin;
164 if (len < 1 || isspace(*begin) || isspace(*(end - 1))) {
165 *p_error |= SDP_PHONE_ERROR;
166 } else {
167 new_phone = calloc(1, sizeof (sdp_list_t));
168 if (new_phone == NULL) {
169 *p_error |= SDP_MEMORY_ERROR;
170 return;
171 }
172 COMMP_COPY_STR(new_phone->value, begin, len);
173 if (new_phone->value == NULL) {
174 free(new_phone);
175 *p_error |= SDP_MEMORY_ERROR;
176 return;
177 }
178 if (*phone == NULL) {
179 *phone = new_phone;
180 } else {
181 tmp = *phone;
182 while (tmp->next != NULL)
183 tmp = tmp->next;
184 tmp->next = new_phone;
185 }
186 }
187 }
188
189 /*
190 * email-fields (e=)
191 * *(%x65 "=" email-address CRLF)
192 * anything between "=" and "CRLF" is considered to be email-address
193 */
194 static void
sdp_parse_email(sdp_list_t ** email,const char * begin,const char * end,uint_t * p_error)195 sdp_parse_email(sdp_list_t **email, const char *begin, const char *end,
196 uint_t *p_error)
197 {
198 int len;
199 sdp_list_t *new_email = NULL;
200 sdp_list_t *tmp = NULL;
201
202 if (*begin++ != COMMP_EQUALS) {
203 *p_error |= SDP_EMAIL_ERROR;
204 return;
205 }
206 len = end - begin;
207 if (len < 1 || isspace(*begin) || isspace(*(end - 1))) {
208 *p_error |= SDP_EMAIL_ERROR;
209 } else {
210 new_email = calloc(1, sizeof (sdp_list_t));
211 if (new_email == NULL) {
212 *p_error |= SDP_MEMORY_ERROR;
213 return;
214 }
215 COMMP_COPY_STR(new_email->value, begin, len);
216 if (new_email->value == NULL) {
217 free(new_email);
218 *p_error |= SDP_MEMORY_ERROR;
219 return;
220 }
221 if (*email == NULL) {
222 *email = new_email;
223 } else {
224 tmp = *email;
225 while (tmp->next != NULL)
226 tmp = tmp->next;
227 tmp->next = new_email;
228 }
229 }
230 }
231
232 /*
233 * origin-field (o=)
234 * %x6f "=" username SP sess-id SP sess-version SP nettype SP addrtype SP
235 * unicast-address CRLF
236 *
237 * username = non-ws-string
238 * sess-id = 1*DIGIT
239 * sess-version = 1*DIGIT
240 * nettype = token
241 * addrtype = token
242 * token = 1*(token-char)
243 * token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
244 * i.e. no space in token-char
245 */
246 static void
sdp_parse_origin(sdp_origin_t ** origin,const char * begin,const char * end,uint_t * p_error)247 sdp_parse_origin(sdp_origin_t **origin, const char *begin, const char *end,
248 uint_t *p_error)
249 {
250 const char *current = NULL;
251 sdp_origin_t *new_origin = NULL;
252
253 if (*begin++ != COMMP_EQUALS) {
254 *p_error |= SDP_ORIGIN_ERROR;
255 return;
256 }
257 /* There can be only one origin field */
258 if (*origin != NULL)
259 return;
260 new_origin = calloc(1, sizeof (sdp_origin_t));
261 if (new_origin == NULL) {
262 *p_error |= SDP_MEMORY_ERROR;
263 return;
264 }
265 /* Get username */
266 current = begin;
267 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
268 goto err_ret;
269 } else {
270 COMMP_COPY_STR(new_origin->o_username, begin, current - begin);
271 if (new_origin->o_username == NULL) {
272 sdp_free_origin(new_origin);
273 *p_error |= SDP_MEMORY_ERROR;
274 return;
275 }
276 }
277 /* Get Session-ID */
278 begin = ++current;
279 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
280 goto err_ret;
281 if (commp_strtoull(begin, current, &new_origin->o_id) != 0)
282 goto err_ret;
283 /* Get Version */
284 begin = ++current;
285 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
286 goto err_ret;
287 if (commp_strtoull(begin, current, &new_origin->o_version) != 0)
288 goto err_ret;
289 /* Get nettype */
290 begin = ++current;
291 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
292 goto err_ret;
293 } else {
294 COMMP_COPY_STR(new_origin->o_nettype, begin, current - begin);
295 if (new_origin->o_nettype == NULL) {
296 sdp_free_origin(new_origin);
297 *p_error |= SDP_MEMORY_ERROR;
298 return;
299 }
300 }
301 /* Get addrtype */
302 begin = ++current;
303 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
304 goto err_ret;
305 } else {
306 COMMP_COPY_STR(new_origin->o_addrtype, begin, current - begin);
307 if (new_origin->o_addrtype == NULL) {
308 sdp_free_origin(new_origin);
309 *p_error |= SDP_MEMORY_ERROR;
310 return;
311 }
312 }
313 /* Get address. Its the last sub-field */
314 begin = ++current;
315 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0)
316 goto err_ret;
317 COMMP_COPY_STR(new_origin->o_address, begin, current - begin);
318 if (new_origin->o_address == NULL) {
319 sdp_free_origin(new_origin);
320 *p_error |= SDP_MEMORY_ERROR;
321 return;
322 }
323 *origin = new_origin;
324 return;
325 err_ret:
326 *p_error |= SDP_ORIGIN_ERROR;
327 sdp_free_origin(new_origin);
328 }
329
330 /*
331 * time-fields (t=)
332 * 1*( %x74 "=" start-time SP stop-time CRLF)
333 * start-time = time / "0"
334 * stop-time = time / "0"
335 * time = POS-DIGIT 9*DIGIT
336 * POS-DIGIT = %x31-39 ; 1 - 9
337 */
338 static sdp_time_t *
sdp_parse_time(sdp_time_t ** time,const char * begin,const char * end,uint_t * p_error)339 sdp_parse_time(sdp_time_t **time, const char *begin, const char *end,
340 uint_t *p_error)
341 {
342 const char *current;
343 sdp_time_t *new_time;
344 sdp_time_t *tmp;
345
346 if (*begin++ != COMMP_EQUALS) {
347 *p_error |= SDP_TIME_ERROR;
348 return (NULL);
349 }
350 new_time = calloc(1, sizeof (sdp_time_t));
351 if (new_time == NULL) {
352 *p_error |= SDP_MEMORY_ERROR;
353 return (NULL);
354 }
355 /* Get start-time */
356 current = begin;
357 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
358 goto err_ret;
359 if (commp_strtoull(begin, current, &new_time->t_start) != 0)
360 goto err_ret;
361 /* Get stop-time */
362 begin = ++current;
363 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0)
364 goto err_ret;
365 if (commp_strtoull(begin, current, &new_time->t_stop) != 0)
366 goto err_ret;
367 /* Now assign time to session structure */
368 if (*time == NULL) {
369 *time = new_time;
370 } else {
371 tmp = *time;
372 while (tmp->t_next != NULL)
373 tmp = tmp->t_next;
374 tmp->t_next = new_time;
375 }
376 return (new_time);
377 err_ret:
378 *p_error |= SDP_TIME_ERROR;
379 sdp_free_time(new_time);
380 return (NULL);
381 }
382
383 /*
384 * connection-field (c=)
385 * [%x63 "=" nettype SP addrtype SP connection-address CRLF]
386 * nettype = token
387 * addrtype = token
388 * connection-address = multicast-address / unicast-address
389 * here, connection-address is parsed as a string.
390 */
391 static void
sdp_parse_connection(sdp_conn_t ** conn,const char * begin,const char * end,uint_t * p_error)392 sdp_parse_connection(sdp_conn_t **conn, const char *begin, const char *end,
393 uint_t *p_error)
394 {
395 const char *current;
396 const char *t_begin;
397 const char *t_current;
398 sdp_conn_t *new_conn;
399 sdp_conn_t *tmp;
400 boolean_t is_IP4 = B_FALSE;
401 boolean_t is_IP6 = B_FALSE;
402
403 if (*begin++ != COMMP_EQUALS) {
404 *p_error |= SDP_CONNECTION_ERROR;
405 return;
406 }
407 new_conn = calloc(1, sizeof (sdp_conn_t));
408 if (new_conn == NULL) {
409 *p_error |= SDP_MEMORY_ERROR;
410 return;
411 }
412 /* Get NetworkType */
413 current = begin;
414 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
415 goto err_ret;
416 } else {
417 COMMP_COPY_STR(new_conn->c_nettype, begin, current - begin);
418 if (new_conn->c_nettype == NULL) {
419 sdp_free_connection(new_conn);
420 *p_error |= SDP_MEMORY_ERROR;
421 return;
422 }
423 }
424 /* Get AddressType */
425 begin = ++current;
426 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
427 goto err_ret;
428 } else {
429 COMMP_COPY_STR(new_conn->c_addrtype, begin, current - begin);
430 if (new_conn->c_addrtype == NULL) {
431 sdp_free_connection(new_conn);
432 *p_error |= SDP_MEMORY_ERROR;
433 return;
434 }
435 }
436 if ((strlen(COMMP_ADDRTYPE_IP4) == strlen(new_conn->c_addrtype)) &&
437 (strncasecmp(new_conn->c_addrtype, COMMP_ADDRTYPE_IP4,
438 strlen(COMMP_ADDRTYPE_IP4)) == 0)) {
439 is_IP4 = B_TRUE;
440 } else if ((strlen(COMMP_ADDRTYPE_IP6) == strlen(new_conn->
441 c_addrtype)) && (strncasecmp(new_conn->c_addrtype,
442 COMMP_ADDRTYPE_IP6, strlen(COMMP_ADDRTYPE_IP6)) == 0)) {
443 is_IP6 = B_TRUE;
444 }
445 /* Get Address. Parsing depends if its IP4,IP6 or something else */
446 begin = ++current;
447 if (!is_IP4 && !is_IP6) {
448 if (commp_find_token(&begin, ¤t, end, COMMP_SP,
449 B_TRUE) != 0) {
450 goto err_ret;
451 }
452 } else {
453 if (commp_find_token(&begin, ¤t, end, COMMP_SLASH,
454 B_FALSE) != 0) {
455 goto err_ret;
456 }
457 if (current != end) {
458 /* SLASH is present. Needs further parsing */
459 t_current = current;
460 t_begin = ++t_current;
461 if (commp_find_token(&t_begin, &t_current, end,
462 COMMP_SLASH, B_FALSE) != 0) {
463 goto err_ret;
464 }
465 if (t_current != end) {
466 /*
467 * Another SLASH present. If is_IP4 true then
468 * this is Address count. If is_IP6 true then
469 * incorrect field as per RFC.
470 */
471 if (is_IP6) {
472 goto err_ret;
473 } else {
474 if (commp_atoi((t_current + 1), end,
475 &new_conn->c_addrcount) != 0) {
476 goto err_ret;
477 }
478 }
479 }
480 if (is_IP6) {
481 if (commp_atoi((current + 1), t_current,
482 &new_conn->c_addrcount) != 0) {
483 goto err_ret;
484 }
485 } else {
486 if (commp_strtoub((current + 1), t_current,
487 &new_conn->c_ttl) != 0) {
488 goto err_ret;
489 }
490 if (new_conn->c_addrcount == 0)
491 new_conn->c_addrcount = 1;
492 }
493 }
494 }
495 COMMP_COPY_STR(new_conn->c_address, begin, current - begin);
496 if (new_conn->c_address == NULL) {
497 sdp_free_connection(new_conn);
498 *p_error |= SDP_MEMORY_ERROR;
499 return;
500 }
501 if (*conn == NULL) {
502 *conn = new_conn;
503 } else {
504 tmp = *conn;
505 while (tmp->c_next != NULL)
506 tmp = tmp->c_next;
507 tmp->c_next = new_conn;
508 }
509 return;
510 err_ret:
511 *p_error |= SDP_CONNECTION_ERROR;
512 sdp_free_connection(new_conn);
513 }
514
515 /*
516 * bandwidth-fields (b=)
517 * *(%x62 "=" bwtype ":" bandwidth CRLF)
518 * bwtype = token
519 * bandwidth = 1*DIGIT
520 */
521 static void
sdp_parse_bandwidth(sdp_bandwidth_t ** bw,const char * begin,const char * end,uint_t * p_error)522 sdp_parse_bandwidth(sdp_bandwidth_t **bw, const char *begin, const char *end,
523 uint_t *p_error)
524 {
525 const char *current;
526 sdp_bandwidth_t *new_bw = NULL;
527 sdp_bandwidth_t *tmp = NULL;
528
529 if (*begin++ != COMMP_EQUALS) {
530 *p_error |= SDP_BANDWIDTH_ERROR;
531 return;
532 }
533 new_bw = calloc(1, sizeof (sdp_bandwidth_t));
534 if (new_bw == NULL) {
535 *p_error |= SDP_MEMORY_ERROR;
536 return;
537 }
538 current = begin;
539 if (commp_find_token(&begin, ¤t, end, COMMP_COLON,
540 B_FALSE) != 0) {
541 goto err_ret;
542 } else {
543 COMMP_COPY_STR(new_bw->b_type, begin, current - begin);
544 if (new_bw->b_type == NULL) {
545 sdp_free_bandwidth(new_bw);
546 *p_error |= SDP_MEMORY_ERROR;
547 return;
548 }
549 }
550 if (current == end)
551 goto err_ret;
552 begin = ++current;
553 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0)
554 goto err_ret;
555 if (commp_strtoull(begin, current, &new_bw->b_value) != 0)
556 goto err_ret;
557 if (*bw == NULL) {
558 *bw = new_bw;
559 } else {
560 tmp = *bw;
561 while (tmp->b_next != NULL)
562 tmp = tmp->b_next;
563 tmp->b_next = new_bw;
564 }
565 return;
566 err_ret:
567 *p_error |= SDP_BANDWIDTH_ERROR;
568 sdp_free_bandwidth(new_bw);
569 }
570
571 /*
572 * repeat-fields (r=)
573 * Not stand-alone. One or more repeat field appear after time field.
574 * %x72 "=" repeat-interval SP typed-time 1*(SP typed-time)
575 * repeat-interval = POS-DIGIT *DIGIT [fixed-len-time-unit]
576 * typed-time = 1*DIGIT [fixed-len-time-unit]
577 * fixed-len-time-unit = %x64 / %x68 / %x6d / %x73
578 */
579 static void
sdp_parse_repeat(sdp_time_t * time,const char * begin,const char * end,uint_t * p_error)580 sdp_parse_repeat(sdp_time_t *time, const char *begin, const char *end,
581 uint_t *p_error)
582 {
583 const char *current;
584 sdp_repeat_t *repeat;
585 sdp_repeat_t *new_repeat;
586 int ret;
587
588 if (*begin++ != COMMP_EQUALS) {
589 *p_error |= SDP_REPEAT_TIME_ERROR;
590 return;
591 }
592 /*
593 * A time field should be present before this field can occur, if
594 * time is NULL then repeat field has occured before time field and
595 * hence fields are out of order.
596 */
597 if (time == NULL)
598 return;
599 /*
600 * Get the latest time field and associate this repeat field
601 * with it.
602 */
603 while (time->t_next != NULL)
604 time = time->t_next;
605 new_repeat = calloc(1, sizeof (sdp_repeat_t));
606 if (new_repeat == NULL) {
607 *p_error |= SDP_MEMORY_ERROR;
608 return;
609 }
610 /*
611 * for a given time field, there could be several repeat fields
612 * add the new repeat field at the end of it.
613 */
614 repeat = time->t_repeat;
615 if (repeat == NULL) {
616 time->t_repeat = new_repeat;
617 } else {
618 while (repeat->r_next != NULL)
619 repeat = repeat->r_next;
620 repeat->r_next = new_repeat;
621 }
622 /*
623 * Populate the elements of sdp_repeat.
624 * Get time-interval
625 */
626 current = begin;
627 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
628 goto err_ret;
629 if (commp_time_to_secs(begin, current, &new_repeat->r_interval) != 0)
630 goto err_ret;
631 /* Get duration. It could be the last sub-field */
632 begin = ++current;
633 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
634 goto err_ret;
635 if (commp_time_to_secs(begin, current, &new_repeat->r_duration) != 0)
636 goto err_ret;
637 ++current;
638 /* Get offsets into sdp_list */
639 if (current >= end)
640 goto err_ret;
641 while (current < end) {
642 begin = current;
643 if (commp_find_token(&begin, ¤t, end, COMMP_SP,
644 B_FALSE) != 0) {
645 goto err_ret;
646 }
647 if ((ret = add_value_to_list(&new_repeat->r_offset, begin,
648 current - begin, B_FALSE)) != 0) {
649 if (ret == ENOMEM) {
650 *p_error |= SDP_MEMORY_ERROR;
651 return;
652 } else {
653 goto err_ret;
654 }
655 }
656 ++current;
657 }
658 /* check for trailing white space character. */
659 if (isspace(*(end - 1)))
660 goto err_ret;
661 return;
662 err_ret:
663 *p_error |= SDP_REPEAT_TIME_ERROR;
664 if (repeat != NULL)
665 repeat->r_next = NULL;
666 else
667 time->t_repeat = NULL;
668 sdp_free_repeat(new_repeat);
669 }
670
671 /*
672 * zone-adjustments (z=)
673 * %x7a "=" time SP ["-"] typed-time *(SP time SP ["-"] typed-time)
674 */
675 static void
sdp_parse_zone(sdp_zone_t ** zone,const char * begin,const char * end,uint_t * p_error)676 sdp_parse_zone(sdp_zone_t **zone, const char *begin, const char *end,
677 uint_t *p_error)
678 {
679 const char *current;
680 sdp_zone_t *new_zone = NULL;
681 sdp_zone_t *tmp = NULL;
682
683 if (*begin++ != COMMP_EQUALS) {
684 *p_error |= SDP_ZONE_ERROR;
685 return;
686 }
687 /* There can be atmost one zone field. */
688 if (*zone != NULL)
689 return;
690 /* Get time and offset */
691 current = begin;
692 while (current < end) {
693 new_zone = calloc(1, sizeof (sdp_zone_t));
694 if (new_zone == NULL) {
695 *p_error |= SDP_MEMORY_ERROR;
696 return;
697 }
698 if (*zone == NULL) {
699 *zone = new_zone;
700 tmp = *zone;
701 } else {
702 tmp->z_next = new_zone;
703 tmp = new_zone;
704 }
705 begin = current;
706 if (commp_find_token(&begin, ¤t, end, COMMP_SP,
707 B_FALSE) != 0) {
708 goto err_ret;
709 }
710 if (commp_strtoull(begin, current, &new_zone->z_time) != 0)
711 goto err_ret;
712 begin = ++current;
713 if (commp_find_token(&begin, ¤t, end, COMMP_SP,
714 B_FALSE) != 0) {
715 goto err_ret;
716 } else {
717 COMMP_COPY_STR(new_zone->z_offset, begin, current -
718 begin);
719 if (new_zone->z_offset == NULL) {
720 *p_error |= SDP_MEMORY_ERROR;
721 return;
722 }
723
724 }
725 ++current;
726 }
727 if (isspace(*(end - 1)))
728 goto err_ret;
729 return;
730 err_ret:
731 *p_error |= SDP_ZONE_ERROR;
732 sdp_free_zone(*zone);
733 *zone = NULL;
734 }
735
736 /*
737 * key-field (k=)
738 * [%x6b "=" key-type CRLF]
739 * key-type = %x70 %x72 %x6f %x6d %x70 %x74 / ; "prompt"
740 * %x63 %x6c %x65 %x61 %x72 ":" text / ; "clear:"
741 * %x62 %x61 %x73 %x65 "64:" base64 / ; "base64:"
742 * %x75 %x72 %x69 ":" uri ; "uri:"
743 */
744 static void
sdp_parse_key(sdp_key_t ** key,const char * begin,const char * end,uint_t * p_error)745 sdp_parse_key(sdp_key_t **key, const char *begin, const char *end,
746 uint_t *p_error)
747 {
748 const char *current;
749 sdp_key_t *new_key;
750
751 if (*begin++ != COMMP_EQUALS) {
752 *p_error |= SDP_KEY_ERROR;
753 return;
754 }
755 /* There can be only one key field */
756 if (*key != NULL)
757 return;
758 new_key = calloc(1, sizeof (sdp_key_t));
759 if (new_key == NULL) {
760 *p_error |= SDP_MEMORY_ERROR;
761 return;
762 }
763 /* Get Method name */
764 current = begin;
765 if (commp_find_token(&begin, ¤t, end, COMMP_COLON,
766 B_FALSE) != 0) {
767 goto err_ret;
768 } else {
769 COMMP_COPY_STR(new_key->k_method, begin, current - begin);
770 if (new_key->k_method == NULL) {
771 sdp_free_key(new_key);
772 *p_error |= SDP_MEMORY_ERROR;
773 return;
774 }
775 }
776 /* Get key, if exists. */
777 if (*current == COMMP_COLON) {
778 ++current;
779 if (current == end)
780 goto err_ret;
781 COMMP_COPY_STR(new_key->k_enckey, current, end - current);
782 if (new_key->k_enckey == NULL) {
783 sdp_free_key(new_key);
784 *p_error |= SDP_MEMORY_ERROR;
785 return;
786 }
787 }
788 *key = new_key;
789 return;
790 err_ret:
791 *p_error |= SDP_KEY_ERROR;
792 sdp_free_key(new_key);
793 }
794
795 /*
796 * attribute-fields (a=)
797 * *(%x61 "=" attribute CRLF)
798 * attribute = (att-field ":" att-value) / att-field
799 * att-field = token
800 * att-value = byte-string
801 */
802 static void
sdp_parse_attribute(sdp_attr_t ** attr,const char * begin,const char * end,uint_t * p_error)803 sdp_parse_attribute(sdp_attr_t **attr, const char *begin, const char *end,
804 uint_t *p_error)
805 {
806 const char *current;
807 sdp_attr_t *new_attr;
808 sdp_attr_t *tmp;
809
810 if (*begin++ != COMMP_EQUALS) {
811 *p_error |= SDP_ATTRIBUTE_ERROR;
812 return;
813 }
814 new_attr = calloc(1, sizeof (sdp_attr_t));
815 if (new_attr == NULL) {
816 *p_error |= SDP_MEMORY_ERROR;
817 return;
818 }
819 /* Get Attribute Name */
820 current = begin;
821 if (commp_find_token(&begin, ¤t, end, COMMP_COLON,
822 B_FALSE) != 0) {
823 goto err_ret;
824 } else {
825 COMMP_COPY_STR(new_attr->a_name, begin, current - begin);
826 if (new_attr->a_name == NULL) {
827 sdp_free_attribute(new_attr);
828 *p_error |= SDP_MEMORY_ERROR;
829 return;
830 }
831 }
832 /* Get Attribute Value */
833 if (*current == COMMP_COLON) {
834 ++current;
835 if (current == end)
836 goto err_ret;
837 COMMP_COPY_STR(new_attr->a_value, current, end - current);
838 if (new_attr->a_value == NULL) {
839 sdp_free_attribute(new_attr);
840 *p_error |= SDP_MEMORY_ERROR;
841 return;
842 }
843 }
844 if (*attr == NULL) {
845 *attr = new_attr;
846 } else {
847 tmp = *attr;
848 while (tmp->a_next != NULL)
849 tmp = tmp->a_next;
850 tmp->a_next = new_attr;
851 }
852 return;
853 err_ret:
854 *p_error |= SDP_ATTRIBUTE_ERROR;
855 sdp_free_attribute(new_attr);
856 }
857
858 /*
859 * media-field (m=)
860 * %x6d "=" media SP port ["/" integer] SP proto 1*(SP fmt) CRLF
861 */
862 static sdp_media_t *
sdp_parse_media(sdp_session_t * session,const char * begin,const char * end,uint_t * p_error)863 sdp_parse_media(sdp_session_t *session, const char *begin, const char *end,
864 uint_t *p_error)
865 {
866 const char *current;
867 const char *fake_end;
868 sdp_media_t *new_media;
869 sdp_media_t *tmp;
870
871 if (*begin++ != COMMP_EQUALS) {
872 *p_error |= SDP_MEDIA_ERROR;
873 return (NULL);
874 }
875
876 new_media = calloc(1, sizeof (sdp_media_t));
877 if (new_media == NULL) {
878 *p_error |= SDP_MEMORY_ERROR;
879 return (NULL);
880 }
881 new_media->m_session = session;
882 /* Get media name */
883 current = begin;
884 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
885 goto err_ret;
886 } else {
887 COMMP_COPY_STR(new_media->m_name, begin, current - begin);
888 if (new_media->m_name == NULL) {
889 sdp_free_media(new_media);
890 *p_error |= SDP_MEMORY_ERROR;
891 return (NULL);
892 }
893 }
894 /* Get port */
895 begin = ++current;
896 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0)
897 goto err_ret;
898 fake_end = current;
899 current = begin;
900 if (commp_find_token(&begin, ¤t, fake_end, COMMP_SLASH,
901 B_FALSE) != 0) {
902 goto err_ret;
903 }
904 if (commp_atoui(begin, current, &new_media->m_port) != 0)
905 goto err_ret;
906 /* Get portcount */
907 if (*current == COMMP_SLASH) {
908 begin = ++current;
909 if (commp_find_token(&begin, ¤t, fake_end, COMMP_SP,
910 B_FALSE) != 0) {
911 goto err_ret;
912 }
913 if (commp_atoi(begin, current, &new_media->m_portcount) != 0)
914 goto err_ret;
915 } else {
916 new_media->m_portcount = 1;
917 }
918 /* Get Protocol */
919 begin = ++current;
920 if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) {
921 goto err_ret;
922 } else {
923 COMMP_COPY_STR(new_media->m_proto, begin, current - begin);
924 if (new_media->m_proto == NULL) {
925 sdp_free_media(new_media);
926 *p_error |= SDP_MEMORY_ERROR;
927 return (NULL);
928 }
929 }
930 ++current;
931 /* Get format list */
932 if (current >= end)
933 goto err_ret;
934 while (current < end) {
935 begin = current;
936 if (commp_find_token(&begin, ¤t, end, COMMP_SP,
937 B_FALSE) != 0) {
938 goto err_ret;
939 }
940 if (add_value_to_list(&new_media->m_format, begin,
941 current - begin, B_TRUE) != 0) {
942 sdp_free_media(new_media);
943 *p_error |= SDP_MEMORY_ERROR;
944 return (NULL);
945 }
946 ++current;
947 }
948 /* check for trailing white space character. */
949 if (isspace(*(end - 1)))
950 goto err_ret;
951 /* Assign new media to the media list */
952 tmp = session->s_media;
953 if (tmp == NULL) {
954 session->s_media = new_media;
955 } else {
956 while (tmp->m_next != NULL)
957 tmp = tmp->m_next;
958 tmp->m_next = new_media;
959 }
960 return (new_media);
961 err_ret:
962 *p_error |= SDP_MEDIA_ERROR;
963 sdp_free_media(new_media);
964 return (NULL);
965 }
966
967 /*
968 * This function ensures that a field is in the right order in SDP descripton.
969 * It also identifies cases where a field ('v', 'o, 'i', et al) that must occur
970 * once but occurs several times in SDP description. error cannot be NULL.
971 */
972 static void
sdp_check_order(char prev,char * order,int * error)973 sdp_check_order(char prev, char *order, int *error)
974 {
975 *error = 0;
976 while (*order != '\0') {
977 if (*order++ == prev)
978 return;
979 }
980 *error = 1;
981 }
982
983 /*
984 * This function determines the SDP field and calls the appropriate parse
985 * function. It also ensures that the SDP fields are in strict order.
986 */
987 static void
sdp_handle_fields(sdp_description_t * description,sdp_session_t * _session,const char * begin,const char * end)988 sdp_handle_fields(sdp_description_t *description, sdp_session_t *_session,
989 const char *begin, const char *end)
990 {
991 boolean_t u_field = B_FALSE;
992 int error = 0; /* fields order error */
993 char prev = description->d_prev;
994 char m_prev = description->d_mprev;
995
996 switch (*begin) {
997 case SDP_VERSION_FIELD:
998 sdp_check_order(prev, SDP_VERSION_ORDER, &error);
999 description->d_version = B_TRUE;
1000 sdp_parse_version(&_session->s_version, begin + 1, end,
1001 &description->d_perror);
1002 break;
1003 case SDP_ORIGIN_FIELD:
1004 sdp_check_order(prev, SDP_ORIGIN_ORDER, &error);
1005 description->d_origin = B_TRUE;
1006 sdp_parse_origin(&_session->s_origin, begin + 1, end,
1007 &description->d_perror);
1008 break;
1009 case SDP_NAME_FIELD:
1010 sdp_check_order(prev, SDP_NAME_ORDER, &error);
1011 description->d_name = B_TRUE;
1012 sdp_parse_name(&_session->s_name, begin + 1, end,
1013 &description->d_perror);
1014 break;
1015 case SDP_INFO_FIELD:
1016 if (description->d_mparsed) {
1017 sdp_check_order(m_prev, SDP_M_INFO_ORDER,
1018 &error);
1019 if (description->d_lmedia == NULL)
1020 break;
1021 sdp_parse_info(&(description->d_lmedia->
1022 m_info), begin + 1, end, &description->
1023 d_perror);
1024 } else {
1025 sdp_check_order(prev, SDP_INFO_ORDER, &error);
1026 sdp_parse_info(&_session->s_info, begin + 1,
1027 end, &description->d_perror);
1028 }
1029 break;
1030 case SDP_URI_FIELD:
1031 sdp_check_order(prev, SDP_URI_ORDER, &error);
1032 sdp_parse_uri(&_session->s_uri, begin + 1, end,
1033 &description->d_perror);
1034 break;
1035 case SDP_EMAIL_FIELD:
1036 sdp_check_order(prev, SDP_EMAIL_ORDER, &error);
1037 sdp_parse_email(&_session->s_email, begin + 1, end,
1038 &description->d_perror);
1039 break;
1040 case SDP_PHONE_FIELD:
1041 sdp_check_order(prev, SDP_PHONE_ORDER, &error);
1042 sdp_parse_phone(&_session->s_phone, begin + 1, end,
1043 &description->d_perror);
1044 break;
1045 case SDP_CONNECTION_FIELD:
1046 if (description->d_mparsed) {
1047 sdp_check_order(m_prev, SDP_M_CONN_ORDER,
1048 &error);
1049 --description->d_mccount;
1050 if (description->d_lmedia == NULL)
1051 break;
1052 sdp_parse_connection(&(description->d_lmedia->
1053 m_conn), begin + 1, end,
1054 &description->d_perror);
1055 } else {
1056 /*
1057 * RFC - 4566 says that session section should
1058 * have only one connection field, while media
1059 * section can have many
1060 */
1061 sdp_check_order(prev, SDP_CONN_ORDER, &error);
1062 description->d_conn = B_TRUE;
1063 if (_session->s_conn != NULL)
1064 break;
1065 sdp_parse_connection(&_session->s_conn,
1066 begin + 1, end, &description->d_perror);
1067 }
1068 break;
1069 case SDP_BANDWIDTH_FIELD:
1070 if (description->d_mparsed) {
1071 sdp_check_order(m_prev, SDP_M_BW_ORDER, &error);
1072 if (description->d_lmedia == NULL)
1073 break;
1074 sdp_parse_bandwidth(&(description->d_lmedia->
1075 m_bw), begin + 1, end,
1076 &description->d_perror);
1077 } else {
1078 sdp_check_order(prev, SDP_BW_ORDER, &error);
1079 sdp_parse_bandwidth(&_session->s_bw,
1080 begin + 1, end, &description->d_perror);
1081 }
1082 break;
1083 case SDP_TIME_FIELD:
1084 if (!description->d_tparsed || description->d_prev !=
1085 SDP_REPEAT_FIELD) {
1086 sdp_check_order(prev, SDP_TIME_ORDER, &error);
1087 }
1088 description->d_tparsed = B_TRUE;
1089 description->d_ltime = sdp_parse_time(&_session->
1090 s_time, begin + 1, end, &description->d_perror);
1091 break;
1092 case SDP_REPEAT_FIELD:
1093 sdp_check_order(prev, SDP_REPEAT_ORDER, &error);
1094 if (description->d_ltime == NULL)
1095 break;
1096 /* we pass time, as repeat is associated with time */
1097 sdp_parse_repeat(description->d_ltime, begin + 1, end,
1098 &description->d_perror);
1099 break;
1100 case SDP_ZONE_FIELD:
1101 sdp_check_order(prev, SDP_ZONE_ORDER, &error);
1102 sdp_parse_zone(&_session->s_zone, begin + 1, end,
1103 &description->d_perror);
1104 break;
1105 case SDP_KEY_FIELD:
1106 if (description->d_mparsed) {
1107 sdp_check_order(m_prev, SDP_M_KEY_ORDER,
1108 &error);
1109 if (description->d_lmedia == NULL)
1110 break;
1111 sdp_parse_key(&(description->d_lmedia->m_key),
1112 begin + 1, end, &description->d_perror);
1113 } else {
1114 sdp_check_order(prev, SDP_KEY_ORDER, &error);
1115 sdp_parse_key(&_session->s_key, begin + 1, end,
1116 &description->d_perror);
1117 }
1118 break;
1119 case SDP_ATTRIBUTE_FIELD:
1120 if (description->d_mparsed) {
1121 sdp_check_order(m_prev, SDP_M_ATTR_ORDER,
1122 &error);
1123 if (description->d_lmedia == NULL)
1124 break;
1125 sdp_parse_attribute(&(description->d_lmedia->
1126 m_attr), begin + 1, end,
1127 &description->d_perror);
1128 } else {
1129 sdp_check_order(prev, SDP_ATTR_ORDER, &error);
1130 sdp_parse_attribute(&_session->s_attr,
1131 begin + 1, end, &description->d_perror);
1132 }
1133 break;
1134 case SDP_MEDIA_FIELD:
1135 if (!description->d_mparsed) {
1136 sdp_check_order(prev, SDP_MEDIA_ORDER, &error);
1137 description->d_mccount = 1;
1138 } else {
1139 if (description->d_mccount == 1)
1140 description->d_mconn = B_FALSE;
1141 description->d_mccount = 1;
1142 }
1143 description->d_mparsed = B_TRUE;
1144 description->d_lmedia = sdp_parse_media(_session,
1145 begin + 1, end, &description->d_perror);
1146 break;
1147 default:
1148 /* Unknown field type. Ignore it */
1149 u_field = B_TRUE;
1150 break;
1151 }
1152 if (error)
1153 description->d_perror |= SDP_FIELDS_ORDER_ERROR;
1154 if (!u_field) {
1155 if (!description->d_mparsed)
1156 description->d_prev = *begin;
1157 else
1158 description->d_mprev = *begin;
1159 }
1160 }
1161
1162 /*
1163 * Parses the SDP info
1164 */
1165 int
sdp_parse(const char * sdp_info,int len,int flags,sdp_session_t ** session,uint_t * p_error)1166 sdp_parse(const char *sdp_info, int len, int flags, sdp_session_t **session,
1167 uint_t *p_error)
1168 {
1169
1170 const char *f_begin;
1171 const char *f_end;
1172 sdp_description_t *description;
1173 const char *start;
1174 const char *end;
1175 const char *current;
1176
1177 if (sdp_info == NULL || len == 0 || p_error == NULL || flags != 0 ||
1178 session == NULL) {
1179 if (session != NULL)
1180 *session = NULL;
1181 return (EINVAL);
1182 }
1183 *session = NULL;
1184 *p_error = 0;
1185 description = calloc(1, sizeof (sdp_description_t));
1186 if (description == NULL) {
1187 return (ENOMEM);
1188 }
1189 /* Needed later to check for mandatory fields */
1190 description->d_prev = COMMP_SP;
1191 description->d_mconn = B_TRUE;
1192 *session = sdp_new_session();
1193 if (*session == NULL) {
1194 free(description);
1195 return (ENOMEM);
1196 }
1197 start = sdp_info;
1198 end = start + len;
1199 if (commp_skip_white_space(&start, end) != 0) {
1200 free(description);
1201 free(*session);
1202 *session = NULL;
1203 return (EINVAL);
1204 }
1205 current = start;
1206 f_begin = current;
1207 while ((current < end) && !(description->d_perror &
1208 SDP_MEMORY_ERROR)) {
1209 /*
1210 * RFC says parser SHOULD be tolerant to records ending
1211 * with a single newline character too.
1212 */
1213 if (strncmp(COMMP_CRLF, current, strlen(COMMP_CRLF)) == 0) {
1214 f_end = current;
1215 sdp_handle_fields(description, *session, f_begin,
1216 f_end);
1217 COMMP_SKIP_CRLF(current);
1218 (void) commp_skip_white_space(¤t, end);
1219 f_begin = current;
1220 } else if (strncmp(COMMP_LF, current, strlen(COMMP_LF)) == 0) {
1221 f_end = current;
1222 sdp_handle_fields(description, *session, f_begin,
1223 f_end);
1224 COMMP_SKIP_LF(current);
1225 (void) commp_skip_white_space(¤t, end);
1226 f_begin = current;
1227 } else {
1228 current++;
1229 }
1230 }
1231 if (description->d_perror & SDP_MEMORY_ERROR) {
1232 free(description);
1233 sdp_free_session(*session);
1234 *session = NULL;
1235 return (ENOMEM);
1236 }
1237 /*
1238 * Check for mandatory fields v, o, s, t fields. For connection field,
1239 * RFC says; a connection field must be present in every media
1240 * description or at the session-level
1241 */
1242 if (description->d_mccount == 1)
1243 description->d_mconn = B_FALSE;
1244 if (!(description->d_version && description->d_origin &&
1245 description->d_name && description->d_tparsed &&
1246 (description->d_conn || (description->d_mparsed &&
1247 description->d_mconn)))) {
1248 description->d_perror |= SDP_MISSING_FIELDS;
1249 }
1250 *p_error = description->d_perror;
1251 free(description);
1252 return (0);
1253 }
1254