diff --git a/core/AmSdp.cpp b/core/AmSdp.cpp index b2cfbb5a..f09d5bd4 100644 --- a/core/AmSdp.cpp +++ b/core/AmSdp.cpp @@ -1,56 +1,51 @@ /* - * $Id$ - * - * Copyright (C) 2002-2003 Fhg Fokus - * - * This file is part of sems, a free SIP media server. - * - * sems is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version - * - * For a license to use the ser software under conditions - * other than those described here, or to purchase support for this - * software, please contact iptel.org by e-mail at the following addresses: - * info@iptel.org - * - * sems 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *parse or be parsed */ + #include #include #include +#include +#include #include "AmConfig.h" #include "AmSdp.h" #include "AmUtils.h" #include "AmPlugIn.h" #include "AmSession.h" +#include "ErrorSdp.h" #include "amci/amci.h" #include "log.h" +#include +#include +using std::string; +using std::map; + // Not on Solaris! #if !defined (__SVR4) && !defined (__sun) #include "strings.h" #endif -inline char* get_next_line(char* s); -inline bool parse_string_tok(char*& s, string& res, char sep_char = ' '); -inline bool parse_type(char*& s, int& v, char** ref, const char* name); +static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s); +static void parse_sdp_connection(AmSdp* sdp_msg, char* s, char t); +static void parse_sdp_media(AmSdp* sdp_msg, char* s); +static void parse_sdp_attr(AmSdp* sdp_msg, char* s); +static void parse_sdp_origin(AmSdp* sdp_masg, char* s); -static bool parse_sdp_connection(AmSdp* sdp_msg, char*& s, char*&); -static bool parse_sdp_media(AmSdp* sdp_msg, char*& s, char*&); -static bool parse_sdp_attribute(AmSdp* sdp_msg, char*& s, char*&); +inline char* get_next_line(char* s); +static char* is_eql_next(char* s); +static char* parse_until(char* s, char end); +static bool contains(char* s, char* next_line, char c); +enum parse_st {SDP_DESCR, SDP_MEDIA}; +enum sdp_connection_st {NET_TYPE, ADDR_TYPE, IP4, IP6}; +enum sdp_media_st {MEDIA, PORT, PROTO, FMT}; +enum sdp_attr_rtpmap_st {TYPE, ENC_NAME, CLK_RATE, ENC_PARAM}; +enum sdp_attr_fmtp_st {FORMAT, FORMAT_PARAM}; +enum sdp_origin_st {USER, ID, VERSION_ST, NETTYPE, ADDR, UNICAST_ADDR}; // inline functions @@ -58,7 +53,7 @@ inline string net_t_2_str(int nt) { switch(nt){ case NT_IN: return "IN"; - default: return ""; + default: return ""; } } @@ -67,18 +62,20 @@ inline string addr_t_2_str(int at) switch(at){ case AT_V4: return "IP4"; case AT_V6: return "IP6"; - default: return ""; + default: return ""; } } + inline string media_t_2_str(int mt) { switch(mt){ - case MT_AUDIO: return "audio"; - case MT_VIDEO: return "video"; + case MT_AUDIO: return "audio"; + case MT_VIDEO: return "video"; case MT_APPLICATION: return "application"; - case MT_DATA: return "data"; - default: return ""; + case MT_TEXT: return "text"; + case MT_MESSAGE: return "message"; + default: return ""; } } @@ -86,54 +83,31 @@ inline string transport_p_2_str(int tp) { switch(tp){ case TP_RTPAVP: return "RTP/AVP"; - case TP_UDP: return "udp"; - default: return ""; + case TP_UDP: return "udp"; + case TP_RTPSAVP: return "RTP/SAVP"; + default: return ""; } } -bool SdpPayload::operator == (int r) -{ - DBG("pl == r: payload_type = %i; r = %i\n",payload_type, r); - return payload_type == r; +bool SdpPayload::operator == (int r) +{ + DBG("pl == r: payload_type = %i; r = %i\n", payload_type, r); + return payload_type == r; } - -// WARNING: -// -// If you call this function from a handler function, -// do not forget to set 'next_line' to the proper value -// (else, it could think EOT has been reached !) -// -// Generally: call this function if you know what your doing ! -// Else call 'parse_sdp_line' -// -inline bool parse_sdp_line_ex( AmSdp* sdp_msg, char*& s, char discr, - bool optional, - bool (*parse_func)(AmSdp*,char*&,char*&), - bool only_one, char*& next_line); - -// WARNING: -// -// Do not touch the handler's third parameter until you known what you are doing. -// -inline bool parse_sdp_line( AmSdp* sdp_msg, char*& s, char discr, - bool optional, bool (*parse_func)(AmSdp*,char*&,char*&), - bool only_one = true ); - // // class AmSdp: Methods // -AmSdp::AmSdp() +AmSdp::AmSdp() : remote_active(false), telephone_event_pt(NULL) - -{ +{ } AmSdp::AmSdp(const AmSdp& p_sdp_msg) - : version(p_sdp_msg.version), - origin(p_sdp_msg.origin), - sessionName(p_sdp_msg.sessionName), + : version(p_sdp_msg.version), + origin(p_sdp_msg.origin), + sessionName(p_sdp_msg.sessionName), conn(p_sdp_msg.conn), media(p_sdp_msg.media), telephone_event_pt(NULL), @@ -142,6 +116,7 @@ AmSdp::AmSdp(const AmSdp& p_sdp_msg) memcpy(r_buf,p_sdp_msg.r_buf,BUFFER_SIZE); } + void AmSdp::setBody(const char* _sdp_msg) { if (!memchr(_sdp_msg, '\0', BUFFER_SIZE)) { @@ -150,111 +125,94 @@ void AmSdp::setBody(const char* _sdp_msg) strcpy(r_buf, _sdp_msg); } - int AmSdp::parse() { char* s = r_buf; - media.clear(); - - bool ret = - parse_sdp_line(this,s,'v',false,NULL) || - parse_sdp_line(this,s,'o',false,NULL) || - parse_sdp_line(this,s,'s',false,NULL) || - parse_sdp_line(this,s,'i',true,NULL) || - parse_sdp_line(this,s,'u',true,NULL) || - parse_sdp_line(this,s,'e',true,NULL,false) || - parse_sdp_line(this,s,'p',true,NULL,false) || - parse_sdp_line(this,s,'c',true,parse_sdp_connection) || - parse_sdp_line(this,s,'b',true,NULL,false) || - parse_sdp_line(this,s,'a',true,NULL,false) || - parse_sdp_line(this,s,'t',true,NULL,false) || - parse_sdp_line(this,s,'k',true,NULL) || - parse_sdp_line(this,s,'a',true,NULL,false) || - parse_sdp_line(this,s,'m',false,parse_sdp_media,false); - + + bool ret = parse_sdp_line_ex(this,s); + if(!ret && conn.address.empty()){ for(vector::iterator it = media.begin(); !ret && (it != media.end()); ++it) ret = it->conn.address.empty(); - + if(ret){ - ERROR("A connection field must be present in every\n"); + ERROR("A connection field must be field must be present in every\n"); ERROR("media description or at the session level.\n"); } } - + telephone_event_pt = findPayload("telephone-event"); - + return ret; + } - -int AmSdp::genResponse(const string& localip, int localport, - string& out_buf, bool single_codec) +int AmSdp::genResponse(const string& localip, int localport, string& out_buf, bool single_codec) { string l_ip = "IP4 " + localip; - + #ifdef SUPPORT_IPV6 if(localip.find('.') == string::npos) - l_ip = "IP6 " + localip; + l_ip = "IP6" + localip; #endif - - out_buf = - "v=0\r\n" - "o=- 0 0 IN " + l_ip + "\r\n" - "s=session\r\n"; - if (!uri.empty()) - out_buf+="u="+uri+"\r\n"; - out_buf+= - "c=IN " + l_ip + "\r\n" - "t=0 0\r\n" - "m=audio " + int2str(localport) + " RTP/AVP"; - - string payloads; - string options; - - for(vector::iterator it = sup_pl.begin(); - it != sup_pl.end(); ++it){ - - payloads += " " + int2str((*it)->payload_type); - options += "a=rtpmap:" + int2str((*it)->payload_type) + " " - + (*it)->encoding_name + "/" + int2str((*it)->clock_rate) + "\r\n"; - - if ((*it)->sdp_format_parameters.size()) { - // return format parameters as sent in the invite - // (we have initialized our codec with those) - options += "a=fmtp:" + int2str((*it)->payload_type) + " " - + (*it)->sdp_format_parameters + "\r\n"; - } - if (single_codec) break; - } - - if (hasTelephoneEvent()) - payloads += " " + int2str(telephone_event_pt->payload_type); - - out_buf += payloads + "\r\n" - + options; - - if (hasTelephoneEvent()) - { - out_buf += "a=rtpmap:" + int2str(telephone_event_pt->payload_type) + " " + - telephone_event_pt->encoding_name + "/" + - int2str(telephone_event_pt->clock_rate) + "\r\n" - "a=fmtp:" + int2str(telephone_event_pt->payload_type) + " 0-15\r\n"; - } - - if(remote_active /* dir == SdpMedia::DirActive */) - out_buf += "a=direction:passive\r\n"; - - return 0; + + out_buf = + "v=0\r\n" + "o=username 0 0 IN " + l_ip + "\r\n" + "s=session\r\n" + "c=IN " + l_ip + "\r\n" + "t=0 0\r\n" + "m=audio " + int2str(localport) + " RTP/AVP"; + + string payloads; + string options; + + for(vector::iterator it = sup_pl.begin(); + it != sup_pl.end(); ++it){ + payloads += " " + int2str((*it)->payload_type); + if((*it)->encoding_param <= 0){ + options += "a=rtpmap:" + int2str((*it)->payload_type) + " " + + (*it)->encoding_name + "/" + int2str((*it)->clock_rate) + "\r\n"; + }else{ + options += "a=rtpmap:" + int2str((*it)->payload_type) + " " + + (*it)->encoding_name + "/" + int2str((*it)->clock_rate) + "/" + int2str((*it)->encoding_param) + "\r\n"; + + } + + if((*it)->sdp_format_parameters.size()){ + options += "a=fmtp:" + int2str((*it)->payload_type) + " " + + (*it)->sdp_format_parameters + "\r\n"; + } + if (single_codec) break; + } + + if (hasTelephoneEvent()) + payloads += " " + int2str(telephone_event_pt->payload_type); + + out_buf += payloads + "\r\n" + +options; + + if(hasTelephoneEvent()) + { + out_buf += "a=rtpmap:" + int2str(telephone_event_pt->payload_type) + " " + + telephone_event_pt->encoding_name + "/" + + int2str(telephone_event_pt->clock_rate) + "\r\n" + "a=fmtp:" + int2str(telephone_event_pt->payload_type) + " 0-15\r\n"; + } + + if(remote_active /* dir == SdpMedia::DirActive */) + out_buf += "a=direction:passive\r\n"; + + return 0; } -int AmSdp::genRequest(const string& localip,int localport, string& out_buf) +int AmSdp::genRequest(const string& localip, int localport, string& out_buf) { AmPlugIn* plugin = AmPlugIn::instance(); - const std::map& payloads = plugin->getPayloads(); - const std::map& payload_order = plugin->getPayloadOrder(); + const map& payloads = plugin->getPayloads(); + const map& payload_order = plugin->getPayloadOrder(); if(payloads.empty()){ ERROR("no payload plugin loaded.\n"); @@ -263,105 +221,104 @@ int AmSdp::genRequest(const string& localip,int localport, string& out_buf) string l_ip = "IP4 " + localip; -#ifdef SUPPORT_IPV6 +#ifdef SUPPORT_IPV4 if(localip.find('.') == string::npos) - l_ip = "IP6 " + localip; + l_ip= "IP6 " + localip; #endif out_buf = "v=0\r\n" - "o=- 0 0 IN " + l_ip + "\r\n" - "s=session\r\n"; - if (!uri.empty()) - out_buf+="u="+uri+"\r\n"; - out_buf+= + "o=username 0 0 IN " + l_ip + "\r\n" + "s=session\r\n" "c=IN " + l_ip + "\r\n" "t=0 0\r\n" "m=audio " + int2str(localport) + " RTP/AVP "; - + std::map::const_iterator it = payload_order.begin(); out_buf += int2str((it++)->second); for(; it != payload_order.end(); ++it) - out_buf += string(" ") + int2str(it->second); + out_buf += string(" ") + int2str(it->second); out_buf += "\r\n"; - + for (it = payload_order.begin(); it != payload_order.end(); ++it) { - std::map::const_iterator it2 = payloads.find(it->second); - if (it2 != payloads.end()) { - out_buf += "a=rtpmap:" + int2str(it2->first) - + " " + string(it2->second->name) - + "/" + int2str(it2->second->sample_rate) - + "\r\n"; - } else { - ERROR("Payload %d was not found in payloads map!\n", it->second); - return -1; - } + map::const_iterator it2 = payloads.find(it->second); + if(it2 != payloads.end()){ + out_buf += "a=rtpmap:" + int2str(it2->first) + + " " + string(it2->second->name) + + "/" + int2str(it2->second->sample_rate) + + "\r\n"; + } else { + ERROR("Payload %d was not found in payloads map!\n", it->second); + return -1; + } } - return 0; + } -const vector& AmSdp::getCompatiblePayloads(int media_type, string& addr, int& port) +const vector& AmSdp::getCompatiblePayloads(int media_type, string& addr, int& port) { - vector::iterator m_it; + vector::iterator m_it; SdpPayload *payload; sup_pl.clear(); AmPlugIn* pi = AmPlugIn::instance(); - - for( m_it = media.begin(); m_it != media.end(); ++m_it ){ - + for(m_it = media.begin(); m_it != media.end(); ++m_it){ + //DBG("type found: %d\n", m_it->type); + //DBG("media port: %d\n", m_it->port); + //DBG("media transport: %d\n", m_it->transport); + // for(int i=0; i < 8 ; i++){ + // DBG("media payloads: %d\n", m_it->payloads[i].payload_type); + // DBG("media payloads: %s\n", m_it->payloads[i].encoding_name.c_str()); + // DBG("media clock rates: %d\n", m_it->payloads[i].clock_rate); + //} + // DBG("type found: %d\n", m_it->payloads[0].t); if( (media_type != m_it->type) ) continue; vector::iterator it = m_it->payloads.begin(); - for(; it != m_it->payloads.end(); ++it ) { - - amci_payload_t* a_pl = NULL; - if(it->payload_type < DYNAMIC_PAYLOAD_TYPE_START){ + for(; it!= m_it->payloads.end(); ++it) { + amci_payload_t* a_pl = NULL; + if(it->payload_type < DYNAMIC_PAYLOAD_TYPE_START) { // try static payloads a_pl = pi->payload(it->payload_type); } - if( a_pl ) { - - payload = &(*it); + if( a_pl) { + payload = &(*it); payload->int_pt = a_pl->payload_id; payload->encoding_name = a_pl->name; payload->clock_rate = a_pl->sample_rate; sup_pl.push_back(payload); } - else { + else { // Try dynamic payloads - // and give a chance to broken + // and give a chance to broken // implementation using a static payload number // for dynamic ones. - if(it->encoding_name == "telephone-event") continue; int int_pt = getDynPayload(it->encoding_name, it->clock_rate); - if(int_pt != -1){ - payload = &(*it); payload->int_pt = int_pt; sup_pl.push_back(payload); } } } - if (sup_pl.size() > 0) + if( sup_pl.size() > 0) { - if (m_it->conn.address.empty()) - { - DBG("using global address: %s\n",conn.address.c_str()); - addr = conn.address; - } + if(m_it->conn.address.empty()) + { + DBG("using global address: %s\n", m_it->conn.address.c_str()); + addr = conn.address; + } else { - DBG("using media specific address: %s\n",m_it->conn.address.c_str()); + DBG("using media specific address: %s\n", m_it->conn.address.c_str()); addr = m_it->conn.address; } @@ -374,7 +331,7 @@ const vector& AmSdp::getCompatiblePayloads(int media_type, string& } return sup_pl; } - + bool AmSdp::hasTelephoneEvent() { return telephone_event_pt != NULL; @@ -387,10 +344,10 @@ int AmSdp::getDynPayload(const string& name, int rate) for(std::map::const_iterator pl_it = ref_payloads.begin(); pl_it != ref_payloads.end(); ++pl_it) - if( !strcasecmp(name.c_str(), pl_it->second->name) - && (rate == pl_it->second->sample_rate) ) + if( (name == pl_it->second->name) + && (rate == pl_it->second->sample_rate) ) return pl_it->first; - + return -1; } @@ -401,315 +358,461 @@ const SdpPayload *AmSdp::findPayload(const string& name) for (m_it = media.begin(); m_it != media.end(); ++m_it) { vector::iterator it = m_it->payloads.begin(); - for(; it != m_it->payloads.end(); ++it ) - { + for(; it != m_it->payloads.end(); ++it) + + { if (it->encoding_name == name) - { + { return new SdpPayload(*it); - } - } + } + } } - return NULL; } - - -// enum { TP_NONE=0, TP_RTPAVP, TP_UDP }; -char* transport_prot_lookup[] = { "RTP/AVP", "udp", 0 }; - -inline bool parse_transport_prot(char*& s, int& tp) -{ return parse_type(s,tp,transport_prot_lookup,"transport protocol"); } - -// enum { MT_NONE=0, MT_AUDIO, MT_VIDEO, MT_APPLICATION, MT_DATA }; -char* media_type_lookup[] = { "audio", "video", "application", "data", 0 }; -#define parse_media_type(s,mt) parse_type(s,mt,media_type_lookup,"media type") - -//inline bool parse_media_type(char*& s, int& mt) -//{ return parse_type(s,mt,media_type_lookup,"media type"); } - -// enum { NT_OTHER=0, NT_IN }; -char* net_type_lookup[] = { "IN", 0 }; - -inline bool parse_net_type(char*& s, int& network) -{ return parse_type(s,network,net_type_lookup,"net type"); } - -// enum { AT_NONE=0, AT_V4, AT_V6 }; -char* addr_type_lookup[] = { "IP4", "IP6", 0 }; - -inline bool parse_addr_type(char*& s, int& addr_t) -{ return parse_type(s,addr_t,addr_type_lookup,"address type"); } - - - -inline char* get_next_line(char* s) +//parser +static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s) { - char* next_line=s; - // search for next line - while( *next_line != '\0') { + char* next=0; + register parse_st state; + //default state + state=SDP_DESCR; + DBG("parse_sdp_line_ex: parsing sdp messages .....\n%s\n", s); - if(*next_line == 13){ - *next_line = '\0'; - next_line += 2; - break; - } - else if(*next_line == 10){ - *(next_line++) = '\0'; + while(*s != '\0'){ + switch(state){ + case SDP_DESCR: + switch(*s){ + case 'v': + { + s = is_eql_next(s); + next = get_next_line(s); + string version(s, int(next-s)-2); + str2i(version, sdp_msg->version); + DBG("parse_sdp_line_ex: found version\n"); + s = next; + state = SDP_DESCR; + break; + + } + case 'o': + DBG("parse_sdp_line_ex: found origin\n"); + s = is_eql_next(s); + parse_sdp_origin(sdp_msg, s); + s = get_next_line(s); + state = SDP_DESCR; + break; + case 's': + { + DBG("parse_sdp_line_ex: found session\n"); + s = is_eql_next(s); + next = get_next_line(s); + string sessionName(s, int(next-s)-2); + sdp_msg->sessionName = sessionName; + s = next; + break; + } + case 'i': + case 'u': + case 'e': + case 'p': + case 'b': + case 't': + case 'k': + DBG("parse_sdp_line_ex: found some random characters\n"); + s = is_eql_next(s); + next = get_next_line(s); + s = next; + state = SDP_DESCR; + break; + case 'a': + DBG("parse_sdp_line_ex: found attributes\n"); + s = is_eql_next(s); + next = get_next_line(s); + // parse_sdp_attr(sdp_msg, s); + s = next; + state = SDP_DESCR; + break; + case 'c': + DBG("parse_sdp_line_ex: found connection\n"); + s = is_eql_next(s); + parse_sdp_connection(sdp_msg, s, 'd'); + s = get_next_line(s); + state = SDP_DESCR; + break; + case 'm': + DBG("parse_sdp_line_ex: found media\n"); + state = SDP_MEDIA; + break; + + default: + { + next = get_next_line(s); + string line(s, int(next-s)-1); + DBG("parse_sdp_line: skipping unknown Session description %s=\n", (char*)line.c_str()); + s = next; + break; + } + } break; - } - next_line++; - } - return next_line; -} -// WARNING: -// -// If you call this function from a handler function, -// do not forget to set 'next_line' to the proper value -// (else, it could think EOT has been reached !) -// -// Generally: call this function if you know what your doing ! -// Else call 'parse_sdp_line' -// -inline bool parse_sdp_line_ex( AmSdp* sdp_msg, char*& s, char discr, - bool optional, - bool (*parse_func)(AmSdp*,char*&,char*&), - bool only_one, char*& next_line) -{ - while(true){ - - if((*s == '\0') && !optional){ - ERROR("parse_sdp_line : unexpected end of text while looking for '%c'\n",discr); - return true; - } - - if(*s == discr) { - - if( *(++s) != '=' ){ - ERROR("parse_sdp_line : expected '=' but " - "<%c> found \n",*s); - return true; + case SDP_MEDIA: + switch(*s){ + case 'm': + s = is_eql_next(s); + parse_sdp_media(sdp_msg, s); + s = get_next_line(s); + state = SDP_MEDIA; + break; + case 'i': + s = is_eql_next(s); + s = get_next_line(s); + state = SDP_MEDIA; + break; + case 'c': + s = is_eql_next(s); + DBG("parse_sdp_line: found media connection\n"); + parse_sdp_connection(sdp_msg, s, 'm'); + s = get_next_line(s); + state = SDP_MEDIA; + break; + case 'b': + s = is_eql_next(s); + s = get_next_line(s); + state = SDP_MEDIA; + break; + case 'k': + s = is_eql_next(s); + s = get_next_line(s); + state = SDP_MEDIA; + break; + case 'a': + s = is_eql_next(s); + DBG("parse_sdp_line: found media attr\n"); + parse_sdp_attr(sdp_msg, s); + s = get_next_line(s); + state = SDP_MEDIA; + break; + + default : + { + next = get_next_line(s); + string line(s, int(next-s)-1); + DBG("parse_sdp_line: skipping unknown Media description %s=\n", (char*)line.c_str()); + s = next; + break; + } } - s++; - - next_line = get_next_line(s); - bool ret=false; - if(parse_func) - ret = (*parse_func)(sdp_msg,s,next_line); - - s = next_line; - if(only_one || (*s != discr)) - return ret; - - continue; - } - else if(!optional){ - ERROR(" parse_sdp_line : parameter '%c=' was " - "not found\n",discr); - return true; - } - - // token is optional and has not been found. - return false; - } -} - -// WARNING: -// -// Do not touch the handler's third parameter until you known what you are doing. -// -inline bool parse_sdp_line( AmSdp* sdp_msg, char*& s, char discr, - bool optional, bool (*parse_func)(AmSdp*,char*&,char*&), - bool only_one) -{ - char* next_line=0; - return parse_sdp_line_ex(sdp_msg,s,discr,optional,parse_func,only_one,next_line); -} - -inline bool parse_string_tok(char*& s, string& res, char sep_char) -{ - for( ;(*s != '\0') && (*s == ' '); s++); - char* begin = s; - - while(*s != '\0'){ - - if(*s == sep_char){ - *(s++) = '\0'; break; } - s++; } - - res = begin; - return res.empty(); + DBG("parse_sdp_line_ex: parsing sdp message done :) \n"); + return false; } -inline bool parse_type(char*& s, int& v, char** ref, const char* name) -{ - string e; - v=0; - if(parse_string_tok(s,e)){ - ERROR(" parse_type : while parsing %s\n",name); - return true; - } - char** cur = ref; - while(*cur){ - if(e == *cur){ - v = cur-ref+1; +static void parse_sdp_connection(AmSdp* sdp_msg, char* s, char t) +{ + + char* connection_line=s; + char* next=0; + char* line_end=0; + int parsing=1; + + SdpConnection c; + + line_end = get_next_line(s); + register sdp_connection_st state; + state = NET_TYPE; + + DBG("parse_sdp_line_ex: parse_sdp_connection: parsing sdp connection\n"); + + while(parsing){ + switch(state){ + case NET_TYPE: + //Ignore NET_TYPE since it is always IN + connection_line +=3; + state = ADDR_TYPE; break; + case ADDR_TYPE: + { + string addr_type(connection_line,3); + connection_line +=4; + if(addr_type == "IP4"){ + c.addrType = 1; + state = IP4; + }else if(addr_type == "IP6"){ + c.addrType = 2; + state = IP6; + }else{ + ERROR("parse_sdp_connection: Unknow addr_type in c=\n"); + c.addrType = 0; + parsing = 0; + } + break; + } + case IP4: + { + if(contains(connection_line, line_end, '/')){ + next = parse_until(s, '/'); + string ip4(connection_line,int(next-connection_line)-1); + c.address = ip4; + char* s = (char*)ip4.c_str(); + inet_aton(s, &c.ipv4.sin_addr); + }else{ + string ip4(connection_line, int(line_end-connection_line)-1); + c.address = ip4; + char* s = (char*)ip4.c_str(); + inet_aton(s, &c.ipv4.sin_addr); + } + parsing = 0; + break; + } + + case IP6: + { + if(contains(connection_line, line_end, '/')){ + next = parse_until(s, '/'); + string ip6(connection_line, int(next-connection_line)-1); + c.address = ip6; + char* s = (char*)ip6.c_str(); + inet_pton(AF_INET6, s, &c.ipv6.sin6_addr); + }else{ + string ip6(connection_line, int(line_end-connection_line)-1); + c.address = ip6; + char* s = (char*)ip6.c_str(); + inet_pton(AF_INET6, s, &c.ipv6.sin6_addr); + } + parsing = 0; + break; + } } - cur++; - } - if(!*cur){ - ERROR("unkown %s: <%s>\n",name,e.c_str()); - return true; - } - return false; -} - -/* - static bool parse_sdp_version(AmSdp* sdp_msg, char*& s, char*&) - { - return str2i(s,sdp_msg->version); } -*/ - -inline bool parse_sdp_connection_struct(SdpConnection& c, char*& s) -{ - return parse_net_type(s,c.network) || - parse_addr_type(s,c.addrType) || - parse_string_tok(s,c.address); -} - -/* - static bool parse_sdp_origin(AmSdp* sdp_msg, char*& s, char*&) - { - SdpOrigin& o = sdp_msg->origin; - return parse_string_tok(s,o.user) || - str2i(s,o.sessId) || - str2i(s,o.sessV) || - parse_sdp_connection_struct(o.conn,s); - parse_net_type(s,o.conn.network) || - parse_addr_type(s,o.conn.addrType) || - parse_string_tok(s,o.conn.address); + if(t == 'd') + sdp_msg->conn = c; + if(t == 'm'){ + SdpMedia& media = sdp_msg->media.back(); + media.conn = c; } -*/ -static bool parse_sdp_connection(AmSdp* sdp_msg, char*& s, char*&) -{ - return parse_sdp_connection_struct(sdp_msg->media.empty() ? - sdp_msg->conn : - sdp_msg->media.back().conn, - s); + DBG("parse_sdp_line_ex: parse_sdp_connection: done parsing sdp connection\n"); + return; } -inline bool parse_codec_list(char*& s, vector& payloads) -{ - unsigned int payload; - - while(*s != '\0'){ - - if(!str2i(s,payload)){ - - payloads.push_back(SdpPayload(payload)); - } - else { - ERROR("invalid payload number found in media line\n"); - return true; - } - } - - return false; -} -static bool parse_sdp_media(AmSdp* sdp_msg, char*& s, char*& next_line) +static void parse_sdp_media(AmSdp* sdp_msg, char* s) { SdpMedia m; + + //clear(m); + + register sdp_media_st state; + state = MEDIA; + int parsing = 1; + char* media_line=s; + char* next=0; + char* line_end=0; + line_end = get_next_line(media_line); + SdpPayload payload; + unsigned int payload_type; + DBG("parse_sdp_line_ex: parse_sdp_media: parsing media description, %s \n", s); m.dir = SdpMedia::DirBoth; - char* old_s = s; - bool ret = parse_media_type(s,m.type) || - str2i(s,m.port) || - parse_transport_prot(s,m.transport); - - if(ret){ - ERROR("while parsing 'm=%s' line.\n",old_s); - return true; + while(parsing){ + switch(state){ + case MEDIA: + { + next = parse_until(media_line, ' '); + string media(media_line, int(next-media_line)-1); + if(media_type(media) < 0 ) + ERROR("parse_sdp_media: Unknow media type\n"); + m.type = media_type(media); + media_line = next; + state = PORT; + break; + } + case PORT: + { + next = parse_until(media_line, ' '); + //check for multiple ports + if(contains(media_line, next, '/')){ + //port number + next = parse_until(media_line, '/'); + string port(media_line, int(next-media_line)-1); + str2i(port, m.port); + //number of ports + media_line = next; + next = parse_until(media_line, ' '); + string nports(media_line, int(next-media_line)-1); + str2i(nports, m.nports); + }else{ + //port number + next = parse_until(media_line, ' '); + const string port(media_line, int(next-media_line)-1); + str2i(port, m.port); + media_line = next; + } + state = PROTO; + break; + } + case PROTO: + { + next = parse_until(media_line, ' '); + string proto(media_line, int(next-media_line)-1); + if(transport_type(proto) < 0){ + ERROR("parse_sdp_media: Unknow transport protocol\n"); + state = FMT; + break; + } + m.transport = transport_type(proto); + media_line = next; + state = FMT; + break; + } + case FMT: + { + if(contains(media_line, line_end, ' ')){ + next = parse_until(media_line, ' '); + //if(next < line_end){ + string value(media_line, int(next-media_line)-1); + media_line = next; + payload.type = m.type; + str2i(value, payload_type); + payload.payload_type = payload_type; + m.payloads.push_back(payload); + state = FMT; + //check if this lines is also the last + }else if (*(line_end-1) == '\0'){ + string last_value(media_line, int(line_end-media_line)-1); + payload.type = m.type; + str2i(last_value, payload_type); + payload.payload_type = payload_type; + m.payloads.push_back(payload); + parsing = 0; + //if not + }else{ + //check if it should be -1 or -2 + string last_value(media_line, int(line_end-media_line)-1); + payload.type = m.type; + str2i(last_value, payload_type); + payload.payload_type = payload_type; + m.payloads.push_back(payload); + parsing=0; + } + break; + } + } } - - if(!ret && (m.transport == TP_RTPAVP)) - ret = ret || parse_codec_list(s,m.payloads); - sdp_msg->media.push_back(m); - s = next_line; - DBG("next_line=<%s>\n",next_line); - ret = ret - // Media title - || parse_sdp_line(sdp_msg,s,'i',true,NULL) - // connection information - optional if included at session-level - || parse_sdp_line(sdp_msg,s,'c',true,parse_sdp_connection) - // bandwidth information - || parse_sdp_line(sdp_msg,s,'b',true,NULL,false) - // encryption key - || parse_sdp_line(sdp_msg,s,'k',true,NULL) - // zero or more media attribute lines - || parse_sdp_line(sdp_msg,s,'a',true,parse_sdp_attribute,false); - - if(ret){ - ERROR("while parsing media attributes.\n"); - return true; - } - - next_line = get_next_line(s); - DBG("ret=%i; next_line=<%s>\n",ret,next_line); - - return ret; + DBG("parse_sdp_line_ex: parse_sdp_media: done parsing media description \n"); + return; } -static bool parse_sdp_attribute(AmSdp* sdp_msg, char*& s, char*& next_line) +static void parse_sdp_attr(AmSdp* sdp_msg, char* s) { - DBG("parse_sdp_attribute: s=%s\n",s); + + DBG("parse_sdp_line_ex: parse_sdp_attr.......\n"); if(sdp_msg->media.empty()){ ERROR("While parsing media options: no actual media !\n"); - return true; + return; } - + SdpMedia& media = sdp_msg->media.back(); - - char* sep=0; - for( sep=s; *sep!='\0' && *sep!=':'; sep++ ); - - if( *sep == ':' ){ - - // attribute definition: 'attribute:value' - string attr_name(s,int(sep-s)); - char* old_s = s; - s = sep + 1; - - if(attr_name == "rtpmap"){ - - //fmt: " /[/]" - unsigned int payload_type=0,clock_rate=0; - string encoding_name, params; - bool ret = str2i(s,payload_type) - || parse_string_tok(s,encoding_name,'/') - || str2i(s,clock_rate,'/'); - - if(ret){ - ERROR("while parsing 'a=%s'\n",old_s); - return true; + SdpPayload payload; + + register sdp_attr_rtpmap_st rtpmap_st; + register sdp_attr_fmtp_st fmtp_st; + rtpmap_st = TYPE; + fmtp_st = FORMAT; + char* attr_line=s; + char* next=0; + char* line_end=0; + int parsing = 1; + line_end = get_next_line(attr_line); + + unsigned int payload_type, clock_rate, encoding_param = 0; + string encoding_name, params; + + if(contains(attr_line, line_end, ':')){ + next = parse_until(attr_line, ':'); + string attr(attr_line, int(next-attr_line)-1); + attr_line = next; + if(attr == "rtpmap"){ + while(parsing){ + switch(rtpmap_st){ + case TYPE: + { + next = parse_until(attr_line, ' '); + string type(attr_line, int(next-attr_line)-1); + str2i(type,payload_type); + attr_line = next; + rtpmap_st = ENC_NAME; + break; + } + case ENC_NAME: + { + if(contains(s, line_end, '/')){ + next = parse_until(attr_line, '/'); + string enc_name(attr_line, int(next-attr_line)-1); + encoding_name = enc_name; + attr_line = next; + rtpmap_st = CLK_RATE; + break; + }else{ + rtpmap_st = ENC_PARAM; + break; + } + } + case CLK_RATE: + { + // check for posible encoding parameters after clock rate + if(contains(attr_line, line_end, '/')){ + next = parse_until(attr_line, '/'); + string clk_rate(attr_line, int(next-attr_line)-1); + str2i(clk_rate, clock_rate); + attr_line = next; + rtpmap_st = ENC_PARAM; + //last line check + }else if (*(line_end-1) == '\0') { + string clk_rate(attr_line, int(line_end-attr_line)-1); + str2i(clk_rate, clock_rate); + parsing = 0; + //more lines to come + }else{ + string clk_rate(attr_line, int(line_end-attr_line)-1); + str2i(clk_rate, clock_rate); + parsing=0; + } + + break; + } + case ENC_PARAM: + { + next = parse_until(attr_line, ' '); + if(next < line_end){ + string value(attr_line, int(next-attr_line)-1); + str2i(value, encoding_param); + attr_line = next; + params += value; + params += ' '; + rtpmap_st = ENC_PARAM; + }else{ + string last_value(attr_line, int(line_end-attr_line)-1); + str2i(last_value, encoding_param); + params += last_value; + parsing = 0; + } + break; + } + break; + } } - - parse_string_tok(s,params,'\0'); - DBG("sdp attribute: pt=%u; enc=%s; cr=%u\n", - payload_type,encoding_name.c_str(),clock_rate); - + vector::iterator pl_it; - + for( pl_it=media.payloads.begin(); (pl_it != media.payloads.end()) && (pl_it->payload_type != int(payload_type)); @@ -718,52 +821,234 @@ static bool parse_sdp_attribute(AmSdp* sdp_msg, char*& s, char*& next_line) if(pl_it != media.payloads.end()){ *pl_it = SdpPayload( int(payload_type), encoding_name, - int(clock_rate)); + int(clock_rate), + int(encoding_param)); } - return ret; - } - else if (attr_name == "fmtp") { - // fmt: " parameters" (?) - // z.b. a=fmtp:101 0-15 - unsigned int payload_type=0; - string params; - bool ret = str2i(s, payload_type) || parse_string_tok(s, params, '\0'); - - vector::iterator pl_it; + - for( pl_it=media.payloads.begin(); - (pl_it != media.payloads.end()) - && (pl_it->payload_type != int(payload_type)); - ++pl_it); + }else if(attr == "fmtp"){ + while(parsing){ + switch(fmtp_st){ + case FORMAT: + { + next = parse_until(attr_line, ' '); + string fmtp_format(attr_line, int(next-attr_line)-1); + str2i(fmtp_format, payload_type); + attr_line = next; + fmtp_st = FORMAT_PARAM; + break; + } + case FORMAT_PARAM: + { + next = parse_until(attr_line, ' '); + if(next < line_end){ + string value(attr_line, int(next-attr_line)-1); + attr_line = next; + params += value; + params += ' '; + fmtp_st = FORMAT_PARAM; + }else{ + string last_value(attr_line, int(line_end-attr_line)-1); + params += last_value; + parsing = 0; + } + break; + } + break; + } + } + // payload.type = media.type; + //payload.payload_type = payload_type; + //payload.sdp_format_parameters = params; - - if(pl_it != media.payloads.end()) - pl_it->sdp_format_parameters = params; - - return ret; - } - else if(attr_name == "direction"){ - if(!strncmp(s,"active",6/*sizeof("active")*/)) - media.dir = SdpMedia::DirActive; - else if(!strncmp(s,"passive",7/*sizeof("passive")*/)) - media.dir = SdpMedia::DirPassive; - else if(!strncmp(s,"both",4/*sizeof("both")*/)) - media.dir = SdpMedia::DirBoth; - else - DBG("unknown value for a=direction:%s",s); - } - else { - DBG("unknown attribute definition '%s'\n",old_s); + + }else{ + attr_check(attr); + attr_line = next; + string value(attr_line, int(line_end-attr_line)-1); + //payload.type = media.type; + //payload.encoding_name = attr; + //payload.sdp_format_parameters = value; + } + }else{ + string attr(attr_line, int(line_end-attr_line)-1); + attr_check(attr); + //payload.type = media.type; + //payload.encoding_name = attr; + } - else { - // flag: 'flag_name' - DBG("flag definition is not yet supported (%s)\n",s); + + media.payloads.push_back(payload); + + DBG("parse_sdp_line_ex: parse_sdp_attr: done parsing sdp attributes\n"); + return; +} + +static void parse_sdp_origin(AmSdp* sdp_msg, char* s) +{ + char* origin_line = s; + char* next=0; + char* line_end=0; + line_end = get_next_line(s); + + register sdp_origin_st origin_st; + origin_st = USER; + int parsing = 1; + + SdpOrigin origin; + + DBG("parse_sdp_line_ex: parse_sdp_origin: parsing sdp origin\n"); + + while(parsing){ + switch(origin_st) + { + case USER: + { + next = parse_until(origin_line, ' '); + if(next > line_end){ + DBG("parse_sdp_origin: ST_USER: Incorrect number of value in o=\n"); + origin_st = UNICAST_ADDR; + break; + } + string user(origin_line, int(next-origin_line)-1); + origin.user = user; + origin_line = next; + origin_st = ID; + break; + } + case ID: + { + next = parse_until(origin_line, ' '); + if(next > line_end){ + DBG("parse_sdp_origin: ST_ID: Incorrect number of value in o=\n"); + origin_st = UNICAST_ADDR; + break; + } + string id(origin_line, int(next-origin_line)-1); + str2i(id, origin.sessId); + origin_line = next; + origin_st = VERSION_ST; + break; + } + case VERSION_ST: + { + next = parse_until(origin_line, ' '); + if(next > line_end){ + DBG("parse_sdp_origin: ST_VERSION: Incorrect number of value in o=\n"); + origin_st = UNICAST_ADDR; + break; + } + string version(origin_line, int(next-origin_line)-1); + str2i(version, origin.sessV); + origin_line = next; + origin_st = NETTYPE; + break; + } + case NETTYPE: + { + next = parse_until(origin_line, ' '); + if(next > line_end){ + DBG("parse_sdp_origin: ST_NETTYPE: Incorrect number of value in o=\n"); + origin_st = UNICAST_ADDR; + break; + } + string net_type(origin_line, int(next-origin_line)-1); + origin_line = next; + origin_st = ADDR; + break; + } + case ADDR: + { + next = parse_until(origin_line, ' '); + if(next > line_end){ + DBG("parse_sdp_origin: ST_ADDR: Incorrect number of value in o=\n"); + origin_st = UNICAST_ADDR; + break; + } + string addr_type(origin_line, int(next-origin_line)-1); + origin_line = next; + origin_st = UNICAST_ADDR; + break; + } + case UNICAST_ADDR: + { + next = parse_until(origin_line, ' '); + //check if line contains more values than allowed + if(next > line_end){ + string unicast_addr(origin_line, int(line_end-origin_line)-1); + }else{ + DBG("parse_sdp_origin: 'o=' contains more values than allowed; these values will be ignored\n"); + string unicast_addr(origin_line, int(next-origin_line)-1); + } + parsing = 0; + break; + } + } } + + sdp_msg->origin = origin; + + DBG("parse_sdp_line_ex: parse_sdp_origin: done parsing sdp origin\n"); + return; +} + + +/* + *HELPER FUNCTIONS + */ +static bool contains(char* s, char* next_line, char c) +{ + char* line=s; + while(line != next_line-1){ + if(*line == c) + return true; + *line++; + } return false; } +static char* parse_until(char* s, char end) +{ + char* line=s; + while(*line != end ){ + line++; + } + line++; + return line; +} + + +static char* is_eql_next(char* s) +{ + char* current_line=s; + if(*(++current_line) != '='){ + DBG("parse_sdp_line: expected '=' but found <%c> \n", *current_line); + } + current_line +=1; + return current_line; +} + +inline char* get_next_line(char* s) +{ + char* next_line=s; + //search for next line + while( *next_line != '\0') { + if(*next_line == 13){ + next_line +=2; + break; + } + else if(*next_line == 10){ + next_line +=1; + break; + } + next_line++; + } + if(*next_line == '\0') + next_line +=1; + return next_line; +} diff --git a/core/AmSdp.h b/core/AmSdp.h index f8f30f22..40a71a82 100644 --- a/core/AmSdp.h +++ b/core/AmSdp.h @@ -31,6 +31,7 @@ #include #include #include +#include using std::string; @@ -50,9 +51,9 @@ enum NetworkType { NT_OTHER=0, NT_IN }; /** address type */ enum AddressType { AT_NONE=0, AT_V4, AT_V6 }; /** media type */ -enum MediaType { MT_NONE=0, MT_AUDIO, MT_VIDEO, MT_APPLICATION, MT_DATA }; +enum MediaType { MT_NONE=0, MT_AUDIO, MT_VIDEO, MT_APPLICATION, MT_TEXT, MT_MESSAGE }; /** transport protocol */ -enum TransProt { TP_NONE=0, TP_RTPAVP, TP_UDP }; +enum TransProt { TP_NONE=0, TP_RTPAVP, TP_UDP, TP_RTPSAVP }; /** \brief c=... line in SDP*/ struct SdpConnection @@ -61,6 +62,9 @@ struct SdpConnection int network; /** @see AddressType */ int addrType; + + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; /** IP address */ string address; @@ -82,18 +86,21 @@ struct SdpOrigin */ struct SdpPayload { + int type; int int_pt; // internal payload type int payload_type; // SDP payload type string encoding_name; int clock_rate; // sample rate (Hz) + string format; string sdp_format_parameters; + int encoding_param; - SdpPayload() : int_pt(-1), payload_type(-1), clock_rate(-1) {} + SdpPayload() : int_pt(-1), payload_type(-1), clock_rate(-1), encoding_param(-1) {} - SdpPayload(int pt) : int_pt(-1), payload_type(pt), clock_rate(-1) {} + SdpPayload(int pt) : int_pt(-1), payload_type(pt), clock_rate(-1), encoding_param(-1) {} - SdpPayload(int pt, const string& name, int rate) - : int_pt(-1), payload_type(pt), encoding_name(name), clock_rate(rate) {} + SdpPayload(int pt, const string& name, int rate, int param) + : int_pt(-1), payload_type(pt), encoding_name(name), clock_rate(rate), encoding_param(param) {} bool operator == (int r); }; @@ -110,6 +117,7 @@ struct SdpMedia int type; unsigned int port; + unsigned int nports; int transport; SdpConnection conn; // c= Direction dir; // a=direction @@ -147,7 +155,7 @@ public: unsigned int version; // v= SdpOrigin origin; // o= string sessionName; // s= - string uri; // u= + string uri; // u= SdpConnection conn; // c= std::vector media; // m= ... [a=rtpmap:...]+ diff --git a/core/AmUtils.cpp b/core/AmUtils.cpp index 72e001ff..14f946b3 100644 --- a/core/AmUtils.cpp +++ b/core/AmUtils.cpp @@ -165,10 +165,21 @@ bool str2i(char*& str, unsigned int& result, char sep) ret=ret*10+*str-'0'; i++; if (i>10) goto error_digits; - } else if( *str == sep ) + } else { + + bool eol = false; + switch(*str){ + case 0xd: + case 0xa: + case 0x0: + eol = true; + } + + if( (*str != sep) && !eol ) + goto error_char; + break; - else - goto error_char; + } } result = ret; @@ -178,7 +189,7 @@ bool str2i(char*& str, unsigned int& result, char sep) DBG("str2i: too many letters in [%s]\n", init); return true; error_char: - DBG("str2i: unexpected char %c in %s\n", *str, init); + DBG("str2i: unexpected char 0x%x in %s\n", *str, init); return true; } diff --git a/core/ErrorSdp.h b/core/ErrorSdp.h new file mode 100644 index 00000000..24a74b6e --- /dev/null +++ b/core/ErrorSdp.h @@ -0,0 +1,92 @@ +/* + * + */ + + +#ifndef __ErrorSdp__ +#define __ErrorSdp__ + +#include +#include +#include +#include "log.h" +using namespace std; + + +/* + *Check if known media type is used + */ +static int media_type(string media) +{ + if(media == "audio") + return 1; + else if(media == "video") + return 2; + else if(media == "application") + return 3; + else if(media == "text") + return 4; + else if(media == "message") + return 5; + else + return -1; +} + +static int transport_type(string transport) +{ + if(transport == "RTP/AVP") + return 1; + else if(transport == "UDP") + return 2; + else if(transport == "RTP/SAVP") + return 3; + else + return -1; +} + +/* +*Check if known attribute name is used +*/ +static bool attr_check(string attr) +{ + if(attr == "cat") + return true; + else if(attr == "keywds") + return true; + else if(attr == "tool") + return true; + else if(attr == "ptime") + return true; + else if(attr == "maxptime") + return true; + else if(attr == "recvonly") + return true; + else if(attr == "sendrecv") + return true; + else if(attr == "sendonly") + return true; + else if(attr == "inactive") + return true; + else if(attr == "orient") + return true; + else if(attr == "type") + return true; + else if(attr == "charset") + return true; + else if(attr == "sdplang") + return true; + else if(attr == "lang") + return true; + else if(attr == "framerate") + return true; + else if(attr == "quality") + return true; + else + { + DBG("sdp_parse_attr: Unknow attribute name used: %s, plz see RFC4566\n", (char*)attr.c_str()); + return false; + } +} + + +#endif