Import commercial version of YATE SIP and SDP implementation.

This commit is contained in:
Ioana Stanciu 2023-05-23 15:16:31 +03:00
parent 3539304e63
commit afc46246ca
13 changed files with 1068 additions and 169 deletions

View File

@ -155,6 +155,9 @@
; OBSOLETE - please use "enable" in section [options]
;options=enable
; update: bool: Enable receiving UPDATE transactions (RFC 3311)
;update=disable
; prack: bool: Enable acknowledging provisional 1xx answers (RFC 3262)
;prack=disable
@ -247,10 +250,15 @@
; Defaults to enable
;honor_dtmf_detect=enable
; rfc2833: bool: Offer RFC2833 telephone-event by default
; rfc2833: bool: Offer RFC2833 telephone-event 8KHz by default
; A numeric payload >= 96 can be provided
;rfc2833=yes
; rfc2833_RATE: bool: Offer RFC2833 telephone-event for specific rate (non 8KHz) by default
; A numeric payload >= 96 can be provided
; Supported rates (parameters): rfc2833_16000, rfc2833_32000
;rfc2833_RATE=yes
; privacy: bool: Process and generate privacy related SIP headers
;privacy=disable
@ -260,6 +268,9 @@
; forward_sdp: bool: Include the raw SDP body to be used as-is for forwarding RTP
;forward_sdp=disable
; forward_gpmd: bool: Propagate GPMD even when not forwarding RTP
;forward_gpmd=disable
; rtp_start: bool: Start RTP when sending 200 on incoming instead of receiving ACK
;rtp_start=disable
@ -370,12 +381,36 @@
; If set this parameter must be less than 'tcp_keepalive'
;tcp_keepalive_first=0
; ssdp_prefix: string: Prefix to use when handling SDP session level parameters
; This parameter is used when setting them in yate messages or handling them from there
; This parameter is applied on reload
; Prefix used to set parsed SDP: <ssdp_prefix>_ (default: ssdp_)
; Prefix used to update from yate messages: o<ssdp_prefix>_ (default: ossdp_)
; When updated from yate messages the prefix must be set in 'ossdp-prefix' message parameter
;ssdp_prefix=ssdp
; initial_headers: boolean: Put all headers from initial requests in yate message
; Handled for incoming channel preroute, user (un)register and messages sent on SIP
; requests received outside a dialog
; This parameter is applied on reload
;initial_headers=no
; reinvite_wait_initial: boolean: Wait for answered initial transaction termination when need to send
; a re-INVITE and initial transaction was not terminated
; Applicable for the inbound call leg
; This parameter is handled when answer (200 OK) was sent to initial transaction
; If enabled the module will not send an UPDATE even if supported by remote
; This parameter can be overridden from routing
; This parameter is applied on reload
;reinvite_wait_initial=no
; mixed_provisional: boolean: Accept mixed (non)reliable provisional responses to initial transaction
; When enabled (default) the dialog will accept non reliable provisional messages
; after receiving a reliable one
; This parameter can be overridden from routing
; This parameter is applied on reload
;mixed_provisional=yes
; warn_bind_fail_delay: integer/string: Delay failed to bind debug message
; This parameter may be used when listener is going to bind on an IP which may become
; available later

View File

@ -6,3 +6,4 @@ core*
*.orig
*~
.*.swp

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -23,16 +23,24 @@
namespace TelEngine {
const TokenDict SDPMedia::s_sdpDir[] = {
{"sendrecv", DirBidir},
{"sendonly", DirSend},
{"recvonly", DirRecv},
{"inactive", DirInactive},
{0, 0},
};
/*
* SDPMedia
*/
SDPMedia::SDPMedia(const char* media, const char* transport, const char* formats,
int rport, int lport)
: NamedList(media),
m_audio(true), m_video(false), m_modified(false), m_securable(true),
m_audio(true), m_video(false), m_modified(false), m_securable(true), m_haveRfc3833(false),
m_localChanged(false),
m_transport(transport), m_formats(formats),
m_rfc2833(String::boolText(false))
m_lDir(0), m_rDir(0)
{
DDebug(DebugAll,"SDPMedia::SDPMedia('%s','%s','%s',%d,%d) [%p]",
media,transport,formats,rport,lport,this);
@ -103,10 +111,10 @@ bool SDPMedia::sameAs(const SDPMedia* other, bool ignorePort, bool checkStarted)
}
// Check RFC 2833
if (m_rfc2833 != m.m_rfc2833) {
if (m_rfc2833.payload(m_format) != m.m_rfc2833.payload(m_format)) {
XDebug(DebugAll,
"SDPMedia::sameAs(%p) rfc2833=%s other_rfc2833=%s: not matched [%p]",
other,m_rfc2833.c_str(),m.m_rfc2833.c_str(),this);
"SDPMedia::sameAs(%p) format='%s' rfc2833=%d other_rfc2833=%d: not matched [%p]",
other,m_format.c_str(),m_rfc2833.payload(m_format),m.m_rfc2833.payload(m_format),this);
return false;
}
@ -231,6 +239,34 @@ void SDPMedia::crypto(const char* desc, bool remote)
m_securable = false;
}
void SDPMedia::direction(int value, bool remote)
{
int& v = remote ? m_rDir : m_lDir;
if (v == value)
return;
DDebug(DebugAll,"SDPMedia set %s direction %s -> %s [%p]",remote ? "remote" : "local",
lookup(v,s_sdpDir),lookup(value,s_sdpDir),this);
v = value;
}
// Retrieve negotiated media direction to be sent to remote or set in RTP
int SDPMedia::direction(int sessLDir)
{
int dir = m_lDir ? m_lDir : sessLDir;
if (!m_rDir || dir)
return dir;
switch (m_rDir) {
case DirBidir:
case DirInactive:
return m_rDir;
case DirSend:
return DirRecv;
case DirRecv:
return DirSend;
}
return 0;
}
// Put the list of net media in a parameter list
void SDPMedia::putMedia(NamedList& msg, bool putPort)
{
@ -240,7 +276,7 @@ void SDPMedia::putMedia(NamedList& msg, bool putPort)
if (mappings())
msg.addParam("rtp_mapping" + suffix(),mappings());
if (isAudio())
msg.addParam("rtp_rfc2833",rfc2833());
m_rfc2833.put(msg);
if (putPort)
msg.addParam("rtp_port" + suffix(),remotePort());
if (remoteCrypto())
@ -261,6 +297,7 @@ void SDPMedia::putMedia(NamedList& msg, bool putPort)
// Copy RTP related data from old media
void SDPMedia::keepRtp(const SDPMedia& other)
{
m_haveRfc3833 = other.m_haveRfc3833;
m_formats = other.m_formats;
m_format = other.m_format;
m_rfc2833 = other.m_rfc2833;

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -23,6 +23,13 @@
namespace TelEngine {
// RFC 2833 default payloads
#define RFC2833_8khz 101
#define RFC2833_16khz 108
#define RFC2833_32khz 109
static Rfc2833 s_rfc2833;
/*
* SDPParser
*/
@ -60,6 +67,7 @@ const TokenDict SDPParser::s_payloads[] = {
{ "h263-1998", 111 },
{ "h263-2000", 112 },
{ "h264", 114 },
{ "h265", 116 },
{ "vp8", 113 },
{ "vp9", 115 },
{ "mpv", 32 },
@ -104,6 +112,7 @@ const TokenDict SDPParser::s_rtpmap[] = {
{ "H263-1998/90000", 111 },
{ "H263-2000/90000", 112 },
{ "H264/90000", 114 },
{ "H265/90000", 116 },
{ "VP8/90000", 113 },
{ "VP9/90000", 115 },
{ "MPV/90000", 32 },
@ -133,6 +142,76 @@ static const TokenDict s_sdpFmtParamsCheck[] = {
{ 0, 0 },
};
const String Rfc2833::s_rates[RateCount] = {"8000", "16000", "32000"};
void Rfc2833::update(const NamedList& params, const Rfc2833& defaults, bool force, const String& param)
{
String pref = param.safe("rfc2833");
for (int i = 0; i < RateCount; ++i) {
const String* p = params.getParam((i == Rate8khz) ? pref : pref + "_" + s_rates[i]);
if (p)
update(i,*p,defaults);
else if (force)
m_payloads[i] = defaults[i];
}
}
// Update payload for specific rate
void Rfc2833::update(int rate, const String& value, const Rfc2833& defaults)
{
if (rate >= RateCount)
return;
if (value.toBoolean(true)) {
int val = value.toInteger();
if (96 <= val && val <= 127)
m_payloads[rate] = val;
else
m_payloads[rate] = defaults[rate];
}
else
m_payloads[rate] = -1;
}
void Rfc2833::put(NamedList& params, const String& param) const
{
String pref = param.safe("rtp_rfc2833");
for (int i = 0; i < RateCount; ++i) {
if (m_payloads[i] >= 0) {
if (i == Rate8khz)
params.addParam(pref,String(m_payloads[i]));
else
params.addParam(pref + "_" + s_rates[i],String(m_payloads[i]));
}
else if (i == Rate8khz)
params.addParam(pref,String::boolText(false));
}
}
String& Rfc2833::dump(String& buf) const
{
String tmp;
for (int i = 0; i < RateCount; ++i) {
if (m_payloads[i] >= 0)
tmp.append(s_rates[i] + "=" + String(m_payloads[i]),",");
}
return buf.append(tmp);
}
// Select RFC 2833 rate for given media format
int Rfc2833::fmtRate(const String& fmt)
{
int pos = fmt.find('/');
if (pos <= 0)
return Rate8khz;
int r = rate(fmt.substr(pos + 1));
// G722 uses 8KHz
if (r != Rate8khz && fmt.substr(0,pos) == YSTRING("g722"))
return Rate8khz;
return r;
}
// Utility used in SDPParser::parse
// Retrieve a SDP line 'param<payload>' contents
// Trim contents spaces
@ -156,7 +235,7 @@ static inline String& getPayloadLine(String& buf, ObjList& list, int payload, co
// Parse a received SDP body
ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedia,
const String& media, bool force)
const String& media, bool force, bool handleDir)
{
DDebug(DebugAll,"SDPParser::parse(%p,%s,%p,'%s',%s)",
&sdp,addr.c_str(),oldMedia,media.safe(),String::boolText(force));
@ -178,6 +257,20 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
addr = tmp;
}
}
// Obtain session level direction
int sessRDir = 0;
if (handleDir) {
for (const ObjList* o = sdp.lines().skipNull(); o; o = o->skipNext()) {
const NamedString* l = static_cast<const NamedString*>(o->get());
if (l->name() == YSTRING("m"))
break;
if (l->name() != YSTRING("a"))
continue;
SDPMedia::setDirection(sessRDir,*l);
if (sessRDir)
break;
}
}
Lock lock(this);
ObjList* lst = 0;
bool defcodecs = m_codecs.getBoolValue("default",true);
@ -220,7 +313,9 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
ObjList* dest = &params;
bool first = true;
int ptime = 0;
int rfc2833 = -1;
Rfc2833 rfc2833;
Rfc2833 mediaAvailable;
int dir = sessRDir;
// Remember format related lines
ObjList fmtLines;
ObjList* fmtA = &fmtLines;
@ -291,7 +386,9 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
continue;
}
if (line.startsWith("TELEPHONE-EVENT/")) {
rfc2833 = var;
int rate = Rfc2833::rate(line.substr(16));
if (rate < Rfc2833::RateCount)
rfc2833[rate] = var;
payload.clear();
continue;
}
@ -323,10 +420,15 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
int pos = line.find(':');
if (pos >= 0)
dest = dest->append(new NamedString(line.substr(0,pos),line.substr(pos+1)));
else
else {
dest = dest->append(new NamedString(line));
if (handleDir)
SDPMedia::setDirection(dir,line);
}
}
}
else if (handleDir)
SDPMedia::setDirection(dir,line);
}
if (var < 0)
break;
@ -432,10 +534,24 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
dest = dest->append(new NamedString("fmtp:" + payload,fmtp));
if ((payload == "g729") && m_hacks.getBoolValue(YSTRING("g729_annexb"),annexB))
aux << ",g729b";
int r = Rfc2833::fmtRate(payload);
if (r < Rfc2833::RateCount)
mediaAvailable[r] = 1;
}
}
fmt += aux;
DDebug(this,DebugAll,"Formats '%s' mappings '%s'",fmt.c_str(),mappings.c_str());
// Change supported RFC 2833 from available media rates
for (int i = 0; i < Rfc2833::RateCount; ++i)
if (mediaAvailable[i] < 0)
rfc2833[i] = -1;
#ifdef DEBUG
String extraD;
if (type == YSTRING("audio")) {
String tmp;
extraD << " RFC 2833: " << rfc2833.dump(tmp);
}
DDebug(this,DebugAll,"Formats '%s' mappings '%s'%s",fmt.c_str(),mappings.c_str(),extraD.safe());
#endif
SDPMedia* net = 0;
// try to take the media descriptor from the old list
if (oldMedia) {
@ -456,6 +572,7 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
net->mappings(mappings);
net->rfc2833(rfc2833);
net->crypto(crypto,true);
net->direction(dir,true);
if (!lst)
lst = new ObjList;
lst->append(net);
@ -469,7 +586,13 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
// Update configuration
void SDPParser::initialize(const NamedList* codecs, const NamedList* hacks, const NamedList* general)
{
const NamedList& safeGen = general ? *general : NamedList::empty();
Lock lock(this);
if (s_rfc2833[Rfc2833::Rate8khz] < 0) {
s_rfc2833[Rfc2833::Rate8khz] = RFC2833_8khz;
s_rfc2833[Rfc2833::Rate16khz] = RFC2833_16khz;
s_rfc2833[Rfc2833::Rate32khz] = RFC2833_32khz;
}
m_codecs.clearParams();
m_hacks.clearParams();
if (codecs)
@ -497,20 +620,18 @@ void SDPParser::initialize(const NamedList* codecs, const NamedList* hacks, cons
m_audioFormats.c_str());
}
m_ignorePort = m_hacks.getBoolValue("ignore_sdp_port",false);
m_rfc2833 = 101;
m_rfc2833.update(safeGen,s_rfc2833);
String tmp;
Debug(this,DebugAll,"Initialized RFC 2833: %s",m_rfc2833.dump(tmp).c_str());
m_secure = false;
m_gpmd = false;
m_sdpForward = false;
if (general) {
if (general->getBoolValue("rfc2833",true)) {
m_rfc2833 = general->getIntValue("rfc2833",m_rfc2833);
if (m_rfc2833 < 96 || m_rfc2833 > 127)
m_rfc2833 = 101;
}
else
m_rfc2833 = -1;
m_secure = general->getBoolValue("secure",m_secure);
m_gpmd = general->getBoolValue("forward_gpmd",m_gpmd);
m_sdpForward = general->getBoolValue("forward_sdp",m_sdpForward);
}
m_ssdpParam = general ? general->getValue(YSTRING("ssdp_prefix"),"ssdp") : "ssdp";
}
}; // namespace TelEngine

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -31,7 +31,9 @@ SDPSession::SDPSession(SDPParser* parser)
m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
m_secure(m_parser->m_secure), m_rfc2833(m_parser->m_rfc2833),
m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
m_ipv6(false), m_gpmd(parser->m_gpmd),
m_amrExtra(""), m_parsedParams(0), m_createSdpParams(""),
m_enabler(0), m_ptr(0)
{
setSdpDebug();
}
@ -40,14 +42,17 @@ SDPSession::SDPSession(SDPParser* parser, NamedList& params)
: m_parser(parser), m_mediaStatus(MediaMissing),
m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
m_ipv6(false), m_gpmd(false),
m_amrExtra(""), m_parsedParams(0), m_createSdpParams(""),
m_enabler(0), m_ptr(0)
{
setSdpDebug();
m_rtpForward = params.getBoolValue("rtp_forward");
m_rtpForward = params.getBoolValue(YSTRING("rtp_forward"));
m_sdpForward = params.getBoolValue(YSTRING("forward_sdp"),m_sdpForward);
m_secure = params.getBoolValue("secure",parser->m_secure);
m_secure = params.getBoolValue(YSTRING("secure"),parser->m_secure);
m_gpmd = params.getBoolValue(YSTRING("forward_gpmd"),parser->m_gpmd);
m_rfc2833 = parser->m_rfc2833;
setRfc2833(params.getParam("rfc2833"));
setRfc2833(params,false);
}
SDPSession::~SDPSession()
@ -91,8 +96,15 @@ bool SDPSession::setMedia(ObjList* media, bool preserveExisting)
}
// Put the list of net media in a parameter list
void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort, const NamedList* sessParams)
{
if (sessParams) {
const char* prefix = sessParams->safe("ssdp_");
for (ObjList* o = sessParams->paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* ns = static_cast<const NamedString*>(o->get());
msg.addParam(prefix + ns->name(),*ns);
}
}
if (!mList)
return;
bool audio = false;
@ -110,15 +122,19 @@ void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
}
// Update the RFC 2833 availability and payload
void SDPSession::setRfc2833(const String& value)
void SDPSession::setRfc2833(const String& value, int rate)
{
if (value.toBoolean(true)) {
m_rfc2833 = value.toInteger(m_parser->m_rfc2833);
if (m_rfc2833 < 96 || m_rfc2833 > 127)
m_rfc2833 = value.toBoolean(false) ? 101 : m_parser->m_rfc2833;
}
else
m_rfc2833 = -1;
m_rfc2833.update(rate,value,m_parser->m_rfc2833);
}
// Update the RFC 2833 availability and payload
void SDPSession::setRfc2833(const NamedList& params, bool force)
{
m_rfc2833.update(params,m_parser->m_rfc2833,force);
#ifdef DEBUG
String tmp;
TraceDebug(m_traceId,m_enabler,DebugAll,"Updated RFC 2833: %s [%p]",m_rfc2833.dump(tmp).c_str(),m_ptr);
#endif
}
// Build and dispatch a chan.rtp message for a given media. Update media on success
@ -147,6 +163,7 @@ bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
if (!sdpPrefix.endsWith("_"))
sdpPrefix += "_";
unsigned int n = m->length();
int dir = SDPMedia::DirUnknown;
for (unsigned int j = 0; j < n; j++) {
const NamedString* param = m->getParam(j);
if (!param)
@ -157,8 +174,10 @@ bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
"Updating (from RTP message) %s parameter '%s' to '%s' [%p]",
media->c_str(),tmp.c_str(),param->c_str(),this);
media->parameter(tmp,*param,false);
SDPMedia::setDirection(dir,tmp);
}
}
media->direction(dir,false);
}
if (m_secure) {
int tag = m->getIntValue("crypto_tag",1);
@ -225,6 +244,7 @@ bool SDPSession::updateSDP(const NamedList& params, bool defaults)
DDebug(m_enabler,DebugAll,"SDPSession::updateSdp('%s',%s) [%p]",
params.c_str(),String::boolText(defaults),m_ptr);
const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
updateSessionParams(params);
ObjList* lst = 0;
unsigned int n = params.length();
String defFormats;
@ -309,6 +329,7 @@ bool SDPSession::updateRtpSDP(const NamedList& params)
String addr;
ObjList* tmp = updateRtpSDP(params,addr,m_rtpMedia);
if (tmp) {
updateSessionParams(params);
bool chg = (m_rtpLocalAddr != addr);
m_rtpLocalAddr = addr;
return setMedia(tmp) || chg;
@ -387,21 +408,21 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
sdp->addLine("t","0 0");
Lock lock(m_parser);
addSdpParams(sdp,m_createSdpParams);
int sessDir = 0;
// TODO: Add session level direction
bool defcodecs = m_parser->m_codecs.getBoolValue("default",true);
for (ObjList* ml = mList->skipNull(); ml; ml = ml->skipNext()) {
SDPMedia* m = static_cast<SDPMedia*>(ml->get());
int rfc2833 = 0;
if ((m_rfc2833 >= 0) && m->isAudio()) {
Rfc2833 rfc2833;
if (m->isAudio()) {
if (!m_rtpForward) {
rfc2833 = m->rfc2833().toInteger(m_rfc2833);
if (rfc2833 < 96 || rfc2833 > 127)
rfc2833 = 101;
}
else if (m->rfc2833().toBoolean(true)) {
rfc2833 = m->rfc2833().toInteger();
if (rfc2833 < 96 || rfc2833 > 127)
rfc2833 = 0;
const Rfc2833& mr = m->rfc2833();
for (int i = 0; i < Rfc2833::RateCount; ++i)
rfc2833[i] = (mr[i] >= 0) ? mr[i] : m_rfc2833[i];
}
else
rfc2833 = m->rfc2833();
}
String mline(m->fmtList());
ObjList* l = mline.split(',',false);
@ -413,6 +434,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
String frm;
int ptime = 0;
ObjList* f = l;
Rfc2833 rfc2833Send;
for (; f; f = f->next()) {
const String* s = static_cast<const String*>(f->get());
if (s) {
@ -467,7 +489,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
}
// allocate free and non-standard is possible
for (pload = 96; pload < 127; pload++) {
if (pload == rfc2833)
if (rfc2833.includes(pload))
continue;
if (lookup(pload,SDPParser::s_rtpmap))
continue;
@ -480,7 +502,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
break;
// none free, allocate from "standard" ones too
for (pload = 96; pload < 127; pload++) {
if (pload == rfc2833)
if (rfc2833.includes(pload))
continue;
if ((bmap & (1 << (pload - 96))) == 0) {
payload = pload;
@ -500,6 +522,12 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
defcode = payload;
const char* map = lookup(defcode,SDPParser::s_rtpmap);
if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) {
// Update RFC 2833 support to advertise
if (m->isAudio()) {
int r = Rfc2833::fmtRate(*s);
if (r < Rfc2833::RateCount)
rfc2833Send[r] = rfc2833[r];
}
frm << " " << payload;
String* temp = new String("rtpmap:");
*temp << payload << " " << map;
@ -536,7 +564,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
setFmtpLine(temp,payload,*fmtp);
if (temp)
dest = dest->append(temp);
if (mediaList) {
if (mediaList || m_gpmd) {
// RTP forward propagates General Purpose Media Descriptor
const String* gpmd = m->getParam("gpmd:" + *s);
if (gpmd) {
@ -552,12 +580,15 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
TelEngine::destruct(l);
TelEngine::destruct(map);
if (rfc2833 && frm) {
// claim to support telephone events
frm << " " << rfc2833;
String* s = new String;
*s << "rtpmap:" << rfc2833 << " telephone-event/8000";
dest = dest->append(s);
if (frm && m->isAudio()) {
for (int i = 0; i < Rfc2833::RateCount; ++i) {
if (rfc2833Send[i] < 0)
continue;
frm << " " << rfc2833Send[i];
String* s = new String;
s->printf("rtpmap:%d telephone-event/%s",rfc2833Send[i],Rfc2833::rateValue(i).c_str());
dest = dest->append(s);
}
}
if (frm.null()) {
@ -584,26 +615,9 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
sdp->addLine("m",mline + frm);
bool enc = false;
if (m->isModified()) {
unsigned int n = m->length();
for (unsigned int i = 0; i < n; i++) {
const NamedString* param = m->getParam(i);
if (param && (param->name().find(':') < 0)) {
const char* type = "a";
String tmp = param->name();
if (tmp.startSkip("BW-",false)) {
if (!tmp)
continue;
type = "b";
}
else
enc = enc || (tmp == "encryption");
if (*param)
tmp << ":" << *param;
sdp->addLine(type,tmp);
}
}
}
bool addedDir = false;
if (m->isModified())
addSdpParams(sdp,*m,&enc,&addedDir);
for (f = rtpmap.skipNull(); f; f = f->skipNext()) {
String* s = static_cast<String*>(f->get());
if (s)
@ -614,6 +628,11 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
if (!enc)
sdp->addLine("a","encryption:optional");
}
if (!addedDir) {
const char* dir = lookup(m->direction(sessDir),SDPMedia::s_sdpDir);
if (dir)
sdp->addLine("a",dir);
}
}
// increment version if body hash changed
if ((YSTRING_INIT_HASH != m_sdpHash) && (sdp->hash() != m_sdpHash))
@ -655,6 +674,7 @@ MimeSdpBody* SDPSession::createPasstroughSDP(NamedList& msg, bool update,
msg.setParam("rtp_forward","accepted");
return new MimeSdpBody("application/sdp",raw->safe(),raw->length());
}
updateSessionParams(msg);
String addr;
ObjList* lst = updateRtpSDP(msg,addr,update ? m_rtpMedia : 0,allowEmptyAddr);
if (!lst)
@ -813,7 +833,7 @@ bool SDPSession::addRtpParams(NamedList& msg, const String& natAddr,
SDPMedia* m = static_cast<SDPMedia*>(o->get());
msg.addParam("rtp_port" + m->suffix(),m->remotePort());
if (m->isAudio())
msg.addParam("rtp_rfc2833",m->rfc2833());
m->rfc2833().put(msg);
}
addSdpParams(msg,body);
return true;
@ -836,8 +856,11 @@ void SDPSession::resetSdp(bool all)
m_host.clear();
if (all) {
m_secure = m_parser->secure();
m_gpmd = m_parser->gpmd();
m_rfc2833 = m_parser->rfc2833();
}
setSessionParams(0);
m_createSdpParams.clearParams();
}
// Build a populated chan.rtp message
@ -864,7 +887,11 @@ Message* SDPSession::buildChanRtp(SDPMedia* media, const char* addr, bool start,
int payload = SDPMedia::payloadMapping(media->mappings(),media->format());
if (payload >= 0)
m->addParam("payload",String(payload));
m->addParam("evpayload",media->rfc2833());
int evpayload = media->selectRfc2833(media->format());
if (evpayload >= 0)
m->addParam("evpayload",String(evpayload));
else
m->addParam("evpayload",String::boolText(false));
}
if (m_secure) {
if (media->remoteCrypto()) {
@ -970,20 +997,32 @@ ObjList* SDPSession::updateRtpSDP(const NamedList& params, String& rtpAddr, ObjL
rtp = new SDPMedia(tmp,trans,fmts,-1,port);
append = true;
}
int dir = SDPMedia::DirUnknown;
if (sdpPrefix) {
for (unsigned int j = 0; j < n; j++) {
const NamedString* param = params.getParam(j);
if (!param)
continue;
tmp = param->name();
if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0))
if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0)) {
rtp->parameter(tmp,*param,append);
SDPMedia::setDirection(dir,tmp);
}
}
}
rtp->mappings(params.getValue("rtp_mapping" + rtp->suffix()));
if (audio)
rtp->rfc2833(params.getIntValue("rtp_rfc2833",-1));
if (audio) {
Rfc2833 rfc2833;
for (int i = 0; i < Rfc2833::RateCount; ++i) {
if (i == Rfc2833::Rate8khz)
rfc2833[i] = params.getIntValue(YSTRING("rtp_rfc2833"),-1);
else
rfc2833[i] = params.getIntValue("rtp_rfc2833_" + Rfc2833::rateValue(i),-1);
}
rtp->rfc2833(rfc2833);
}
rtp->crypto(params.getValue("crypto" + rtp->suffix()),false);
rtp->direction(dir,false);
if (!lst)
lst = new ObjList;
lst->append(rtp);
@ -1055,6 +1094,66 @@ void SDPSession::setFormatsExtra(const NamedList& list, bool out)
}
}
// Parse a received SDP body, process session level parameters.
NamedList* SDPSession::parseSessionParams(const MimeSdpBody* sdp)
{
NamedList* nl = 0;
for (const ObjList* o = (sdp ? sdp->lines().skipNull() : 0); o; o = o->skipNext()) {
const NamedString* l = static_cast<const NamedString*>(o->get());
if (l->name() == YSTRING("m"))
break;
const char* pref = 0;
if (l->name() == YSTRING("b"))
pref = "BW-";
else if (l->name() != YSTRING("a"))
continue;
NamedString* ns = 0;
int pos = l->find(':');
if (pos >= 0)
ns = new NamedString(pref + l->substr(0,pos),l->substr(pos + 1));
else
ns = new NamedString(pref + *l);
XDebug(m_enabler,DebugAll,"Parsed sess SDP param %s='%s' [%p]",
ns->name().c_str(),ns->safe(),m_ptr);
if (!nl) {
Lock lck(m_parser);
String pref = m_parser->m_ssdpParam;
if (!pref)
break;
nl = new NamedList(pref + "_");
}
nl->addParam(ns);
}
return nl;
}
// Replace SDP sesison
void SDPSession::updateSessionParams(const NamedList& nl)
{
String pref;
Lock lck(m_parser);
if (m_parser->m_ssdpParam)
pref = "o" + m_parser->m_ssdpParam;
lck.drop();
pref = nl.getValue(YSTRING("ossdp-prefix"),pref);
XDebug(m_enabler,DebugAll,"updateSessionParams('%s') pref='%s' [%p]",
nl.c_str(),pref.c_str(),m_ptr);
if (!pref)
return;
pref << "_";
m_createSdpParams.clearParams();
int pLen = pref.length();
for (ObjList* o = nl.paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* p = static_cast<const NamedString*>(o->get());
if (p->name().startsWith(pref) && p->name().rfind('_') < pLen) {
NamedString* ns = new NamedString(p->name().substr(pLen),*p);
m_createSdpParams.addParam(ns);
XDebug(m_enabler,DebugAll,"Added sess create SDP param %s='%s' [%p]",
ns->name().c_str(),ns->safe(),m_ptr);
}
}
}
// Add extra AMR params to fmtp line
void SDPSession::addFmtpAmrExtra(String& buf, const String* fmtp)
{
@ -1094,6 +1193,32 @@ void SDPSession::addFmtpAmrExtra(String& buf, const String* fmtp)
TelEngine::destruct(l);
}
// Add session or media parameters
void SDPSession::addSdpParams(MimeSdpBody* sdp, const NamedList& params, bool* enc, bool* dir)
{
for (ObjList* o = params.paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* p = static_cast<const NamedString*>(o->get());
if (p->name().find(':') >= 0)
continue;
const char* type = "a";
String tmp = p->name();
if (tmp.startSkip("BW-",false)) {
if (!tmp)
continue;
type = "b";
}
else {
if (dir)
*dir = *dir || lookup(tmp,SDPMedia::s_sdpDir);
if (enc)
*enc = *enc || (tmp == YSTRING("encryption"));
}
if (*p)
tmp << ":" << *p;
sdp->addLine(type,tmp);
}
}
}; // namespace TelEngine
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -54,6 +54,138 @@ class SDPMedia;
class SDPSession;
class SDPParser;
/**
* This class holds RFC 2833 payloads for known audio rates
* @short RFC 2833 payloads for known audio rates
*/
class YSDP_API Rfc2833
{
public:
/**
* Suported (known) rates
*/
enum Rate {
Rate8khz = 0,
Rate16khz,
Rate32khz,
RateCount,
};
/**
* Constructor
*/
inline Rfc2833()
{ set(); }
/**
* Check if a payload is present in the list
* @param payload Payload to check
*/
inline bool includes(int payload) const {
for (int i = 0; i < RateCount; ++i)
if (m_payloads[i] == payload)
return true;
return false;
}
/**
* Select RFC 2833 payload for given media format
* @param fmt Format internal name
* @return Payload value, negative if not available
*/
inline int payload(const String& fmt) const {
int r = fmtRate(fmt);
return r < RateCount ? m_payloads[r] : -1;
}
/**
* Replace all values
* @param other Optional values to replace with. Reset if not present
*/
inline void set(const Rfc2833* other = 0) {
for (int i = 0; i < RateCount; ++i)
m_payloads[i] = other ? (*other)[i] : -1;
}
/**
* Update payloads
* @param params Parameters list
* @param defaults Payload list with default values
* @param force True to force updating if a needed parameter is not present
* @param param Parameter name. defaults to "rfc2833"
*/
void update(const NamedList& params, const Rfc2833& defaults, bool force = true,
const String& param = String::empty());
/**
* Update payload for specific rate
* @param value Payload
* @param rate Rate value
* @param defaults Payload list with default values
*/
void update(int rate, const String& value, const Rfc2833& defaults);
/**
* Put RFC 2833 parameters in a parameter list
* @param params Destination list
* @param param Parameter name. Defaults to "rtp_rfc2833"
*/
void put(NamedList& params, const String& param = String::empty()) const;
inline int operator[](int index) const
{ return m_payloads[index]; }
inline int& operator[](int index)
{ return m_payloads[index]; }
/**
* Assignment operator
*/
inline Rfc2833& operator=(const Rfc2833& other)
{ set(&other); return *this; }
/**
* Dump (append) payloads to destination string
* Format: rate=payload[rate1=payload1...],
* @param buf Destination string
* @return Destination string
*/
String& dump(String& buf) const;
/**
* Select RFC 2833 rate for given media format
* @param fmt Format internal name
* @return Rate value as enumeration
*/
static int fmtRate(const String& fmt);
/**
* Retrieve rate enumeration value from rate value
* @param s Rate value
* @return Rate as enumeration, RateCount if not found
*/
static inline int rate(const String& s) {
for (int r = 0; r < RateCount; ++r)
if (s_rates[r] == s)
return r;
return RateCount;
}
/**
* Retrieve rate value from rate enumeration value
* @param index Rate enumeration index
* @return Rate name, empty string if not found
*/
static inline const String& rateValue(int index)
{ return index < RateCount ? s_rates[index] : String::empty(); }
protected:
static const String s_rates[RateCount];
int m_payloads[RateCount];
};
/**
* This class holds a single SDP media description
* @short SDP media description
@ -61,6 +193,17 @@ class SDPParser;
class YSDP_API SDPMedia : public NamedList
{
public:
/**
* RTP session/media direction
*/
enum Direction {
DirUnknown = 0,
DirRecv = 1,
DirSend = 2,
DirBidir = 3,
DirInactive = 4,
};
/**
* Constructor
* @param media Media type name
@ -176,23 +319,35 @@ public:
{ if (newMap) m_mappings = newMap; }
/**
* Retrieve RFC2833 status or payload of this media
* @return RFC2833 status or payload of this media
* Retrieve RFC 2833 payloads of this media
* @return RFC 2833 payloads of this media
*/
inline const String& rfc2833() const
inline const Rfc2833& rfc2833() const
{ return m_rfc2833; }
/**
* Set RFC2833 status or payload of this media
* @param payload SDP numeric payload to set.
* Set it to a negative value to reset RFC2833
* Set RFC 2833 payloads of this media
* @param values SDP RFC 2833 payloads to set
*/
inline void rfc2833(int payload)
{
if (payload >= 0)
m_rfc2833 = payload;
else
m_rfc2833 = String::boolText(false);
inline void rfc2833(const Rfc2833& values)
{ m_rfc2833 = values; }
/**
* Check if RFC 2833 was selected for this media
* @return True if RFC 2833 was selected for this media, false otherwise
*/
inline bool haveRfc2833() const
{ return m_haveRfc3833; }
/**
* Select RFC 2833 payload for given media format
* @param fmt Format internal name
* @return Payload value, negative if not available
*/
inline int selectRfc2833(const String& fmt) {
int rVal = m_rfc2833.payload(fmt);
m_haveRfc3833 = (rVal >= 0);
return rVal;
}
/**
@ -286,6 +441,20 @@ public:
*/
void crypto(const char* desc, bool remote);
/**
* Set media direction
* @param value New direction
* @param remote True to set the remote direction, false to set the local one
*/
void direction(int value, bool remote);
/**
* Retrieve negotiated media direction to be sent to remote
* @param sessLDir SDP session level local direction, if known
* @return Media direction
*/
int direction(int sessLDir = 0);
/**
* Put this net media in a parameter list
* @param msg Destination list
@ -299,6 +468,17 @@ public:
*/
void keepRtp(const SDPMedia& other);
/**
* Retrieve direction if known
* @param dir Destination variable to set the direction in
* @param name Direction name
*/
static inline void setDirection(int& dir, const char* name) {
int d = lookup(name,s_sdpDir);
if (d != DirUnknown)
dir = d;
}
/**
* Retrieve format mapping to payload
* @param mappings Mappings list
@ -307,11 +487,17 @@ public:
*/
static int payloadMapping(const String& mappings, const String& fmt);
/**
* SDP media direction dictionary
*/
static const TokenDict s_sdpDir[];
private:
bool m_audio;
bool m_video;
bool m_modified;
bool m_securable;
bool m_haveRfc3833;
// local rtp data changed flag
bool m_localChanged;
// suffix used for this type
@ -330,12 +516,15 @@ private:
String m_mappings;
// local media port
String m_lPort;
// payload for telephone/event
String m_rfc2833;
// payloads for telephone/event
Rfc2833 m_rfc2833;
// remote security descriptor
String m_rCrypto;
// local security descriptor
String m_lCrypto;
// Local / remote media direction
int m_lDir;
int m_rDir;
};
@ -401,8 +590,10 @@ public:
* @param msg Destination list
* @param media List of SDP media information
* @param putPort True to add the media port
* @param sessParams Optional session level SDP parameters
*/
static void putMedia(NamedList& msg, ObjList* media, bool putPort = true);
static void putMedia(NamedList& msg, ObjList* media, bool putPort = true,
const NamedList* sessParams = 0);
/**
* Put session media parameters into a list of parameters
@ -410,7 +601,7 @@ public:
* @param putPort True to add the media port
*/
inline void putMedia(NamedList& msg, bool putPort = true)
{ putMedia(msg,m_rtpMedia,putPort); }
{ putMedia(msg,m_rtpMedia,putPort,m_parsedParams); }
/**
* Retrieve a single media description
@ -423,15 +614,16 @@ public:
/**
* Update the RFC 2833 availability and payload
* @param value String to get payload or availability
* @param rate Rate to set
*/
void setRfc2833(const String& value);
void setRfc2833(const String& value, int rate = Rfc2833::Rate8khz);
/**
* Update the RFC 2833 availability and payload
* @param value Pointer to string to get payload or availability
* @param params Parameters list
* @param force Force update even if needed parameters are not present
*/
inline void setRfc2833(const String* value)
{ if (value) setRfc2833(*value); }
void setRfc2833(const NamedList& params, bool force);
/**
* Build and dispatch a chan.rtp message for a given media. Update media on success
@ -656,6 +848,37 @@ protected:
*/
void setFormatsExtra(const NamedList& list, bool out);
/**
* Parse a received SDP body, process session level parameters
* @param sdp Pointer to received SDP body
* @return List with handled session level parameters, may be NULL
*/
NamedList* parseSessionParams(const MimeSdpBody* sdp);
/**
* Replace parsed SDP session level parameters
* Consume receveived pointer
* @param params Parsed SDP session params
*/
inline void setSessionParams(NamedList* params) {
TelEngine::destruct(m_parsedParams);
m_parsedParams = params;
}
/**
* Parse a received SDP body, process session level parameters.
* Replace
* @param sdp Pointer to received SDP body
*/
inline void processSessionParams(const MimeSdpBody* sdp)
{ setSessionParams(parseSessionParams(sdp)); }
/**
* Update SDP session level parameters to be used when building SDP
* @param params Parameters list
*/
void updateSessionParams(const NamedList& params);
SDPParser* m_parser;
int m_mediaStatus;
bool m_rtpForward; // Forward RTP flag
@ -671,13 +894,18 @@ protected:
unsigned int m_sdpHash; // SDP content hash
String m_host;
bool m_secure;
int m_rfc2833; // Payload of RFC 2833 for remote party
Rfc2833 m_rfc2833; // Payloads of RFC 2833 for remote party
bool m_ipv6; // IPv6 support
bool m_gpmd; // Handle GPMD even if not RTP forwarding
NamedList m_amrExtra; // Extra AMR codec parameters
NamedList* m_parsedParams; // Parsed session level parameters
NamedList m_createSdpParams; // Session level parameters used on SDP create
private:
// Add extra AMR params to fmtp line
void addFmtpAmrExtra(String& buf, const String* fmtp);
// Add session or media parameters when creating SDP
void addSdpParams(MimeSdpBody* sdp, const NamedList& params, bool* enc = 0, bool* dir = 0);
DebugEnabler* m_enabler; // Debug enabler used for output
void* m_ptr; // Pointer to show in debug messages
@ -701,8 +929,7 @@ public:
*/
inline SDPParser(const char* dbgName, const char* sessName, const char* fmts = "alaw,mulaw")
: Mutex(true,"SDPParser"),
m_rfc2833(101),
m_sdpForward(false), m_secure(false), m_ignorePort(false),
m_sdpForward(false), m_secure(false), m_gpmd(false), m_ignorePort(false),
m_sessionName(sessName), m_audioFormats(fmts),
m_codecs(""), m_hacks("")
{ debugName(dbgName); }
@ -716,10 +943,10 @@ public:
{ Lock lock(this); buf = m_audioFormats; }
/**
* Get the RFC 2833 offer payload
* @return Payload for RFC 2883 telephony events, negative if not offered
* Get the RFC 2833 offer payloads
* @return Payloads for RFC 2883 telephony events
*/
inline int rfc2833() const
inline Rfc2833 rfc2833() const
{ return m_rfc2833; }
/**
@ -729,6 +956,13 @@ public:
inline bool secure() const
{ return m_secure; }
/**
* Get the propagate GPMD flag
* @return True if session GPMD will be set in SDP when not forwarding
*/
inline bool gpmd() const
{ return m_gpmd; }
/**
* Get the SDP forward flag
* @return True if raw SDP should be added to RTP forward offer
@ -753,10 +987,11 @@ public:
* @param media Optional expected media type. If not empty this will be the
* only media type returned (if found)
* @param force Force updating formats even if incompatible with old ones
* @param handleDir Handle media direction, false to ignore it
* @return List of SDPMedia objects, may be NULL
*/
ObjList* parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedia = 0,
const String& media = String::empty(), bool force = false);
const String& media = String::empty(), bool force = false, bool handleDir = true);
/**
* Parse a received SDP body, returns NULL if SDP is not present
@ -768,11 +1003,12 @@ public:
* @param media Optional expected media type. If not empty this will be the
* only media type returned (if found)
* @param force Force updating formats even if incompatible with old ones
* @param handleDir Handle media direction, false to ignore it
* @return List of SDPMedia objects, may be NULL
*/
inline ObjList* parse(const MimeSdpBody* sdp, String& addr, ObjList* oldMedia = 0,
const String& media = String::empty(), bool force = false)
{ return sdp ? parse(*sdp,addr,oldMedia,media,force) : 0; }
const String& media = String::empty(), bool force = false, bool handleDir = true)
{ return sdp ? parse(*sdp,addr,oldMedia,media,force,handleDir) : 0; }
/**
* Update configuration. This method should be called after a configuration file is loaded
@ -793,14 +1029,16 @@ public:
static const TokenDict s_rtpmap[];
private:
int m_rfc2833; // RFC 2833 payload offered to remote
Rfc2833 m_rfc2833; // RFC 2833 payloads offered to remote
bool m_sdpForward; // Include raw SDP for forwarding
bool m_secure; // Offer SRTP
bool m_gpmd; // Propagate GPMD when not forwarding
bool m_ignorePort; // Ignore port only changes in SDP
String m_sessionName;
String m_audioFormats; // Default audio formats to be advertised to remote party
NamedList m_codecs; // Codecs configuration list
NamedList m_hacks; // Various potentially standard breaking settings
String m_ssdpParam; // Session level parameters prefix
};
}; // namespace TelEngine

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -4,7 +4,7 @@
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing

View File

@ -5,7 +5,7 @@
* Yet Another Sip Channel
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -47,6 +47,11 @@ class SIPDriver;
#define EXPIRES_DEF 600
#define EXPIRES_MAX 3600
// Parameters for PRACK 18x retransmission and 2xx waiting
#define PRACK_TIMER 2000000
#define PRACK_TRIES 4
#define PRACK_MWAIT 10000000
// TCP transport idle values in seconds
// Outgoing: interval to send keep alive
// Incoming: interval allowed to stay with refcounter=1 and no data received/sent
@ -744,6 +749,8 @@ public:
Lock lck(this);
m_transList.clear();
}
inline bool update() const
{ return m_update; }
inline bool prack() const
{ return m_prack; }
inline bool info() const
@ -753,6 +760,7 @@ public:
private:
static bool copyAuthParams(NamedList* dest, const NamedList& src, bool ok = true);
YateSIPEndPoint* m_ep;
bool m_update;
bool m_prack;
bool m_info;
bool m_fork;
@ -1048,6 +1056,7 @@ public:
virtual bool msgDrop(Message& msg, const char* reason);
virtual bool msgUpdate(Message& msg);
virtual bool msgControl(Message& msg);
virtual void checkTimers(Message& msg, const Time& tmr);
virtual bool callPrerouted(Message& msg, bool handled);
virtual bool callRouted(Message& msg);
virtual void callAccept(Message& msg);
@ -1058,6 +1067,8 @@ public:
void doBye(SIPTransaction* t);
void doCancel(SIPTransaction* t);
bool doInfo(SIPTransaction* t);
void doPrack(SIPTransaction* t);
void doUpdate(SIPTransaction* t);
void doRefer(SIPTransaction* t);
void doMessage(SIPTransaction* t);
void reInvite(SIPTransaction* t);
@ -1118,14 +1129,21 @@ private:
void clearTransaction();
void detachTransaction2();
void startPendingUpdate();
void reInviteUpdate(SIPTransaction* t);
bool processTransaction2(SIPEvent* ev, const SIPMessage* msg, int code);
SIPMessage* createDlgMsg(const char* method, const char* uri = 0);
void updateTarget(const SIPMessage* msg, bool force = false, bool chgParty = true);
void emitUpdate();
bool emitPRACK(const SIPMessage* msg);
bool startClientReInvite(NamedList& msg, bool rtpForward);
void emitPRACK(const SIPMessage* msg);
// Check if a response is an 1xx (non 100) one. Check if acceptable and PRACK needs to be sent
int is1xxPrack(const SIPMessage* msg);
void sendProvisional(SIPMessage* msg);
bool startClientReInvite(NamedList& msg, bool rtpForward, bool* disc = 0);
bool startClientUpdate(NamedList& msg);
bool reInviteForward(SIPTransaction* t, MimeSdpBody* sdp, int invite);
bool reInviteProxy(SIPTransaction* t, MimeSdpBody* sdp, int invite);
bool continueReInviteWait();
void dropReInviteWait(NamedList* res = 0);
// Build the 'call.route' and NOTIFY messages needed by the transfer thread
bool initTransfer(Message*& msg, SIPMessage*& sipNotify, const SIPMessage* sipRefer,
const MimeHeaderLine* refHdr, const URI& uri, const MimeHeaderLine* replaces);
@ -1170,6 +1188,8 @@ private:
bool m_byebye;
// should we CANCEL?
bool m_cancel;
// does remote support UPDATE?
bool m_update;
int m_state;
String m_reason;
int m_reasonCode;
@ -1196,11 +1216,19 @@ private:
int m_reInviting;
// sequence number of last transmitted PRACK
int m_lastRseq;
// PRACK parameters
ObjList m_prackQueue;
uint64_t m_prackTimer;
int m_prackCount;
bool m_prackUsed;
// media parameters before we sent a reINVITE
NamedList m_revert;
bool m_silent; // Silently discard SIP dialog
bool m_stopOCall;
String m_traceId;
bool m_reinviteWait;
NamedList* m_reinviteWaitParams;
bool m_provNonReliable;
};
class YateSIPGenerate : public GenObject
@ -1360,6 +1388,8 @@ static bool s_warnPacketUDP = true; // Warn when receiving possible truncat
static uint64_t s_warnBindFailDelay = 0; // Delay listener failed to bind warning
static bool s_warnNoDefTranUDP = true; // Warn when a default UDP transport is not set
static bool s_initialHeaders = false; // Put all headers as received in initial message sent to yate
static bool s_reinviteWait = false; // Wait for initial transaction termination when need to send re-INVITE
static bool s_provNonReliable = true; // Accept non reliable provisional messages after a reliable one was received
static bool s_sipt_isup = false; // Control the application/isup body processing
static bool s_printMsg = true; // Print sent/received SIP messages to output
static ObjList* s_authCopyHeader = 0; // Copy headers in user.auth
@ -4973,7 +5003,7 @@ void YateTCPParty::destroyed()
YateSIPEngine::YateSIPEngine(YateSIPEndPoint* ep)
: SIPEngine(s_cfg.getValue("general","useragent")),
m_ep(ep), m_prack(false), m_info(false), m_foreignAuth(false),
m_ep(ep), m_update(false), m_prack(false), m_info(false), m_foreignAuth(false),
m_traceIds(0)
{
addAllowed("INVITE");
@ -4987,6 +5017,9 @@ YateSIPEngine::YateSIPEngine(YateSIPEndPoint* ep)
addAllowed("REFER");
if (s_enable_options)
addAllowed("OPTIONS");
m_update = s_cfg.getBoolValue("general","update");
if (m_update)
addAllowed("UPDATE");
m_prack = s_cfg.getBoolValue("general","prack");
if (m_prack)
addAllowed("PRACK");
@ -5880,6 +5913,24 @@ bool YateSIPEndPoint::incoming(SIPEvent* e, SIPTransaction* t)
if (!done)
t->setResponse(415);
}
else if (plugin.ep()->engine()->prack() && t->getMethod() == YSTRING("PRACK")) {
YateSIPConnection* conn = plugin.findCall(t->getCallID(),true);
if (conn) {
conn->doPrack(t);
conn->deref();
}
else
t->setResponse(481);
}
else if (plugin.ep()->engine()->update() && t->getMethod() == YSTRING("UPDATE")) {
YateSIPConnection* conn = plugin.findCall(t->getCallID(),true);
if (conn) {
conn->doUpdate(t);
conn->deref();
}
else
t->setResponse(481);
}
else if (s_enable_register && t->getMethod() == YSTRING("REGISTER"))
regReq(e,t);
else if (s_enable_options && t->getMethod() == YSTRING("OPTIONS"))
@ -6497,14 +6548,17 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr)
: Channel(plugin,0,false),
SDPSession(&plugin.parser()),
YateSIPPartyHolder(this,driver(),tr->traceId()),
m_tr(tr), m_tr2(0), m_hungup(false), m_byebye(true), m_cancel(false),
m_tr(tr), m_tr2(0), m_hungup(false), m_byebye(true), m_cancel(false), m_update(false),
m_state(Incoming), m_port(0), m_route(0), m_routes(0),
m_authBye(true), m_autoChangeParty(tr->getEngine()->autoChangeParty()),
m_checkAllowInfo(s_checkAllowInfo), m_missingAllowInfoDefVal(s_missingAllowInfoDefVal),
m_honorDtmfDetect(s_honorDtmfDetect),
m_referring(false), m_refer(s_referChan), m_referUpdate(s_refer_update),
m_reInviting(ReinviteNone), m_lastRseq(0),
m_revert(""), m_silent(false), m_stopOCall(false), m_traceId(tr->traceId())
m_prackTimer(0), m_prackCount(0), m_prackUsed(false),
m_revert(""), m_silent(false), m_stopOCall(false), m_traceId(tr->traceId()),
m_reinviteWait(s_reinviteWait), m_reinviteWaitParams(0),
m_provNonReliable(s_provNonReliable)
{
m_ipv6 = s_ipv6;
setSdpDebug(this,this,m_traceId);
@ -6572,6 +6626,12 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr)
copySipHeaders(*m,*ev->getMessage(),true,
static_cast<const YateSIPEngine*>(tr->getEngine())->foreignAuth(),s_initialHeaders);
hl = m_tr->initialMessage()->getHeader("Allow");
if (hl) {
static const Regexp r("^\\(.*[[:space:],]\\)\\?UPDATE\\([[:space:],].*\\)\\?$",false,true);
m_update = r.matches(*hl);
}
const char* reason = 0;
hl = m_tr->initialMessage()->getHeader("Referred-By");
if (hl)
@ -6648,7 +6708,8 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr)
setRtpLocalAddr(m_rtpLocalAddr);
MimeSdpBody* sdp = getSdpBody(ev->getMessage()->body);
if (sdp) {
setMedia(plugin.parser().parse(sdp,m_rtpAddr,m_rtpMedia));
processSessionParams(sdp);
setMedia(plugin.parser().parse(sdp,m_rtpAddr,m_rtpMedia,String::empty(),false,false));
if (m_rtpMedia) {
m_rtpForward = true;
// guess if the call comes from behind a NAT
@ -6691,6 +6752,7 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr)
s->addParam(s_username,m_user);
s->copyParam(*m,YSTRING("connection_id"));
s->copyParam(*m,YSTRING("trace_id"));
s->addParam("ocs_session_id",m->getValue(YSTRING("ocs_session_id")),false);
Engine::enqueue(s);
}
@ -6699,15 +6761,18 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char
: Channel(plugin,0,true),
SDPSession(&plugin.parser()),
YateSIPPartyHolder(this,driver(),msg.getValue(YSTRING("trace_id"))),
m_tr(0), m_tr2(0), m_hungup(false), m_byebye(true), m_cancel(true),
m_tr(0), m_tr2(0), m_hungup(false), m_byebye(true), m_cancel(true), m_update(false),
m_state(Outgoing), m_port(0), m_route(0), m_routes(0),
m_authBye(false), m_autoChangeParty(true),
m_checkAllowInfo(s_checkAllowInfo), m_missingAllowInfoDefVal(s_missingAllowInfoDefVal),
m_honorDtmfDetect(s_honorDtmfDetect),
m_referring(false), m_refer(s_referChan), m_referUpdate(s_refer_update),
m_reInviting(ReinviteNone), m_lastRseq(0),
m_prackTimer(0), m_prackCount(0), m_prackUsed(false),
m_revert(""), m_silent(false), m_stopOCall(msg.getBoolValue(YSTRING("stop_call"))),
m_traceId(msg.getValue(YSTRING("trace_id")))
m_traceId(msg.getValue(YSTRING("trace_id"))),
m_reinviteWait(false), m_reinviteWaitParams(0),
m_provNonReliable(s_provNonReliable)
{
TraceDebug(m_traceId,this,DebugAll,"YateSIPConnection::YateSIPConnection(%p,'%s') [%p]",
&msg,uri.c_str(),this);
@ -6729,6 +6794,7 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char
setReason();
m_checkAllowInfo = msg.getBoolValue(YSTRING("ocheck_allow_info"),m_checkAllowInfo);
m_missingAllowInfoDefVal = msg.getBoolValue(YSTRING("omissing_allow_info"),m_missingAllowInfoDefVal);
m_provNonReliable = msg.getBoolValue(YSTRING("mixed_provisional"),m_provNonReliable);
String* meths = msg.getParam("odtmfmethods");
s_globalMutex.lock();
if (meths)
@ -6741,9 +6807,10 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char
s_globalMutex.unlock();
m_honorDtmfDetect = msg.getBoolValue(YSTRING("ohonor_dtmf_detect"),m_honorDtmfDetect);
m_secure = msg.getBoolValue(YSTRING("secure"),plugin.parser().secure());
setRfc2833(msg.getParam(YSTRING("rfc2833")));
setRfc2833(msg,true);
m_rtpForward = msg.getBoolValue(YSTRING("rtp_forward"));
m_sdpForward = msg.getBoolValue(YSTRING("forward_sdp"),m_sdpForward);
m_gpmd = msg.getBoolValue(YSTRING("forward_gpmd"),m_gpmd);
m_user = msg.getValue(YSTRING("user"));
String tmp;
bool genSips = sips(msg.getBoolValue(YSTRING("sips"),line && line->sips()));
@ -6833,7 +6900,7 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char
*hl = display + " " + *hl;
}
}
if (plugin.ep()->engine()->prack())
if (msg.getBoolValue(YSTRING("prack"),plugin.ep()->engine()->prack()))
m->addHeader("Supported","100rel");
m->getParty()->getAddr(m_host,m_port,false);
SocketAddr::appendTo(m_address,m_host,m_port);
@ -6943,6 +7010,7 @@ void YateSIPConnection::destroyed()
clearTransaction();
TelEngine::destruct(m_route);
TelEngine::destruct(m_routes);
TelEngine::destruct(m_reinviteWaitParams);
Channel::destroyed();
}
@ -7015,6 +7083,7 @@ void YateSIPConnection::hangup()
if (m_hungup)
return;
m_hungup = true;
m_prackTimer = 0;
const char* error = lookup(m_reasonCode,dict_errors);
TraceDebug(m_traceId,this,DebugAll,"YateSIPConnection::hangup() state=%d trans=%p error='%s' code=%d reason='%s' [%p]",
m_state,m_tr,error,m_reasonCode,m_reason.c_str(),this);
@ -7105,6 +7174,7 @@ SIPMessage* YateSIPConnection::createDlgMsg(const char* method, const char* uri)
if (!uri)
uri = m_uri;
SIPMessage* m = new SIPMessage(method,uri);
m->msgTraceId = m_traceId;
m->addRoutes(m_routes);
setSipParty(m,plugin.findLine(m_line),true,m_host,m_port);
if (!m->getParty()) {
@ -7173,26 +7243,20 @@ void YateSIPConnection::emitUpdate()
Engine::enqueue(m);
}
// Emit a PRovisional ACK if enabled in the engine, return true to handle them
bool YateSIPConnection::emitPRACK(const SIPMessage* msg)
// Emit a PRovisional ACK
void YateSIPConnection::emitPRACK(const SIPMessage* msg)
{
if (!(msg && msg->isAnswer() && (msg->code > 100) && (msg->code < 200)))
return false;
if (!plugin.ep()->engine()->prack())
return true;
if (!msg)
return;
const MimeHeaderLine* rs = msg->getHeader("RSeq");
const MimeHeaderLine* cs = msg->getHeader("CSeq");
if (!(rs && cs))
return true;
return;
int seq = rs->toInteger(0,10);
// return false only if we already seen this provisional response
if (seq == m_lastRseq)
return false;
if (seq < m_lastRseq) {
TraceDebug(m_traceId,this,DebugMild,"Not sending PRACK for RSeq %d < %d [%p]",
seq,m_lastRseq,this);
return false;
}
if (m_prackUsed && seq <= m_lastRseq)
return;
// once we saw a valid RSeq all incoming 1xx messages must have a valid sequence
m_prackUsed = true;
String tmp;
const MimeHeaderLine* co = msg->getHeader("Contact");
if (co) {
@ -7203,14 +7267,50 @@ bool YateSIPConnection::emitPRACK(const SIPMessage* msg)
}
SIPMessage* m = createDlgMsg("PRACK",tmp);
if (!m)
return true;
return;
m_lastRseq = seq;
tmp = *rs;
tmp << " " << *cs;
m->addHeader("RAck",tmp);
plugin.ep()->engine()->addMessage(m,&m_autoChangeParty);
m->deref();
return true;
}
// Check if a response is an 1xx (non 100) one
// Return:
// -1: not a provisional
// 0: PRACK required, not acceptable
// 1: PRACK not required
// 2: PRACK required
int YateSIPConnection::is1xxPrack(const SIPMessage* msg)
{
if (!(msg && msg->isAnswer() && (msg->code > 100) && (msg->code < 200)))
return -1;
if (!plugin.ep()->engine()->prack())
return 1;
const MimeHeaderLine* rs = msg->getHeader("RSeq");
const MimeHeaderLine* cs = msg->getHeader("CSeq");
if (!(rs && cs)) {
if (m_provNonReliable || !m_prackUsed)
return 1;
TraceDebug(m_traceId,this,DebugNote,
"Dropping %d: PRACK already used but not required for this one [%p]",
msg->code,this);
return 0;
}
if (!m_prackUsed)
return 2;
int seq = rs->toInteger(0,10);
if (seq > m_lastRseq)
return 2;
// Retransmission or too old: drop
if (seq == m_lastRseq)
TraceDebug(m_traceId,this,DebugAll,"Dropping retransmitted %d RSeq %d [%p]",
msg->code,seq,this);
else
TraceDebug(m_traceId,this,DebugMild,"Dropping %d with old RSeq %d < %d [%p]",
msg->code,seq,m_lastRseq,this);
return 0;
}
// Creates a SDP for provisional (1xx) messages
@ -7318,6 +7418,7 @@ bool YateSIPConnection::process(SIPEvent* ev)
m_dialog = *tr->recentMessage();
mylock.drop();
int provPrack = -1;
if (msg && !msg->isOutgoing() && msg->isAnswer() && (code >= 300) && (code <= 699)) {
updateTags = false;
m_cancel = false;
@ -7391,6 +7492,7 @@ bool YateSIPConnection::process(SIPEvent* ev)
hangup();
}
else if (!m_hungup && code >= 100) {
provPrack = is1xxPrack(msg);
Lock lck(paramMutex());
parameters().setParam("cause_sip",String(code));
if (msg && msg->reason)
@ -7407,6 +7509,7 @@ bool YateSIPConnection::process(SIPEvent* ev)
if (!ev->isActive()) {
Lock lock(driver());
bool hadTr = m_tr;
if (m_tr) {
DDebug(this,DebugInfo,"YateSIPConnection clearing transaction %p [%p]",
m_tr,this);
@ -7427,7 +7530,10 @@ bool YateSIPConnection::process(SIPEvent* ev)
else {
if (updateTags)
emitUpdate();
startPendingUpdate();
if (!provPrack)
return false;
if (!hadTr || continueReInviteWait())
startPendingUpdate();
}
return false;
}
@ -7436,10 +7542,19 @@ bool YateSIPConnection::process(SIPEvent* ev)
emitUpdate();
return false;
}
if (!m_update) {
const MimeHeaderLine* hl = msg->getHeader("Allow");
if (hl) {
static const Regexp r("^\\(.*[[:space:],]\\)\\?UPDATE\\([[:space:],].*\\)\\?$",false,true);
m_update = r.matches(*hl);
}
}
String natAddr;
MimeSdpBody* sdp = getSdpBody(msg->body);
if (sdp) {
DDebug(this,DebugInfo,"YateSIPConnection got SDP [%p]",this);
processSessionParams(sdp);
setMedia(plugin.parser().parse(sdp,m_rtpAddr,m_rtpMedia));
// guess if the call comes from behind a NAT
if (s_auto_nat && isNatBetween(m_rtpAddr,m_host)) {
@ -7507,7 +7622,9 @@ bool YateSIPConnection::process(SIPEvent* ev)
startPendingUpdate();
}
}
if (emitPRACK(msg)) {
if (provPrack > 0) {
if (2 == provPrack)
emitPRACK(msg);
if (s_multi_ringing || (m_state < Ringing)) {
const char* name = "call.progress";
const char* reason = 0;
@ -7549,6 +7666,7 @@ bool YateSIPConnection::process(SIPEvent* ev)
if (msg->isACK()) {
DDebug(this,DebugInfo,"YateSIPConnection got ACK [%p]",this);
if (!tr->autoAck()) {
dropReInviteWait();
Message* m = message("call.update");
m->addParam("operation","notify");
if (sdp)
@ -7594,6 +7712,7 @@ bool YateSIPConnection::processTransaction2(SIPEvent* ev, const SIPMessage* msg,
DDebug(this,DebugInfo,"YateSIPConnection got reINVITE ACK %s SDP [%p]",
(sdp ? "with" : "without"), this);
if (sdp) {
processSessionParams(sdp);
setMedia(plugin.parser().parse(sdp,m_rtpAddr,m_rtpMedia));
// guess if the call comes from behind a NAT
if (s_auto_nat && isNatBetween(m_rtpAddr,m_host)) {
@ -7624,6 +7743,7 @@ bool YateSIPConnection::processTransaction2(SIPEvent* ev, const SIPMessage* msg,
if (code < 300) {
MimeSdpBody* sdp = getSdpBody(msg->body);
while (sdp) {
processSessionParams(sdp);
String addr;
ObjList* lst = plugin.parser().parse(sdp,addr,0,String::empty(),m_rtpForward);
if (!lst)
@ -7676,6 +7796,7 @@ bool YateSIPConnection::processTransaction2(SIPEvent* ev, const SIPMessage* msg,
MimeSdpBody* sdp = getSdpBody(msg->body);
if (sdp) {
DDebug(this,DebugInfo,"YateSIPConnection got reINVITE SDP [%p]",this);
processSessionParams(sdp);
setMedia(plugin.parser().parse(sdp,m_rtpAddr,m_rtpMedia,String::empty(),
m_rtpForward));
// guess if the call comes from behind a NAT
@ -7728,9 +7849,15 @@ void YateSIPConnection::reInvite(SIPTransaction* t)
if (!checkUser(t))
return;
DDebug(this,DebugAll,"YateSIPConnection::reInvite(%p) [%p]",t,this);
reInviteUpdate(t);
}
void YateSIPConnection::reInviteUpdate(SIPTransaction* t)
{
Lock mylock(driver());
int invite = m_reInviting;
if (m_tr || m_tr2 || (invite == ReinviteRequest) || (invite == ReinviteReceived)) {
if ((m_tr && !plugin.ep()->engine()->update()) || m_tr2
|| (invite == ReinviteRequest) || (invite == ReinviteReceived)) {
// another request pending - refuse this one
t->setResponse(491);
return;
@ -7739,6 +7866,7 @@ void YateSIPConnection::reInvite(SIPTransaction* t)
t->setResponse(481);
return;
}
dropReInviteWait();
m_reInviting = ReinviteReceived;
mylock.drop();
m_dialog.adjustCSeq(t->initialMessage());
@ -7782,7 +7910,7 @@ bool YateSIPConnection::reInviteForward(SIPTransaction* t, MimeSdpBody* sdp, int
natAddr = addr;
addr = m_host;
}
TraceDebug(m_traceId,this,DebugAll,"reINVITE RTP addr '%s'",addr.c_str());
TraceDebug(m_traceId,this,DebugAll,"reINVITE/UPDATE RTP addr '%s'",addr.c_str());
}
// for pass-trough RTP we need support from our peer
@ -7820,8 +7948,10 @@ bool YateSIPConnection::reInviteForward(SIPTransaction* t, MimeSdpBody* sdp, int
msg.addParam("rtp_addr",addr);
if (natAddr)
msg.addParam("rtp_nat_addr",natAddr);
putMedia(msg,lst);
NamedList* sessParams = parseSessionParams(sdp);
putMedia(msg,lst,true,sessParams);
TelEngine::destruct(lst);
TelEngine::destruct(sessParams);
if (sdp)
addSdpParams(msg,sdp);
else {
@ -7871,6 +8001,7 @@ bool YateSIPConnection::reInviteProxy(SIPTransaction* t, MimeSdpBody* sdp, int i
}
bool audioChg = (getMedia(YSTRING("audio")) != 0);
audioChg ^= ((*lst)[YSTRING("audio")] != 0);
NamedList* sessParams = parseSessionParams(sdp);
bool keepExisting = true;
Message ver("call.update");
@ -7883,11 +8014,11 @@ bool YateSIPConnection::reInviteProxy(SIPTransaction* t, MimeSdpBody* sdp, int i
if (natAddr)
ver.addParam("rtp_nat_addr",natAddr);
ver.addParam("audio_changed",String::boolText(audioChg));
putMedia(ver,lst);
putMedia(ver,lst,true,sessParams);
addSdpParams(ver,sdp);
if (!Engine::dispatch(ver) || (ver.retValue() == YSTRING("error")) || (ver.retValue() == "-")) {
TelEngine::destruct(lst);
TelEngine::destruct(sessParams);
SIPMessage* m = new SIPMessage(t->initialMessage(),
ver.getIntValue(YSTRING("error"),dict_errors,488),ver.getValue(YSTRING("reason")));
copySipHeaders(*m,ver);
@ -7907,6 +8038,7 @@ bool YateSIPConnection::reInviteProxy(SIPTransaction* t, MimeSdpBody* sdp, int i
setMedia(0);
}
setMedia(lst,keepExisting);
setSessionParams(sessParams);
m_mediaStatus = MediaMissing;
// let RTP guess again the local interface or use the enforced address
@ -7944,6 +8076,41 @@ bool YateSIPConnection::reInviteProxy(SIPTransaction* t, MimeSdpBody* sdp, int i
return true;
}
bool YateSIPConnection::continueReInviteWait()
{
if (!m_reinviteWaitParams)
return true;
NamedList* nl = m_reinviteWaitParams;
m_reinviteWaitParams = 0;
bool disc = false;
TraceDebug(m_traceId,this,DebugAll,"Handling pending client re-invite [%p]",this);
if (m_tr2)
dropReInviteWait();
else if (!startClientReInvite(*nl,true,&disc))
dropReInviteWait(nl);
TelEngine::destruct(nl);
return !disc;
}
void YateSIPConnection::dropReInviteWait(NamedList* res)
{
if (!(m_reinviteWaitParams || res))
return;
Lock lck(driver());
if (!(m_reinviteWaitParams || res))
return;
TelEngine::destruct(m_reinviteWaitParams);
Message* m = message("call.update");
m->addParam("operation","reject");
if (res)
m->copyParams(*res,"error,reason");
else {
m->addParam("error","failure");
m->addParam("reason","Another INVITE Pending");
}
Engine::enqueue(m);
}
bool YateSIPConnection::checkUser(SIPTransaction* t, bool refuse)
{
// don't try to authenticate requests from server
@ -8076,6 +8243,53 @@ bool YateSIPConnection::doInfo(SIPTransaction* t)
return true;
}
void YateSIPConnection::doPrack(SIPTransaction* t)
{
if (m_authBye && !checkUser(t))
return;
DDebug(this,DebugAll,"doPrack(%p) [%p]",t,this);
const MimeHeaderLine* ra = t->initialMessage()->getHeader("RAck");
if (!ra) {
t->setResponse(400);
return;
}
String tmp = *ra;
int rseq = 0;
tmp >> rseq;
if (rseq <= 0) {
t->setResponse(400);
return;
}
if (rseq != m_lastRseq || !m_prackUsed) {
t->setResponse(406);
return;
}
t->setResponse(200);
Lock lock(driver());
m_prackTimer = 0;
SIPMessage* msg = static_cast<SIPMessage*>(m_prackQueue.remove(false));
if (msg) {
m_prackTimer = Time::now() + PRACK_TIMER;
m_prackCount = PRACK_TRIES;
msg->addHeader("Require","100rel");
msg->addHeader("RSeq",String(++m_lastRseq));
RefPointer<SIPTransaction> tr = m_tr;
lock.drop();
if (tr) {
tr->setResponse(msg);
msg->deref();
}
}
}
void YateSIPConnection::doUpdate(SIPTransaction* t)
{
if (m_authBye && !checkUser(t))
return;
DDebug(this,DebugAll,"doUpdate(%p) [%p]",t,this);
reInviteUpdate(t);
}
void YateSIPConnection::doRefer(SIPTransaction* t)
{
if (!m_refer) {
@ -8264,8 +8478,7 @@ bool YateSIPConnection::msgProgress(Message& msg)
SIPMessage* m = new SIPMessage(m_tr->initialMessage(), code);
copySipHeaders(*m,msg);
m->setBody(buildSIPBody(msg,createProvisionalSDP(msg)));
m_tr->setResponse(m);
m->deref();
sendProvisional(m);
}
setStatus("progressing");
return true;
@ -8281,8 +8494,7 @@ bool YateSIPConnection::msgRinging(Message& msg)
SIPMessage* m = new SIPMessage(m_tr->initialMessage(), 180);
copySipHeaders(*m,msg);
m->setBody(buildSIPBody(msg,createProvisionalSDP(msg)));
m_tr->setResponse(m);
m->deref();
sendProvisional(m);
}
setStatus("ringing");
return true;
@ -8290,6 +8502,20 @@ bool YateSIPConnection::msgRinging(Message& msg)
bool YateSIPConnection::msgAnswered(Message& msg)
{
if (m_prackTimer) {
// we are not allowed to send a 2xx until all 1xx got acknowledged
uint64_t limit = Time::now() + PRACK_MWAIT;
while (m_prackTimer) {
if (Time::now() > limit) {
setReason("PRACK Timeout",408,driver());
hangup();
return false;
}
Thread::idle();
}
}
if (m_hungup)
return false;
Channel::msgAnswered(msg);
Lock lock(driver());
if (m_hungup)
@ -8428,14 +8654,24 @@ bool YateSIPConnection::msgUpdate(Message& msg)
return false;
}
if (*oper == YSTRING("request")) {
if (m_tr || m_tr2) {
DDebug(this,DebugWarn,"Update request rejected, pending:%s%s [%p]",
m_tr ? " invite" : "",m_tr2 ? " reinvite" : "",this);
msg.setParam("error","pending");
msg.setParam("reason","Another INVITE Pending");
return false;
if (!m_tr2) {
if (!m_tr)
return startClientReInvite(msg,true);
if (m_reinviteWait && m_state == Established) {
if (!m_reinviteWaitParams) {
DDebug(this,DebugAll,"Delaying re-INVITE until ACK [%p]",this);
m_reinviteWaitParams = new NamedList(msg);
return true;
}
}
else if (m_update)
return startClientUpdate(msg);
}
return startClientReInvite(msg,true);
DDebug(this,DebugWarn,"Update request rejected, pending:%s%s [%p]",
m_tr ? " invite" : "",m_tr2 ? " reinvite" : "",this);
msg.setParam("error","pending");
msg.setParam("reason","Another INVITE Pending");
return false;
}
if (*oper == YSTRING("initiate")) {
if (m_reInviting != ReinviteNone) {
@ -8596,6 +8832,57 @@ void YateSIPConnection::statusParams(String& str)
str << ",inviting=" << (m_tr != 0);
}
void YateSIPConnection::sendProvisional(SIPMessage* msg)
{
if (!msg)
return;
// this method is called with the driver already locked
if (m_tr) {
if (m_prackUsed) {
if (m_prackTimer) {
// some other message is unacknowledged - just queue and exit
m_prackQueue.append(msg);
return;
}
m_prackTimer = Time::now() + PRACK_TIMER;
m_prackCount = PRACK_TRIES;
msg->addHeader("Require","100rel");
msg->addHeader("RSeq",String(++m_lastRseq));
}
m_tr->setResponse(msg);
}
msg->deref();
}
void YateSIPConnection::checkTimers(Message& msg, const Time& tmr)
{
Channel::checkTimers(msg,tmr);
if (m_prackTimer) {
Lock lock(driver());
if (m_prackTimer && (m_prackTimer < tmr)) {
if (--m_prackCount > 0) {
m_prackTimer += PRACK_TIMER;
RefPointer<SIPTransaction> tr = m_tr;
lock.drop();
if (tr)
tr->setTransmit();
}
else {
m_prackQueue.clear();
m_prackTimer = (uint64_t)-1;
paramMutex().lock();
parameters().setParam("cause_sip","408");
parameters().clearParam("reason_sip");
paramMutex().unlock();
setReason("PRACK Timeout",408);
lock.drop();
hangup();
return;
}
}
}
}
bool YateSIPConnection::callPrerouted(Message& msg, bool handled)
{
bool ok = Channel::callPrerouted(msg,handled);
@ -8612,7 +8899,9 @@ bool YateSIPConnection::callRouted(Message& msg)
m_rtpForward = msg.getBoolValue(YSTRING("rtp_forward"));
m_sdpForward = msg.getBoolValue(YSTRING("forward_sdp"),m_sdpForward);
}
setRfc2833(msg.getParam(YSTRING("rfc2833")));
m_gpmd = msg.getBoolValue(YSTRING("forward_gpmd"),m_gpmd);
m_reinviteWait = msg.getBoolValue(YSTRING("reinvite_wait_initial"),m_reinviteWait);
setRfc2833(msg,false);
updateRtpNatAddress(&msg);
Channel::callRouted(msg);
Lock lock(driver());
@ -8706,6 +8995,8 @@ void YateSIPConnection::callAccept(Message& msg)
if (m_tr && !infoAllowed(m_tr->initialMessage()))
m_dtmfMethods.reset(DtmfMethods::Info);
}
m_prackUsed = plugin.ep()->engine()->prack() && msg.getBoolValue(YSTRING("iprack"),m_prackUsed);
m_reinviteWait = msg.getBoolValue(YSTRING("reinvite_wait_initial"),m_reinviteWait);
setRefer(msg);
Channel::callAccept(msg);
@ -8744,7 +9035,7 @@ void YateSIPConnection::callRejected(const char* error, const char* reason, cons
}
// Start a client reINVITE transaction
bool YateSIPConnection::startClientReInvite(NamedList& msg, bool rtpForward)
bool YateSIPConnection::startClientReInvite(NamedList& msg, bool rtpForward, bool* disc)
{
bool hadRtp = !m_rtpForward;
bool forced = msg.getBoolValue(YSTRING("rtp_forced"));
@ -8774,6 +9065,8 @@ bool YateSIPConnection::startClientReInvite(NamedList& msg, bool rtpForward)
if (hadRtp) {
TraceDebug(m_traceId,this,DebugWarn,"Could not build SDP for reINVITE, hanging up [%p]",this);
disconnect("nomedia");
if (disc)
*disc = true;
}
return false;
}
@ -8783,9 +9076,12 @@ bool YateSIPConnection::startClientReInvite(NamedList& msg, bool rtpForward)
copySipHeaders(*m,msg);
if (s_privacy)
copyPrivacy(*m,msg);
if (sdp)
m->setBody(sdp);
Message* ymsg = YOBJECT(Message,&msg);
if (ymsg)
m->setBody(buildSIPBody(*ymsg,sdp));
else
m->setBody(sdp);
if (!sdp)
m->addHeader(new MimeHeaderLine("Accept","application/sdp"));
m_tr2 = plugin.ep()->engine()->addMessage(m,&m_autoChangeParty);
if (m_tr2) {
@ -8798,6 +9094,50 @@ bool YateSIPConnection::startClientReInvite(NamedList& msg, bool rtpForward)
return true;
}
// Start a client UPDATE transaction
bool YateSIPConnection::startClientUpdate(NamedList& msg)
{
bool hadRtp = !m_rtpForward;
m_rtpForward = msg.getBoolValue(YSTRING("rtp_forward"),m_rtpForward);
// this is the point of no return
if (hadRtp && m_rtpForward)
clearEndpoint();
MimeSdpBody* sdp = 0;
if (m_rtpForward)
sdp = createPasstroughSDP(msg,false,true);
else {
updateFormats(msg,true);
sdp = createSDP();
}
if (!sdp) {
msg.setParam("error","failure");
msg.setParam("reason","Could not build the SDP");
if (hadRtp) {
TraceDebug(m_traceId,this,DebugWarn,"Could not build SDP for UPDATE, hanging up [%p]",this);
disconnect("nomedia");
}
return false;
}
TraceDebug(m_traceId,this,DebugNote,"Initiating UPDATE (%s RTP before) [%p]",
hadRtp ? "had" : "no",this);
SIPMessage* m = createDlgMsg("UPDATE");
copySipHeaders(*m,msg);
if (s_privacy)
copyPrivacy(*m,msg);
Message* ymsg = YOBJECT(Message,&msg);
if (ymsg)
m->setBody(buildSIPBody(*ymsg,sdp));
else
m->setBody(sdp);
m_tr2 = plugin.ep()->engine()->addMessage(m,&m_autoChangeParty);
if (m_tr2) {
m_tr2->ref();
m_tr2->setUserData(this);
}
m->deref();
return true;
}
// Emit pending update if possible, method is called with driver mutex hold
void YateSIPConnection::startPendingUpdate()
{
@ -9013,7 +9353,7 @@ bool YateSIPConnection::sendTone(Message& msg, const char* tone, int meth, bool&
ObjList* l = m_rtpMedia->find("audio");
const SDPMedia* m = static_cast<const SDPMedia*>(l ? l->get() : 0);
if (meth == DtmfMethods::Rfc2833) {
ok = m && m->rfc2833().toBoolean(true);
ok = m && m->haveRfc2833();
if (ok)
msg.setParam("targetid",m->id());
}
@ -9921,6 +10261,8 @@ void SIPDriver::initialize()
s_rtp_preserve = s_cfg.getBoolValue("hacks","ignore_sdp_addr",false);
s_changeParty2xx = s_cfg.getBoolValue("general","change_party_2xx",false);
s_initialHeaders = s_cfg.getBoolValue("general","initial_headers");
s_reinviteWait = s_cfg.getBoolValue("general","reinvite_wait_initial");
s_provNonReliable = s_cfg.getBoolValue("general","mixed_provisional",true);
m_parser.initialize(s_cfg.getSection("codecs"),s_cfg.getSection("hacks"),s_cfg.getSection("general"));
s_trace = s_cfg.getBoolValue("general","trace");
if (!m_endpoint) {