xref: /illumos-gate/usr/src/lib/libcommputil/common/sdp_parse.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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
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
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
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
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
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
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
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, &current, 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, &current, 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, &current, 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, &current, 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, &current, 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, &current, 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 *
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, &current, 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, &current, 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
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, &current, 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, &current, 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, &current, end, COMMP_SP,
449 		    B_TRUE) != 0) {
450 			goto err_ret;
451 		}
452 	} else {
453 		if (commp_find_token(&begin, &current, 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
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, &current, 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, &current, 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
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, &current, 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, &current, 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, &current, 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
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, &current, 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, &current, 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
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, &current, 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
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, &current, 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 *
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, &current, 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, &current, end, COMMP_SP, B_FALSE) != 0)
897 		goto err_ret;
898 	fake_end = current;
899 	current = begin;
900 	if (commp_find_token(&begin, &current, 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, &current, 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, &current, 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, &current, 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
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
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
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(&current, 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(&current, 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