xref: /illumos-gate/usr/src/lib/libcommputil/common/sdp.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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  * Contains implementation of various interfaces exported by library
29  */
30 
31 #include <stdio.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sdp.h>
38 
39 #include "sdp_parse.h"
40 #include "commp_util.h"
41 
42 #define	FIELD_EQUALS_CRLF_LEN		4  /* first two characters and CRLF */
43 
44 #define	SDP_ATTR_TO_STR(m_attr) {					\
45 	while ((m_attr) != NULL) {					\
46 		if ((m_attr)->a_value != NULL) {			\
47 			wrote = snprintf(buf, len, "a=%s%c%s%s",	\
48 			    (m_attr)->a_name, COMMP_COLON, (m_attr)->  	\
49 			    a_value, COMMP_CRLF);			\
50 		} else {						\
51 			wrote = snprintf(buf, len, "a=%s%s", (m_attr)-> \
52 			    a_name, COMMP_CRLF);			\
53 		}							\
54 		len = len - wrote;					\
55 		buf = buf + wrote;					\
56 		(m_attr) = (m_attr)->a_next;				\
57 	}								\
58 }
59 
60 #define	SDP_KEY_TO_STR(m_key) {						\
61 	if ((m_key) != NULL) {						\
62 		if ((m_key)->k_enckey != NULL) {			\
63 			wrote = snprintf(buf, len, "k=%s%c%s%s",	\
64 			    (m_key)->k_method, COMMP_COLON, (m_key)->	\
65 			    k_enckey, COMMP_CRLF);			\
66 		} else {						\
67 			wrote = snprintf(buf, len, "k=%s%s", (m_key)->	\
68 			    k_method, COMMP_CRLF);			\
69 		}							\
70 		len = len - wrote;					\
71 		buf = buf + wrote;					\
72 	}								\
73 }
74 
75 #define	SDP_BANDWIDTH_TO_STR(m_bw) {					\
76 	while ((m_bw) != NULL) {					\
77 		wrote = snprintf(buf, len, "b=%s%c%llu%s", (m_bw)->	\
78 		    b_type, COMMP_COLON, (m_bw)->b_value, COMMP_CRLF);	\
79 		len = len - wrote;					\
80 		buf = buf + wrote;					\
81 		(m_bw) = (m_bw)->b_next;				\
82 	}								\
83 }
84 
85 #define	SDP_INFORMATION_TO_STR(m_info) {				       \
86 	if ((m_info) != NULL) {						       \
87 		wrote = snprintf(buf, len, "i=%s%s", (m_info), COMMP_CRLF);    \
88 		len = len - wrote;					       \
89 		buf = buf + wrote;					       \
90 	}								       \
91 }
92 
93 #define	SDP_CONNECTION_TO_STR(m_conn) {					       \
94 	while ((m_conn) != NULL) {					       \
95 		if (strcasecmp((m_conn)->c_addrtype,			       \
96 		    COMMP_ADDRTYPE_IP4) == 0) {				       \
97 			if ((m_conn)->c_addrcount > 1) {		       \
98 				wrote = snprintf(buf, len, "c=%s %s %s/%d/%d"  \
99 				    "%s", (m_conn)->c_nettype, (m_conn)->      \
100 				    c_addrtype, (m_conn)->c_address, (m_conn)->\
101 				    c_ttl, (m_conn)->c_addrcount, COMMP_CRLF); \
102 			} else if ((m_conn)->c_addrcount == 1) {	       \
103 				wrote = snprintf(buf, len, "c=%s %s %s/%d%s",  \
104 				    (m_conn)->c_nettype, (m_conn)->c_addrtype, \
105 				    (m_conn)->c_address, (m_conn)->c_ttl,      \
106 				    COMMP_CRLF);			       \
107 			} else {					       \
108 				wrote = snprintf(buf, len, "c=%s %s %s%s",     \
109 				    (m_conn)->c_nettype, (m_conn)->c_addrtype, \
110 				    (m_conn)->c_address, COMMP_CRLF);	       \
111 			}						       \
112 		} else if (strcasecmp((m_conn)->c_addrtype,		       \
113 		    COMMP_ADDRTYPE_IP6) == 0) {                                \
114 			if ((m_conn)->c_addrcount <= 1) {		       \
115 				wrote = snprintf(buf, len, "c=%s %s %s%s",     \
116 				    (m_conn)->c_nettype, (m_conn)->c_addrtype, \
117 				    (m_conn)->c_address, COMMP_CRLF);	       \
118 			} else {					       \
119 				wrote = snprintf(buf, len, "c=%s %s %s/%d%s",  \
120 				    (m_conn)->c_nettype, (m_conn)->c_addrtype, \
121 				    (m_conn)->c_address, (m_conn)->c_addrcount,\
122 				    COMMP_CRLF);			       \
123 			}						       \
124 		} else {						       \
125 			wrote = snprintf(buf, len, "c=%s %s %s%s", (m_conn)->  \
126 			    c_nettype, (m_conn)->c_addrtype, (m_conn)->        \
127 			    c_address, COMMP_CRLF);			       \
128 		}							       \
129 		len = len - wrote;					       \
130 		buf = buf + wrote;					       \
131 		(m_conn) = (m_conn)->c_next;				       \
132 	}								       \
133 }
134 
135 #define	SDP_ADD_KEY(d_key, s_key) {					\
136 	if ((s_key) != NULL) {						\
137 		if (sdp_add_key(&(d_key), (s_key)->k_method,		\
138 		    (s_key)->k_enckey) != 0) {				\
139 			sdp_free_session(new_sess);			\
140 			return (NULL);					\
141 		}							\
142 	}								\
143 }
144 
145 #define	SDP_ADD_ATTRIBUTE(d_attr, s_attr) {				\
146 	while ((s_attr) != NULL) {					\
147 		if (sdp_add_attribute(&(d_attr), (s_attr)->a_name,	\
148 		    (s_attr)->a_value) != 0) {		 		\
149 			sdp_free_session(new_sess);			\
150 			return (NULL);					\
151 		}							\
152 		(s_attr) = (s_attr)->a_next;				\
153 	}								\
154 }
155 
156 #define	SDP_ADD_BANDWIDTH(d_bw, s_bw) {					\
157 	while ((s_bw) != NULL) {					\
158 		if (sdp_add_bandwidth(&(d_bw), (s_bw)->b_type,		\
159 		    (s_bw)->b_value) != 0) {				\
160 			sdp_free_session(new_sess);			\
161 			return (NULL);					\
162 		}							\
163 		(s_bw) = (s_bw)->b_next;				\
164 	}								\
165 }
166 
167 #define	SDP_ADD_CONNECTION(d_conn, s_conn) {				\
168 	while ((s_conn) != NULL) {					\
169 		if (sdp_add_connection(&(d_conn), (s_conn)->c_nettype,	\
170 		    (s_conn)->c_addrtype, (s_conn)->c_address,		\
171 		    (s_conn)->c_ttl, (s_conn)->c_addrcount) != 0) {	\
172 			sdp_free_session(new_sess);			\
173 			return (NULL);					\
174 		}							\
175 		(s_conn) = (s_conn)->c_next;				\
176 	}								\
177 }
178 
179 #define	SDP_LEN_CONNECTION(m_conn) {					  \
180 	while ((m_conn) != NULL) {					  \
181 		len += FIELD_EQUALS_CRLF_LEN;				  \
182 		len += strlen((m_conn)->c_nettype);			  \
183 		len += strlen((m_conn)->c_addrtype) + 1;		  \
184 		len += strlen((m_conn)->c_address) + 1;			  \
185 		len += snprintf(buf, 1, "%u", (m_conn)->c_ttl) + 1;	  \
186 		len += snprintf(buf, 1, "%d", (m_conn)->c_addrcount) + 1; \
187 		(m_conn) = (m_conn)->c_next;				  \
188 	}								  \
189 }
190 
191 #define	SDP_LEN_BANDWIDTH(m_bw) {					\
192 	while ((m_bw) != NULL) {					\
193 		len += FIELD_EQUALS_CRLF_LEN;				\
194 		len += strlen((m_bw)->b_type);				\
195 		len += snprintf(buf, 1, "%llu", (m_bw)->b_value) + 1;	\
196 		(m_bw) = (m_bw)->b_next;				\
197 	}								\
198 }
199 
200 #define	SDP_LEN_KEY(m_key) {						\
201 	if ((m_key) != NULL) {						\
202 		len += FIELD_EQUALS_CRLF_LEN;				\
203 		len += strlen((m_key)->k_method);			\
204 		if ((m_key)->k_enckey != NULL)				\
205 			len += strlen((m_key)->k_enckey) + 1;		\
206 	}								\
207 }
208 
209 #define	SDP_LEN_ATTRIBUTE(m_attr) {					\
210 	while ((m_attr) != NULL) {					\
211 		len += FIELD_EQUALS_CRLF_LEN;				\
212 		len += strlen((m_attr)->a_name);			\
213 		if ((m_attr)->a_value != NULL)				\
214 			len += strlen((m_attr)->a_value) + 1;		\
215 		(m_attr) = (m_attr)->a_next;				\
216 	}								\
217 }
218 
219 /*
220  * Given a media list and media name ("audio", "video", et al), it searches
221  * the list for that media. Returns NULL if media not present.
222  */
223 sdp_media_t *
224 sdp_find_media(sdp_media_t *media, const char *name)
225 {
226 	if (media == NULL || name == NULL || (strlen(name) == 0)) {
227 		return (NULL);
228 	}
229 	while (media != NULL) {
230 		if (media->m_name != NULL) {
231 			if (strcasecmp(name, media->m_name) == 0)
232 				return (media);
233 		}
234 		media = media->m_next;
235 	}
236 	return (media);
237 }
238 
239 /*
240  * Given a attribute list and name of the attribute ("rtpmap", "fmtp", et al),
241  * this API searches the list for that attribute. Returns NULL if not found.
242  */
243 sdp_attr_t *
244 sdp_find_attribute(sdp_attr_t *attr, const char *name)
245 {
246 	if (attr == NULL || name == NULL || (strlen(name) == 0)) {
247 		return (NULL);
248 	}
249 	while (attr != NULL) {
250 		if (attr->a_name != NULL) {
251 			if (strcasecmp(attr->a_name, name) == 0)
252 				return (attr);
253 		}
254 		attr = attr->a_next;
255 	}
256 	return (attr);
257 }
258 
259 /*
260  * Given a media list and a format number, this API will return the rtpmap
261  * attribute matching the format number.
262  */
263 sdp_attr_t *
264 sdp_find_media_rtpmap(sdp_media_t *media, const char *format)
265 {
266 	sdp_attr_t		*attr = NULL;
267 	char			*tmp = NULL;
268 
269 	if (media == NULL || format == NULL || (strlen(format) == 0)) {
270 		return (NULL);
271 	}
272 	attr = media->m_attr;
273 	while (attr != NULL) {
274 		if (attr->a_name != NULL && (strcasecmp(attr->a_name,
275 		    SDP_RTPMAP) == 0)) {
276 			if (attr->a_value != NULL) {
277 				tmp = attr->a_value;
278 				while (isspace(*tmp))
279 					++tmp;
280 				if (strncasecmp(tmp, format,
281 				    strlen(format)) == 0) {
282 					return (attr);
283 				}
284 			}
285 		}
286 		attr = attr->a_next;
287 	}
288 	return (attr);
289 }
290 
291 /*
292  * Adds origin field to the session.
293  * o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
294  */
295 int
296 sdp_add_origin(sdp_session_t *session, const char *name, uint64_t id,
297     uint64_t ver, const char *nettype, const char *addrtype,
298     const char *address)
299 {
300 	sdp_origin_t		*origin;
301 	int			ret = 0;
302 
303 	if (session == NULL || name == NULL || nettype == NULL ||
304 	    addrtype == NULL || address == NULL) {
305 		return (EINVAL);
306 	}
307 	if (session->s_origin != NULL)
308 		return (EPROTO);
309 	origin = calloc(1, sizeof (sdp_origin_t));
310 	if (origin == NULL)
311 		return (ENOMEM);
312 	origin->o_id = id;
313 	origin->o_version = ver;
314 	if ((ret = commp_add_str(&origin->o_username, name, strlen(name))) != 0)
315 		goto err_ret;
316 	if ((ret = commp_add_str(&origin->o_nettype, nettype,
317 	    strlen(nettype))) != 0) {
318 		goto err_ret;
319 	}
320 	if ((ret = commp_add_str(&origin->o_addrtype, addrtype,
321 	    strlen(addrtype))) != 0) {
322 		goto err_ret;
323 	}
324 	if ((ret = commp_add_str(&origin->o_address, address,
325 	    strlen(address))) != 0) {
326 		goto err_ret;
327 	}
328 	session->s_origin = origin;
329 	return (ret);
330 err_ret:
331 	sdp_free_origin(origin);
332 	return (ret);
333 }
334 
335 /*
336  * Adds session name field to the session.
337  * s=<session name>
338  */
339 int
340 sdp_add_name(sdp_session_t *session, const char *name)
341 {
342 	if (session == NULL || name == NULL)
343 		return (EINVAL);
344 	if (session->s_name != NULL)
345 		return (EPROTO);
346 	return (commp_add_str(&session->s_name, name, strlen(name)));
347 }
348 
349 /*
350  * Adds session information field to the session or media section of SDP.
351  * i=<session description>
352  */
353 int
354 sdp_add_information(char **information, const char *value)
355 {
356 	if (information == NULL || value == NULL)
357 		return (EINVAL);
358 	if (*information != NULL)
359 		return (EPROTO);
360 	return (commp_add_str(information, value, strlen(value)));
361 }
362 
363 /*
364  * Adds uri field to the session.
365  * u=<uri>
366  */
367 int
368 sdp_add_uri(sdp_session_t *session, const char *uri)
369 {
370 	if (session == NULL || uri == NULL)
371 		return (EINVAL);
372 	if (session->s_uri != NULL)
373 		return (EPROTO);
374 	return (commp_add_str(&session->s_uri, uri, strlen(uri)));
375 }
376 
377 /*
378  * Adds email address field to the session.
379  * e=<email-address>
380  */
381 int
382 sdp_add_email(sdp_session_t *session, const char *email)
383 {
384 	if (session == NULL || email == NULL || (strlen(email) == 0))
385 		return (EINVAL);
386 	return (add_value_to_list(&session->s_email, email, strlen(email),
387 	    B_TRUE));
388 }
389 
390 /*
391  * Adds phone number field to the session.
392  * p=<phone-number>
393  */
394 int
395 sdp_add_phone(sdp_session_t *session, const char *phone)
396 {
397 	if (session == NULL || phone == NULL || (strlen(phone) == 0))
398 		return (EINVAL);
399 	return (add_value_to_list(&session->s_phone, phone, strlen(phone),
400 	    B_TRUE));
401 }
402 
403 /*
404  * Adds connection field to the session or media section of SDP
405  * c=<nettype> <addrtype> <connection-address>[/ttl]/<number of addresses>
406  */
407 int
408 sdp_add_connection(sdp_conn_t **conn, const char *nettype, const char *addrtype,
409     const char *address, uint8_t ttl, int addrcount)
410 {
411 	sdp_conn_t		*tmp;
412 	sdp_conn_t		*new_conn;
413 	int			ret = 0;
414 
415 	if (conn == NULL || nettype == NULL || addrtype == NULL ||
416 	    address == NULL) {
417 		return (EINVAL);
418 	}
419 	new_conn = calloc(1, sizeof (sdp_conn_t));
420 	if (new_conn == NULL)
421 		return (ENOMEM);
422 	new_conn->c_ttl = ttl;
423 	new_conn->c_addrcount = addrcount;
424 	if ((ret = commp_add_str(&new_conn->c_nettype, nettype,
425 	    strlen(nettype))) != 0) {
426 		goto err_ret;
427 	}
428 	if ((ret = commp_add_str(&new_conn->c_addrtype, addrtype,
429 	    strlen(addrtype))) != 0) {
430 		goto err_ret;
431 	}
432 	if ((ret = commp_add_str(&new_conn->c_address, address,
433 	    strlen(address))) != 0) {
434 		goto err_ret;
435 	}
436 	if (*conn == NULL) {
437 		*conn = new_conn;
438 	} else {
439 		tmp = *conn;
440 		while (tmp->c_next != NULL)
441 			tmp = tmp->c_next;
442 		tmp->c_next = new_conn;
443 	}
444 	return (ret);
445 err_ret:
446 	sdp_free_connection(new_conn);
447 	return (ret);
448 }
449 
450 /*
451  * Adds bandwidth field to the session or media section of SDP.
452  * b=<bwtype>:<bandwidth>
453  */
454 int
455 sdp_add_bandwidth(sdp_bandwidth_t **bw, const char *type, uint64_t value)
456 {
457 	sdp_bandwidth_t		*new_bw;
458 	sdp_bandwidth_t		*tmp;
459 	int			ret = 0;
460 
461 	if (bw == NULL || type == NULL)
462 		return (EINVAL);
463 	new_bw = calloc(1, sizeof (sdp_bandwidth_t));
464 	if (new_bw == NULL)
465 		return (ENOMEM);
466 	new_bw->b_value = value;
467 	if ((ret = commp_add_str(&new_bw->b_type, type, strlen(type))) != 0) {
468 		free(new_bw);
469 		return (ret);
470 	}
471 	if (*bw == NULL) {
472 		*bw = new_bw;
473 	} else {
474 		tmp = *bw;
475 		while (tmp->b_next != NULL)
476 			tmp = tmp->b_next;
477 		tmp->b_next = new_bw;
478 	}
479 	return (ret);
480 }
481 
482 /*
483  * Adds time field to the session
484  * t=<start-time> <stop-time>
485  */
486 int
487 sdp_add_time(sdp_session_t *session, uint64_t starttime, uint64_t stoptime,
488     sdp_time_t **time)
489 {
490 	sdp_time_t		*new_time;
491 	sdp_time_t		*tmp;
492 
493 	if (time != NULL)
494 		*time = NULL;
495 	if (session == NULL) {
496 		return (EINVAL);
497 	}
498 	new_time = calloc(1, sizeof (sdp_time_t));
499 	if (new_time == NULL) {
500 		return (ENOMEM);
501 	}
502 	new_time->t_start = starttime;
503 	new_time->t_stop = stoptime;
504 	tmp = session->s_time;
505 	if (tmp == NULL)
506 		session->s_time = new_time;
507 	else {
508 		while (tmp->t_next != NULL)
509 			tmp = tmp->t_next;
510 		tmp->t_next = new_time;
511 	}
512 	if (time != NULL)
513 		*time = new_time;
514 	return (0);
515 }
516 
517 /*
518  * Adds repeat field to the time structure of session
519  * r=<repeat interval> <active duration> <offsets from start-time>
520  */
521 int
522 sdp_add_repeat(sdp_time_t *time, uint64_t interval, uint64_t duration,
523     const char *offset)
524 {
525 	sdp_repeat_t		*tmp;
526 	sdp_repeat_t		*new_repeat;
527 	int			ret = 0;
528 
529 	if (time == NULL || offset == NULL)
530 		return (EINVAL);
531 	new_repeat = calloc(1, sizeof (sdp_repeat_t));
532 	if (new_repeat == NULL)
533 		return (ENOMEM);
534 	new_repeat->r_interval = interval;
535 	new_repeat->r_duration = duration;
536 	if ((ret = sdp_str_to_list(&new_repeat->r_offset, offset,
537 	    strlen(offset), B_FALSE)) != 0) {
538 		goto err_ret;
539 	}
540 	tmp = time->t_repeat;
541 	if (tmp == NULL) {
542 		time->t_repeat = new_repeat;
543 	} else {
544 		while (tmp->r_next != NULL)
545 			tmp = tmp->r_next;
546 		tmp->r_next = new_repeat;
547 	}
548 	return (ret);
549 err_ret:
550 	sdp_free_repeat(new_repeat);
551 	return (ret);
552 }
553 
554 /*
555  * Adds time zone field to the session
556  * z=<adjustment time> <offset> <adjustment time> <offset> ....
557  */
558 int
559 sdp_add_zone(sdp_session_t *session, uint64_t time, const char *offset)
560 {
561 	sdp_zone_t		*new_zone;
562 	sdp_zone_t		*tmp;
563 	int			ret = 0;
564 
565 	if (session == NULL || offset == NULL)
566 		return (EINVAL);
567 	new_zone = calloc(1, sizeof (sdp_zone_t));
568 	if (new_zone == NULL)
569 		return (ENOMEM);
570 	new_zone->z_time = time;
571 	if ((ret = commp_add_str(&new_zone->z_offset, offset,
572 	    strlen(offset))) != 0) {
573 		free(new_zone);
574 		return (ret);
575 	}
576 	tmp = session->s_zone;
577 	if (tmp == NULL) {
578 		session->s_zone = new_zone;
579 	} else {
580 		while (tmp->z_next != NULL) {
581 			tmp = tmp->z_next;
582 		}
583 		tmp->z_next = new_zone;
584 	}
585 	return (ret);
586 }
587 
588 /*
589  * Adds key field to session or media section of SDP.
590  * k=<method>
591  * k=<method>:<encryption key>
592  */
593 int
594 sdp_add_key(sdp_key_t **key, const char *method, const char *enckey)
595 {
596 	int			ret = 0;
597 
598 	if (key == NULL || method == NULL)
599 		return (EINVAL);
600 	if (*key != NULL)
601 		return (EPROTO);
602 	*key = calloc(1, sizeof (sdp_key_t));
603 	if (*key == NULL)
604 		return (ENOMEM);
605 	if ((ret = commp_add_str(&((*key)->k_method), method,
606 	    strlen(method))) != 0) {
607 		goto err_ret;
608 	}
609 	if (enckey != NULL) {
610 		if ((ret = commp_add_str(&((*key)->k_enckey), enckey,
611 		    strlen(enckey))) != 0) {
612 			goto err_ret;
613 		}
614 	}
615 	return (ret);
616 err_ret:
617 	sdp_free_key(*key);
618 	*key = NULL;
619 	return (ret);
620 }
621 
622 /*
623  * Adds attribute field to session or media section of SDP.
624  * a=<attribute>
625  * a=<attribute>:<value>
626  */
627 int
628 sdp_add_attribute(sdp_attr_t **attr, const char *name, const char *value)
629 {
630 	sdp_attr_t		*tmp;
631 	sdp_attr_t		*new_attr;
632 	int			ret = 0;
633 
634 	if (attr == NULL || name == NULL)
635 		return (EINVAL);
636 	new_attr = calloc(1, sizeof (sdp_attr_t));
637 	if (new_attr == NULL)
638 		return (ENOMEM);
639 	if ((ret = commp_add_str(&new_attr->a_name, name, strlen(name))) != 0)
640 		goto err_ret;
641 	if (value != NULL) {
642 		if ((ret = commp_add_str(&new_attr->a_value, value,
643 		    strlen(value))) != 0) {
644 			goto err_ret;
645 		}
646 	}
647 	tmp = *attr;
648 	if (tmp == NULL) {
649 		*attr = new_attr;
650 	} else {
651 		while (tmp->a_next != NULL)
652 			tmp = tmp->a_next;
653 		tmp->a_next = new_attr;
654 	}
655 	return (ret);
656 err_ret:
657 	sdp_free_attribute(new_attr);
658 	return (ret);
659 }
660 
661 /*
662  * Adds media field to the session.
663  * m=<media> <port>[/portcount] <proto> <fmt> ...
664  */
665 int
666 sdp_add_media(sdp_session_t *session, const char *name, uint_t port,
667     int portcount, const char *protocol, const char *fmt, sdp_media_t **media)
668 {
669 	sdp_media_t		*tmp;
670 	sdp_media_t		*new_media;
671 	int			ret = 0;
672 
673 	if (media != NULL)
674 		*media = NULL;
675 	if (session == NULL || name == NULL || protocol == NULL ||
676 	    portcount <= 0 || fmt == NULL) {
677 		return (EINVAL);
678 	}
679 	new_media = calloc(1, sizeof (sdp_media_t));
680 	if (new_media == NULL) {
681 		return (ENOMEM);
682 	}
683 	new_media->m_session = session;
684 	new_media->m_port = port;
685 	new_media->m_portcount = portcount;
686 	if ((ret = commp_add_str(&new_media->m_name, name, strlen(name))) != 0)
687 		goto err_ret;
688 	if ((ret = commp_add_str(&new_media->m_proto, protocol,
689 	    strlen(protocol))) != 0) {
690 		goto err_ret;
691 	}
692 	if ((ret = sdp_str_to_list(&new_media->m_format, fmt,
693 	    strlen(fmt), B_TRUE)) != 0) {
694 		goto err_ret;
695 	}
696 	tmp = session->s_media;
697 	if (tmp == NULL) {
698 		session->s_media = new_media;
699 	} else {
700 		while (tmp->m_next != NULL)
701 			tmp = tmp->m_next;
702 		tmp->m_next = new_media;
703 	}
704 	if (media != NULL)
705 		*media = new_media;
706 	return (0);
707 err_ret:
708 	sdp_free_media(new_media);
709 	return (ret);
710 }
711 
712 /*
713  * This internal API is required by sdp_session_to_str(). It determines the
714  * length of buffer that is required to hold the session. Since the RFC does
715  * not limit the size of various sub-fields in the field. We need to scan
716  * through the structure to determine the length.
717  */
718 int
719 sdp_get_length(const sdp_session_t *session)
720 {
721 	int			len = 0;
722 	char			buf[1];
723 	sdp_list_t		*list;
724 	sdp_conn_t		*conn;
725 	sdp_bandwidth_t		*bw;
726 	sdp_zone_t		*zone;
727 	sdp_time_t		*time;
728 	sdp_repeat_t		*repeat;
729 	sdp_attr_t		*attr;
730 	sdp_media_t		*media;
731 
732 	len += FIELD_EQUALS_CRLF_LEN;
733 	len += snprintf(buf, 1, "%d", session->s_version);
734 	if (session->s_origin != NULL) {
735 		len += FIELD_EQUALS_CRLF_LEN;
736 		len += strlen(session->s_origin->o_username);
737 		len += snprintf(buf, 1, "%llu", session->s_origin->o_id) + 1;
738 		len += snprintf(buf, 1, "%llu", session->s_origin->o_version)
739 		    + 1;
740 		len += strlen(session->s_origin->o_nettype) + 1;
741 		len += strlen(session->s_origin->o_addrtype) + 1;
742 		len += strlen(session->s_origin->o_address) + 1;
743 	}
744 	if (session->s_name != NULL)
745 		len += strlen(session->s_name) + FIELD_EQUALS_CRLF_LEN;
746 	if (session->s_info != NULL)
747 		len += strlen(session->s_info) + FIELD_EQUALS_CRLF_LEN;
748 	if (session->s_uri != NULL)
749 		len += strlen(session->s_uri) + FIELD_EQUALS_CRLF_LEN;
750 	list = session->s_email;
751 	while (list != NULL) {
752 		len += strlen((char *)list->value) + FIELD_EQUALS_CRLF_LEN;
753 		list = list->next;
754 	}
755 	list = session->s_phone;
756 	while (list != NULL) {
757 		len += strlen((char *)list->value) + FIELD_EQUALS_CRLF_LEN;
758 		list = list->next;
759 	}
760 	conn = session->s_conn;
761 	SDP_LEN_CONNECTION(conn);
762 	bw = session->s_bw;
763 	SDP_LEN_BANDWIDTH(bw);
764 	time = session->s_time;
765 	while (time != NULL) {
766 		len += FIELD_EQUALS_CRLF_LEN;
767 		len += snprintf(buf, 1, "%llu", time->t_start);
768 		len += snprintf(buf, 1, "%llu", time->t_stop) + 1;
769 		repeat = time->t_repeat;
770 		while (repeat != NULL) {
771 			len += FIELD_EQUALS_CRLF_LEN;
772 			len += snprintf(buf, 1, "%llu", repeat->r_interval);
773 			len += snprintf(buf, 1, "%llu", repeat->r_duration) + 1;
774 			list = repeat->r_offset;
775 			while (list != NULL) {
776 				len += snprintf(buf, 1, "%llu",
777 				    *(uint64_t *)list->value) + 1;
778 				list = list->next;
779 			}
780 			repeat = repeat->r_next;
781 		}
782 		time = time->t_next;
783 	}
784 	if (session->s_zone != NULL)
785 		len += FIELD_EQUALS_CRLF_LEN;
786 	zone = session->s_zone;
787 	while (zone != NULL) {
788 		len += snprintf(buf, 1, "%llu", zone->z_time) + 1;
789 		len += strlen(zone->z_offset) + 1;
790 		zone = zone->z_next;
791 	}
792 	SDP_LEN_KEY(session->s_key);
793 	attr = session->s_attr;
794 	SDP_LEN_ATTRIBUTE(attr);
795 	media = session->s_media;
796 	while (media != NULL) {
797 		len += FIELD_EQUALS_CRLF_LEN;
798 		len += strlen(media->m_name);
799 		len += snprintf(buf, 1, "%u", media->m_port) + 1;
800 		len += snprintf(buf, 1, "%d", media->m_portcount) + 1;
801 		len += strlen(media->m_proto) + 1;
802 		list = media->m_format;
803 		while (list != NULL) {
804 			len += strlen((char *)list->value) + 1;
805 			list = list->next;
806 		}
807 		if (media->m_info != NULL)
808 			len += strlen(media->m_info) + FIELD_EQUALS_CRLF_LEN;
809 		conn = media->m_conn;
810 		SDP_LEN_CONNECTION(conn);
811 		bw = media->m_bw;
812 		SDP_LEN_BANDWIDTH(bw);
813 		SDP_LEN_KEY(media->m_key);
814 		attr = media->m_attr;
815 		SDP_LEN_ATTRIBUTE(attr);
816 		media = media->m_next;
817 	}
818 	return (len);
819 }
820 
821 /*
822  * Given a session structure it clones (deep copy) and returns the cloned copy
823  */
824 sdp_session_t *
825 sdp_clone_session(const sdp_session_t *session)
826 {
827 	sdp_session_t		*new_sess;
828 	sdp_origin_t		*origin;
829 	sdp_list_t		*list;
830 	sdp_time_t		*time;
831 	sdp_time_t		*new_time;
832 	sdp_repeat_t		*repeat;
833 	sdp_media_t		*media;
834 	sdp_media_t		*new_media;
835 	sdp_conn_t		*conn;
836 	sdp_bandwidth_t		*bw;
837 	sdp_attr_t		*attr;
838 	sdp_zone_t		*zone;
839 	char			*offset = NULL;
840 	char			*format = NULL;
841 
842 	if (session == NULL)
843 		return (NULL);
844 	new_sess = calloc(1, sizeof (sdp_session_t));
845 	if (new_sess == NULL)
846 		return (NULL);
847 	new_sess->sdp_session_version = session->sdp_session_version;
848 	new_sess->s_version = session->s_version;
849 	origin = session->s_origin;
850 	if (origin != NULL && (sdp_add_origin(new_sess, origin->o_username,
851 	    origin->o_id, origin->o_version, origin->o_nettype, origin->
852 	    o_addrtype, origin->o_address) != 0)) {
853 		goto err_ret;
854 	}
855 	if (session->s_name != NULL && sdp_add_name(new_sess, session->
856 	    s_name) != 0) {
857 		goto err_ret;
858 	}
859 	if (session->s_info != NULL && sdp_add_information(&new_sess->
860 	    s_info, session->s_info) != 0) {
861 		goto err_ret;
862 	}
863 	if (session->s_uri != NULL && sdp_add_uri(new_sess, session->
864 	    s_uri) != 0) {
865 		goto err_ret;
866 	}
867 	list = session->s_email;
868 	while (list != NULL) {
869 		if (sdp_add_email(new_sess, (char *)list->value) != 0)
870 			goto err_ret;
871 		list = list->next;
872 	}
873 	list = session->s_phone;
874 	while (list != NULL) {
875 		if (sdp_add_phone(new_sess, (char *)list->value) != 0)
876 			goto err_ret;
877 		list = list->next;
878 	}
879 	conn = session->s_conn;
880 	SDP_ADD_CONNECTION(new_sess->s_conn, conn);
881 	bw = session->s_bw;
882 	SDP_ADD_BANDWIDTH(new_sess->s_bw, bw);
883 	time = session->s_time;
884 	while (time != NULL) {
885 		if (sdp_add_time(new_sess, time->t_start, time->t_stop,
886 		    &new_time) != 0) {
887 			goto err_ret;
888 		}
889 		repeat = time->t_repeat;
890 		while (repeat != NULL) {
891 			if (sdp_list_to_str(repeat->r_offset, &offset,
892 			    B_FALSE) != 0) {
893 				goto err_ret;
894 			}
895 			if (sdp_add_repeat(new_time, repeat->r_interval,
896 			    repeat->r_duration, offset) != 0) {
897 				free(offset);
898 				goto err_ret;
899 			}
900 			free(offset);
901 			repeat = repeat->r_next;
902 		}
903 		time = time->t_next;
904 	}
905 	zone = session->s_zone;
906 	while (zone != NULL) {
907 		if (sdp_add_zone(new_sess, zone->z_time, zone->z_offset) != 0)
908 			goto err_ret;
909 		zone = zone->z_next;
910 	}
911 	SDP_ADD_KEY(new_sess->s_key, session->s_key);
912 	attr = session->s_attr;
913 	SDP_ADD_ATTRIBUTE(new_sess->s_attr, attr);
914 	media = session->s_media;
915 	while (media != NULL) {
916 		if (sdp_list_to_str(media->m_format, &format, B_TRUE) != 0)
917 			goto err_ret;
918 		if (sdp_add_media(new_sess, media->m_name,
919 		    media->m_port, media->m_portcount, media->m_proto,
920 		    format, &new_media) != 0) {
921 			free(format);
922 			goto err_ret;
923 		}
924 		free(format);
925 		if (media->m_info != NULL) {
926 			if (sdp_add_information(&new_media->m_info,
927 			    media->m_info) != 0) {
928 				goto err_ret;
929 			}
930 		}
931 		conn = media->m_conn;
932 		SDP_ADD_CONNECTION(new_media->m_conn, conn);
933 		bw = media->m_bw;
934 		SDP_ADD_BANDWIDTH(new_media->m_bw, bw);
935 		SDP_ADD_KEY(new_media->m_key, media->m_key);
936 		attr = media->m_attr;
937 		SDP_ADD_ATTRIBUTE(new_media->m_attr, attr);
938 		new_media->m_session = new_sess;
939 		media = media->m_next;
940 	}
941 	return (new_sess);
942 err_ret:
943 	sdp_free_session(new_sess);
944 	return (NULL);
945 }
946 
947 /*
948  * should i check if individual members are NULL, if not snprintf
949  * will core dump.
950  */
951 /*
952  * Given a session structure, this API converts it into character
953  * buffer, which will be used as a payload later on.
954  */
955 char *
956 sdp_session_to_str(const sdp_session_t *session, int *error)
957 {
958 	char			*ret = NULL;
959 	char			*buf = NULL;
960 	int			len = 0;
961 	int			s_len = 0;
962 	int			wrote = 0;
963 	sdp_origin_t		*origin;
964 	sdp_list_t		*list;
965 	sdp_conn_t		*conn;
966 	sdp_attr_t		*attr;
967 	sdp_bandwidth_t		*bw;
968 	sdp_time_t		*time;
969 	sdp_repeat_t		*repeat;
970 	sdp_zone_t		*zone;
971 	sdp_media_t		*media;
972 
973 	if (error != NULL)
974 		*error = 0;
975 	if (session == NULL) {
976 		if (error != NULL)
977 			*error = EINVAL;
978 		return (NULL);
979 	}
980 	s_len = sdp_get_length(session);
981 	ret = malloc(s_len + 1);
982 	if (ret == NULL) {
983 		if (error != NULL)
984 			*error = ENOMEM;
985 		return (NULL);
986 	}
987 	buf = ret;
988 	len = s_len + 1;
989 	wrote = snprintf(buf, len, "v=%d%s", session->s_version, COMMP_CRLF);
990 	len = len - wrote;
991 	buf = buf + wrote;
992 	origin = session->s_origin;
993 	if (origin != NULL) {
994 		wrote = snprintf(buf, len, "o=%s %llu %llu %s %s %s%s",
995 		    origin->o_username, origin->o_id, origin->o_version,
996 		    origin->o_nettype, origin->o_addrtype, origin->o_address,
997 		    COMMP_CRLF);
998 		len = len - wrote;
999 		buf = buf + wrote;
1000 	}
1001 	if (session->s_name != NULL) {
1002 		wrote = snprintf(buf, len, "s=%s%s", session->s_name,
1003 		    COMMP_CRLF);
1004 		len = len - wrote;
1005 		buf = buf + wrote;
1006 	}
1007 	SDP_INFORMATION_TO_STR(session->s_info);
1008 	if (session->s_uri != NULL) {
1009 		wrote = snprintf(buf, len, "u=%s%s", session->s_uri,
1010 		    COMMP_CRLF);
1011 		len = len - wrote;
1012 		buf = buf + wrote;
1013 	}
1014 	list = session->s_email;
1015 	while (list != NULL) {
1016 		wrote = snprintf(buf, len, "e=%s%s", (char *)list->value,
1017 		    COMMP_CRLF);
1018 		len = len - wrote;
1019 		buf = buf + wrote;
1020 		list = list->next;
1021 	}
1022 	list = session->s_phone;
1023 	while (list != NULL) {
1024 		wrote = snprintf(buf, len, "p=%s%s", (char *)list->value,
1025 		    COMMP_CRLF);
1026 		len = len - wrote;
1027 		buf = buf + wrote;
1028 		list = list->next;
1029 	}
1030 	conn = session->s_conn;
1031 	SDP_CONNECTION_TO_STR(conn);
1032 	bw = session->s_bw;
1033 	SDP_BANDWIDTH_TO_STR(bw);
1034 	time = session->s_time;
1035 	while (time != NULL) {
1036 		wrote = snprintf(buf, len, "t=%llu %llu%s", time->t_start,
1037 		    time->t_stop, COMMP_CRLF);
1038 		len = len - wrote;
1039 		buf = buf + wrote;
1040 		repeat = time->t_repeat;
1041 		while (repeat != NULL) {
1042 			wrote = snprintf(buf, len, "r=%llu %llu", repeat->
1043 			    r_interval, repeat->r_duration);
1044 			len = len - wrote;
1045 			buf = buf + wrote;
1046 			list = repeat->r_offset;
1047 			while (list != NULL) {
1048 				wrote = snprintf(buf, len, " %llu",
1049 				    *(uint64_t *)list->value);
1050 				len = len - wrote;
1051 				buf = buf + wrote;
1052 				list = list->next;
1053 			}
1054 			wrote = snprintf(buf, len, "%s", COMMP_CRLF);
1055 			len = len - wrote;
1056 			buf = buf + wrote;
1057 			repeat = repeat->r_next;
1058 		}
1059 		time = time->t_next;
1060 	}
1061 	zone = session->s_zone;
1062 	if (zone != NULL) {
1063 		wrote = snprintf(buf, len, "z=%llu %s", zone->z_time,
1064 		    zone->z_offset);
1065 		len = len - wrote;
1066 		buf = buf + wrote;
1067 		zone = zone->z_next;
1068 		while (zone != NULL) {
1069 			wrote = snprintf(buf, len, " %llu %s", zone->z_time,
1070 			    zone->z_offset);
1071 			len = len - wrote;
1072 			buf = buf + wrote;
1073 			zone = zone->z_next;
1074 		}
1075 		wrote = snprintf(buf, len, "%s", COMMP_CRLF);
1076 		len = len - wrote;
1077 		buf = buf + wrote;
1078 	}
1079 	SDP_KEY_TO_STR(session->s_key);
1080 	attr = session->s_attr;
1081 	SDP_ATTR_TO_STR(attr);
1082 	media = session->s_media;
1083 	while (media != NULL) {
1084 		if (media->m_portcount == 1) {
1085 			wrote = snprintf(buf, len, "m=%s %d %s", media->m_name,
1086 			    media->m_port, media->m_proto);
1087 		} else {
1088 			wrote = snprintf(buf, len, "m=%s %d/%d %s", media->
1089 			    m_name, media->m_port, media->m_portcount, media->
1090 			    m_proto);
1091 		}
1092 		len = len - wrote;
1093 		buf = buf + wrote;
1094 		list = media->m_format;
1095 		while (list != NULL) {
1096 			wrote = snprintf(buf, len, " %s", (char *)list->value);
1097 			len = len - wrote;
1098 			buf = buf + wrote;
1099 			list = list->next;
1100 		}
1101 		wrote = snprintf(buf, len, "%s", COMMP_CRLF);
1102 		len = len - wrote;
1103 		buf = buf + wrote;
1104 		SDP_INFORMATION_TO_STR(media->m_info);
1105 		conn = media->m_conn;
1106 		SDP_CONNECTION_TO_STR(conn);
1107 		bw = media->m_bw;
1108 		SDP_BANDWIDTH_TO_STR(bw);
1109 		SDP_KEY_TO_STR(media->m_key);
1110 		attr = media->m_attr;
1111 		SDP_ATTR_TO_STR(attr);
1112 		media = media->m_next;
1113 	}
1114 	assert(len >= 1);
1115 	*buf = '\0';
1116 	return (ret);
1117 }
1118 
1119 /*
1120  * Given a session structure and the field ('v', 'o', 's', et al), this API
1121  * deletes the corresponding structure element. It frees the memory and sets the
1122  * pointer to NULL
1123  */
1124 int
1125 sdp_delete_all_field(sdp_session_t *session, const char field)
1126 {
1127 	if (session == NULL)
1128 		return (EINVAL);
1129 	switch (field) {
1130 		case SDP_ORIGIN_FIELD:
1131 			sdp_free_origin(session->s_origin);
1132 			session->s_origin = NULL;
1133 			break;
1134 		case SDP_NAME_FIELD:
1135 			free(session->s_name);
1136 			session->s_name = NULL;
1137 			break;
1138 		case SDP_INFO_FIELD:
1139 			free(session->s_info);
1140 			session->s_info = NULL;
1141 			break;
1142 		case SDP_URI_FIELD:
1143 			free(session->s_uri);
1144 			session->s_uri = NULL;
1145 			break;
1146 		case SDP_EMAIL_FIELD:
1147 			sdp_free_list(session->s_email);
1148 			session->s_email = NULL;
1149 			break;
1150 		case SDP_PHONE_FIELD:
1151 			sdp_free_list(session->s_phone);
1152 			session->s_phone = NULL;
1153 			break;
1154 		case SDP_CONNECTION_FIELD:
1155 			sdp_free_connection(session->s_conn);
1156 			session->s_conn = NULL;
1157 			break;
1158 		case SDP_BANDWIDTH_FIELD:
1159 			sdp_free_bandwidth(session->s_bw);
1160 			session->s_bw = NULL;
1161 			break;
1162 		case SDP_TIME_FIELD:
1163 			sdp_free_time(session->s_time);
1164 			session->s_time = NULL;
1165 			break;
1166 		case SDP_ZONE_FIELD:
1167 			sdp_free_zone(session->s_zone);
1168 			session->s_zone = NULL;
1169 			break;
1170 		case SDP_KEY_FIELD:
1171 			sdp_free_key(session->s_key);
1172 			session->s_key = NULL;
1173 			break;
1174 		case SDP_ATTRIBUTE_FIELD:
1175 			sdp_free_attribute(session->s_attr);
1176 			session->s_attr = NULL;
1177 			break;
1178 		case SDP_MEDIA_FIELD:
1179 			sdp_free_media(session->s_media);
1180 			session->s_media = NULL;
1181 			break;
1182 		default:
1183 			return (EINVAL);
1184 	}
1185 	return (0);
1186 }
1187 
1188 /*
1189  * Given a media structure and the field ('i', 'b', 'c', et al), this API
1190  * deletes the corresponding structure element. It frees the memory and sets
1191  * the pointer to NULL.
1192  */
1193 int
1194 sdp_delete_all_media_field(sdp_media_t *media, const char field)
1195 {
1196 	if (media == NULL)
1197 		return (EINVAL);
1198 	switch (field) {
1199 		case SDP_INFO_FIELD:
1200 			free(media->m_info);
1201 			media->m_info = NULL;
1202 			break;
1203 		case SDP_CONNECTION_FIELD:
1204 			sdp_free_connection(media->m_conn);
1205 			media->m_conn = NULL;
1206 			break;
1207 		case SDP_BANDWIDTH_FIELD:
1208 			sdp_free_bandwidth(media->m_bw);
1209 			media->m_bw = NULL;
1210 			break;
1211 		case SDP_KEY_FIELD:
1212 			sdp_free_key(media->m_key);
1213 			media->m_key = NULL;
1214 			break;
1215 		case SDP_ATTRIBUTE_FIELD:
1216 			sdp_free_attribute(media->m_attr);
1217 			media->m_attr = NULL;
1218 			break;
1219 		default:
1220 			return (EINVAL);
1221 	}
1222 	return (0);
1223 }
1224 
1225 /*
1226  * Given a media list and the media, this API deletes that media from the
1227  * list. It frees the memory corresponding to that media.
1228  */
1229 int
1230 sdp_delete_media(sdp_media_t **l_media, sdp_media_t *media)
1231 {
1232 	sdp_media_t		*cur;
1233 	sdp_media_t		*prev;
1234 
1235 	if (l_media == NULL || *l_media == NULL || media == NULL)
1236 		return (EINVAL);
1237 	cur = *l_media;
1238 	prev = NULL;
1239 	while (cur != NULL && cur != media) {
1240 		prev = cur;
1241 		cur = cur->m_next;
1242 	}
1243 	if (cur == NULL)
1244 		return (EINVAL);
1245 	if (cur == *l_media)
1246 		*l_media = cur->m_next;
1247 	else
1248 		prev->m_next = cur->m_next;
1249 	cur->m_next = NULL;
1250 	sdp_free_media(cur);
1251 	return (0);
1252 }
1253 
1254 /*
1255  * Given an attribute list and an attribute, this API deletes that attribue
1256  * from the list. It frees the memory corresponding to that attribute.
1257  */
1258 int
1259 sdp_delete_attribute(sdp_attr_t **l_attr, sdp_attr_t *attr)
1260 {
1261 	sdp_attr_t		*cur;
1262 	sdp_attr_t		*prev;
1263 
1264 	if (l_attr == NULL || *l_attr == NULL || attr == NULL)
1265 		return (EINVAL);
1266 	cur = *l_attr;
1267 	prev = NULL;
1268 	while (cur != NULL && cur != attr) {
1269 		prev = cur;
1270 		cur = cur->a_next;
1271 	}
1272 	if (cur == NULL)
1273 		return (EINVAL);
1274 	if (cur == *l_attr)
1275 		*l_attr = cur->a_next;
1276 	else
1277 		prev->a_next = cur->a_next;
1278 	cur->a_next = NULL;
1279 	sdp_free_attribute(cur);
1280 	return (0);
1281 }
1282 
1283 /*
1284  * Allocates a new sdp session structure and assigns a version number to it.
1285  * Currently one version is defined and it is 1. This will be useful in future
1286  * in the unlikely need to change the structure.
1287  */
1288 sdp_session_t *
1289 sdp_new_session()
1290 {
1291 	sdp_session_t	*session = NULL;
1292 
1293 	session = calloc(1, sizeof (sdp_session_t));
1294 	if (session != NULL)
1295 		session->sdp_session_version = SDP_SESSION_VERSION_1;
1296 	return (session);
1297 }
1298