# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.lib HDRS = sdp.h HDRDIR = common # Hammerhead: amd64-only SUBDIRS = $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install .KEEP_STATE: all clean clobber install: $(SUBDIRS) install_h: $(ROOTHDRS) check: $(CHECKHDRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include ../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # LIBRARY = libcommputil.a VERS = .1 OBJECTS = commp_util.o sdp_parse_helper.o sdp.o sdp_parse.o include ../../Makefile.lib SRCDIR = ../common LIBS = $(DYNLIB) LDLIBS += -lc CFLAGS += $(CCVERBOSE) CPPFLAGS += -DNDEBUG .KEEP_STATE: all: $(LIBS) include ../../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Helper functions to skip white spaces, find tokens, find separators and free * memory. */ #include #include #include #include #include "commp_util.h" /* * Skip to the next non-whitespace */ int commp_skip_white_space(const char **begin, const char *end) { while (*begin < end) { if (!isspace(**begin)) return (0); (*begin)++; } return (1); } /* * Finds the token in the char buffer. *current will be pointing to the * token when function returns. If the char buffer has leading token, * it returns 1. */ int commp_find_token(const char **begin, const char **current, const char *end, char token, boolean_t last) { *current = *begin; while (*current < end) { if (!last && (**current == token)) break; else if (isspace(**current)) return (1); (*current)++; } /* Checks for leading white space */ if (*current == *begin) return (1); else return (0); } /* * atoi function */ int commp_atoi(const char *begin, const char *end, int *num) { boolean_t num_found = B_FALSE; *num = 0; while (begin < end) { if (isdigit(*begin)) { *num = (*num * 10) + (*begin - '0'); num_found = B_TRUE; begin++; } else { break; } } if (!num_found || (begin != end)) return (EINVAL); return (0); } /* * Given a string converts it to unsigned long long int. */ int commp_strtoull(const char *begin, const char *end, uint64_t *num) { boolean_t num_found = B_FALSE; *num = 0; while (begin < end) { if (isdigit(*begin)) { *num = (*num * 10) + (*begin - '0'); num_found = B_TRUE; begin++; } else { break; } } if (!num_found || (begin != end)) return (EINVAL); return (0); } /* * Given a string converts it to unsigned byte */ int commp_strtoub(const char *begin, const char *end, uint8_t *num) { boolean_t num_found = B_FALSE; *num = 0; while (begin < end) { if (isdigit(*begin)) { *num = (*num * 10) + (*begin - '0'); num_found = B_TRUE; begin++; } else { break; } } if (!num_found || (begin != end)) return (EINVAL); return (0); } /* * Given a string converts it to unsigned int */ int commp_atoui(const char *begin, const char *end, uint_t *num) { boolean_t num_found = B_FALSE; *num = 0; while (begin < end) { if (isdigit(*begin)) { *num = (*num * 10) + (*begin - '0'); num_found = B_TRUE; begin++; } else { break; } } if (!num_found || (begin != end)) return (EINVAL); return (0); } /* * allocates memory and copies string to new memory */ int commp_add_str(char **dest, const char *src, int len) { if (len == 0) return (EINVAL); (*dest) = calloc(1, len + 1); if (*dest == NULL) return (ENOMEM); (void) strncpy(*dest, src, len); return (0); } /* * This function converts strings like "5d" to equivalent time in secs. * For eg. 1h = 3600, 10d = 86400 */ int commp_time_to_secs(const char *begin, const char *end, uint64_t *num) { uint_t factor = 0; if (!isdigit(*(end - 1))) { switch (*(end - 1)) { case 'd': factor = COMMP_SECS_IN_DAY; break; case 'h': factor = COMMP_SECS_IN_HOUR; break; case 'm': factor = COMMP_SECS_IN_MIN; break; case 's': factor = 1; break; default: return (EINVAL); } --end; } if (commp_strtoull(begin, end, num) != 0) return (EINVAL); if (factor != 0) (*num) = (*num) * factor; return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _COMMP_UTIL_H #define _COMMP_UTIL_H #ifdef __cplusplus extern "C" { #endif #include #define COMMP_CRLF "\r\n" #define COMMP_LF "\n" #define COMMP_SKIP_CRLF(msg_ptr) ((msg_ptr) = (msg_ptr) + 2) #define COMMP_SKIP_LF(msg_ptr) ((msg_ptr) = (msg_ptr) + 1) #define COMMP_SECS_IN_DAY 86400 #define COMMP_SECS_IN_HOUR 3600 #define COMMP_SECS_IN_MIN 60 #define COMMP_SP ' ' #define COMMP_CR '\r' #define COMMP_COLON ':' #define COMMP_SLASH '/' #define COMMP_EQUALS '=' #define COMMP_ADDRTYPE_IP4 "IP4" #define COMMP_ADDRTYPE_IP6 "IP6" #define COMMP_COPY_STR(dst, src, len) { \ (dst) = calloc(1, (len) + 1); \ if ((dst) != NULL) { \ (void) strncpy((dst), (src), (len)); \ } \ } extern int commp_skip_white_space(const char **, const char *); extern int commp_find_token(const char **, const char **, const char *, char, boolean_t); extern int commp_atoi(const char *, const char *, int *); extern int commp_strtoull(const char *, const char *, uint64_t *); extern int commp_strtoub(const char *, const char *, uint8_t *); extern int commp_atoui(const char *, const char *, uint_t *); extern int commp_time_to_secs(const char *, const char *, uint64_t *); extern int commp_add_str(char **, const char *, int); #ifdef __cplusplus } #endif #endif /* _COMMP_UTIL_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: sdp_parse; sdp_find_media; sdp_find_attribute; sdp_find_media_rtpmap; sdp_clone_session; sdp_new_session; sdp_add_origin; sdp_add_name; sdp_add_information; sdp_add_uri; sdp_add_email; sdp_add_phone; sdp_add_connection; sdp_add_bandwidth; sdp_add_repeat; sdp_add_time; sdp_add_zone; sdp_add_key; sdp_add_attribute; sdp_add_media; sdp_session_to_str; sdp_delete_all_field; sdp_delete_all_media_field; sdp_delete_media; sdp_delete_attribute; sdp_free_session; local: *; }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Contains implementation of various interfaces exported by library */ #include #include #include #include #include #include #include #include "sdp_parse.h" #include "commp_util.h" #define FIELD_EQUALS_CRLF_LEN 4 /* first two characters and CRLF */ #define SDP_ATTR_TO_STR(m_attr) { \ while ((m_attr) != NULL) { \ if ((m_attr)->a_value != NULL) { \ wrote = snprintf(buf, len, "a=%s%c%s%s", \ (m_attr)->a_name, COMMP_COLON, (m_attr)-> \ a_value, COMMP_CRLF); \ } else { \ wrote = snprintf(buf, len, "a=%s%s", (m_attr)-> \ a_name, COMMP_CRLF); \ } \ len = len - wrote; \ buf = buf + wrote; \ (m_attr) = (m_attr)->a_next; \ } \ } #define SDP_KEY_TO_STR(m_key) { \ if ((m_key) != NULL) { \ if ((m_key)->k_enckey != NULL) { \ wrote = snprintf(buf, len, "k=%s%c%s%s", \ (m_key)->k_method, COMMP_COLON, (m_key)-> \ k_enckey, COMMP_CRLF); \ } else { \ wrote = snprintf(buf, len, "k=%s%s", (m_key)-> \ k_method, COMMP_CRLF); \ } \ len = len - wrote; \ buf = buf + wrote; \ } \ } #define SDP_BANDWIDTH_TO_STR(m_bw) { \ while ((m_bw) != NULL) { \ wrote = snprintf(buf, len, "b=%s%c%llu%s", (m_bw)-> \ b_type, COMMP_COLON, (m_bw)->b_value, COMMP_CRLF); \ len = len - wrote; \ buf = buf + wrote; \ (m_bw) = (m_bw)->b_next; \ } \ } #define SDP_INFORMATION_TO_STR(m_info) { \ if ((m_info) != NULL) { \ wrote = snprintf(buf, len, "i=%s%s", (m_info), COMMP_CRLF); \ len = len - wrote; \ buf = buf + wrote; \ } \ } #define SDP_CONNECTION_TO_STR(m_conn) { \ while ((m_conn) != NULL) { \ if (strcasecmp((m_conn)->c_addrtype, \ COMMP_ADDRTYPE_IP4) == 0) { \ if ((m_conn)->c_addrcount > 1) { \ wrote = snprintf(buf, len, "c=%s %s %s/%d/%d" \ "%s", (m_conn)->c_nettype, (m_conn)-> \ c_addrtype, (m_conn)->c_address, (m_conn)->\ c_ttl, (m_conn)->c_addrcount, COMMP_CRLF); \ } else if ((m_conn)->c_addrcount == 1) { \ wrote = snprintf(buf, len, "c=%s %s %s/%d%s", \ (m_conn)->c_nettype, (m_conn)->c_addrtype, \ (m_conn)->c_address, (m_conn)->c_ttl, \ COMMP_CRLF); \ } else { \ wrote = snprintf(buf, len, "c=%s %s %s%s", \ (m_conn)->c_nettype, (m_conn)->c_addrtype, \ (m_conn)->c_address, COMMP_CRLF); \ } \ } else if (strcasecmp((m_conn)->c_addrtype, \ COMMP_ADDRTYPE_IP6) == 0) { \ if ((m_conn)->c_addrcount <= 1) { \ wrote = snprintf(buf, len, "c=%s %s %s%s", \ (m_conn)->c_nettype, (m_conn)->c_addrtype, \ (m_conn)->c_address, COMMP_CRLF); \ } else { \ wrote = snprintf(buf, len, "c=%s %s %s/%d%s", \ (m_conn)->c_nettype, (m_conn)->c_addrtype, \ (m_conn)->c_address, (m_conn)->c_addrcount,\ COMMP_CRLF); \ } \ } else { \ wrote = snprintf(buf, len, "c=%s %s %s%s", (m_conn)-> \ c_nettype, (m_conn)->c_addrtype, (m_conn)-> \ c_address, COMMP_CRLF); \ } \ len = len - wrote; \ buf = buf + wrote; \ (m_conn) = (m_conn)->c_next; \ } \ } #define SDP_ADD_KEY(d_key, s_key) { \ if ((s_key) != NULL) { \ if (sdp_add_key(&(d_key), (s_key)->k_method, \ (s_key)->k_enckey) != 0) { \ sdp_free_session(new_sess); \ return (NULL); \ } \ } \ } #define SDP_ADD_ATTRIBUTE(d_attr, s_attr) { \ while ((s_attr) != NULL) { \ if (sdp_add_attribute(&(d_attr), (s_attr)->a_name, \ (s_attr)->a_value) != 0) { \ sdp_free_session(new_sess); \ return (NULL); \ } \ (s_attr) = (s_attr)->a_next; \ } \ } #define SDP_ADD_BANDWIDTH(d_bw, s_bw) { \ while ((s_bw) != NULL) { \ if (sdp_add_bandwidth(&(d_bw), (s_bw)->b_type, \ (s_bw)->b_value) != 0) { \ sdp_free_session(new_sess); \ return (NULL); \ } \ (s_bw) = (s_bw)->b_next; \ } \ } #define SDP_ADD_CONNECTION(d_conn, s_conn) { \ while ((s_conn) != NULL) { \ if (sdp_add_connection(&(d_conn), (s_conn)->c_nettype, \ (s_conn)->c_addrtype, (s_conn)->c_address, \ (s_conn)->c_ttl, (s_conn)->c_addrcount) != 0) { \ sdp_free_session(new_sess); \ return (NULL); \ } \ (s_conn) = (s_conn)->c_next; \ } \ } #define SDP_LEN_CONNECTION(m_conn) { \ while ((m_conn) != NULL) { \ len += FIELD_EQUALS_CRLF_LEN; \ len += strlen((m_conn)->c_nettype); \ len += strlen((m_conn)->c_addrtype) + 1; \ len += strlen((m_conn)->c_address) + 1; \ len += snprintf(buf, 1, "%u", (m_conn)->c_ttl) + 1; \ len += snprintf(buf, 1, "%d", (m_conn)->c_addrcount) + 1; \ (m_conn) = (m_conn)->c_next; \ } \ } #define SDP_LEN_BANDWIDTH(m_bw) { \ while ((m_bw) != NULL) { \ len += FIELD_EQUALS_CRLF_LEN; \ len += strlen((m_bw)->b_type); \ len += snprintf(buf, 1, "%llu", (m_bw)->b_value) + 1; \ (m_bw) = (m_bw)->b_next; \ } \ } #define SDP_LEN_KEY(m_key) { \ if ((m_key) != NULL) { \ len += FIELD_EQUALS_CRLF_LEN; \ len += strlen((m_key)->k_method); \ if ((m_key)->k_enckey != NULL) \ len += strlen((m_key)->k_enckey) + 1; \ } \ } #define SDP_LEN_ATTRIBUTE(m_attr) { \ while ((m_attr) != NULL) { \ len += FIELD_EQUALS_CRLF_LEN; \ len += strlen((m_attr)->a_name); \ if ((m_attr)->a_value != NULL) \ len += strlen((m_attr)->a_value) + 1; \ (m_attr) = (m_attr)->a_next; \ } \ } /* * Given a media list and media name ("audio", "video", et al), it searches * the list for that media. Returns NULL if media not present. */ sdp_media_t * sdp_find_media(sdp_media_t *media, const char *name) { if (media == NULL || name == NULL || (strlen(name) == 0)) { return (NULL); } while (media != NULL) { if (media->m_name != NULL) { if (strcasecmp(name, media->m_name) == 0) return (media); } media = media->m_next; } return (media); } /* * Given a attribute list and name of the attribute ("rtpmap", "fmtp", et al), * this API searches the list for that attribute. Returns NULL if not found. */ sdp_attr_t * sdp_find_attribute(sdp_attr_t *attr, const char *name) { if (attr == NULL || name == NULL || (strlen(name) == 0)) { return (NULL); } while (attr != NULL) { if (attr->a_name != NULL) { if (strcasecmp(attr->a_name, name) == 0) return (attr); } attr = attr->a_next; } return (attr); } /* * Given a media list and a format number, this API will return the rtpmap * attribute matching the format number. */ sdp_attr_t * sdp_find_media_rtpmap(sdp_media_t *media, const char *format) { sdp_attr_t *attr = NULL; char *tmp = NULL; if (media == NULL || format == NULL || (strlen(format) == 0)) { return (NULL); } attr = media->m_attr; while (attr != NULL) { if (attr->a_name != NULL && (strcasecmp(attr->a_name, SDP_RTPMAP) == 0)) { if (attr->a_value != NULL) { tmp = attr->a_value; while (isspace(*tmp)) ++tmp; if (strncasecmp(tmp, format, strlen(format)) == 0) { return (attr); } } } attr = attr->a_next; } return (attr); } /* * Adds origin field to the session. * o= */ 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) { sdp_origin_t *origin; int ret = 0; if (session == NULL || name == NULL || nettype == NULL || addrtype == NULL || address == NULL) { return (EINVAL); } if (session->s_origin != NULL) return (EPROTO); origin = calloc(1, sizeof (sdp_origin_t)); if (origin == NULL) return (ENOMEM); origin->o_id = id; origin->o_version = ver; if ((ret = commp_add_str(&origin->o_username, name, strlen(name))) != 0) goto err_ret; if ((ret = commp_add_str(&origin->o_nettype, nettype, strlen(nettype))) != 0) { goto err_ret; } if ((ret = commp_add_str(&origin->o_addrtype, addrtype, strlen(addrtype))) != 0) { goto err_ret; } if ((ret = commp_add_str(&origin->o_address, address, strlen(address))) != 0) { goto err_ret; } session->s_origin = origin; return (ret); err_ret: sdp_free_origin(origin); return (ret); } /* * Adds session name field to the session. * s= */ int sdp_add_name(sdp_session_t *session, const char *name) { if (session == NULL || name == NULL) return (EINVAL); if (session->s_name != NULL) return (EPROTO); return (commp_add_str(&session->s_name, name, strlen(name))); } /* * Adds session information field to the session or media section of SDP. * i= */ int sdp_add_information(char **information, const char *value) { if (information == NULL || value == NULL) return (EINVAL); if (*information != NULL) return (EPROTO); return (commp_add_str(information, value, strlen(value))); } /* * Adds uri field to the session. * u= */ int sdp_add_uri(sdp_session_t *session, const char *uri) { if (session == NULL || uri == NULL) return (EINVAL); if (session->s_uri != NULL) return (EPROTO); return (commp_add_str(&session->s_uri, uri, strlen(uri))); } /* * Adds email address field to the session. * e= */ int sdp_add_email(sdp_session_t *session, const char *email) { if (session == NULL || email == NULL || (strlen(email) == 0)) return (EINVAL); return (add_value_to_list(&session->s_email, email, strlen(email), B_TRUE)); } /* * Adds phone number field to the session. * p= */ int sdp_add_phone(sdp_session_t *session, const char *phone) { if (session == NULL || phone == NULL || (strlen(phone) == 0)) return (EINVAL); return (add_value_to_list(&session->s_phone, phone, strlen(phone), B_TRUE)); } /* * Adds connection field to the session or media section of SDP * c= [/ttl]/ */ int sdp_add_connection(sdp_conn_t **conn, const char *nettype, const char *addrtype, const char *address, uint8_t ttl, int addrcount) { sdp_conn_t *tmp; sdp_conn_t *new_conn; int ret = 0; if (conn == NULL || nettype == NULL || addrtype == NULL || address == NULL) { return (EINVAL); } new_conn = calloc(1, sizeof (sdp_conn_t)); if (new_conn == NULL) return (ENOMEM); new_conn->c_ttl = ttl; new_conn->c_addrcount = addrcount; if ((ret = commp_add_str(&new_conn->c_nettype, nettype, strlen(nettype))) != 0) { goto err_ret; } if ((ret = commp_add_str(&new_conn->c_addrtype, addrtype, strlen(addrtype))) != 0) { goto err_ret; } if ((ret = commp_add_str(&new_conn->c_address, address, strlen(address))) != 0) { goto err_ret; } if (*conn == NULL) { *conn = new_conn; } else { tmp = *conn; while (tmp->c_next != NULL) tmp = tmp->c_next; tmp->c_next = new_conn; } return (ret); err_ret: sdp_free_connection(new_conn); return (ret); } /* * Adds bandwidth field to the session or media section of SDP. * b=: */ int sdp_add_bandwidth(sdp_bandwidth_t **bw, const char *type, uint64_t value) { sdp_bandwidth_t *new_bw; sdp_bandwidth_t *tmp; int ret = 0; if (bw == NULL || type == NULL) return (EINVAL); new_bw = calloc(1, sizeof (sdp_bandwidth_t)); if (new_bw == NULL) return (ENOMEM); new_bw->b_value = value; if ((ret = commp_add_str(&new_bw->b_type, type, strlen(type))) != 0) { free(new_bw); return (ret); } if (*bw == NULL) { *bw = new_bw; } else { tmp = *bw; while (tmp->b_next != NULL) tmp = tmp->b_next; tmp->b_next = new_bw; } return (ret); } /* * Adds time field to the session * t= */ int sdp_add_time(sdp_session_t *session, uint64_t starttime, uint64_t stoptime, sdp_time_t **time) { sdp_time_t *new_time; sdp_time_t *tmp; if (time != NULL) *time = NULL; if (session == NULL) { return (EINVAL); } new_time = calloc(1, sizeof (sdp_time_t)); if (new_time == NULL) { return (ENOMEM); } new_time->t_start = starttime; new_time->t_stop = stoptime; tmp = session->s_time; if (tmp == NULL) session->s_time = new_time; else { while (tmp->t_next != NULL) tmp = tmp->t_next; tmp->t_next = new_time; } if (time != NULL) *time = new_time; return (0); } /* * Adds repeat field to the time structure of session * r= */ int sdp_add_repeat(sdp_time_t *time, uint64_t interval, uint64_t duration, const char *offset) { sdp_repeat_t *tmp; sdp_repeat_t *new_repeat; int ret = 0; if (time == NULL || offset == NULL) return (EINVAL); new_repeat = calloc(1, sizeof (sdp_repeat_t)); if (new_repeat == NULL) return (ENOMEM); new_repeat->r_interval = interval; new_repeat->r_duration = duration; if ((ret = sdp_str_to_list(&new_repeat->r_offset, offset, strlen(offset), B_FALSE)) != 0) { goto err_ret; } tmp = time->t_repeat; if (tmp == NULL) { time->t_repeat = new_repeat; } else { while (tmp->r_next != NULL) tmp = tmp->r_next; tmp->r_next = new_repeat; } return (ret); err_ret: sdp_free_repeat(new_repeat); return (ret); } /* * Adds time zone field to the session * z= .... */ int sdp_add_zone(sdp_session_t *session, uint64_t time, const char *offset) { sdp_zone_t *new_zone; sdp_zone_t *tmp; int ret = 0; if (session == NULL || offset == NULL) return (EINVAL); new_zone = calloc(1, sizeof (sdp_zone_t)); if (new_zone == NULL) return (ENOMEM); new_zone->z_time = time; if ((ret = commp_add_str(&new_zone->z_offset, offset, strlen(offset))) != 0) { free(new_zone); return (ret); } tmp = session->s_zone; if (tmp == NULL) { session->s_zone = new_zone; } else { while (tmp->z_next != NULL) { tmp = tmp->z_next; } tmp->z_next = new_zone; } return (ret); } /* * Adds key field to session or media section of SDP. * k= * k=: */ int sdp_add_key(sdp_key_t **key, const char *method, const char *enckey) { int ret = 0; if (key == NULL || method == NULL) return (EINVAL); if (*key != NULL) return (EPROTO); *key = calloc(1, sizeof (sdp_key_t)); if (*key == NULL) return (ENOMEM); if ((ret = commp_add_str(&((*key)->k_method), method, strlen(method))) != 0) { goto err_ret; } if (enckey != NULL) { if ((ret = commp_add_str(&((*key)->k_enckey), enckey, strlen(enckey))) != 0) { goto err_ret; } } return (ret); err_ret: sdp_free_key(*key); *key = NULL; return (ret); } /* * Adds attribute field to session or media section of SDP. * a= * a=: */ int sdp_add_attribute(sdp_attr_t **attr, const char *name, const char *value) { sdp_attr_t *tmp; sdp_attr_t *new_attr; int ret = 0; if (attr == NULL || name == NULL) return (EINVAL); new_attr = calloc(1, sizeof (sdp_attr_t)); if (new_attr == NULL) return (ENOMEM); if ((ret = commp_add_str(&new_attr->a_name, name, strlen(name))) != 0) goto err_ret; if (value != NULL) { if ((ret = commp_add_str(&new_attr->a_value, value, strlen(value))) != 0) { goto err_ret; } } tmp = *attr; if (tmp == NULL) { *attr = new_attr; } else { while (tmp->a_next != NULL) tmp = tmp->a_next; tmp->a_next = new_attr; } return (ret); err_ret: sdp_free_attribute(new_attr); return (ret); } /* * Adds media field to the session. * m= [/portcount] ... */ 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) { sdp_media_t *tmp; sdp_media_t *new_media; int ret = 0; if (media != NULL) *media = NULL; if (session == NULL || name == NULL || protocol == NULL || portcount <= 0 || fmt == NULL) { return (EINVAL); } new_media = calloc(1, sizeof (sdp_media_t)); if (new_media == NULL) { return (ENOMEM); } new_media->m_session = session; new_media->m_port = port; new_media->m_portcount = portcount; if ((ret = commp_add_str(&new_media->m_name, name, strlen(name))) != 0) goto err_ret; if ((ret = commp_add_str(&new_media->m_proto, protocol, strlen(protocol))) != 0) { goto err_ret; } if ((ret = sdp_str_to_list(&new_media->m_format, fmt, strlen(fmt), B_TRUE)) != 0) { goto err_ret; } tmp = session->s_media; if (tmp == NULL) { session->s_media = new_media; } else { while (tmp->m_next != NULL) tmp = tmp->m_next; tmp->m_next = new_media; } if (media != NULL) *media = new_media; return (0); err_ret: sdp_free_media(new_media); return (ret); } /* * This internal API is required by sdp_session_to_str(). It determines the * length of buffer that is required to hold the session. Since the RFC does * not limit the size of various sub-fields in the field. We need to scan * through the structure to determine the length. */ int sdp_get_length(const sdp_session_t *session) { int len = 0; char buf[1]; sdp_list_t *list; sdp_conn_t *conn; sdp_bandwidth_t *bw; sdp_zone_t *zone; sdp_time_t *time; sdp_repeat_t *repeat; sdp_attr_t *attr; sdp_media_t *media; len += FIELD_EQUALS_CRLF_LEN; len += snprintf(buf, 1, "%d", session->s_version); if (session->s_origin != NULL) { len += FIELD_EQUALS_CRLF_LEN; len += strlen(session->s_origin->o_username); len += snprintf(buf, 1, "%llu", session->s_origin->o_id) + 1; len += snprintf(buf, 1, "%llu", session->s_origin->o_version) + 1; len += strlen(session->s_origin->o_nettype) + 1; len += strlen(session->s_origin->o_addrtype) + 1; len += strlen(session->s_origin->o_address) + 1; } if (session->s_name != NULL) len += strlen(session->s_name) + FIELD_EQUALS_CRLF_LEN; if (session->s_info != NULL) len += strlen(session->s_info) + FIELD_EQUALS_CRLF_LEN; if (session->s_uri != NULL) len += strlen(session->s_uri) + FIELD_EQUALS_CRLF_LEN; list = session->s_email; while (list != NULL) { len += strlen((char *)list->value) + FIELD_EQUALS_CRLF_LEN; list = list->next; } list = session->s_phone; while (list != NULL) { len += strlen((char *)list->value) + FIELD_EQUALS_CRLF_LEN; list = list->next; } conn = session->s_conn; SDP_LEN_CONNECTION(conn); bw = session->s_bw; SDP_LEN_BANDWIDTH(bw); time = session->s_time; while (time != NULL) { len += FIELD_EQUALS_CRLF_LEN; len += snprintf(buf, 1, "%llu", time->t_start); len += snprintf(buf, 1, "%llu", time->t_stop) + 1; repeat = time->t_repeat; while (repeat != NULL) { len += FIELD_EQUALS_CRLF_LEN; len += snprintf(buf, 1, "%llu", repeat->r_interval); len += snprintf(buf, 1, "%llu", repeat->r_duration) + 1; list = repeat->r_offset; while (list != NULL) { len += snprintf(buf, 1, "%llu", *(uint64_t *)list->value) + 1; list = list->next; } repeat = repeat->r_next; } time = time->t_next; } if (session->s_zone != NULL) len += FIELD_EQUALS_CRLF_LEN; zone = session->s_zone; while (zone != NULL) { len += snprintf(buf, 1, "%llu", zone->z_time) + 1; len += strlen(zone->z_offset) + 1; zone = zone->z_next; } SDP_LEN_KEY(session->s_key); attr = session->s_attr; SDP_LEN_ATTRIBUTE(attr); media = session->s_media; while (media != NULL) { len += FIELD_EQUALS_CRLF_LEN; len += strlen(media->m_name); len += snprintf(buf, 1, "%u", media->m_port) + 1; len += snprintf(buf, 1, "%d", media->m_portcount) + 1; len += strlen(media->m_proto) + 1; list = media->m_format; while (list != NULL) { len += strlen((char *)list->value) + 1; list = list->next; } if (media->m_info != NULL) len += strlen(media->m_info) + FIELD_EQUALS_CRLF_LEN; conn = media->m_conn; SDP_LEN_CONNECTION(conn); bw = media->m_bw; SDP_LEN_BANDWIDTH(bw); SDP_LEN_KEY(media->m_key); attr = media->m_attr; SDP_LEN_ATTRIBUTE(attr); media = media->m_next; } return (len); } /* * Given a session structure it clones (deep copy) and returns the cloned copy */ sdp_session_t * sdp_clone_session(const sdp_session_t *session) { sdp_session_t *new_sess; sdp_origin_t *origin; sdp_list_t *list; sdp_time_t *time; sdp_time_t *new_time; sdp_repeat_t *repeat; sdp_media_t *media; sdp_media_t *new_media; sdp_conn_t *conn; sdp_bandwidth_t *bw; sdp_attr_t *attr; sdp_zone_t *zone; char *offset = NULL; char *format = NULL; if (session == NULL) return (NULL); new_sess = calloc(1, sizeof (sdp_session_t)); if (new_sess == NULL) return (NULL); new_sess->sdp_session_version = session->sdp_session_version; new_sess->s_version = session->s_version; origin = session->s_origin; if (origin != NULL && (sdp_add_origin(new_sess, origin->o_username, origin->o_id, origin->o_version, origin->o_nettype, origin-> o_addrtype, origin->o_address) != 0)) { goto err_ret; } if (session->s_name != NULL && sdp_add_name(new_sess, session-> s_name) != 0) { goto err_ret; } if (session->s_info != NULL && sdp_add_information(&new_sess-> s_info, session->s_info) != 0) { goto err_ret; } if (session->s_uri != NULL && sdp_add_uri(new_sess, session-> s_uri) != 0) { goto err_ret; } list = session->s_email; while (list != NULL) { if (sdp_add_email(new_sess, (char *)list->value) != 0) goto err_ret; list = list->next; } list = session->s_phone; while (list != NULL) { if (sdp_add_phone(new_sess, (char *)list->value) != 0) goto err_ret; list = list->next; } conn = session->s_conn; SDP_ADD_CONNECTION(new_sess->s_conn, conn); bw = session->s_bw; SDP_ADD_BANDWIDTH(new_sess->s_bw, bw); time = session->s_time; while (time != NULL) { if (sdp_add_time(new_sess, time->t_start, time->t_stop, &new_time) != 0) { goto err_ret; } repeat = time->t_repeat; while (repeat != NULL) { if (sdp_list_to_str(repeat->r_offset, &offset, B_FALSE) != 0) { goto err_ret; } if (sdp_add_repeat(new_time, repeat->r_interval, repeat->r_duration, offset) != 0) { free(offset); goto err_ret; } free(offset); repeat = repeat->r_next; } time = time->t_next; } zone = session->s_zone; while (zone != NULL) { if (sdp_add_zone(new_sess, zone->z_time, zone->z_offset) != 0) goto err_ret; zone = zone->z_next; } SDP_ADD_KEY(new_sess->s_key, session->s_key); attr = session->s_attr; SDP_ADD_ATTRIBUTE(new_sess->s_attr, attr); media = session->s_media; while (media != NULL) { if (sdp_list_to_str(media->m_format, &format, B_TRUE) != 0) goto err_ret; if (sdp_add_media(new_sess, media->m_name, media->m_port, media->m_portcount, media->m_proto, format, &new_media) != 0) { free(format); goto err_ret; } free(format); if (media->m_info != NULL) { if (sdp_add_information(&new_media->m_info, media->m_info) != 0) { goto err_ret; } } conn = media->m_conn; SDP_ADD_CONNECTION(new_media->m_conn, conn); bw = media->m_bw; SDP_ADD_BANDWIDTH(new_media->m_bw, bw); SDP_ADD_KEY(new_media->m_key, media->m_key); attr = media->m_attr; SDP_ADD_ATTRIBUTE(new_media->m_attr, attr); new_media->m_session = new_sess; media = media->m_next; } return (new_sess); err_ret: sdp_free_session(new_sess); return (NULL); } /* * should i check if individual members are NULL, if not snprintf * will core dump. */ /* * Given a session structure, this API converts it into character * buffer, which will be used as a payload later on. */ char * sdp_session_to_str(const sdp_session_t *session, int *error) { char *ret = NULL; char *buf = NULL; int len = 0; int s_len = 0; int wrote = 0; sdp_origin_t *origin; sdp_list_t *list; sdp_conn_t *conn; sdp_attr_t *attr; sdp_bandwidth_t *bw; sdp_time_t *time; sdp_repeat_t *repeat; sdp_zone_t *zone; sdp_media_t *media; if (error != NULL) *error = 0; if (session == NULL) { if (error != NULL) *error = EINVAL; return (NULL); } s_len = sdp_get_length(session); ret = malloc(s_len + 1); if (ret == NULL) { if (error != NULL) *error = ENOMEM; return (NULL); } buf = ret; len = s_len + 1; wrote = snprintf(buf, len, "v=%d%s", session->s_version, COMMP_CRLF); len = len - wrote; buf = buf + wrote; origin = session->s_origin; if (origin != NULL) { wrote = snprintf(buf, len, "o=%s %llu %llu %s %s %s%s", origin->o_username, origin->o_id, origin->o_version, origin->o_nettype, origin->o_addrtype, origin->o_address, COMMP_CRLF); len = len - wrote; buf = buf + wrote; } if (session->s_name != NULL) { wrote = snprintf(buf, len, "s=%s%s", session->s_name, COMMP_CRLF); len = len - wrote; buf = buf + wrote; } SDP_INFORMATION_TO_STR(session->s_info); if (session->s_uri != NULL) { wrote = snprintf(buf, len, "u=%s%s", session->s_uri, COMMP_CRLF); len = len - wrote; buf = buf + wrote; } list = session->s_email; while (list != NULL) { wrote = snprintf(buf, len, "e=%s%s", (char *)list->value, COMMP_CRLF); len = len - wrote; buf = buf + wrote; list = list->next; } list = session->s_phone; while (list != NULL) { wrote = snprintf(buf, len, "p=%s%s", (char *)list->value, COMMP_CRLF); len = len - wrote; buf = buf + wrote; list = list->next; } conn = session->s_conn; SDP_CONNECTION_TO_STR(conn); bw = session->s_bw; SDP_BANDWIDTH_TO_STR(bw); time = session->s_time; while (time != NULL) { wrote = snprintf(buf, len, "t=%llu %llu%s", time->t_start, time->t_stop, COMMP_CRLF); len = len - wrote; buf = buf + wrote; repeat = time->t_repeat; while (repeat != NULL) { wrote = snprintf(buf, len, "r=%llu %llu", repeat-> r_interval, repeat->r_duration); len = len - wrote; buf = buf + wrote; list = repeat->r_offset; while (list != NULL) { wrote = snprintf(buf, len, " %llu", *(uint64_t *)list->value); len = len - wrote; buf = buf + wrote; list = list->next; } wrote = snprintf(buf, len, "%s", COMMP_CRLF); len = len - wrote; buf = buf + wrote; repeat = repeat->r_next; } time = time->t_next; } zone = session->s_zone; if (zone != NULL) { wrote = snprintf(buf, len, "z=%llu %s", zone->z_time, zone->z_offset); len = len - wrote; buf = buf + wrote; zone = zone->z_next; while (zone != NULL) { wrote = snprintf(buf, len, " %llu %s", zone->z_time, zone->z_offset); len = len - wrote; buf = buf + wrote; zone = zone->z_next; } wrote = snprintf(buf, len, "%s", COMMP_CRLF); len = len - wrote; buf = buf + wrote; } SDP_KEY_TO_STR(session->s_key); attr = session->s_attr; SDP_ATTR_TO_STR(attr); media = session->s_media; while (media != NULL) { if (media->m_portcount == 1) { wrote = snprintf(buf, len, "m=%s %d %s", media->m_name, media->m_port, media->m_proto); } else { wrote = snprintf(buf, len, "m=%s %d/%d %s", media-> m_name, media->m_port, media->m_portcount, media-> m_proto); } len = len - wrote; buf = buf + wrote; list = media->m_format; while (list != NULL) { wrote = snprintf(buf, len, " %s", (char *)list->value); len = len - wrote; buf = buf + wrote; list = list->next; } wrote = snprintf(buf, len, "%s", COMMP_CRLF); len = len - wrote; buf = buf + wrote; SDP_INFORMATION_TO_STR(media->m_info); conn = media->m_conn; SDP_CONNECTION_TO_STR(conn); bw = media->m_bw; SDP_BANDWIDTH_TO_STR(bw); SDP_KEY_TO_STR(media->m_key); attr = media->m_attr; SDP_ATTR_TO_STR(attr); media = media->m_next; } assert(len >= 1); *buf = '\0'; return (ret); } /* * Given a session structure and the field ('v', 'o', 's', et al), this API * deletes the corresponding structure element. It frees the memory and sets the * pointer to NULL */ int sdp_delete_all_field(sdp_session_t *session, const char field) { if (session == NULL) return (EINVAL); switch (field) { case SDP_ORIGIN_FIELD: sdp_free_origin(session->s_origin); session->s_origin = NULL; break; case SDP_NAME_FIELD: free(session->s_name); session->s_name = NULL; break; case SDP_INFO_FIELD: free(session->s_info); session->s_info = NULL; break; case SDP_URI_FIELD: free(session->s_uri); session->s_uri = NULL; break; case SDP_EMAIL_FIELD: sdp_free_list(session->s_email); session->s_email = NULL; break; case SDP_PHONE_FIELD: sdp_free_list(session->s_phone); session->s_phone = NULL; break; case SDP_CONNECTION_FIELD: sdp_free_connection(session->s_conn); session->s_conn = NULL; break; case SDP_BANDWIDTH_FIELD: sdp_free_bandwidth(session->s_bw); session->s_bw = NULL; break; case SDP_TIME_FIELD: sdp_free_time(session->s_time); session->s_time = NULL; break; case SDP_ZONE_FIELD: sdp_free_zone(session->s_zone); session->s_zone = NULL; break; case SDP_KEY_FIELD: sdp_free_key(session->s_key); session->s_key = NULL; break; case SDP_ATTRIBUTE_FIELD: sdp_free_attribute(session->s_attr); session->s_attr = NULL; break; case SDP_MEDIA_FIELD: sdp_free_media(session->s_media); session->s_media = NULL; break; default: return (EINVAL); } return (0); } /* * Given a media structure and the field ('i', 'b', 'c', et al), this API * deletes the corresponding structure element. It frees the memory and sets * the pointer to NULL. */ int sdp_delete_all_media_field(sdp_media_t *media, const char field) { if (media == NULL) return (EINVAL); switch (field) { case SDP_INFO_FIELD: free(media->m_info); media->m_info = NULL; break; case SDP_CONNECTION_FIELD: sdp_free_connection(media->m_conn); media->m_conn = NULL; break; case SDP_BANDWIDTH_FIELD: sdp_free_bandwidth(media->m_bw); media->m_bw = NULL; break; case SDP_KEY_FIELD: sdp_free_key(media->m_key); media->m_key = NULL; break; case SDP_ATTRIBUTE_FIELD: sdp_free_attribute(media->m_attr); media->m_attr = NULL; break; default: return (EINVAL); } return (0); } /* * Given a media list and the media, this API deletes that media from the * list. It frees the memory corresponding to that media. */ int sdp_delete_media(sdp_media_t **l_media, sdp_media_t *media) { sdp_media_t *cur; sdp_media_t *prev; if (l_media == NULL || *l_media == NULL || media == NULL) return (EINVAL); cur = *l_media; prev = NULL; while (cur != NULL && cur != media) { prev = cur; cur = cur->m_next; } if (cur == NULL) return (EINVAL); if (cur == *l_media) *l_media = cur->m_next; else prev->m_next = cur->m_next; cur->m_next = NULL; sdp_free_media(cur); return (0); } /* * Given an attribute list and an attribute, this API deletes that attribue * from the list. It frees the memory corresponding to that attribute. */ int sdp_delete_attribute(sdp_attr_t **l_attr, sdp_attr_t *attr) { sdp_attr_t *cur; sdp_attr_t *prev; if (l_attr == NULL || *l_attr == NULL || attr == NULL) return (EINVAL); cur = *l_attr; prev = NULL; while (cur != NULL && cur != attr) { prev = cur; cur = cur->a_next; } if (cur == NULL) return (EINVAL); if (cur == *l_attr) *l_attr = cur->a_next; else prev->a_next = cur->a_next; cur->a_next = NULL; sdp_free_attribute(cur); return (0); } /* * Allocates a new sdp session structure and assigns a version number to it. * Currently one version is defined and it is 1. This will be useful in future * in the unlikely need to change the structure. */ sdp_session_t * sdp_new_session() { sdp_session_t *session = NULL; session = calloc(1, sizeof (sdp_session_t)); if (session != NULL) session->sdp_session_version = SDP_SESSION_VERSION_1; return (session); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SDP_H #define _SDP_H #ifdef __cplusplus extern "C" { #endif #include #define SDP_VERSION_FIELD 'v' #define SDP_ORIGIN_FIELD 'o' #define SDP_NAME_FIELD 's' #define SDP_INFO_FIELD 'i' #define SDP_URI_FIELD 'u' #define SDP_EMAIL_FIELD 'e' #define SDP_PHONE_FIELD 'p' #define SDP_CONNECTION_FIELD 'c' #define SDP_BANDWIDTH_FIELD 'b' #define SDP_TIME_FIELD 't' #define SDP_REPEAT_FIELD 'r' #define SDP_ZONE_FIELD 'z' #define SDP_KEY_FIELD 'k' #define SDP_ATTRIBUTE_FIELD 'a' #define SDP_MEDIA_FIELD 'm' /* SDP Parse errors */ #define SDP_VERSION_ERROR 0x00000001 #define SDP_ORIGIN_ERROR 0x00000002 #define SDP_NAME_ERROR 0x00000004 #define SDP_INFO_ERROR 0x00000008 #define SDP_URI_ERROR 0x00000010 #define SDP_EMAIL_ERROR 0x00000020 #define SDP_PHONE_ERROR 0x00000040 #define SDP_CONNECTION_ERROR 0x00000080 #define SDP_BANDWIDTH_ERROR 0x00000100 #define SDP_TIME_ERROR 0x00000200 #define SDP_REPEAT_TIME_ERROR 0x00000400 #define SDP_ZONE_ERROR 0x00000800 #define SDP_KEY_ERROR 0x00001000 #define SDP_ATTRIBUTE_ERROR 0x00002000 #define SDP_MEDIA_ERROR 0x00004000 #define SDP_FIELDS_ORDER_ERROR 0x00008000 #define SDP_MISSING_FIELDS 0x00010000 #define SDP_AUDIO "audio" #define SDP_VIDEO "video" #define SDP_TEXT "text" #define SDP_APPLICATION "application" #define SDP_MESSAGE "message" #define SDP_RTPMAP "rtpmap" #define SDP_SESSION_VERSION_1 1 typedef struct sdp_list { void *value; struct sdp_list *next; } sdp_list_t; /* * SDP origin field. * o= */ typedef struct sdp_origin { char *o_username; uint64_t o_id; uint64_t o_version; char *o_nettype; char *o_addrtype; char *o_address; } sdp_origin_t; /* * SDP connection field. * c= [/ttl]/ */ typedef struct sdp_conn { char *c_nettype; char *c_addrtype; char *c_address; int c_addrcount; struct sdp_conn *c_next; uint8_t c_ttl; } sdp_conn_t; /* * SDP repeat field. Always found in time structure. * r= */ typedef struct sdp_repeat { uint64_t r_interval; uint64_t r_duration; sdp_list_t *r_offset; struct sdp_repeat *r_next; } sdp_repeat_t; /* * SDP time field. * t= */ typedef struct sdp_time { uint64_t t_start; uint64_t t_stop; sdp_repeat_t *t_repeat; struct sdp_time *t_next; } sdp_time_t; /* * SDP time zone field. * z= .... */ typedef struct sdp_zone { uint64_t z_time; char *z_offset; struct sdp_zone *z_next; } sdp_zone_t; /* * SDP attribute field. * a= or a=: */ typedef struct sdp_attr { char *a_name; char *a_value; struct sdp_attr *a_next; } sdp_attr_t; /* * SDP bandwidth field. * b=: */ typedef struct sdp_bandwidth { char *b_type; uint64_t b_value; struct sdp_bandwidth *b_next; } sdp_bandwidth_t; /* * SDP key field to session or media section of SDP. * k= or k=: */ typedef struct sdp_key { char *k_method; char *k_enckey; } sdp_key_t; typedef struct sdp_session sdp_session_t; /* * SDP media section, contains media fields and other fields within * media section. * m= [/portcount] ... */ typedef struct sdp_media { char *m_name; uint_t m_port; int m_portcount; char *m_proto; sdp_list_t *m_format; char *m_info; sdp_conn_t *m_conn; sdp_bandwidth_t *m_bw; sdp_key_t *m_key; sdp_attr_t *m_attr; struct sdp_media *m_next; sdp_session_t *m_session; } sdp_media_t; struct sdp_session { int sdp_session_version; int s_version; sdp_origin_t *s_origin; char *s_name; char *s_info; char *s_uri; sdp_list_t *s_email; sdp_list_t *s_phone; sdp_conn_t *s_conn; sdp_bandwidth_t *s_bw; sdp_time_t *s_time; sdp_zone_t *s_zone; sdp_key_t *s_key; sdp_attr_t *s_attr; sdp_media_t *s_media; }; extern int sdp_parse(const char *, int, int, sdp_session_t **, uint_t *); extern sdp_media_t *sdp_find_media(sdp_media_t *, const char *); extern sdp_attr_t *sdp_find_attribute(sdp_attr_t *, const char *); extern sdp_attr_t *sdp_find_media_rtpmap(sdp_media_t *, const char *); extern sdp_session_t *sdp_clone_session(const sdp_session_t *); extern sdp_session_t *sdp_new_session(); extern int sdp_add_origin(sdp_session_t *, const char *, uint64_t, uint64_t, const char *, const char *, const char *); extern int sdp_add_name(sdp_session_t *, const char *); extern int sdp_add_information(char **, const char *); extern int sdp_add_uri(sdp_session_t *, const char *); extern int sdp_add_email(sdp_session_t *, const char *); extern int sdp_add_phone(sdp_session_t *, const char *); extern int sdp_add_connection(sdp_conn_t **, const char *, const char *, const char *, uint8_t, int); extern int sdp_add_bandwidth(sdp_bandwidth_t **, const char *, uint64_t); extern int sdp_add_repeat(sdp_time_t *, uint64_t, uint64_t, const char *); extern int sdp_add_time(sdp_session_t *, uint64_t, uint64_t, sdp_time_t **); extern int sdp_add_zone(sdp_session_t *, uint64_t, const char *); extern int sdp_add_key(sdp_key_t **, const char *, const char *); extern int sdp_add_attribute(sdp_attr_t **, const char *, const char *); extern int sdp_add_media(sdp_session_t *, const char *, uint_t, int, const char *, const char *, sdp_media_t **); extern int sdp_delete_all_field(sdp_session_t *, const char); extern int sdp_delete_all_media_field(sdp_media_t *, const char); extern int sdp_delete_media(sdp_media_t **, sdp_media_t *); extern int sdp_delete_attribute(sdp_attr_t **, sdp_attr_t *); extern void sdp_free_session(sdp_session_t *); extern char *sdp_session_to_str(const sdp_session_t *, int *); #ifdef __cplusplus } #endif #endif /* _SDP_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Parses the SDP description as per the SDP grammar defined in Section 9 of * RFC 4566 */ #include #include #include #include #include #include "sdp_parse.h" #include "commp_util.h" /* * proto-version-field (v=) * %x76 "=" 1*DIGIT CRLF */ static void sdp_parse_version(int *version, const char *begin, const char *end, uint_t *p_error) { if (*begin++ != COMMP_EQUALS || commp_atoi(begin, end, version) != 0) *p_error |= SDP_VERSION_ERROR; } /* * session-name-field (s=) * %x73 "=" text CRLF * text = byte-string * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF) * ;any byte except NUL, CR, or LF */ static void sdp_parse_name(char **name, const char *begin, const char *end, uint_t *p_error) { int len; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_NAME_ERROR; return; } /* there can be only one name field */ if (*name != NULL) return; len = end - begin; if (len < 1) { *p_error |= SDP_NAME_ERROR; } else { COMMP_COPY_STR(*name, begin, len); if (*name == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } } } /* * information-field (i=) * [%x69 "=" text CRLF] * text = byte-string * byte-string = 1*(%x01-09/%x0B-0C/%x0E-FF) * any byte except NUL, CR, or LF */ static void sdp_parse_info(char **info, const char *begin, const char *end, uint_t *p_error) { int len; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_INFO_ERROR; return; } /* There can be only one info field */ if (*info != NULL) return; len = end - begin; if (len < 1) { *p_error |= SDP_INFO_ERROR; } else { COMMP_COPY_STR(*info, begin, len); if (*info == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } } } /* * uri-field (u=) * [%x75 "=" uri CRLF] * anything between "=" and "CRLF" is considered to be URI. */ static void sdp_parse_uri(char **uri, const char *begin, const char *end, uint_t *p_error) { int len; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_URI_ERROR; return; } /* There can be only one uri field */ if (*uri != NULL) return; len = end - begin; if (len < 1 || isspace(*begin) || isspace (*(end - 1))) { *p_error |= SDP_URI_ERROR; } else { COMMP_COPY_STR(*uri, begin, len); if (*uri == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } } } /* * phone-fields (p=) * *(%x70 "=" phone-number CRLF) * anything between "=" and "CRLF" is considered to be phone-number */ static void sdp_parse_phone(sdp_list_t **phone, const char *begin, const char *end, uint_t *p_error) { int len; sdp_list_t *new_phone = NULL; sdp_list_t *tmp = NULL; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_PHONE_ERROR; return; } len = end - begin; if (len < 1 || isspace(*begin) || isspace(*(end - 1))) { *p_error |= SDP_PHONE_ERROR; } else { new_phone = calloc(1, sizeof (sdp_list_t)); if (new_phone == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } COMMP_COPY_STR(new_phone->value, begin, len); if (new_phone->value == NULL) { free(new_phone); *p_error |= SDP_MEMORY_ERROR; return; } if (*phone == NULL) { *phone = new_phone; } else { tmp = *phone; while (tmp->next != NULL) tmp = tmp->next; tmp->next = new_phone; } } } /* * email-fields (e=) * *(%x65 "=" email-address CRLF) * anything between "=" and "CRLF" is considered to be email-address */ static void sdp_parse_email(sdp_list_t **email, const char *begin, const char *end, uint_t *p_error) { int len; sdp_list_t *new_email = NULL; sdp_list_t *tmp = NULL; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_EMAIL_ERROR; return; } len = end - begin; if (len < 1 || isspace(*begin) || isspace(*(end - 1))) { *p_error |= SDP_EMAIL_ERROR; } else { new_email = calloc(1, sizeof (sdp_list_t)); if (new_email == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } COMMP_COPY_STR(new_email->value, begin, len); if (new_email->value == NULL) { free(new_email); *p_error |= SDP_MEMORY_ERROR; return; } if (*email == NULL) { *email = new_email; } else { tmp = *email; while (tmp->next != NULL) tmp = tmp->next; tmp->next = new_email; } } } /* * origin-field (o=) * %x6f "=" username SP sess-id SP sess-version SP nettype SP addrtype SP * unicast-address CRLF * * username = non-ws-string * sess-id = 1*DIGIT * sess-version = 1*DIGIT * nettype = token * addrtype = token * token = 1*(token-char) * token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E * i.e. no space in token-char */ static void sdp_parse_origin(sdp_origin_t **origin, const char *begin, const char *end, uint_t *p_error) { const char *current = NULL; sdp_origin_t *new_origin = NULL; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_ORIGIN_ERROR; return; } /* There can be only one origin field */ if (*origin != NULL) return; new_origin = calloc(1, sizeof (sdp_origin_t)); if (new_origin == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } /* Get username */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_origin->o_username, begin, current - begin); if (new_origin->o_username == NULL) { sdp_free_origin(new_origin); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get Session-ID */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; if (commp_strtoull(begin, current, &new_origin->o_id) != 0) goto err_ret; /* Get Version */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; if (commp_strtoull(begin, current, &new_origin->o_version) != 0) goto err_ret; /* Get nettype */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_origin->o_nettype, begin, current - begin); if (new_origin->o_nettype == NULL) { sdp_free_origin(new_origin); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get addrtype */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_origin->o_addrtype, begin, current - begin); if (new_origin->o_addrtype == NULL) { sdp_free_origin(new_origin); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get address. Its the last sub-field */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) goto err_ret; COMMP_COPY_STR(new_origin->o_address, begin, current - begin); if (new_origin->o_address == NULL) { sdp_free_origin(new_origin); *p_error |= SDP_MEMORY_ERROR; return; } *origin = new_origin; return; err_ret: *p_error |= SDP_ORIGIN_ERROR; sdp_free_origin(new_origin); } /* * time-fields (t=) * 1*( %x74 "=" start-time SP stop-time CRLF) * start-time = time / "0" * stop-time = time / "0" * time = POS-DIGIT 9*DIGIT * POS-DIGIT = %x31-39 ; 1 - 9 */ static sdp_time_t * sdp_parse_time(sdp_time_t **time, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_time_t *new_time; sdp_time_t *tmp; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_TIME_ERROR; return (NULL); } new_time = calloc(1, sizeof (sdp_time_t)); if (new_time == NULL) { *p_error |= SDP_MEMORY_ERROR; return (NULL); } /* Get start-time */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; if (commp_strtoull(begin, current, &new_time->t_start) != 0) goto err_ret; /* Get stop-time */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) goto err_ret; if (commp_strtoull(begin, current, &new_time->t_stop) != 0) goto err_ret; /* Now assign time to session structure */ if (*time == NULL) { *time = new_time; } else { tmp = *time; while (tmp->t_next != NULL) tmp = tmp->t_next; tmp->t_next = new_time; } return (new_time); err_ret: *p_error |= SDP_TIME_ERROR; sdp_free_time(new_time); return (NULL); } /* * connection-field (c=) * [%x63 "=" nettype SP addrtype SP connection-address CRLF] * nettype = token * addrtype = token * connection-address = multicast-address / unicast-address * here, connection-address is parsed as a string. */ static void sdp_parse_connection(sdp_conn_t **conn, const char *begin, const char *end, uint_t *p_error) { const char *current; const char *t_begin; const char *t_current; sdp_conn_t *new_conn; sdp_conn_t *tmp; boolean_t is_IP4 = B_FALSE; boolean_t is_IP6 = B_FALSE; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_CONNECTION_ERROR; return; } new_conn = calloc(1, sizeof (sdp_conn_t)); if (new_conn == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } /* Get NetworkType */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_conn->c_nettype, begin, current - begin); if (new_conn->c_nettype == NULL) { sdp_free_connection(new_conn); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get AddressType */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_conn->c_addrtype, begin, current - begin); if (new_conn->c_addrtype == NULL) { sdp_free_connection(new_conn); *p_error |= SDP_MEMORY_ERROR; return; } } if ((strlen(COMMP_ADDRTYPE_IP4) == strlen(new_conn->c_addrtype)) && (strncasecmp(new_conn->c_addrtype, COMMP_ADDRTYPE_IP4, strlen(COMMP_ADDRTYPE_IP4)) == 0)) { is_IP4 = B_TRUE; } else if ((strlen(COMMP_ADDRTYPE_IP6) == strlen(new_conn-> c_addrtype)) && (strncasecmp(new_conn->c_addrtype, COMMP_ADDRTYPE_IP6, strlen(COMMP_ADDRTYPE_IP6)) == 0)) { is_IP6 = B_TRUE; } /* Get Address. Parsing depends if its IP4,IP6 or something else */ begin = ++current; if (!is_IP4 && !is_IP6) { if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) { goto err_ret; } } else { if (commp_find_token(&begin, ¤t, end, COMMP_SLASH, B_FALSE) != 0) { goto err_ret; } if (current != end) { /* SLASH is present. Needs further parsing */ t_current = current; t_begin = ++t_current; if (commp_find_token(&t_begin, &t_current, end, COMMP_SLASH, B_FALSE) != 0) { goto err_ret; } if (t_current != end) { /* * Another SLASH present. If is_IP4 true then * this is Address count. If is_IP6 true then * incorrect field as per RFC. */ if (is_IP6) { goto err_ret; } else { if (commp_atoi((t_current + 1), end, &new_conn->c_addrcount) != 0) { goto err_ret; } } } if (is_IP6) { if (commp_atoi((current + 1), t_current, &new_conn->c_addrcount) != 0) { goto err_ret; } } else { if (commp_strtoub((current + 1), t_current, &new_conn->c_ttl) != 0) { goto err_ret; } if (new_conn->c_addrcount == 0) new_conn->c_addrcount = 1; } } } COMMP_COPY_STR(new_conn->c_address, begin, current - begin); if (new_conn->c_address == NULL) { sdp_free_connection(new_conn); *p_error |= SDP_MEMORY_ERROR; return; } if (*conn == NULL) { *conn = new_conn; } else { tmp = *conn; while (tmp->c_next != NULL) tmp = tmp->c_next; tmp->c_next = new_conn; } return; err_ret: *p_error |= SDP_CONNECTION_ERROR; sdp_free_connection(new_conn); } /* * bandwidth-fields (b=) * *(%x62 "=" bwtype ":" bandwidth CRLF) * bwtype = token * bandwidth = 1*DIGIT */ static void sdp_parse_bandwidth(sdp_bandwidth_t **bw, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_bandwidth_t *new_bw = NULL; sdp_bandwidth_t *tmp = NULL; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_BANDWIDTH_ERROR; return; } new_bw = calloc(1, sizeof (sdp_bandwidth_t)); if (new_bw == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_COLON, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_bw->b_type, begin, current - begin); if (new_bw->b_type == NULL) { sdp_free_bandwidth(new_bw); *p_error |= SDP_MEMORY_ERROR; return; } } if (current == end) goto err_ret; begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_TRUE) != 0) goto err_ret; if (commp_strtoull(begin, current, &new_bw->b_value) != 0) goto err_ret; if (*bw == NULL) { *bw = new_bw; } else { tmp = *bw; while (tmp->b_next != NULL) tmp = tmp->b_next; tmp->b_next = new_bw; } return; err_ret: *p_error |= SDP_BANDWIDTH_ERROR; sdp_free_bandwidth(new_bw); } /* * repeat-fields (r=) * Not stand-alone. One or more repeat field appear after time field. * %x72 "=" repeat-interval SP typed-time 1*(SP typed-time) * repeat-interval = POS-DIGIT *DIGIT [fixed-len-time-unit] * typed-time = 1*DIGIT [fixed-len-time-unit] * fixed-len-time-unit = %x64 / %x68 / %x6d / %x73 */ static void sdp_parse_repeat(sdp_time_t *time, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_repeat_t *repeat; sdp_repeat_t *new_repeat; int ret; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_REPEAT_TIME_ERROR; return; } /* * A time field should be present before this field can occur, if * time is NULL then repeat field has occured before time field and * hence fields are out of order. */ if (time == NULL) return; /* * Get the latest time field and associate this repeat field * with it. */ while (time->t_next != NULL) time = time->t_next; new_repeat = calloc(1, sizeof (sdp_repeat_t)); if (new_repeat == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } /* * for a given time field, there could be several repeat fields * add the new repeat field at the end of it. */ repeat = time->t_repeat; if (repeat == NULL) { time->t_repeat = new_repeat; } else { while (repeat->r_next != NULL) repeat = repeat->r_next; repeat->r_next = new_repeat; } /* * Populate the elements of sdp_repeat. * Get time-interval */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; if (commp_time_to_secs(begin, current, &new_repeat->r_interval) != 0) goto err_ret; /* Get duration. It could be the last sub-field */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; if (commp_time_to_secs(begin, current, &new_repeat->r_duration) != 0) goto err_ret; ++current; /* Get offsets into sdp_list */ if (current >= end) goto err_ret; while (current < end) { begin = current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } if ((ret = add_value_to_list(&new_repeat->r_offset, begin, current - begin, B_FALSE)) != 0) { if (ret == ENOMEM) { *p_error |= SDP_MEMORY_ERROR; return; } else { goto err_ret; } } ++current; } /* check for trailing white space character. */ if (isspace(*(end - 1))) goto err_ret; return; err_ret: *p_error |= SDP_REPEAT_TIME_ERROR; if (repeat != NULL) repeat->r_next = NULL; else time->t_repeat = NULL; sdp_free_repeat(new_repeat); } /* * zone-adjustments (z=) * %x7a "=" time SP ["-"] typed-time *(SP time SP ["-"] typed-time) */ static void sdp_parse_zone(sdp_zone_t **zone, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_zone_t *new_zone = NULL; sdp_zone_t *tmp = NULL; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_ZONE_ERROR; return; } /* There can be atmost one zone field. */ if (*zone != NULL) return; /* Get time and offset */ current = begin; while (current < end) { new_zone = calloc(1, sizeof (sdp_zone_t)); if (new_zone == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } if (*zone == NULL) { *zone = new_zone; tmp = *zone; } else { tmp->z_next = new_zone; tmp = new_zone; } begin = current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } if (commp_strtoull(begin, current, &new_zone->z_time) != 0) goto err_ret; begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_zone->z_offset, begin, current - begin); if (new_zone->z_offset == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } } ++current; } if (isspace(*(end - 1))) goto err_ret; return; err_ret: *p_error |= SDP_ZONE_ERROR; sdp_free_zone(*zone); *zone = NULL; } /* * key-field (k=) * [%x6b "=" key-type CRLF] * key-type = %x70 %x72 %x6f %x6d %x70 %x74 / ; "prompt" * %x63 %x6c %x65 %x61 %x72 ":" text / ; "clear:" * %x62 %x61 %x73 %x65 "64:" base64 / ; "base64:" * %x75 %x72 %x69 ":" uri ; "uri:" */ static void sdp_parse_key(sdp_key_t **key, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_key_t *new_key; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_KEY_ERROR; return; } /* There can be only one key field */ if (*key != NULL) return; new_key = calloc(1, sizeof (sdp_key_t)); if (new_key == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } /* Get Method name */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_COLON, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_key->k_method, begin, current - begin); if (new_key->k_method == NULL) { sdp_free_key(new_key); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get key, if exists. */ if (*current == COMMP_COLON) { ++current; if (current == end) goto err_ret; COMMP_COPY_STR(new_key->k_enckey, current, end - current); if (new_key->k_enckey == NULL) { sdp_free_key(new_key); *p_error |= SDP_MEMORY_ERROR; return; } } *key = new_key; return; err_ret: *p_error |= SDP_KEY_ERROR; sdp_free_key(new_key); } /* * attribute-fields (a=) * *(%x61 "=" attribute CRLF) * attribute = (att-field ":" att-value) / att-field * att-field = token * att-value = byte-string */ static void sdp_parse_attribute(sdp_attr_t **attr, const char *begin, const char *end, uint_t *p_error) { const char *current; sdp_attr_t *new_attr; sdp_attr_t *tmp; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_ATTRIBUTE_ERROR; return; } new_attr = calloc(1, sizeof (sdp_attr_t)); if (new_attr == NULL) { *p_error |= SDP_MEMORY_ERROR; return; } /* Get Attribute Name */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_COLON, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_attr->a_name, begin, current - begin); if (new_attr->a_name == NULL) { sdp_free_attribute(new_attr); *p_error |= SDP_MEMORY_ERROR; return; } } /* Get Attribute Value */ if (*current == COMMP_COLON) { ++current; if (current == end) goto err_ret; COMMP_COPY_STR(new_attr->a_value, current, end - current); if (new_attr->a_value == NULL) { sdp_free_attribute(new_attr); *p_error |= SDP_MEMORY_ERROR; return; } } if (*attr == NULL) { *attr = new_attr; } else { tmp = *attr; while (tmp->a_next != NULL) tmp = tmp->a_next; tmp->a_next = new_attr; } return; err_ret: *p_error |= SDP_ATTRIBUTE_ERROR; sdp_free_attribute(new_attr); } /* * media-field (m=) * %x6d "=" media SP port ["/" integer] SP proto 1*(SP fmt) CRLF */ static sdp_media_t * sdp_parse_media(sdp_session_t *session, const char *begin, const char *end, uint_t *p_error) { const char *current; const char *fake_end; sdp_media_t *new_media; sdp_media_t *tmp; if (*begin++ != COMMP_EQUALS) { *p_error |= SDP_MEDIA_ERROR; return (NULL); } new_media = calloc(1, sizeof (sdp_media_t)); if (new_media == NULL) { *p_error |= SDP_MEMORY_ERROR; return (NULL); } new_media->m_session = session; /* Get media name */ current = begin; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_media->m_name, begin, current - begin); if (new_media->m_name == NULL) { sdp_free_media(new_media); *p_error |= SDP_MEMORY_ERROR; return (NULL); } } /* Get port */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) goto err_ret; fake_end = current; current = begin; if (commp_find_token(&begin, ¤t, fake_end, COMMP_SLASH, B_FALSE) != 0) { goto err_ret; } if (commp_atoui(begin, current, &new_media->m_port) != 0) goto err_ret; /* Get portcount */ if (*current == COMMP_SLASH) { begin = ++current; if (commp_find_token(&begin, ¤t, fake_end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } if (commp_atoi(begin, current, &new_media->m_portcount) != 0) goto err_ret; } else { new_media->m_portcount = 1; } /* Get Protocol */ begin = ++current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } else { COMMP_COPY_STR(new_media->m_proto, begin, current - begin); if (new_media->m_proto == NULL) { sdp_free_media(new_media); *p_error |= SDP_MEMORY_ERROR; return (NULL); } } ++current; /* Get format list */ if (current >= end) goto err_ret; while (current < end) { begin = current; if (commp_find_token(&begin, ¤t, end, COMMP_SP, B_FALSE) != 0) { goto err_ret; } if (add_value_to_list(&new_media->m_format, begin, current - begin, B_TRUE) != 0) { sdp_free_media(new_media); *p_error |= SDP_MEMORY_ERROR; return (NULL); } ++current; } /* check for trailing white space character. */ if (isspace(*(end - 1))) goto err_ret; /* Assign new media to the media list */ tmp = session->s_media; if (tmp == NULL) { session->s_media = new_media; } else { while (tmp->m_next != NULL) tmp = tmp->m_next; tmp->m_next = new_media; } return (new_media); err_ret: *p_error |= SDP_MEDIA_ERROR; sdp_free_media(new_media); return (NULL); } /* * This function ensures that a field is in the right order in SDP descripton. * It also identifies cases where a field ('v', 'o, 'i', et al) that must occur * once but occurs several times in SDP description. error cannot be NULL. */ static void sdp_check_order(char prev, char *order, int *error) { *error = 0; while (*order != '\0') { if (*order++ == prev) return; } *error = 1; } /* * This function determines the SDP field and calls the appropriate parse * function. It also ensures that the SDP fields are in strict order. */ static void sdp_handle_fields(sdp_description_t *description, sdp_session_t *_session, const char *begin, const char *end) { boolean_t u_field = B_FALSE; int error = 0; /* fields order error */ char prev = description->d_prev; char m_prev = description->d_mprev; switch (*begin) { case SDP_VERSION_FIELD: sdp_check_order(prev, SDP_VERSION_ORDER, &error); description->d_version = B_TRUE; sdp_parse_version(&_session->s_version, begin + 1, end, &description->d_perror); break; case SDP_ORIGIN_FIELD: sdp_check_order(prev, SDP_ORIGIN_ORDER, &error); description->d_origin = B_TRUE; sdp_parse_origin(&_session->s_origin, begin + 1, end, &description->d_perror); break; case SDP_NAME_FIELD: sdp_check_order(prev, SDP_NAME_ORDER, &error); description->d_name = B_TRUE; sdp_parse_name(&_session->s_name, begin + 1, end, &description->d_perror); break; case SDP_INFO_FIELD: if (description->d_mparsed) { sdp_check_order(m_prev, SDP_M_INFO_ORDER, &error); if (description->d_lmedia == NULL) break; sdp_parse_info(&(description->d_lmedia-> m_info), begin + 1, end, &description-> d_perror); } else { sdp_check_order(prev, SDP_INFO_ORDER, &error); sdp_parse_info(&_session->s_info, begin + 1, end, &description->d_perror); } break; case SDP_URI_FIELD: sdp_check_order(prev, SDP_URI_ORDER, &error); sdp_parse_uri(&_session->s_uri, begin + 1, end, &description->d_perror); break; case SDP_EMAIL_FIELD: sdp_check_order(prev, SDP_EMAIL_ORDER, &error); sdp_parse_email(&_session->s_email, begin + 1, end, &description->d_perror); break; case SDP_PHONE_FIELD: sdp_check_order(prev, SDP_PHONE_ORDER, &error); sdp_parse_phone(&_session->s_phone, begin + 1, end, &description->d_perror); break; case SDP_CONNECTION_FIELD: if (description->d_mparsed) { sdp_check_order(m_prev, SDP_M_CONN_ORDER, &error); --description->d_mccount; if (description->d_lmedia == NULL) break; sdp_parse_connection(&(description->d_lmedia-> m_conn), begin + 1, end, &description->d_perror); } else { /* * RFC - 4566 says that session section should * have only one connection field, while media * section can have many */ sdp_check_order(prev, SDP_CONN_ORDER, &error); description->d_conn = B_TRUE; if (_session->s_conn != NULL) break; sdp_parse_connection(&_session->s_conn, begin + 1, end, &description->d_perror); } break; case SDP_BANDWIDTH_FIELD: if (description->d_mparsed) { sdp_check_order(m_prev, SDP_M_BW_ORDER, &error); if (description->d_lmedia == NULL) break; sdp_parse_bandwidth(&(description->d_lmedia-> m_bw), begin + 1, end, &description->d_perror); } else { sdp_check_order(prev, SDP_BW_ORDER, &error); sdp_parse_bandwidth(&_session->s_bw, begin + 1, end, &description->d_perror); } break; case SDP_TIME_FIELD: if (!description->d_tparsed || description->d_prev != SDP_REPEAT_FIELD) { sdp_check_order(prev, SDP_TIME_ORDER, &error); } description->d_tparsed = B_TRUE; description->d_ltime = sdp_parse_time(&_session-> s_time, begin + 1, end, &description->d_perror); break; case SDP_REPEAT_FIELD: sdp_check_order(prev, SDP_REPEAT_ORDER, &error); if (description->d_ltime == NULL) break; /* we pass time, as repeat is associated with time */ sdp_parse_repeat(description->d_ltime, begin + 1, end, &description->d_perror); break; case SDP_ZONE_FIELD: sdp_check_order(prev, SDP_ZONE_ORDER, &error); sdp_parse_zone(&_session->s_zone, begin + 1, end, &description->d_perror); break; case SDP_KEY_FIELD: if (description->d_mparsed) { sdp_check_order(m_prev, SDP_M_KEY_ORDER, &error); if (description->d_lmedia == NULL) break; sdp_parse_key(&(description->d_lmedia->m_key), begin + 1, end, &description->d_perror); } else { sdp_check_order(prev, SDP_KEY_ORDER, &error); sdp_parse_key(&_session->s_key, begin + 1, end, &description->d_perror); } break; case SDP_ATTRIBUTE_FIELD: if (description->d_mparsed) { sdp_check_order(m_prev, SDP_M_ATTR_ORDER, &error); if (description->d_lmedia == NULL) break; sdp_parse_attribute(&(description->d_lmedia-> m_attr), begin + 1, end, &description->d_perror); } else { sdp_check_order(prev, SDP_ATTR_ORDER, &error); sdp_parse_attribute(&_session->s_attr, begin + 1, end, &description->d_perror); } break; case SDP_MEDIA_FIELD: if (!description->d_mparsed) { sdp_check_order(prev, SDP_MEDIA_ORDER, &error); description->d_mccount = 1; } else { if (description->d_mccount == 1) description->d_mconn = B_FALSE; description->d_mccount = 1; } description->d_mparsed = B_TRUE; description->d_lmedia = sdp_parse_media(_session, begin + 1, end, &description->d_perror); break; default: /* Unknown field type. Ignore it */ u_field = B_TRUE; break; } if (error) description->d_perror |= SDP_FIELDS_ORDER_ERROR; if (!u_field) { if (!description->d_mparsed) description->d_prev = *begin; else description->d_mprev = *begin; } } /* * Parses the SDP info */ int sdp_parse(const char *sdp_info, int len, int flags, sdp_session_t **session, uint_t *p_error) { const char *f_begin; const char *f_end; sdp_description_t *description; const char *start; const char *end; const char *current; if (sdp_info == NULL || len == 0 || p_error == NULL || flags != 0 || session == NULL) { if (session != NULL) *session = NULL; return (EINVAL); } *session = NULL; *p_error = 0; description = calloc(1, sizeof (sdp_description_t)); if (description == NULL) { return (ENOMEM); } /* Needed later to check for mandatory fields */ description->d_prev = COMMP_SP; description->d_mconn = B_TRUE; *session = sdp_new_session(); if (*session == NULL) { free(description); return (ENOMEM); } start = sdp_info; end = start + len; if (commp_skip_white_space(&start, end) != 0) { free(description); free(*session); *session = NULL; return (EINVAL); } current = start; f_begin = current; while ((current < end) && !(description->d_perror & SDP_MEMORY_ERROR)) { /* * RFC says parser SHOULD be tolerant to records ending * with a single newline character too. */ if (strncmp(COMMP_CRLF, current, strlen(COMMP_CRLF)) == 0) { f_end = current; sdp_handle_fields(description, *session, f_begin, f_end); COMMP_SKIP_CRLF(current); (void) commp_skip_white_space(¤t, end); f_begin = current; } else if (strncmp(COMMP_LF, current, strlen(COMMP_LF)) == 0) { f_end = current; sdp_handle_fields(description, *session, f_begin, f_end); COMMP_SKIP_LF(current); (void) commp_skip_white_space(¤t, end); f_begin = current; } else { current++; } } if (description->d_perror & SDP_MEMORY_ERROR) { free(description); sdp_free_session(*session); *session = NULL; return (ENOMEM); } /* * Check for mandatory fields v, o, s, t fields. For connection field, * RFC says; a connection field must be present in every media * description or at the session-level */ if (description->d_mccount == 1) description->d_mconn = B_FALSE; if (!(description->d_version && description->d_origin && description->d_name && description->d_tparsed && (description->d_conn || (description->d_mparsed && description->d_mconn)))) { description->d_perror |= SDP_MISSING_FIELDS; } *p_error = description->d_perror; free(description); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SDP_PARSE_H #define _SDP_PARSE_H #ifdef __cplusplus extern "C" { #endif #include #include #define SDP_MEMORY_ERROR 0x10000000 #define SDP_VERSION_ORDER " " #define SDP_ORIGIN_ORDER "v" #define SDP_NAME_ORDER "o" #define SDP_INFO_ORDER "s" #define SDP_URI_ORDER "is" #define SDP_EMAIL_ORDER "euis" #define SDP_PHONE_ORDER "peuis" #define SDP_CONN_ORDER "peuis" #define SDP_BW_ORDER "bcpeuis" #define SDP_TIME_ORDER "tbcpeuis" #define SDP_REPEAT_ORDER "rt" #define SDP_ZONE_ORDER "rt" #define SDP_KEY_ORDER "zrt" #define SDP_ATTR_ORDER "akzrt" #define SDP_MEDIA_ORDER "makzrt" #define SDP_M_INFO_ORDER "m" #define SDP_M_CONN_ORDER "cim" #define SDP_M_BW_ORDER "bcim" #define SDP_M_KEY_ORDER "bcim" #define SDP_M_ATTR_ORDER "akbcim" typedef struct sdp_description { /* * Following boolean fields are used to * check for presence of mandatory fields * in session structure */ boolean_t d_version; /* Version field */ boolean_t d_origin; /* Origin field */ boolean_t d_name; /* Name field */ boolean_t d_conn; /* Connection field */ boolean_t d_mconn; /* Media connection field */ boolean_t d_mparsed; /* Media parsed */ boolean_t d_tparsed; /* Time parsed */ /* * keeps count of connection fields within * media section */ int d_mccount; sdp_media_t *d_lmedia; /* Last media field */ sdp_time_t *d_ltime; /* Last time field */ uint_t d_perror; /* Parse error */ char d_prev; /* previous field */ char d_mprev; /* previous field in media section */ } sdp_description_t; extern int add_value_to_list(sdp_list_t **, const char *, int, boolean_t); extern int sdp_list_to_str(sdp_list_t *, char **, boolean_t); extern int sdp_str_to_list(sdp_list_t **, const char *, int, boolean_t); extern void sdp_free_repeat(sdp_repeat_t *); extern void sdp_free_origin(sdp_origin_t *); extern void sdp_free_list(sdp_list_t *); extern void sdp_free_connection(sdp_conn_t *); extern void sdp_free_bandwidth(sdp_bandwidth_t *); extern void sdp_free_time(sdp_time_t *); extern void sdp_free_zone(sdp_zone_t *); extern void sdp_free_attribute(sdp_attr_t *); extern void sdp_free_key(sdp_key_t *); extern void sdp_free_media(sdp_media_t *); #ifdef __cplusplus } #endif #endif /* _SDP_PARSE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Helper functions to skip white spaces, find tokens, find separators and free * memory. */ #include #include #include #include #include #include #include #include "sdp_parse.h" #include "commp_util.h" void sdp_free_origin(sdp_origin_t *origin) { if (origin != NULL) { if (origin->o_username != NULL) free(origin->o_username); if (origin->o_nettype != NULL) free(origin->o_nettype); if (origin->o_addrtype != NULL) free(origin->o_addrtype); if (origin->o_address != NULL) free(origin->o_address); free(origin); } } void sdp_free_key(sdp_key_t *key) { if (key != NULL) { if (key->k_method != NULL) free(key->k_method); if (key->k_enckey != NULL) free(key->k_enckey); free(key); } } void sdp_free_zone(sdp_zone_t *zone) { sdp_zone_t *next_zone; while (zone != NULL) { next_zone = zone->z_next; if (zone->z_offset != NULL) free(zone->z_offset); free(zone); zone = next_zone; } } void sdp_free_list(sdp_list_t *list) { sdp_list_t *next_list; while (list != NULL) { next_list = list->next; if (list->value != NULL) free(list->value); free(list); list = next_list; } } void sdp_free_media(sdp_media_t *media) { sdp_media_t *next_media; while (media != NULL) { next_media = media->m_next; if (media->m_name != NULL) free(media->m_name); if (media->m_proto != NULL) free(media->m_proto); if (media->m_format != NULL) sdp_free_list(media->m_format); if (media->m_info != NULL) free(media->m_info); if (media->m_conn != NULL) sdp_free_connection(media->m_conn); if (media->m_bw != NULL) sdp_free_bandwidth(media->m_bw); if (media->m_key != NULL) sdp_free_key(media->m_key); if (media->m_attr != NULL) sdp_free_attribute(media->m_attr); free(media); media = next_media; } } void sdp_free_attribute(sdp_attr_t *attr) { sdp_attr_t *next_attr; while (attr != NULL) { next_attr = attr->a_next; if (attr->a_name != NULL) free(attr->a_name); if (attr->a_value != NULL) free(attr->a_value); free(attr); attr = next_attr; } } void sdp_free_connection(sdp_conn_t *conn) { sdp_conn_t *next_conn; while (conn != NULL) { next_conn = conn->c_next; if (conn->c_nettype != NULL) free(conn->c_nettype); if (conn->c_addrtype != NULL) free(conn->c_addrtype); if (conn->c_address != NULL) free(conn->c_address); free(conn); conn = next_conn; } } void sdp_free_bandwidth(sdp_bandwidth_t *bw) { sdp_bandwidth_t *next_bw; while (bw != NULL) { next_bw = bw->b_next; if (bw->b_type != NULL) free(bw->b_type); free(bw); bw = next_bw; } } void sdp_free_repeat(sdp_repeat_t *repeat) { sdp_repeat_t *next_repeat; while (repeat != NULL) { next_repeat = repeat->r_next; sdp_free_list(repeat->r_offset); free(repeat); repeat = next_repeat; } } void sdp_free_time(sdp_time_t *time) { sdp_time_t *next_time; while (time != NULL) { next_time = time->t_next; sdp_free_repeat(time->t_repeat); free(time); time = next_time; } } void sdp_free_session(sdp_session_t *session) { if (session == NULL) return; if (session->s_origin != NULL) sdp_free_origin(session->s_origin); if (session->s_name != NULL) free(session->s_name); if (session->s_info != NULL) free(session->s_info); if (session->s_uri != NULL) free(session->s_uri); if (session->s_email != NULL) sdp_free_list(session->s_email); if (session->s_phone != NULL) sdp_free_list(session->s_phone); if (session->s_conn != NULL) sdp_free_connection(session->s_conn); if (session->s_bw != NULL) sdp_free_bandwidth(session->s_bw); if (session->s_time != NULL) sdp_free_time(session->s_time); if (session->s_zone != NULL) sdp_free_zone(session->s_zone); if (session->s_key != NULL) sdp_free_key(session->s_key); if (session->s_attr != NULL) sdp_free_attribute(session->s_attr); if (session->s_media != NULL) sdp_free_media(session->s_media); free(session); } /* * Adds text of a given length to a linked list. If the list is NULL to * start with it builds the new list */ int add_value_to_list(sdp_list_t **list, const char *value, int len, boolean_t text) { sdp_list_t *new = NULL; sdp_list_t *tmp = NULL; new = malloc(sizeof (sdp_list_t)); if (new == NULL) return (ENOMEM); new->next = NULL; if (text) new->value = (char *)calloc(1, len + 1); else new->value = (uint64_t *)calloc(1, sizeof (uint64_t)); if (new->value == NULL) { free(new); return (ENOMEM); } if (text) { (void) strncpy(new->value, value, len); } else { if (commp_time_to_secs((char *)value, (char *)(value + len), new->value) != 0) { sdp_free_list(new); return (EINVAL); } } if (*list == NULL) { *list = new; } else { tmp = *list; while (tmp->next != NULL) tmp = tmp->next; tmp->next = new; } return (0); } /* * Given a linked list converts it to space separated string. */ int sdp_list_to_str(sdp_list_t *list, char **buf, boolean_t text) { int size = 0; int wrote = 0; sdp_list_t *tmp; char *ret; char c[1]; if (list == NULL) { *buf = NULL; return (EINVAL); } tmp = list; while (list != NULL) { if (text) size += strlen((char *)list->value); else size += snprintf(c, 1, "%lld", *(uint64_t *)list->value); size++; list = list->next; } list = tmp; if (size > 0) { *buf = calloc(1, size + 1); if (*buf == NULL) return (ENOMEM); ret = *buf; while (list != NULL) { if (text) { wrote = snprintf(ret, size, "%s ", (char *)list->value); } else { wrote = snprintf(ret, size, "%lld ", *(uint64_t *)list->value); } ret = ret + wrote; size = size - wrote; list = list->next; } } else { return (EINVAL); } return (0); } /* * Given a space separated string, converts it into linked list. SDP field * repeat and media can have undefined number of offsets or formats * respectively. We need to capture it in a linked list. */ int sdp_str_to_list(sdp_list_t **list, const char *buf, int len, boolean_t text) { const char *begin; const char *current; const char *end; int ret = 0; if (len == 0) return (EINVAL); current = buf; end = current + len; /* takes care of strings with just spaces */ if (commp_skip_white_space(¤t, end) != 0) return (EINVAL); while (current < end) { (void) commp_skip_white_space(¤t, end); begin = current; while (current < end) { if (isspace(*current)) break; ++current; } if (current != begin) { if ((ret = add_value_to_list(list, begin, current - begin, text)) != 0) { sdp_free_list(*list); *list = NULL; return (ret); } } } return (0); }