Import commercial version of YATE SIP and SDP implementation.
This commit is contained in:
parent
3539304e63
commit
afc46246ca
|
@ -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
|
||||
|
|
|
@ -6,3 +6,4 @@ core*
|
|||
*.orig
|
||||
*~
|
||||
.*.swp
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = ¶ms;
|
||||
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
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue