📄 sdp_parse.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@ingroup sdp_parser * @CFILE sdp_parse.c * @brief Simple SDP parser interface. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Kai Vehmanen <kai.vehmanen@nokia.com> * * @date Created: Fri Feb 18 10:25:08 2000 ppessi */#include "config.h"#include <sofia-sip/su_alloc.h>#include "sofia-sip/sdp.h"#include <stddef.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <stdio.h>#include <limits.h>#include <assert.h>/** @typedef struct sdp_parser_s sdp_parser_t; * * SDP parser handle. * * The SDP parser handle is returned by sdp_parse(). It contains either * successfully parse SDP session #sdp_session_t or an error message. * If sdp_session() returns non-NULL, parsing was successful. * * @sa #sdp_session_t, sdp_parse(), sdp_session(), sdp_parsing_error(), * sdp_sanity_check(), sdp_parser_home(), sdp_parser_free(). */struct sdp_parser_s { su_home_t pr_home[1]; union { char pru_error[128]; sdp_session_t pru_session[1]; } pr_output; char *pr_message; sdp_mode_t pr_session_mode; unsigned pr_ok : 1; unsigned pr_strict : 1; unsigned pr_anynet : 1; unsigned pr_mode_0000 : 1; unsigned pr_mode_manual : 1; unsigned pr_insane : 1; unsigned pr_c_missing : 1; unsigned pr_config : 1;};#define is_posdigit(c) ((c) >= '1' && (c) <= '9')#define is_digit(c) ((c) >= '0' && (c) <= '9')#define is_space(c) ((c) == ' ')#define is_tab(c) ((c) == '\t')#define pr_error pr_output.pru_error#define pr_session pr_output.pru_session#define STRICT(pr) (pr->pr_strict)/* Static parser object used when running out of memory */static const struct sdp_parser_s no_mem_error ={ { SU_HOME_INIT(no_mem_error) }, { "sdp: not enough memory" }};/* Internal prototypes */static void parse_message(sdp_parser_t *p);static int parsing_error(sdp_parser_t *p, char const *fmt, ...);/** Parse an SDP message. * * The function sdp_parse() parses an SDP message @a msg of size @a * msgsize. Parsing is done according to the given @a flags. The SDP message * may not contain a NUL. * * The parsing result is stored to an #sdp_session_t structure. * * @param home memory home * @param msg pointer to message * @param msgsize size of the message (excluding final NUL, if any) * @param flags flags affecting the parsing. * * The following flags are used by parser: * * @li #sdp_f_strict Parser should accept only messages conforming strictly * to the specification. * @li #sdp_f_anynet Parser accepts unknown network or address types. * @li #sdp_f_insane Do not run sanity check. * @li #sdp_f_c_missing Sanity check does not require c= for each m= line * @li #sdp_f_mode_0000 Parser regards "c=IN IP4 0.0.0.0" as "a=inactive" * (likewise with c=IN IP6 ::) * @li #sdp_f_mode_manual Do not generate or parse SDP mode * @li #sdp_f_config Parse config files (any line can be missing) * * @return * Always a valid parser handle. * * @todo Parser accepts some non-conforming SDP even with #sdp_f_strict. */sdp_parser_t *sdp_parse(su_home_t *home, char const msg[], issize_t msgsize, int flags){ sdp_parser_t *p; char *b; size_t len; if (msgsize == -1 || msg == NULL) { p = su_home_clone(home, sizeof(*p)); if (p) parsing_error(p, "invalid input message"); else p = (sdp_parser_t*)&no_mem_error; return p; } if (msgsize == -1 && msg) len = strlen(msg); else len = msgsize; if (len > ISSIZE_MAX) len = ISSIZE_MAX; p = su_home_clone(home, sizeof(*p) + len + 1); if (p) { b = strncpy((void *)(p + 1), msg, len); b[len] = 0; p->pr_message = b; p->pr_strict = (flags & sdp_f_strict) != 0; p->pr_anynet = (flags & sdp_f_anynet) != 0; p->pr_mode_0000 = (flags & sdp_f_mode_0000) != 0; p->pr_insane = (flags & sdp_f_insane) != 0; p->pr_c_missing = (flags & sdp_f_c_missing) != 0; if (flags & sdp_f_config) p->pr_c_missing = 1, p->pr_config = 1; p->pr_mode_manual = (flags & sdp_f_mode_manual) != 0; p->pr_session_mode = sdp_sendrecv; parse_message(p); return p; } if (p) sdp_parser_free(p); return (sdp_parser_t*)&no_mem_error;}/** Obtain memory home used by parser */su_home_t *sdp_parser_home(sdp_parser_t *parser){ if (parser != &no_mem_error) return parser->pr_home; else return NULL;}/** Retrieve an SDP session structure. * * The function sdp_session() returns a pointer to the SDP session * structure associated with the SDP parser @a p. The pointer and all the * data in the structure are valid until sdp_parser_free() is called. * * @param p SDP parser * * @return * The function sdp_session() returns a pointer to an parsed SDP message * or NULL, if an error has occurred. */sdp_session_t *sdp_session(sdp_parser_t *p){ return p && p->pr_ok ? p->pr_session : NULL;}/** Get a parsing error message. * * The function sdp_parsing_error() returns the error message associated * with an SDP parser @a p. * * @param p SDP parser * * @return * The function sdp_parsing_error() returns a C string describing parsing * error, or NULL if no error occurred. */char const *sdp_parsing_error(sdp_parser_t *p){ return !p->pr_ok ? p->pr_error : NULL;}/** Free an SDP parser. * * The function sdp_parser_free() frees an SDP parser object along with * the memory blocks associated with it. * * @param p pointer to the SDP parser to be freed */void sdp_parser_free(sdp_parser_t *p){ if (p && p != &no_mem_error) su_home_unref(p->pr_home);}/* ========================================================================= *//* ========================================================================= * Private part *//* Parsing tokens */#define SPACE " "#define TAB "\011"#define CRLF "\015\012"#define ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"#define DIGIT "0123456789"/* ========================================================================= *//* Parsing functions */static void post_session(sdp_parser_t *p, sdp_session_t *sdp);static void parse_origin(sdp_parser_t *p, char *r, sdp_origin_t **result);static void parse_subject(sdp_parser_t *p, char *r, sdp_text_t **result);static void parse_information(sdp_parser_t *p, char *r, sdp_text_t **result);static void parse_uri(sdp_parser_t *p, char *r, sdp_text_t **result);static void parse_email(sdp_parser_t *p, char *r, sdp_list_t **result);static void parse_phone(sdp_parser_t *p, char *r, sdp_list_t **result);static void parse_connection(sdp_parser_t *p, char *r, sdp_connection_t **result);static void parse_bandwidth(sdp_parser_t *p, char *r, sdp_bandwidth_t **result);static void parse_time(sdp_parser_t *p, char *r, sdp_time_t **result);static void parse_repeat(sdp_parser_t *p, char *r, sdp_repeat_t **result);static void parse_zone(sdp_parser_t *p, char *r, sdp_zone_t **result);static void parse_key(sdp_parser_t *p, char *r, sdp_key_t **result);static void parse_session_attr(sdp_parser_t *p, char *r, sdp_attribute_t **result);static void parse_media(sdp_parser_t *p, char *r, sdp_media_t **result);static void parse_payload(sdp_parser_t *p, char *r, sdp_rtpmap_t **result);static void parse_media_attr(sdp_parser_t *p, char *r, sdp_media_t *m, sdp_attribute_t **result);static int parse_rtpmap(sdp_parser_t *p, char *r, sdp_media_t *m);static int parse_fmtp(sdp_parser_t *p, char *r, sdp_media_t *m);static void parse_text_list(sdp_parser_t *p, char *r, sdp_list_t **result);static void parse_descs(sdp_parser_t *p, char *r, char *m, sdp_media_t **result);static int parse_ul(sdp_parser_t *p, char **r, unsigned long *result, unsigned long max_value);static int parse_ull(sdp_parser_t *p, char **r, uint64_t *result, uint64_t max_value);static void parse_alloc_error(sdp_parser_t *p, const char *typename);static char *next(char **message, const char *sep, const char *strip);static char *token(char **message, const char *sep, const char *legal, const char *strip);#if 0static void check_mandatory(sdp_parser_t *p, sdp_session_t *sdp);#endif/* ------------------------------------------------------------------------- * Macro PARSE_ALLOC * * Description: * This macro declares a pointer (v) of given type (t). It then allocates * an structure of given type (t). If allocation was succesful, it assigns * the XX_size member with appropriate value. */#define PARSE_ALLOC(p, t, v) \ t *v = su_salloc(p->pr_home, sizeof(*v)); \ if (!v && (parse_alloc_error(p, #t), 1)) return; /* ------------------------------------------------------------------------- * Macro PARSE_CHECK_REST * * Description: * This macro check if there is extra data at the end of field. */#define PARSE_CHECK_REST(p, s, n)\ if (*s && (parsing_error(p, "extra data after %s (\"%.04s\")", n, s), 1)) \ return/* ------------------------------------------------------------------------- * Function parse_message() - parse an SDP message * * Description: * This function parses an SDP message, which is copied into the * p->pr_message. The p->pr_message is modified during the parsing, * and parts of it are returned in p->pr_session. * * Parameters: * p - pointer to SDP parser object */static void parse_message(sdp_parser_t *p){/* announcement = proto-version origin-field session-name-field information-field uri-field email-fields phone-fields connection-field bandwidth-fields time-fields key-field attribute-fields media-descriptions*/ sdp_session_t *sdp = p->pr_session; char *record, *rest; char const *strip; char *message = p->pr_message; char field = '\0'; sdp_list_t **emails = &sdp->sdp_emails; sdp_list_t **phones = &sdp->sdp_phones; sdp_bandwidth_t **bandwidths = &sdp->sdp_bandwidths; sdp_time_t **times = &sdp->sdp_time; sdp_repeat_t **repeats = NULL; sdp_zone_t **zones = NULL; sdp_attribute_t **attributes = &sdp->sdp_attributes; if (!STRICT(p)) strip = SPACE TAB; /* skip initial whitespace */ else strip = ""; p->pr_ok = 1; p->pr_session->sdp_size = sizeof(p->pr_session); /* Require that version comes first */ record = next(&message, CRLF, strip); if (!record || strcmp(record, "v=0")) { if (!p->pr_config || !record || record[1] != '=') { parsing_error(p, "bad SDP message"); return; } } else { record = next(&message, CRLF, strip); } /* XXX - the lines in SDP are in certain order, which we don't check here. For stricter parsing we might want to parse o= and s= next. */ for (; record && p->pr_ok; record = next(&message, CRLF, strip)) { field = record[0]; rest = record + 2; rest += strspn(rest, strip); if (record[1] != '=') { parsing_error(p, "bad line \"%s\"", record); return; } switch (field) { case 'o': parse_origin(p, rest, &sdp->sdp_origin); break; case 's': parse_subject(p, rest, &sdp->sdp_subject); break; case 'i': parse_information(p, rest, &sdp->sdp_information); break; case 'u': parse_uri(p, rest, &sdp->sdp_uri); break; case 'e': parse_email(p, rest, emails); emails = &(*emails)->l_next; break; case 'p': parse_phone(p, rest, phones); phones = &(*phones)->l_next; break; case 'c': parse_connection(p, rest, &sdp->sdp_connection); break; case 'b': parse_bandwidth(p, rest, bandwidths); bandwidths = &(*bandwidths)->b_next; break; case 't': parse_time(p, rest, times); repeats = &(*times)->t_repeat; zones = &(*times)->t_zone; times = &(*times)->t_next; break; case 'r': if (repeats) parse_repeat(p, rest, repeats); else parsing_error(p, "repeat field without time field"); break; case 'z': if (zones) parse_zone(p, rest, zones), zones = NULL; else parsing_error(p, "zone field without time field"); break; case 'k': parse_key(p, rest, &sdp->sdp_key); break; case 'a': parse_session_attr(p, rest, attributes); if (*attributes) attributes = &(*attributes)->a_next; break; case 'm': parse_descs(p, record, message, &sdp->sdp_media); post_session(p, sdp); return; default: parsing_error(p, "unknown field \"%s\"", record); return; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -