You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/parser/sdp/sdp_helpr_funcs.c

743 lines
19 KiB

/*
* $Id: sdp_helpr_funcs.c 5573 2009-02-09 16:55:22Z osas $
*
* SDP parser helpers
*
* Copyright (C) 2008-2009 SOMA Networks, INC.
* Copyright (C) 2010 VoIP Embedded, Inc
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "../../ut.h"
#include "../msg_parser.h"
#include "../parser_f.h"
#include "../parse_hname2.h"
static struct {
const char *s;
int len;
int is_rtp;
} sup_ptypes[] = {
{.s = "rtp/avp", .len = 7, .is_rtp = 1},
{.s = "udptl", .len = 5, .is_rtp = 0},
{.s = "rtp/avpf", .len = 8, .is_rtp = 1},
{.s = "rtp/savp", .len = 8, .is_rtp = 1},
{.s = "rtp/savpf", .len = 9, .is_rtp = 1},
{.s = "udp", .len = 3, .is_rtp = 0},
{.s = "udp/bfcp", .len = 8, .is_rtp = 0},
{.s = NULL, .len = 0, .is_rtp = 0}
};
#define READ(val) \
(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
#define advance(_ptr,_n,_str,_error) \
do{\
if ((_ptr)+(_n)>(_str).s+(_str).len)\
goto _error;\
(_ptr) = (_ptr) + (_n);\
}while(0);
#define one_of_16( _x , _t ) \
(_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
#define one_of_8( _x , _t ) \
(_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
||_x==_t[5]||_x==_t[6])
/*
* ser_memmem() returns the location of the first occurrence of data
* pattern b2 of size len2 in memory block b1 of size len1 or
* NULL if none is found. Obtained from NetBSD.
*/
static void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2)
{
/* Initialize search pointer */
char *sp = (char *) b1;
/* Initialize pattern pointer */
char *pp = (char *) b2;
/* Initialize end of search address space pointer */
char *eos = sp + len1 - len2;
/* Sanity check */
if(!(b1 && b2 && len1 && len2))
return NULL;
while (sp <= eos) {
if (*sp == *pp)
if (memcmp(sp, pp, len2) == 0)
return sp;
sp++;
}
return NULL;
}
/*
* ser_memrmem() returns the location of the last occurrence of data
* pattern b2 of size len2 in memory block b1 of size len1 or
* NULL if none is found.
*/
static void * ser_memrmem(const void *b1, const void *b2, size_t len1, size_t len2)
{
/* Initialize search pointer */
char *sp = (char *) b1 + len1 - len2;
/* Initialize pattern pointer */
char *pp = (char *) b2;
/* Initialize end of search address space pointer */
char *eos = (char *) b1;
/* Sanity check */
if(!(b1 && b2 && len1 && len2))
return NULL;
while (sp >= eos) {
if (*sp == *pp)
if (memcmp(sp, pp, len2) == 0)
return sp;
sp--;
}
return NULL;
}
int get_mixed_part_delimiter(str* body, str *mp_delimiter)
{
static unsigned int boun[16] = {
0x6e756f62,0x4e756f62,0x6e556f62,0x4e556f62,
0x6e754f62,0x4e754f62,0x6e554f62,0x4e554f62,
0x6e756f42,0x4e756f42,0x6e556f42,0x4e556f42,
0x6e754f42,0x4e754f42,0x6e554f42,0x4e554f42};
static unsigned int dary[16] = {
0x79726164,0x59726164,0x79526164,0x59526164,
0x79724164,0x59724164,0x79524164,0x59524164,
0x79726144,0x59726144,0x79526144,0x59526144,
0x79724144,0x59724144,0x79524144,0x59524144};
str str_type;
unsigned int x;
char *p;
/* LM_DBG("<%.*s>\n",body->len,body->s); */
p = str_type.s = body->s;
str_type.len = body->len;
while (*p!=';' && p<(body->s+body->len))
advance(p,1,str_type,error);
p++;
str_type.s = p;
str_type.len = body->len - (p - body->s);
/* LM_DBG("string to parse: <%.*s>\n",str_type.len,str_type.s); */
/* skip spaces and tabs if any */
while (*p==' ' || *p=='\t')
advance(p,1,str_type,error);
advance(p,4,str_type,error);
x = READ(p-4);
if (!one_of_16(x,boun))
goto other;
advance(p,4,str_type,error);
x = READ(p-4);
if (!one_of_16(x,dary))
goto other;
/* skip spaces and tabs if any */
while (*p==' ' || *p=='\t')
advance(p,1,str_type,error);
if (*p!='=') {
LM_ERR("parse error: no = found after boundary field\n");
goto error;
}
advance(p,1,str_type,error);
while ((*p==' ' || *p=='\t') && p+1<str_type.s+str_type.len)
advance(p,1,str_type,error);
mp_delimiter->len = str_type.len - (int)(p-str_type.s);
mp_delimiter->s = p;
/* check if the boundary value is enclosed in quotes */
if(*p=='"' || *p=='\'') {
if(mp_delimiter->s[mp_delimiter->len-1]==*p) {
mp_delimiter->s = p+1;
mp_delimiter->len -= 2;
if(mp_delimiter->len<=0) {
LM_ERR("invalid boundary field value\n");
goto error;
}
} else {
LM_ERR("missing closing quote in boundary field value\n");
goto error;
}
}
return 1;
error:
return -1;
other:
LM_DBG("'boundary' parsing error\n");
return -1;
}
int extract_rtpmap(str *body,
str *rtpmap_payload, str *rtpmap_encoding, str *rtpmap_clockrate, str *rtpmap_parmas)
{
char *cp, *cp1;
int len;
if (strncasecmp(body->s, "a=rtpmap:", 9) !=0) {
/*LM_DBG("We are not pointing to an a=rtpmap: attribute =>`%.*s'\n", body->len, body->s); */
return -1;
}
cp1 = body->s;
rtpmap_payload->s = cp1 + 9; /* skip `a=rtpmap:' */
rtpmap_payload->len = eat_line(rtpmap_payload->s, body->s + body->len -
rtpmap_payload->s) - rtpmap_payload->s;
trim_len(rtpmap_payload->len, rtpmap_payload->s, *rtpmap_payload);
len = rtpmap_payload->len;
/* */
cp = eat_token_end(rtpmap_payload->s, rtpmap_payload->s + rtpmap_payload->len);
rtpmap_payload->len = cp - rtpmap_payload->s;
if (rtpmap_payload->len <= 0 || cp == rtpmap_payload->s) {
LM_ERR("no encoding in `a=rtpmap'\n");
return -1;
}
len -= rtpmap_payload->len;
rtpmap_encoding->s = cp;
cp = eat_space_end(rtpmap_encoding->s, rtpmap_encoding->s + len);
len -= cp - rtpmap_encoding->s;
if (len <= 0 || cp == rtpmap_encoding->s) {
LM_ERR("no encoding in `a=rtpmap:'\n");
return -1;
}
rtpmap_encoding->s = cp;
cp1 = (char*)ser_memmem(cp, "/", len, 1);
len -= cp1 - cp;
if (len <= 0 || cp == cp1) {
LM_ERR("invalid encoding in `a=rtpmap'\n");
return -1;
}
rtpmap_encoding->len = cp1 - cp;
cp = cp1;
cp1 = (char*)ser_memmem(cp, "/", len, 1);
len -= cp1 - cp;
if (len <= 0) {
LM_ERR("invalid encoding in `a=rtpmap:'\n");
return -1;
}
rtpmap_clockrate->s = cp + 1; /* skip '/' */
rtpmap_clockrate->len = len -1; /* skip '/' */
if ( cp == cp1) {
rtpmap_parmas->s = NULL;
rtpmap_parmas->len = 0;
} else {
rtpmap_parmas->s = cp1 + 1;
rtpmap_parmas->len = cp1 - cp;
}
return 0;
}
int extract_fmtp( str *body, str *fmtp_payload, str *fmtp_string )
{
char *cp, *cp1;
int len;
if (strncasecmp(body->s, "a=fmtp:", 7) !=0) {
/*LM_DBG("We are not pointing to an a=fmtp: attribute =>`%.*s'\n", body->len, body->s); */
return -1;
}
cp1 = body->s;
fmtp_payload->s = cp1 + 7; /* skip `a=fmtp:' */
fmtp_payload->len = eat_line(fmtp_payload->s, body->s + body->len -
fmtp_payload->s) - fmtp_payload->s;
trim_len(fmtp_payload->len, fmtp_payload->s, *fmtp_payload);
len = fmtp_payload->len;
/* */
cp = eat_token_end(fmtp_payload->s, fmtp_payload->s + fmtp_payload->len);
fmtp_payload->len = cp - fmtp_payload->s;
if (fmtp_payload->len <= 0 || cp == fmtp_payload->s) {
LM_ERR("no encoding in `a=fmtp:'\n");
return -1;
}
len -= fmtp_payload->len;
fmtp_string->s = cp;
cp = eat_space_end(fmtp_string->s, fmtp_string->s + len);
len -= cp - fmtp_string->s;
if (len <= 0 || cp == fmtp_string->s) {
LM_ERR("no encoding in `a=fmtp:'\n");
return -1;
}
fmtp_string->s = cp;
fmtp_string->len = eat_line(fmtp_string->s, body->s + body->len -
fmtp_string->s) - fmtp_string->s;
trim_len(fmtp_string->len, fmtp_string->s, *fmtp_string);
return 0;
}
/* generic method for attribute extraction
* field must has format "a=attrname:" */
int extract_field(str *body, str *value, str field)
{
if (strncmp(body->s, field.s, field.len < body->len ? field.len : body->len) !=0) {
/*LM_DBG("We are not pointing to an %.* attribute =>`%.*s'\n", field.len, field.s, body->len, body->s); */
return -1;
}
value->s = body->s + field.len; /* skip `a=attrname:' */
value->len = eat_line(value->s, body->s + body->len -
value->s) - value->s;
trim_len(value->len, value->s, *value);
return 0;
}
int extract_ptime(str *body, str *ptime)
{
static const str field = str_init("a=ptime:");
return extract_field(body, ptime, field);
}
int extract_accept_types(str *body, str *accept_types)
{
static const str field = str_init("a=accept-types:");
return extract_field(body, accept_types, field);
}
int extract_accept_wrapped_types(str *body, str *accept_wrapped_types)
{
static const str field = str_init("a=accept-wrapped-types:");
return extract_field(body, accept_wrapped_types, field);
}
int extract_max_size(str *body, str *max_size)
{
static const str field = str_init("a=max-size:");
return extract_field(body, max_size, field);
}
int extract_path(str *body, str *path)
{
static const str field = str_init("a=path:");
return extract_field(body, path, field);
}
int extract_rtcp(str *body, str *rtcp)
{
static const str field = str_init("a=rtcp:");
return extract_field(body, rtcp, field);
}
int extract_sendrecv_mode(str *body, str *sendrecv_mode, int *is_on_hold)
{
char *cp1;
cp1 = body->s;
if ( !( (strncasecmp(cp1, "a=sendrecv", 10) == 0) ||
(strncasecmp(cp1, "a=recvonly", 10) == 0))) {
if ( !( (strncasecmp(cp1, "a=inactive", 10) == 0) ||
(strncasecmp(cp1, "a=sendonly", 10) == 0) )) {
return -1;
} else {
*is_on_hold = 1;
}
return -1;
}
sendrecv_mode->s = body->s + 2; /* skip `a=' */
sendrecv_mode->len = 8; /* we know the length and therefore we don't need to overkill */
/*
sendrecv_mode->len = eat_line(sendrecv_mode->s, body->s + body->len -
sendrecv_mode->s) - sendrecv_mode->s;
trim_len(sendrecv_mode->len, sendrecv_mode->s, *sendrecv_mode);
*/
return 0;
}
int extract_bwidth(str *body, str *bwtype, str *bwwitdth)
{
char *cp, *cp1;
int len;
cp1 = NULL;
for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
cp1 = (char*)ser_memmem(cp, "b=", len, 2);
if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
break;
cp = cp1 + 2;
}
if (cp1 == NULL)
return -1;
bwtype->s = cp1 + 2;
bwtype->len = eat_line(bwtype->s, body->s + body->len - bwtype->s) - bwtype->s;
trim_len(bwtype->len, bwtype->s, *bwtype);
cp = bwtype->s;
len = bwtype->len;
cp1 = (char*)ser_memmem(cp, ":", len, 1);
len -= cp1 - cp;
if (len <= 0) {
LM_ERR("invalid encoding in `b=%.*s'\n", bwtype->len, bwtype->s);
return -1;
}
bwtype->len = cp1 - cp;
/* skip ':' */
bwwitdth->s = cp1 + 1;
bwwitdth->len = len - 1;
return 0;
}
int extract_mediaip(str *body, str *mediaip, int *pf, char *line)
{
char *cp, *cp1;
int len;
cp1 = NULL;
for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
cp1 = (char*)ser_memmem(cp, line, len, 2);
if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
break;
cp = cp1 + 2;
}
if (cp1 == NULL)
return -1;
mediaip->s = cp1 + 2;
mediaip->len = eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s;
trim_len(mediaip->len, mediaip->s, *mediaip);
if (mediaip->len == 0) {
LM_ERR("no [%s] line in SDP\n",line);
return -1;
}
/* search reverse for IP[4|6] in c=/o= line */
cp = (char*)ser_memrmem(mediaip->s, " IP", mediaip->len, 3);
if (cp == NULL) {
LM_ERR("no `IP[4|6]' in `%s' field\n",line);
return -1;
}
/* safety checks:
* - for lenght, at least 6: ' IP[4|6] x...'
* - white space after
*/
if(cp + 6 > mediaip->s + mediaip->len && cp[4]!=' ') {
LM_ERR("invalid content for `%s' line\n",line);
return -1;
}
switch(cp[3]) {
case '4':
*pf = AF_INET;
break;
case '6':
*pf = AF_INET6;
break;
default:
LM_ERR("invalid addrtype IPx for `%s' line\n",line);
return -1;
}
cp += 5;
/* next token is the IP address */
cp = eat_space_end(cp, mediaip->s + mediaip->len);
len = eat_token_end(cp, mediaip->s + mediaip->len) - cp;
mediaip->s = cp;
mediaip->len = len;
if (mediaip->len == 0) {
LM_ERR("no `IP[4|6]' address in `%s' field\n",line);
return -1;
}
LM_DBG("located IP address [%.*s] in `%s' field\n",
mediaip->len, mediaip->s, line);
return 1;
}
int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload, int *is_rtp)
{
char *cp, *cp1;
int len, i;
cp1 = NULL;
for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
cp1 = (char*)ser_memmem(cp, "m=", len, 2);
if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
break;
cp = cp1 + 2;
}
if (cp1 == NULL) {
LM_ERR("no `m=' in SDP\n");
return -1;
}
mediaport->s = cp1 + 2; /* skip `m=' */
mediaport->len = eat_line(mediaport->s, body->s + body->len -
mediaport->s) - mediaport->s;
trim_len(mediaport->len, mediaport->s, *mediaport);
mediapayload->len = mediaport->len;
mediamedia->s = mediaport->s;
/* Skip media supertype and spaces after it */
cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len);
mediaport->len -= cp - mediaport->s;
mediamedia->len = mediapayload->len - mediaport->len;
if (mediaport->len <= 0 || cp == mediaport->s) {
LM_ERR("no port in `m='\n");
return -1;
}
mediaport->s = cp;
cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len);
mediaport->len -= cp - mediaport->s;
if (mediaport->len <= 0 || cp == mediaport->s) {
LM_ERR("no port in `m='\n");
return -1;
}
/* Extract port */
mediaport->s = cp;
cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len);
mediatransport->len = mediaport->len - (cp - mediaport->s);
if (mediatransport->len <= 0 || cp == mediaport->s) {
LM_ERR("no port in `m='\n");
return -1;
}
mediatransport->s = cp;
mediaport->len = cp - mediaport->s;
/* Skip spaces after port */
cp = eat_space_end(mediatransport->s, mediatransport->s + mediatransport->len);
mediatransport->len -= cp - mediatransport->s;
if (mediatransport->len <= 0 || cp == mediatransport->s) {
LM_ERR("no protocol type in `m='\n");
return -1;
}
/* Extract protocol type */
mediatransport->s = cp;
cp = eat_token_end(mediatransport->s, mediatransport->s + mediatransport->len);
if (cp == mediatransport->s) {
LM_ERR("no protocol type in `m='\n");
return -1;
}
mediatransport->len = cp - mediatransport->s;
mediapayload->s = mediatransport->s + mediatransport->len;
mediapayload->len -= mediapayload->s - mediamedia->s;
cp = eat_space_end(mediapayload->s, mediapayload->s + mediapayload->len);
mediapayload->len -= cp - mediapayload->s;
mediapayload->s = cp;
for (i = 0; sup_ptypes[i].s != NULL; i++)
if (mediatransport->len == sup_ptypes[i].len &&
strncasecmp(mediatransport->s, sup_ptypes[i].s, mediatransport->len) == 0) {
*is_rtp = sup_ptypes[i].is_rtp;
return 0;
}
/* Unproxyable protocol type. Generally it isn't error. */
return 0;
}
/*
* Auxiliary for some functions.
* Returns pointer to first character of found line, or NULL if no such line.
*/
char *find_sdp_line(char* p, char* plimit, char linechar)
{
static char linehead[3] = "x=";
char *cp, *cp1;
linehead[0] = linechar;
/* Iterate through body */
cp = p;
for (;;) {
if (cp >= plimit)
return NULL;
cp1 = ser_memmem(cp, linehead, plimit-cp, 2);
if (cp1 == NULL)
return NULL;
/*
* As it is body, we assume it has previous line and we can
* lookup previous character.
*/
if (cp1[-1] == '\n' || cp1[-1] == '\r')
return cp1;
/*
* Having such data, but not at line beginning.
* Skip them and reiterate. ser_memmem() will find next
* occurence.
*/
if (plimit - cp1 < 2)
return NULL;
cp = cp1 + 2;
}
}
/* This function assumes p points to a line of requested type. */
char * find_next_sdp_line(char* p, char* plimit, char linechar, char* defptr)
{
char *t;
if (p >= plimit || plimit - p < 3)
return defptr;
t = find_sdp_line(p + 2, plimit, linechar);
return t ? t : defptr;
}
/* Find first SDP line starting with linechar. Return defptr if not found */
char * find_first_sdp_line(char* pstart, char* plimit, char linechar,
char* defptr)
{
char *t;
if (pstart >= plimit || plimit - pstart < 3)
return defptr;
t = find_sdp_line(pstart, plimit, linechar);
return t ? t : defptr;
}
/* returns pointer to next header line, and fill hdr_f ;
* if at end of header returns pointer to the last crlf (always buf)*/
char* get_sdp_hdr_field(char* buf, char* end, struct hdr_field* hdr)
{
char* tmp;
char *match;
if ((*buf)=='\n' || (*buf)=='\r'){
/* double crlf or lflf or crcr */
hdr->type=HDR_EOH_T;
return buf;
}
tmp=parse_hname2(buf, end, hdr);
if (hdr->type==HDR_ERROR_T){
LM_ERR("bad header\n");
goto error;
}
/* eliminate leading whitespace */
tmp=eat_lws_end(tmp, end);
if (tmp>=end) {
LM_ERR("hf empty\n");
goto error;
}
/* just skip over it */
hdr->body.s=tmp;
/* find end of header */
/* find lf */
do{
match=memchr(tmp, '\n', end-tmp);
if (match){
match++;
}else {
LM_ERR("bad body for <%s>(%d)\n", hdr->name.s, hdr->type);
tmp=end;
goto error;
}
tmp=match;
}while( match<end &&( (*match==' ')||(*match=='\t') ) );
tmp=match;
hdr->body.len=match-hdr->body.s;
/* jku: if \r covered by current length, shrink it */
trim_r( hdr->body );
hdr->len=tmp-hdr->name.s;
return tmp;
error:
LM_DBG("error exit\n");
hdr->type=HDR_ERROR_T;
hdr->len=tmp-hdr->name.s;
return tmp;
}
char *find_sdp_line_delimiter(char* p, char* plimit, str delimiter)
{
static char delimiterhead[3] = "--";
char *cp, *cp1;
/* Iterate through body */
cp = p;
for (;;) {
if (cp >= plimit)
return NULL;
for(;;) {
cp1 = ser_memmem(cp, delimiterhead, plimit-cp, 2);
if (cp1 == NULL)
return NULL;
/* We matched '--',
* now let's match the boundary delimiter */
if (strncmp(cp1+2, delimiter.s, delimiter.len) == 0)
break;
else
cp = cp1 + 2 + delimiter.len;
if (cp >= plimit)
return NULL;
}
if (cp1[-1] == '\n' || cp1[-1] == '\r')
return cp1;
if (plimit - cp1 < 2 + delimiter.len)
return NULL;
cp = cp1 + 2 + delimiter.len;
}
}
/*
* This function assumes p points to a delimiter type line.
*/
char *find_next_sdp_line_delimiter(char* p, char* plimit, str delimiter, char* defptr)
{
char *t;
if (p >= plimit || plimit - p < 3)
return defptr;
t = find_sdp_line_delimiter(p + 2, plimit, delimiter);
return t ? t : defptr;
}