mirror of
https://github.com/resiprocate/resiprocate.git
synced 2026-01-12 00:05:02 +08:00
917 lines
29 KiB
C++
917 lines
29 KiB
C++
#include "rutil/ResipAssert.h"
|
|
#include <sstream>
|
|
#include <signal.h>
|
|
|
|
#include <resip/stack/Symbols.hxx>
|
|
#include <resip/stack/Tuple.hxx>
|
|
#include <resip/stack/SipStack.hxx>
|
|
#include <rutil/GeneralCongestionManager.hxx>
|
|
#include <rutil/Data.hxx>
|
|
#include <rutil/DnsUtil.hxx>
|
|
#include <rutil/Logger.hxx>
|
|
#include <rutil/ParseBuffer.hxx>
|
|
#include <rutil/Socket.hxx>
|
|
#include <rutil/TransportType.hxx>
|
|
#include <rutil/Timer.hxx>
|
|
|
|
#include "repro/XmlRpcServerBase.hxx"
|
|
#include "repro/XmlRpcConnection.hxx"
|
|
#include "repro/ReproRunner.hxx"
|
|
#include "repro/CommandServer.hxx"
|
|
#include "repro/WebAdmin.hxx"
|
|
|
|
using namespace repro;
|
|
using namespace resip;
|
|
using namespace std;
|
|
|
|
#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
|
|
|
|
CommandServer::CommandServer(ReproRunner& reproRunner,
|
|
Data ipAddr,
|
|
int port,
|
|
IpVersion version,
|
|
WebAdmin* webAdmin) :
|
|
XmlRpcHandler(std::make_shared<XmlRpcSocketServer>(*this, port, version, ipAddr)),
|
|
mReproRunner(reproRunner),
|
|
mWebAdmin(webAdmin)
|
|
{
|
|
}
|
|
|
|
#ifdef BUILD_QPID_PROTON
|
|
CommandServer::CommandServer(ReproRunner& reproRunner,
|
|
const resip::Data& brokerUrl,
|
|
bool broadcast,
|
|
WebAdmin* webAdmin) :
|
|
XmlRpcHandler(std::make_shared<XmlRpcProtonServer>(*this, brokerUrl, broadcast)),
|
|
mReproRunner(reproRunner),
|
|
mWebAdmin(webAdmin)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
CommandServer::~CommandServer()
|
|
{
|
|
}
|
|
|
|
void
|
|
CommandServer::sendResponse(unsigned int connectionId,
|
|
unsigned int requestId,
|
|
const Data& responseData,
|
|
unsigned int resultCode,
|
|
const Data& resultText)
|
|
{
|
|
std::stringstream ss;
|
|
ss << Symbols::CRLF << " <Result Code=\"" << resultCode << "\"";
|
|
ss << ">" << resultText.xmlCharDataEncode() << "</Result>" << Symbols::CRLF;
|
|
if(!responseData.empty())
|
|
{
|
|
ss << " <Data>" << Symbols::CRLF;
|
|
ss << responseData.xmlCharDataEncode();
|
|
ss << " </Data>" << Symbols::CRLF;
|
|
}
|
|
mRpc->sendResponse(connectionId, requestId, ss.str().c_str(), resultCode >= 200 /* isFinal */);
|
|
}
|
|
|
|
void
|
|
CommandServer::handleRequest(unsigned int connectionId, unsigned int requestId, const resip::Data& request)
|
|
{
|
|
DebugLog (<< "CommandServer::handleRequest: connectionId=" << connectionId << ", requestId=" << requestId << ", request=\r\n" << request);
|
|
|
|
try
|
|
{
|
|
ParseBuffer pb(request);
|
|
XMLCursor xml(pb);
|
|
|
|
if(!mReproRunner.getProxy())
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Proxy not running.");
|
|
return;
|
|
}
|
|
|
|
if(isEqualNoCase(xml.getTag(), "GetStackInfo"))
|
|
{
|
|
handleGetStackInfoRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "GetStackStats"))
|
|
{
|
|
handleGetStackStatsRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "ResetStackStats"))
|
|
{
|
|
handleResetStackStatsRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "LogDnsCache"))
|
|
{
|
|
handleLogDnsCacheRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "ClearDnsCache"))
|
|
{
|
|
handleClearDnsCacheRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "GetDnsCache"))
|
|
{
|
|
handleGetDnsCacheRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "GetCongestionStats"))
|
|
{
|
|
handleGetCongestionStatsRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "SetCongestionTolerance"))
|
|
{
|
|
handleSetCongestionToleranceRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "Shutdown"))
|
|
{
|
|
handleShutdownRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "GetProxyConfig"))
|
|
{
|
|
handleGetProxyConfigRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "Restart"))
|
|
{
|
|
handleRestartRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "AddTransport"))
|
|
{
|
|
handleAddTransportRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "RemoveTransport"))
|
|
{
|
|
handleRemoveTransportRequest(connectionId, requestId, xml);
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "HttpRequest"))
|
|
{
|
|
handleHttpRequest(connectionId, requestId, xml);
|
|
}
|
|
else
|
|
{
|
|
WarningLog(<< "CommandServer::handleRequest: Received XML message with unknown method: " << xml.getTag());
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Unknown method");
|
|
}
|
|
}
|
|
catch(resip::BaseException& e)
|
|
{
|
|
WarningLog(<< "CommandServer::handleRequest: ParseException: " << e);
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Parse error");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleGetStackInfoRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleGetStackInfoRequest");
|
|
|
|
Data buffer;
|
|
DataStream strm(buffer);
|
|
mReproRunner.getProxy()->getStack().dump(strm);
|
|
strm.flush();
|
|
|
|
sendResponse(connectionId, requestId, buffer, 200, "Stack info retrieved.");
|
|
}
|
|
|
|
void
|
|
CommandServer::handleGetStackStatsRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleGetStackStatsRequest");
|
|
|
|
Lock lock(mStatisticsWaitersMutex);
|
|
mStatisticsWaiters.push_back(std::make_pair(connectionId, requestId));
|
|
|
|
if(!mReproRunner.getProxy()->getStack().pollStatistics())
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Statistics Manager is not enabled.");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleStatisticsMessage(resip::StatisticsMessage &statsMessage)
|
|
{
|
|
Lock lock(mStatisticsWaitersMutex);
|
|
if(mStatisticsWaiters.size() > 0)
|
|
{
|
|
Data buffer;
|
|
DataStream strm(buffer);
|
|
StatisticsMessage::Payload payload;
|
|
statsMessage.loadOut(payload); // !slg! could optimize by providing stream operator on StatisticsMessage
|
|
strm << payload << endl;
|
|
|
|
StatisticsWaitersList::iterator it = mStatisticsWaiters.begin();
|
|
for(; it != mStatisticsWaiters.end(); it++)
|
|
{
|
|
sendResponse(it->first, it->second, buffer, 200, "Stack stats retrieved.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleResetStackStatsRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleResetStackStatsRequest");
|
|
|
|
mReproRunner.getProxy()->getStack().zeroOutStatistics();
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "Stack stats reset.");
|
|
}
|
|
|
|
void
|
|
CommandServer::handleLogDnsCacheRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleLogDnsCacheRequest");
|
|
|
|
mReproRunner.getProxy()->getStack().logDnsCache();
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "DNS cache logged.");
|
|
}
|
|
|
|
void
|
|
CommandServer::handleClearDnsCacheRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleQueryDnsCacheRequest");
|
|
|
|
mReproRunner.getProxy()->getStack().clearDnsCache();
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "DNS cache cleared.");
|
|
}
|
|
|
|
void
|
|
CommandServer::handleGetDnsCacheRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleGetDnsCacheRequest");
|
|
|
|
mReproRunner.getProxy()->getStack().getDnsCacheDump(make_pair((unsigned long)connectionId, (unsigned long)requestId), this);
|
|
// Note: Response will be sent when callback is invoked
|
|
}
|
|
|
|
void
|
|
CommandServer::onDnsCacheDumpRetrieved(std::pair<unsigned long, unsigned long> key, const Data& dnsCache)
|
|
{
|
|
if(dnsCache.empty())
|
|
{
|
|
sendResponse(key.first, key.second, "empty\r\n", 200, "DNS cache retrieved.");
|
|
}
|
|
else
|
|
{
|
|
sendResponse(key.first, key.second, dnsCache, 200, "DNS cache retrieved.");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleGetCongestionStatsRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleGetCongestionStatsRequest");
|
|
|
|
CongestionManager* congestionManager = mReproRunner.getProxy()->getStack().getCongestionManager();
|
|
if(congestionManager != 0)
|
|
{
|
|
Data buffer;
|
|
DataStream strm(buffer);
|
|
congestionManager->encodeCurrentState(strm);
|
|
|
|
sendResponse(connectionId, requestId, buffer, 200, "Congestion stats retrieved.");
|
|
}
|
|
else
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Congestion Manager is not enabled.");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleSetCongestionToleranceRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleSetCongestionToleranceRequest");
|
|
|
|
Data fifoDescription;
|
|
Data metricData;
|
|
GeneralCongestionManager::MetricType metric;
|
|
unsigned long maxTolerance=0;
|
|
|
|
GeneralCongestionManager* congestionManager = dynamic_cast<GeneralCongestionManager*>(mReproRunner.getProxy()->getStack().getCongestionManager());
|
|
if(congestionManager != 0)
|
|
{
|
|
// Check for Parameters
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "request"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
while(true)
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "fifoDescription"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
fifoDescription = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "metric"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
metricData = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "maxtolerance"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
maxTolerance = xml.getValue().convertUnsignedLong();
|
|
xml.parent();
|
|
}
|
|
}
|
|
if(!xml.nextSibling())
|
|
{
|
|
// break on no more sibilings
|
|
break;
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
|
|
if(isEqualNoCase(metricData, "WAIT_TIME"))
|
|
{
|
|
metric = GeneralCongestionManager::WAIT_TIME;
|
|
}
|
|
else if(isEqualNoCase(metricData, "TIME_DEPTH"))
|
|
{
|
|
metric = GeneralCongestionManager::TIME_DEPTH;
|
|
}
|
|
else if(isEqualNoCase(metricData, "SIZE"))
|
|
{
|
|
metric = GeneralCongestionManager::SIZE;
|
|
}
|
|
else
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Invalid metric specified: must be SIZE, TIME_DEPTH or WAIT_TIME.");
|
|
return;
|
|
}
|
|
|
|
if(maxTolerance == 0)
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Invalid MaxTolerance specified: must be greater than 0.");
|
|
return;
|
|
}
|
|
|
|
if(congestionManager->updateFifoTolerances(fifoDescription, metric, maxTolerance))
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "Congestion Tolerance set.");
|
|
}
|
|
else
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Invalid fifo description provided.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Congestion Manager is not enabled.");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleShutdownRequest(unsigned int connectionId, unsigned int requestId, XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleShutdownRequest");
|
|
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "Shutdown initiated.");
|
|
raise(SIGTERM);
|
|
}
|
|
|
|
void
|
|
CommandServer::handleGetProxyConfigRequest(unsigned int connectionId, unsigned int requestId, resip::XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleGetProxyConfigRequest");
|
|
|
|
Data buffer;
|
|
DataStream strm(buffer);
|
|
strm << mReproRunner.getProxy()->getConfig();
|
|
|
|
sendResponse(connectionId, requestId, buffer, 200, "Proxy config retrieved.");
|
|
}
|
|
|
|
void
|
|
CommandServer::handleRestartRequest(unsigned int connectionId, unsigned int requestId, resip::XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleRestartRequest");
|
|
|
|
mReproRunner.restart();
|
|
if(mReproRunner.getProxy())
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "Restart completed.");
|
|
}
|
|
else
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "Restart failed.");
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleAddTransportRequest(unsigned int connectionId, unsigned int requestId, resip::XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleAddTransportRequest");
|
|
|
|
TransportType type = UDP;
|
|
int port = 0;
|
|
IpVersion ipVersion=V4;
|
|
Data ipInterface;
|
|
Data recordRouteUri;
|
|
uint32_t rcvBufLen = 0;
|
|
bool stunEnabled = false;
|
|
Data sipDomainname;
|
|
SecurityTypes::SSLType sslType = SecurityTypes::SSLv23;
|
|
uint32_t transportFlags = 0;
|
|
Data certificateFilename;
|
|
Data privateKeyFilename;
|
|
SecurityTypes::TlsClientVerificationMode cvm = SecurityTypes::None;
|
|
bool useEmailAsSIP = false;
|
|
|
|
// Check for Parameters
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "request"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
while(true)
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "type"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
type = toTransportType(xml.getValue());
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "port"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
port = xml.getValue().convertInt();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "ipversion"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getValue(), "v6") || xml.getValue() == "6") // be a little leanient with this, allow just a 6
|
|
{
|
|
ipVersion = V6;
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "interface"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
ipInterface = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "rruri"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
recordRouteUri = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "udprcvbuflen"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
rcvBufLen = xml.getValue().convertUnsignedLong();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "stun"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getValue(), "yes"))
|
|
{
|
|
stunEnabled = true;
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "flags"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
transportFlags = xml.getValue().convertUnsignedLong();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "domain"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
sipDomainname = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "ssltype"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getValue(), "SSLv23"))
|
|
{
|
|
sslType = SecurityTypes::SSLv23;
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "certfile"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
certificateFilename = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "keyfile"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
privateKeyFilename = xml.getValue();
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "tlscvm"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getValue(), "opt"))
|
|
{
|
|
cvm = SecurityTypes::Optional;
|
|
}
|
|
else if(isEqualNoCase(xml.getValue(), "man"))
|
|
{
|
|
cvm = SecurityTypes::Mandatory;
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
else if(isEqualNoCase(xml.getTag(), "tlsuseemail"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getValue(), "yes"))
|
|
{
|
|
useEmailAsSIP = true;
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
|
|
if(!xml.nextSibling())
|
|
{
|
|
// break on no more sibilings
|
|
break;
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
|
|
// We need at least port and transport type to be valid to add
|
|
if(port == 0)
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Invalid port specified: must be non-zero.");
|
|
return;
|
|
}
|
|
|
|
if(type == UNKNOWN_TRANSPORT)
|
|
{
|
|
Data errorString("Invalid transport specified: must one of UDP, TCP");
|
|
#if defined( USE_SSL )
|
|
errorString += ", TLS, WS, WSS";
|
|
#endif
|
|
#if defined( USE_DTLS )
|
|
errorString += ", DTLS";
|
|
#endif
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, errorString);
|
|
return;
|
|
}
|
|
|
|
// Build RecordRoute URI if needed
|
|
NameAddr rr;
|
|
if(!recordRouteUri.empty())
|
|
{
|
|
try
|
|
{
|
|
if(isEqualNoCase(recordRouteUri, "auto")) // auto generated record route uri
|
|
{
|
|
if(isSecure(type))
|
|
{
|
|
rr.uri().host()=sipDomainname;
|
|
rr.uri().port()=port;
|
|
rr.uri().param(resip::p_transport)=resip::Tuple::toDataLower(type);
|
|
InfoLog (<< "Transport specific record-route enabled (generated): " << rr);
|
|
}
|
|
else
|
|
{
|
|
rr.uri().host()=ipInterface;
|
|
rr.uri().port()=port;
|
|
rr.uri().param(resip::p_transport)=resip::Tuple::toDataLower(type);
|
|
InfoLog (<< "Transport specific record-route enabled (generated): " << rr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NameAddr rrtemp(recordRouteUri);
|
|
rr = rrtemp;
|
|
InfoLog (<< "Transport specific record-route enabled: " << rr);
|
|
}
|
|
}
|
|
catch(BaseException& e)
|
|
{
|
|
ErrLog (<< "Invalid uri provided in rruri setting (ignoring): " << e);
|
|
}
|
|
}
|
|
|
|
// Build transport info text for response message
|
|
Data transportInfoText("type=");
|
|
transportInfoText += getTransportNameFromType(type).c_str();
|
|
transportInfoText += ", port=" + Data(port);
|
|
transportInfoText += ", ipversion=" + Data((ipVersion == V4 ? "V4" : "V6"));
|
|
if(!ipInterface.empty())
|
|
{
|
|
transportInfoText += ", interface=" + ipInterface;
|
|
}
|
|
if(!recordRouteUri.empty())
|
|
{
|
|
transportInfoText += ", rruri=" + Data::from(rr);
|
|
}
|
|
transportInfoText += ", stun=" + Data((stunEnabled ? "YES" : "NO"));
|
|
transportInfoText += ", flags=" + Data((uint32_t)transportFlags);
|
|
if(rcvBufLen)
|
|
{
|
|
transportInfoText += ", udprcvbuflen=" + Data((uint32_t)rcvBufLen);
|
|
}
|
|
if(isSecure(type))
|
|
{
|
|
if(!sipDomainname.empty())
|
|
{
|
|
transportInfoText += ", domain=" + sipDomainname;
|
|
}
|
|
transportInfoText += ", ssltype=" + Data((sslType == SecurityTypes::TLSv1 ? "TLSv1" : "SSLv23"));
|
|
if(!certificateFilename.empty())
|
|
{
|
|
transportInfoText += ", cert=" + certificateFilename;
|
|
}
|
|
if(!privateKeyFilename.empty())
|
|
{
|
|
transportInfoText += ", key=" + privateKeyFilename;
|
|
}
|
|
transportInfoText += ", tlscvm=" + Data((cvm == SecurityTypes::None ? "NONE" : (cvm == SecurityTypes::Optional ? "OPT" : "MAN")));
|
|
transportInfoText += ", tlsuseemail=" + Data((stunEnabled ? "YES" : "NO"));
|
|
}
|
|
|
|
try
|
|
{
|
|
Transport* transport = mReproRunner.getProxy()->getStack().addTransport(
|
|
type, port, ipVersion, stunEnabled ? StunEnabled : StunDisabled, ipInterface,
|
|
sipDomainname, Data::Empty, sslType, transportFlags, certificateFilename,
|
|
privateKeyFilename, cvm, useEmailAsSIP);
|
|
|
|
if(transport)
|
|
{
|
|
if(!rr.uri().host().empty())
|
|
{
|
|
mReproRunner.getProxy()->addTransportRecordRoute(transport->getKey(), rr);
|
|
}
|
|
if(rcvBufLen)
|
|
{
|
|
transport->setRcvBufLen(rcvBufLen);
|
|
}
|
|
|
|
transportInfoText += ", transportKey=" + Data(transport->getKey());
|
|
|
|
Data text("Transport add requested: ");
|
|
text += transportInfoText;
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, text);
|
|
}
|
|
else
|
|
{
|
|
Data text("Unknown error adding transport: ");
|
|
text += transportInfoText;
|
|
sendResponse(connectionId, requestId, Data::Empty, 500, text);
|
|
}
|
|
}
|
|
catch(BaseException& e)
|
|
{
|
|
Data text("Exception adding transport: ");
|
|
text += transportInfoText;
|
|
text += " - " + e.getMessage();
|
|
sendResponse(connectionId, requestId, Data::Empty, 500, text);
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::handleRemoveTransportRequest(unsigned int connectionId, unsigned int requestId, resip::XMLCursor& xml)
|
|
{
|
|
unsigned int transportKey = 0;
|
|
|
|
InfoLog(<< "CommandServer::handleRemoveTransportRequest");
|
|
|
|
// Check for Parameters
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "request"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
while(true)
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "key"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
transportKey = xml.getValue().convertUnsignedLong();
|
|
xml.parent();
|
|
}
|
|
}
|
|
if(!xml.nextSibling())
|
|
{
|
|
// break on no more sibilings
|
|
break;
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
|
|
// We need transport key to remove
|
|
if(transportKey == 0)
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Invalid transport key specified: must be non-zero.");
|
|
return;
|
|
}
|
|
mReproRunner.getProxy()->getStack().removeTransport(transportKey);
|
|
mReproRunner.getProxy()->removeTransportRecordRoute(transportKey);
|
|
|
|
Data text("Transport remove requested: transportKey=");
|
|
text += Data(transportKey);
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, text);
|
|
}
|
|
|
|
void
|
|
CommandServer::handleHttpRequest(unsigned int connectionId, unsigned int requestId, resip::XMLCursor& xml)
|
|
{
|
|
InfoLog(<< "CommandServer::handleHttpRequest");
|
|
|
|
// Check for Parameters
|
|
std::map<Data, Data> m;
|
|
std::set<Data> keys;
|
|
xmlToMap(m, keys, xml);
|
|
|
|
if(!mWebAdmin)
|
|
{
|
|
// FIXME - do this without binding the WebAdmin instance to a port
|
|
ErrLog(<<"at least one WebAdmin instance must be configured, even if it is only bound to localhost");
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "WebAdmin not available.");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
Data& user = m["User"];
|
|
Data& password = m["Password"];
|
|
Data uri;
|
|
DataStream s(uri);
|
|
s << "/" << m["Page"] << ".html";
|
|
bool firstParam = true;
|
|
for(const auto& n : m)
|
|
{
|
|
const Data& k = n.first;
|
|
const Data& v = n.second;
|
|
if(k == "User" || k == "Password" || k == "Page")
|
|
{
|
|
continue;
|
|
}
|
|
s << (firstParam ? "?" : "&") << k << "=";
|
|
v.urlEncode(s);
|
|
firstParam = false;
|
|
}
|
|
s.flush();
|
|
int pageNumber = -1; // FIXME - add logic for handling response
|
|
mWebAdmin->buildPage(uri, pageNumber, user, password);
|
|
sendResponse(connectionId, requestId, Data::Empty, 200, "HTTP Request processed, response ignored");
|
|
}
|
|
catch(std::exception& e)
|
|
{
|
|
sendResponse(connectionId, requestId, Data::Empty, 400, "Exception.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
CommandServer::xmlToMap(std::map<Data, Data>& m, std::set<Data> keys, resip::XMLCursor& xml)
|
|
{
|
|
// Check for Parameters
|
|
if(xml.firstChild())
|
|
{
|
|
if(isEqualNoCase(xml.getTag(), "request"))
|
|
{
|
|
if(xml.firstChild())
|
|
{
|
|
while(true)
|
|
{
|
|
const Data& key = xml.getTag();
|
|
if(!keys.empty() && keys.find(key) == keys.end())
|
|
{
|
|
ErrLog(<<"unexpected key: " << key);
|
|
m.clear();
|
|
return;
|
|
}
|
|
if(xml.firstChild())
|
|
{
|
|
const Data& value = xml.getValue();
|
|
m[key] = value;
|
|
xml.parent();
|
|
}
|
|
else
|
|
{
|
|
ErrLog(<<"no value for key: " << key);
|
|
}
|
|
if(!xml.nextSibling())
|
|
{
|
|
// break on no more sibilings
|
|
break;
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
}
|
|
xml.parent();
|
|
}
|
|
if(!keys.empty() && (m.size() < keys.size()))
|
|
{
|
|
ErrLog(<<"some keys not found");
|
|
m.clear();
|
|
}
|
|
}
|
|
|
|
|
|
/* ====================================================================
|
|
* The Vovida Software License, Version 1.0
|
|
*
|
|
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
|
|
* Copyright (c) 2010 SIP Spectrum, Inc. All rights reserved.
|
|
* Copyright (c) 2022 Daniel Pocock https://danielpocock.com
|
|
* Copyright (c) 2022 Software Freedom Institute LLC https://softwarefreedom.institute
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
|
|
* and "Vovida Open Communication Application Library (VOCAL)" must
|
|
* not be used to endorse or promote products derived from this
|
|
* software without prior written permission. For written
|
|
* permission, please contact vocal@vovida.org.
|
|
*
|
|
* 4. Products derived from this software may not be called "VOCAL", nor
|
|
* may "VOCAL" appear in their name, without prior written
|
|
* permission of Vovida Networks, Inc.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
|
|
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
|
|
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
|
|
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
* DAMAGE.
|
|
*
|
|
* ====================================================================
|
|
*
|
|
* This software consists of voluntary contributions made by Vovida
|
|
* Networks, Inc. and many individuals on behalf of Vovida Networks,
|
|
* Inc. For more information on Vovida Networks, Inc., please see
|
|
* <http://www.vovida.org/>.
|
|
*
|
|
*/
|