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