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