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 *
sdp_find_media(sdp_media_t * media,const char * name)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 *
sdp_find_attribute(sdp_attr_t * attr,const char * name)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 *
sdp_find_media_rtpmap(sdp_media_t * media,const char * format)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
sdp_add_origin(sdp_session_t * session,const char * name,uint64_t id,uint64_t ver,const char * nettype,const char * addrtype,const char * address)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
sdp_add_name(sdp_session_t * session,const char * name)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
sdp_add_information(char ** information,const char * value)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
sdp_add_uri(sdp_session_t * session,const char * uri)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
sdp_add_email(sdp_session_t * session,const char * email)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
sdp_add_phone(sdp_session_t * session,const char * phone)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
sdp_add_connection(sdp_conn_t ** conn,const char * nettype,const char * addrtype,const char * address,uint8_t ttl,int addrcount)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
sdp_add_bandwidth(sdp_bandwidth_t ** bw,const char * type,uint64_t value)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
sdp_add_time(sdp_session_t * session,uint64_t starttime,uint64_t stoptime,sdp_time_t ** time)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
sdp_add_repeat(sdp_time_t * time,uint64_t interval,uint64_t duration,const char * offset)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
sdp_add_zone(sdp_session_t * session,uint64_t time,const char * offset)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
sdp_add_key(sdp_key_t ** key,const char * method,const char * enckey)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
sdp_add_attribute(sdp_attr_t ** attr,const char * name,const char * value)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
sdp_add_media(sdp_session_t * session,const char * name,uint_t port,int portcount,const char * protocol,const char * fmt,sdp_media_t ** media)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
sdp_get_length(const sdp_session_t * session)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 *
sdp_clone_session(const sdp_session_t * session)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 *
sdp_session_to_str(const sdp_session_t * session,int * error)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
sdp_delete_all_field(sdp_session_t * session,const char field)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
sdp_delete_all_media_field(sdp_media_t * media,const char field)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
sdp_delete_media(sdp_media_t ** l_media,sdp_media_t * media)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
sdp_delete_attribute(sdp_attr_t ** l_attr,sdp_attr_t * attr)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 *
sdp_new_session()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