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