mirror of
https://github.com/resiprocate/resiprocate.git
synced 2026-01-12 00:05:02 +08:00
2067 lines
76 KiB
C++
2067 lines
76 KiB
C++
#if defined(HAVE_CONFIG_H)
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#ifndef WIN32
|
|
#include <syslog.h>
|
|
#endif
|
|
|
|
#include "rutil/ResipAssert.h"
|
|
#include "rutil/Log.hxx"
|
|
#include "rutil/Logger.hxx"
|
|
#include "rutil/DnsUtil.hxx"
|
|
#include "rutil/dns/DnsStub.hxx"
|
|
#include "rutil/GeneralCongestionManager.hxx"
|
|
#include "rutil/TransportType.hxx"
|
|
#include "rutil/hep/HepAgent.hxx"
|
|
|
|
#include "resip/stack/SipStack.hxx"
|
|
#include "resip/stack/Compression.hxx"
|
|
#include "resip/stack/EventStackThread.hxx"
|
|
#include "resip/stack/ExtendedDomainMatcher.hxx"
|
|
#include "resip/stack/HEPSipMessageLoggingHandler.hxx"
|
|
#include "resip/stack/InteropHelper.hxx"
|
|
#include "resip/stack/ConnectionManager.hxx"
|
|
#include "resip/stack/TransactionState.hxx"
|
|
#include "resip/stack/WsCookieContextFactory.hxx"
|
|
|
|
#include "resip/dum/InMemorySyncRegDb.hxx"
|
|
#include "resip/dum/InMemorySyncPubDb.hxx"
|
|
#include "resip/dum/MasterProfile.hxx"
|
|
#include "resip/dum/DialogUsageManager.hxx"
|
|
#include "resip/dum/DumThread.hxx"
|
|
#include "resip/dum/TlsPeerAuthManager.hxx"
|
|
#include "resip/dum/WsCookieAuthManager.hxx"
|
|
|
|
#include "repro/AsyncProcessorWorker.hxx"
|
|
#include "repro/ReproRunner.hxx"
|
|
#include "repro/Proxy.hxx"
|
|
#include "repro/ProxyConfig.hxx"
|
|
#include "repro/BerkeleyDb.hxx"
|
|
#include "resip/stack/Dispatcher.hxx"
|
|
#include "repro/UserAuthGrabber.hxx"
|
|
#include "repro/ProcessorChain.hxx"
|
|
#include "repro/ReproVersion.hxx"
|
|
#include "repro/WebAdmin.hxx"
|
|
#include "repro/WebAdminThread.hxx"
|
|
#include "repro/Registrar.hxx"
|
|
#include "repro/ReproAuthenticatorFactory.hxx"
|
|
#include "repro/ReproServerAuthManager.hxx"
|
|
#include "repro/RegSyncClient.hxx"
|
|
#include "repro/RegSyncServer.hxx"
|
|
#include "repro/RegSyncServerThread.hxx"
|
|
#include "repro/CommandServer.hxx"
|
|
#include "repro/CommandServerThread.hxx"
|
|
#include "repro/BasicWsConnectionValidator.hxx"
|
|
#include "repro/monkeys/CookieAuthenticator.hxx"
|
|
#include "repro/monkeys/IsTrustedNode.hxx"
|
|
#include "repro/monkeys/AmIResponsible.hxx"
|
|
#include "repro/monkeys/DigestAuthenticator.hxx"
|
|
#include "repro/monkeys/LocationServer.hxx"
|
|
#include "repro/monkeys/RecursiveRedirect.hxx"
|
|
#include "repro/monkeys/SimpleStaticRoute.hxx"
|
|
#include "repro/monkeys/StaticRoute.hxx"
|
|
#include "repro/monkeys/StrictRouteFixup.hxx"
|
|
#include "repro/monkeys/OutboundTargetHandler.hxx"
|
|
#include "repro/monkeys/QValueTargetHandler.hxx"
|
|
#include "repro/monkeys/SimpleTargetHandler.hxx"
|
|
#include "repro/monkeys/GeoProximityTargetSorter.hxx"
|
|
#include "repro/monkeys/RequestFilter.hxx"
|
|
#include "repro/monkeys/MessageSilo.hxx"
|
|
#include "repro/monkeys/CertificateAuthenticator.hxx"
|
|
#include "repro/stateAgents/PresenceServer.hxx"
|
|
|
|
#if defined(USE_SSL)
|
|
#include "repro/stateAgents/CertServer.hxx"
|
|
#include "resip/stack/ssl/Security.hxx"
|
|
#define DEFAULT_TLS_METHOD SecurityTypes::SSLv23
|
|
#endif
|
|
|
|
#if defined(USE_MYSQL)
|
|
#include "repro/MySqlDb.hxx"
|
|
#endif
|
|
|
|
#if defined(USE_POSTGRESQL)
|
|
#include "repro/PostgreSqlDb.hxx"
|
|
#endif
|
|
|
|
#include "rutil/WinLeakCheck.hxx"
|
|
|
|
#define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
|
|
|
|
using namespace resip;
|
|
using namespace repro;
|
|
using namespace std;
|
|
|
|
class ReproLogger : public ExternalLogger
|
|
{
|
|
public:
|
|
virtual ~ReproLogger() {}
|
|
/** return true to also do default logging, false to supress default logging. */
|
|
virtual bool operator()(Log::Level level,
|
|
const Subsystem& subsystem,
|
|
const Data& appName,
|
|
const char* file,
|
|
int line,
|
|
const Data& message,
|
|
const Data& messageWithHeaders,
|
|
const Data& instanceName)
|
|
{
|
|
// Log any errors to the screen
|
|
if(level <= Log::Err)
|
|
{
|
|
resipCout << messageWithHeaders << endl;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
ReproLogger g_ReproLogger;
|
|
|
|
class ReproSipMessageLoggingHandler : public Transport::SipMessageLoggingHandler
|
|
{
|
|
public:
|
|
virtual ~ReproSipMessageLoggingHandler(){}
|
|
virtual void outboundMessage(const Tuple &source, const Tuple &destination, const SipMessage &msg)
|
|
{
|
|
InfoLog(<< "\r\n*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*\r\n"
|
|
<< "OUTBOUND: Src=" << source << ", Dst=" << destination << "\r\n\r\n"
|
|
<< msg
|
|
<< "*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
|
|
}
|
|
virtual void outboundRetransmit(const Tuple &source, const Tuple &destination, const SendData &data)
|
|
{
|
|
InfoLog(<< "\r\n*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*\r\n"
|
|
<< "OUTBOUND(retransmit): Src=" << source << ", Dst=" << destination << "\r\n\r\n"
|
|
<< data.data
|
|
<< "*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
|
|
}
|
|
virtual void inboundMessage(const Tuple& source, const Tuple& destination, const SipMessage &msg)
|
|
{
|
|
InfoLog(<< "\r\n*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*\r\n"
|
|
<< "INBOUND: Src=" << source << ", Dst=" << destination << "\r\n\r\n"
|
|
<< msg
|
|
<< "*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
|
|
}
|
|
};
|
|
|
|
class MyProxyConfig : public ProxyConfig
|
|
{
|
|
public:
|
|
AbstractDb *getDatabase(int configIndex)
|
|
{
|
|
ConfigParse::NestedConfigMap m = getConfigNested("Database");
|
|
ConfigParse::NestedConfigMap::iterator it = m.find(configIndex);
|
|
if (it == m.end())
|
|
{
|
|
WarningLog(<< "Failed to find Database settings for index " << configIndex);
|
|
return 0;
|
|
}
|
|
ConfigParse& dbConfig = it->second;
|
|
Data dbType = dbConfig.getConfigData("Type", "");
|
|
dbType.lowercase();
|
|
if (dbType == "berkeleydb")
|
|
{
|
|
Data path = dbConfig.getConfigData("Path",
|
|
getConfigData("DatabasePath", "./", true), true);
|
|
return new BerkeleyDb(path);
|
|
}
|
|
else if (dbType == "mysql")
|
|
{
|
|
#ifdef USE_MYSQL
|
|
Data mySQLServer = dbConfig.getConfigData("Host", Data::Empty);
|
|
if (!mySQLServer.empty())
|
|
{
|
|
return new MySqlDb(dbConfig, mySQLServer,
|
|
dbConfig.getConfigData("User", Data::Empty),
|
|
dbConfig.getConfigData("Password", Data::Empty),
|
|
dbConfig.getConfigData("DatabaseName", Data::Empty),
|
|
dbConfig.getConfigUnsignedLong("Port", 0),
|
|
dbConfig.getConfigData("CustomUserAuthQuery", Data::Empty));
|
|
}
|
|
#else
|
|
ErrLog(<< "Database" << configIndex << " type MySQL support not compiled into repro");
|
|
return 0;
|
|
#endif
|
|
}
|
|
else if (dbType == "postgresql")
|
|
{
|
|
#ifdef USE_POSTGRESQL
|
|
Data postgreSQLConnInfo = dbConfig.getConfigData("ConnInfo", Data::Empty);
|
|
Data postgreSQLServer = dbConfig.getConfigData("Host", Data::Empty);
|
|
if (!postgreSQLConnInfo.empty() || !postgreSQLServer.empty())
|
|
{
|
|
return new PostgreSqlDb(dbConfig, postgreSQLConnInfo, postgreSQLServer,
|
|
dbConfig.getConfigData("User", Data::Empty),
|
|
dbConfig.getConfigData("Password", Data::Empty),
|
|
dbConfig.getConfigData("DatabaseName", Data::Empty),
|
|
dbConfig.getConfigUnsignedLong("Port", 0),
|
|
dbConfig.getConfigData("CustomUserAuthQuery", Data::Empty));
|
|
}
|
|
#else
|
|
ErrLog(<< "Database" << configIndex << " type PostgreSQL support not compiled into repro");
|
|
return 0;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ErrLog(<< "Database" << configIndex << " type '" << dbType << "' not supported / invalid");
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
ReproRunner::ReproRunner()
|
|
: mRunning(false)
|
|
, mRestarting(false)
|
|
, mThreadedStack(false)
|
|
, mUseV4(true)
|
|
, mUseV6 (false)
|
|
, mRegSyncPort(0)
|
|
, mProxyConfig(0)
|
|
, mFdPollGrp(0)
|
|
, mAsyncProcessHandler(0)
|
|
, mSipStack(0)
|
|
, mStackThread(0)
|
|
, mAbstractDb(0)
|
|
, mRuntimeAbstractDb(0)
|
|
, mRegistrationPersistenceManager(0)
|
|
, mPublicationPersistenceManager(0)
|
|
, mAuthFactory(0)
|
|
, mAsyncProcessorDispatcher(0)
|
|
, mMonkeys(0)
|
|
, mLemurs(0)
|
|
, mBaboons(0)
|
|
, mProxy(0)
|
|
, mWebAdminThread(0)
|
|
, mRegistrar(0)
|
|
, mPresenceServer(0)
|
|
, mDum(0)
|
|
, mDumThread(0)
|
|
, mCertServer(0)
|
|
, mRegSyncClient(0)
|
|
, mRegSyncServerV4(0)
|
|
, mRegSyncServerV6(0)
|
|
, mRegSyncServerAMQP(0)
|
|
, mRegSyncServerThread(0)
|
|
, mCommandServerThread(0)
|
|
, mCongestionManager(0)
|
|
{
|
|
}
|
|
|
|
ReproRunner::~ReproRunner()
|
|
{
|
|
if(mRunning) shutdown();
|
|
}
|
|
|
|
bool
|
|
ReproRunner::run(int argc, char** argv)
|
|
{
|
|
if(mRunning) return false;
|
|
|
|
installSignalHandler();
|
|
|
|
if(!mRestarting)
|
|
{
|
|
// Store original arc and argv - so we can reuse them on restart request
|
|
mArgc = argc;
|
|
mArgv = argv;
|
|
}
|
|
|
|
// Parse command line and configuration file
|
|
resip_assert(!mProxyConfig);
|
|
Data defaultConfigFilename("repro.config");
|
|
try
|
|
{
|
|
mProxyConfig = new MyProxyConfig();
|
|
mProxyConfig->parseConfig(mArgc, mArgv, defaultConfigFilename);
|
|
}
|
|
catch(BaseException& ex)
|
|
{
|
|
std::cerr << "Error parsing configuration: " << ex << std::endl;
|
|
#ifndef WIN32
|
|
syslog(LOG_DAEMON | LOG_CRIT, "%s", ex.getMessage().c_str());
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Non-Windows server process stuff
|
|
if(!mRestarting)
|
|
{
|
|
setPidFile(mProxyConfig->getConfigData("PidFile", Data::Empty, true));
|
|
|
|
if(isAlreadyRunning())
|
|
{
|
|
std::cerr << "Already running, will not start two instances. Please stop existing process and/or delete PID file.";
|
|
#ifndef WIN32
|
|
syslog(LOG_DAEMON | LOG_CRIT, "Already running, will not start two instances. Please stop existing process and/or delete PID file.");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if(mProxyConfig->getConfigBool("Daemonize", false))
|
|
{
|
|
daemonize();
|
|
}
|
|
}
|
|
|
|
// Initialize resip logger
|
|
Data loggingType = mProxyConfig->getConfigData("LoggingType", "cout", true);
|
|
Log::initialize(
|
|
*mProxyConfig,
|
|
mArgv[0],
|
|
isEqualNoCase(loggingType, "file") ? &g_ReproLogger : 0);
|
|
|
|
InfoLog( << "Starting repro version " << VersionUtils::instance().releaseVersion() << "...");
|
|
|
|
// Create SipStack and associated objects
|
|
if(!createSipStack())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Load the plugins after creating the stack, as they may need it
|
|
if(!loadPlugins())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Drop privileges (can do this now that sockets are bound)
|
|
Data runAsUser = mProxyConfig->getConfigData("RunAsUser", Data::Empty, true);
|
|
Data runAsGroup = mProxyConfig->getConfigData("RunAsGroup", Data::Empty, true);
|
|
if(!runAsUser.empty())
|
|
{
|
|
InfoLog( << "Trying to drop privileges, configured uid = " << runAsUser << " gid = " << runAsGroup);
|
|
dropPrivileges(runAsUser, runAsGroup);
|
|
}
|
|
|
|
// Create datastore
|
|
if(!createDatastore())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Create authentication mechanism
|
|
createAuthenticatorFactory();
|
|
|
|
// Create DialogUsageManager that handles ServerRegistration,
|
|
// and potentially certificate subscription server
|
|
createDialogUsageManager();
|
|
|
|
// Create the Proxy and associate objects
|
|
if(!createProxy())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Create HTTP WebAdmin and Thread
|
|
if(!createWebAdmin())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Create reg sync components if required
|
|
createRegSync();
|
|
|
|
// Create command server if required
|
|
if(!mRestarting)
|
|
{
|
|
createCommandServer();
|
|
}
|
|
|
|
// Make it all go - startup all threads
|
|
mThreadedStack = mProxyConfig->getConfigBool("ThreadedStack", true);
|
|
if(mThreadedStack)
|
|
{
|
|
// If configured, then start the sub-threads within the stack
|
|
mSipStack->run();
|
|
}
|
|
mStackThread->run();
|
|
if(mDumThread)
|
|
{
|
|
mDumThread->run();
|
|
}
|
|
mProxy->run();
|
|
if(mWebAdminThread)
|
|
{
|
|
mWebAdminThread->run();
|
|
}
|
|
if(!mRestarting && mCommandServerThread)
|
|
{
|
|
mCommandServerThread->run();
|
|
}
|
|
if(mRegSyncServerThread)
|
|
{
|
|
mRegSyncServerThread->run();
|
|
}
|
|
if(mRegSyncClient)
|
|
{
|
|
mRegSyncClient->run();
|
|
}
|
|
|
|
mRunning = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ReproRunner::shutdown()
|
|
{
|
|
if(!mRunning) return;
|
|
|
|
// Tell all threads to shutdown
|
|
if(mWebAdminThread)
|
|
{
|
|
mWebAdminThread->shutdown();
|
|
}
|
|
if(mDumThread)
|
|
{
|
|
mDumThread->shutdown();
|
|
}
|
|
mProxy->shutdown();
|
|
mStackThread->shutdown();
|
|
if(!mRestarting && mCommandServerThread) // leave command server running if we are restarting
|
|
{
|
|
mCommandServerThread->shutdown();
|
|
}
|
|
if(mRegSyncServerThread)
|
|
{
|
|
mRegSyncServerThread->shutdown();
|
|
}
|
|
if(mRegSyncClient)
|
|
{
|
|
mRegSyncClient->shutdown();
|
|
}
|
|
|
|
// Wait for all threads to shutdown, and destroy objects
|
|
mProxy->join();
|
|
if(mThreadedStack)
|
|
{
|
|
mSipStack->shutdownAndJoinThreads();
|
|
}
|
|
mStackThread->join();
|
|
if(mWebAdminThread)
|
|
{
|
|
mWebAdminThread->join();
|
|
}
|
|
if(mDumThread)
|
|
{
|
|
mDumThread->join();
|
|
}
|
|
if(mAuthFactory)
|
|
{
|
|
// Both proxy and dum threads are down at this point, we can
|
|
// destroy the authFactory and its authRequest dispatcher
|
|
// and associated threads now
|
|
delete mAuthFactory;
|
|
mAuthFactory = 0;
|
|
}
|
|
if(mAsyncProcessorDispatcher)
|
|
{
|
|
// Both proxy and dum threads are down at this point, we can
|
|
// destroy the async processor dispatcher and associated threads now
|
|
delete mAsyncProcessorDispatcher;
|
|
mAsyncProcessorDispatcher = 0;
|
|
}
|
|
if(!mRestarting && mCommandServerThread) // we leave command server running during restart
|
|
{
|
|
mCommandServerThread->join();
|
|
}
|
|
if(mRegSyncServerThread)
|
|
{
|
|
mRegSyncServerThread->join();
|
|
}
|
|
if(mRegSyncClient)
|
|
{
|
|
mRegSyncClient->join();
|
|
}
|
|
|
|
mSipStack->setCongestionManager(0);
|
|
|
|
cleanupObjects();
|
|
mRunning = false;
|
|
}
|
|
|
|
void
|
|
ReproRunner::restart()
|
|
{
|
|
if(!mRunning) return;
|
|
mRestarting = true;
|
|
shutdown();
|
|
run(0, 0);
|
|
mRestarting = false;
|
|
}
|
|
|
|
void
|
|
ReproRunner::onReload()
|
|
{
|
|
mSipStack->onReload();
|
|
// Let the plugins know
|
|
#ifdef DSO_PLUGINS
|
|
if(mPluginManager)
|
|
{
|
|
mPluginManager->onReload();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ReproRunner::cleanupObjects()
|
|
{
|
|
if(!mRestarting)
|
|
{
|
|
// We leave command server running during restart
|
|
delete mCommandServerThread; mCommandServerThread = 0;
|
|
for(std::list<CommandServer*>::iterator it = mCommandServerList.begin(); it != mCommandServerList.end(); it++)
|
|
{
|
|
delete (*it);
|
|
}
|
|
mCommandServerList.clear();
|
|
}
|
|
delete mRegSyncServerThread; mRegSyncServerThread = 0;
|
|
delete mRegSyncServerAMQP; mRegSyncServerAMQP = 0;
|
|
delete mRegSyncServerV6; mRegSyncServerV6 = 0;
|
|
delete mRegSyncServerV4; mRegSyncServerV4 = 0;
|
|
delete mRegSyncClient; mRegSyncClient = 0;
|
|
#if defined(USE_SSL)
|
|
delete mCertServer; mCertServer = 0;
|
|
#endif
|
|
delete mDumThread; mDumThread = 0;
|
|
delete mDum; mDum = 0;
|
|
delete mRegistrar; mRegistrar = 0;
|
|
delete mPresenceServer; mPresenceServer = 0;
|
|
delete mWebAdminThread; mWebAdminThread = 0;
|
|
for(std::list<WebAdmin*>::iterator it = mWebAdminList.begin(); it != mWebAdminList.end(); it++)
|
|
{
|
|
delete (*it);
|
|
}
|
|
mWebAdminList.clear();
|
|
delete mProxy; mProxy = 0;
|
|
delete mBaboons; mBaboons = 0;
|
|
delete mLemurs; mLemurs = 0;
|
|
delete mMonkeys; mMonkeys = 0;
|
|
delete mAuthFactory; mAuthFactory = 0;
|
|
delete mAsyncProcessorDispatcher; mAsyncProcessorDispatcher = 0;
|
|
if(!mRestarting)
|
|
{
|
|
// If we are restarting then leave the In Memory Registration and Publication database intact
|
|
delete mRegistrationPersistenceManager; mRegistrationPersistenceManager = 0;
|
|
delete mPublicationPersistenceManager; mPublicationPersistenceManager = 0;
|
|
}
|
|
delete mAbstractDb; mAbstractDb = 0;
|
|
delete mRuntimeAbstractDb; mRuntimeAbstractDb = 0;
|
|
delete mStackThread; mStackThread = 0;
|
|
delete mSipStack; mSipStack = 0;
|
|
delete mCongestionManager; mCongestionManager = 0;
|
|
delete mAsyncProcessHandler; mAsyncProcessHandler = 0;
|
|
delete mFdPollGrp; mFdPollGrp = 0;
|
|
delete mProxyConfig; mProxyConfig = 0;
|
|
}
|
|
|
|
bool
|
|
ReproRunner::loadPlugins()
|
|
{
|
|
std::vector<Data> pluginNames;
|
|
mProxyConfig->getConfigValue("LoadPlugins", pluginNames);
|
|
|
|
#ifdef DSO_PLUGINS
|
|
mPluginManager.reset(new ReproPluginManager(*mSipStack, mProxyConfig));
|
|
return mPluginManager->loadPlugins(pluginNames);
|
|
#else
|
|
if (pluginNames.empty()) return true;
|
|
|
|
CritLog(<< "LoadPlugins is specified in the configuration but repro was compiled without plugin support");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ReproRunner::setOpenSSLCTXOptionsFromConfig(const Data& configVar, long& opts)
|
|
{
|
|
#ifdef USE_SSL
|
|
std::set<Data> values;
|
|
if(mProxyConfig->getConfigValue(configVar, values))
|
|
{
|
|
opts = 0;
|
|
for(std::set<Data>::iterator it = values.begin();
|
|
it != values.end(); it++)
|
|
{
|
|
opts |= Security::parseOpenSSLCTXOption(*it);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
ReproRunner::createSipStack()
|
|
{
|
|
// Override T1 timer if configured to do so
|
|
unsigned long overrideT1 = mProxyConfig->getConfigUnsignedLong("TimerT1", 0);
|
|
if(overrideT1)
|
|
{
|
|
WarningLog(<< "Overriding T1! (new value is " << overrideT1 << ")");
|
|
resip::Timer::resetT1(overrideT1);
|
|
}
|
|
|
|
// Set TCP Connect timeout
|
|
resip::Timer::TcpConnectTimeout = mProxyConfig->getConfigUnsignedLong("TCPConnectTimeout", 10000); // Default to 10 seconds
|
|
|
|
// Set DNS Greylist Duration
|
|
resip::TransactionState::DnsGreylistDurationMs = mProxyConfig->getConfigUnsignedLong("DNSGreylistDuration", 1800000); // Default to 30mins
|
|
|
|
unsigned long messageSizeLimit = mProxyConfig->getConfigUnsignedLong("StreamMessageSizeLimit", 0);
|
|
if(messageSizeLimit > 0)
|
|
{
|
|
DebugLog(<< "Using maximum message size "<< messageSizeLimit << " on stream-based transports");
|
|
ConnectionBase::setMessageSizeMax(messageSizeLimit);
|
|
}
|
|
|
|
// Create Security (TLS / Certificates) and Compression (SigComp) objects if
|
|
// pre-precessor defines are enabled
|
|
Security* security = 0;
|
|
Compression* compression = 0;
|
|
#ifdef USE_SSL
|
|
setOpenSSLCTXOptionsFromConfig(
|
|
"OpenSSLCTXSetOptions", BaseSecurity::OpenSSLCTXSetOptions);
|
|
setOpenSSLCTXOptionsFromConfig(
|
|
"OpenSSLCTXClearOptions", BaseSecurity::OpenSSLCTXClearOptions);
|
|
Security::CipherList cipherList = Security::StrongestSuite;
|
|
Data ciphers = mProxyConfig->getConfigData("OpenSSLCipherList", Data::Empty);
|
|
if(!ciphers.empty())
|
|
{
|
|
cipherList = ciphers;
|
|
}
|
|
Data certPath = mProxyConfig->getConfigData("CertificatePath", Data::Empty);
|
|
Data dHParamsFilename = mProxyConfig->getConfigData("TlsDHParamsFilename", Data::Empty);
|
|
if(certPath.empty())
|
|
{
|
|
security = new Security(cipherList, mProxyConfig->getConfigData("TLSPrivateKeyPassPhrase", Data::Empty), dHParamsFilename);
|
|
}
|
|
else
|
|
{
|
|
security = new Security(certPath, cipherList, mProxyConfig->getConfigData("TLSPrivateKeyPassPhrase", Data::Empty), dHParamsFilename);
|
|
}
|
|
std::vector<Data> caDirNames;
|
|
mProxyConfig->getConfigValue("CADirectory", caDirNames);
|
|
for(std::vector<Data>::const_iterator caDir = caDirNames.begin();
|
|
caDir != caDirNames.end(); caDir++)
|
|
{
|
|
security->addCADirectory(*caDir);
|
|
}
|
|
std::vector<Data> caFileNames;
|
|
mProxyConfig->getConfigValue("CAFile", caFileNames);
|
|
for(std::vector<Data>::const_iterator caFile = caFileNames.begin();
|
|
caFile != caFileNames.end(); caFile++)
|
|
{
|
|
security->addCAFile(*caFile);
|
|
}
|
|
BaseSecurity::setAllowWildcardCertificates(mProxyConfig->getConfigBool("AllowWildcardCertificates", false));
|
|
#endif
|
|
|
|
#ifdef USE_SIGCOMP
|
|
compression = new Compression(Compression::DEFLATE);
|
|
#endif
|
|
|
|
// Create EventThreadInterruptor used to wake up the stack for
|
|
// for reasons other than an Fd signalling
|
|
resip_assert(!mFdPollGrp);
|
|
mFdPollGrp = FdPollGrp::create();
|
|
resip_assert(!mAsyncProcessHandler);
|
|
mAsyncProcessHandler = new EventThreadInterruptor(*mFdPollGrp);
|
|
|
|
// Set Flags that will enable/disable IPv4 and/or IPv6, based on
|
|
// configuration and pre-processor flags
|
|
mUseV4 = !mProxyConfig->getConfigBool("DisableIPv4", false);
|
|
#ifdef USE_IPV6
|
|
mUseV6 = mProxyConfig->getConfigBool("EnableIPv6", true);
|
|
#else
|
|
bool useV6 = false;
|
|
#endif
|
|
if (mUseV4) InfoLog (<< "V4 enabled");
|
|
if (mUseV6) InfoLog (<< "V6 enabled");
|
|
|
|
// Build DNS Server list from config
|
|
DnsStub::NameserverList dnsServers;
|
|
std::vector<resip::Data> dnsServersConfig;
|
|
mProxyConfig->getConfigValue("DNSServers", dnsServersConfig);
|
|
for(std::vector<resip::Data>::iterator it = dnsServersConfig.begin(); it != dnsServersConfig.end(); it++)
|
|
{
|
|
if((mUseV4 && DnsUtil::isIpV4Address(*it)) || (mUseV6 && DnsUtil::isIpV6Address(*it)))
|
|
{
|
|
InfoLog(<< "Using DNS Server from config: " << *it);
|
|
dnsServers.push_back(Tuple(*it, 0, UNKNOWN_TRANSPORT).toGenericIPAddress());
|
|
}
|
|
}
|
|
|
|
// Create the SipStack Object
|
|
resip_assert(!mSipStack);
|
|
mSipStack = new SipStack(security,
|
|
dnsServers,
|
|
mAsyncProcessHandler,
|
|
/*stateless*/false,
|
|
/*socketFunc*/0,
|
|
compression,
|
|
mFdPollGrp);
|
|
|
|
// Set any enum suffixes from configuration
|
|
std::vector<Data> enumSuffixes;
|
|
mProxyConfig->getConfigValue("EnumSuffixes", enumSuffixes);
|
|
if (enumSuffixes.size() > 0)
|
|
{
|
|
mSipStack->setEnumSuffixes(enumSuffixes);
|
|
}
|
|
|
|
// Set any enum domains from configuration
|
|
std::map<Data,Data> enumDomains;
|
|
std::vector<Data> _enumDomains;
|
|
mProxyConfig->getConfigValue("EnumDomains", _enumDomains);
|
|
if (enumSuffixes.size() > 0)
|
|
{
|
|
for(std::vector<Data>::iterator it = _enumDomains.begin(); it != _enumDomains.end(); it++)
|
|
{
|
|
enumDomains[*it] = *it;
|
|
}
|
|
mSipStack->setEnumDomains(enumDomains);
|
|
}
|
|
|
|
// Add External Stats handler
|
|
mSipStack->setExternalStatsHandler(this);
|
|
|
|
// Set Transport SipMessage Logging Handler - if enabled
|
|
Data captureHost;
|
|
mProxyConfig->getConfigValue("CaptureHost", captureHost);
|
|
if(!captureHost.empty())
|
|
{
|
|
int capturePort = mProxyConfig->getConfigInt("CapturePort", 9060);
|
|
int captureAgentID = mProxyConfig->getConfigInt("CaptureAgentID", 2001);
|
|
auto agent = std::make_shared<HepAgent>(captureHost, capturePort, captureAgentID);
|
|
mSipStack->addTransportSipMessageLoggingHandler(std::make_shared<HEPSipMessageLoggingHandler>(agent));
|
|
}
|
|
else if(mProxyConfig->getConfigBool("EnableSipMessageLogging", false))
|
|
{
|
|
mSipStack->addTransportSipMessageLoggingHandler(std::make_shared<ReproSipMessageLoggingHandler>());
|
|
}
|
|
|
|
// Add stack transports
|
|
bool allTransportsSpecifyRecordRoute=false;
|
|
if(!addTransports(allTransportsSpecifyRecordRoute))
|
|
{
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
|
|
// Enable and configure RFC5626 Outbound support
|
|
InteropHelper::setOutboundVersion(mProxyConfig->getConfigInt("OutboundVersion", 5626));
|
|
InteropHelper::setOutboundSupported(mProxyConfig->getConfigBool("DisableOutbound", false) ? false : true);
|
|
InteropHelper::setRRTokenHackEnabled(mProxyConfig->getConfigBool("EnableFlowTokens", false));
|
|
InteropHelper::setAllowInboundFlowTokensForNonDirectClients(mProxyConfig->getConfigBool("AllowInboundFlowTokensForNonDirectClients", false));
|
|
InteropHelper::setAssumeFirstHopSupportsOutboundEnabled(mProxyConfig->getConfigBool("AssumeFirstHopSupportsOutbound", false));
|
|
InteropHelper::setAssumeFirstHopSupportsFlowTokensEnabled(mProxyConfig->getConfigBool("AssumeFirstHopSupportsFlowTokens", false));
|
|
Data clientNATDetectionMode = mProxyConfig->getConfigData("ClientNatDetectionMode", "DISABLED");
|
|
if(isEqualNoCase(clientNATDetectionMode, "ENABLED"))
|
|
{
|
|
InteropHelper::setClientNATDetectionMode(InteropHelper::ClientNATDetectionEnabled);
|
|
}
|
|
else if(isEqualNoCase(clientNATDetectionMode, "PRIVATE_TO_PUBLIC"))
|
|
{
|
|
InteropHelper::setClientNATDetectionMode(InteropHelper::ClientNATDetectionPrivateToPublicOnly);
|
|
}
|
|
ConnectionManager::MinimumGcHeadroom = mProxyConfig->getConfigUnsignedLong("TCPMinimumGCHeadroom", 0);
|
|
unsigned long tcpConnectionGCAge = mProxyConfig->getConfigUnsignedLong("TCPConnectionGCAge", 0);
|
|
if(tcpConnectionGCAge > 0)
|
|
{
|
|
ConnectionManager::MinimumGcAge = tcpConnectionGCAge * 1000;
|
|
ConnectionManager::EnableAgressiveGc = true;
|
|
}
|
|
unsigned long outboundFlowTimer = mProxyConfig->getConfigUnsignedLong("FlowTimer", 0);
|
|
if(outboundFlowTimer > 0)
|
|
{
|
|
InteropHelper::setFlowTimerSeconds(outboundFlowTimer);
|
|
if(tcpConnectionGCAge == 0)
|
|
{
|
|
// This should be set too when using outboundFlowTimer
|
|
ConnectionManager::MinimumGcAge = 7200000;
|
|
}
|
|
ConnectionManager::EnableAgressiveGc = true;
|
|
}
|
|
|
|
// Decide whether or not to add rport to the Via header
|
|
InteropHelper::setRportEnabled(mProxyConfig->getConfigBool("AddViaRport", true));
|
|
|
|
// Check Path and RecordRoute settings, print warning if features are enabled that
|
|
// require record-routing and record-route uri(s) is not configured
|
|
bool forceRecordRoute = mProxyConfig->getConfigBool("ForceRecordRouting", false);
|
|
Uri recordRouteUri;
|
|
mProxyConfig->getConfigValue("RecordRouteUri", recordRouteUri);
|
|
if((InteropHelper::getOutboundSupported()
|
|
|| InteropHelper::getRRTokenHackEnabled()
|
|
|| InteropHelper::getClientNATDetectionMode() != InteropHelper::ClientNATDetectionDisabled
|
|
|| forceRecordRoute
|
|
)
|
|
&& !(allTransportsSpecifyRecordRoute || !recordRouteUri.host().empty()))
|
|
{
|
|
CritLog(<< "In order for outbound support, the Record-Route flow-token"
|
|
" hack, or force-record-route to work, you MUST specify a Record-Route URI. Launching "
|
|
"without...");
|
|
InteropHelper::setOutboundSupported(false);
|
|
InteropHelper::setRRTokenHackEnabled(false);
|
|
InteropHelper::setClientNATDetectionMode(InteropHelper::ClientNATDetectionDisabled);
|
|
forceRecordRoute=false;
|
|
}
|
|
|
|
// Configure misc. stack settings
|
|
mSipStack->setFixBadDialogIdentifiers(false);
|
|
mSipStack->setFixBadCSeqNumbers(false);
|
|
int statsLogInterval = mProxyConfig->getConfigInt("StatisticsLogInterval", 60);
|
|
if(statsLogInterval > 0)
|
|
{
|
|
mSipStack->setStatisticsInterval(statsLogInterval);
|
|
mSipStack->statisticsManagerEnabled() = true;
|
|
}
|
|
else
|
|
{
|
|
mSipStack->statisticsManagerEnabled() = false;
|
|
}
|
|
|
|
// Create Congestion Manager, if required
|
|
resip_assert(!mCongestionManager);
|
|
if(mProxyConfig->getConfigBool("CongestionManagement", true))
|
|
{
|
|
Data metricData = mProxyConfig->getConfigData("CongestionManagementMetric", "WAIT_TIME", true);
|
|
GeneralCongestionManager::MetricType metric = GeneralCongestionManager::WAIT_TIME;
|
|
if(isEqualNoCase(metricData, "TIME_DEPTH"))
|
|
{
|
|
metric = GeneralCongestionManager::TIME_DEPTH;
|
|
}
|
|
else if(isEqualNoCase(metricData, "SIZE"))
|
|
{
|
|
metric = GeneralCongestionManager::SIZE;
|
|
}
|
|
else if(!isEqualNoCase(metricData, "WAIT_TIME"))
|
|
{
|
|
WarningLog( << "CongestionManagementMetric specified as an unknown value (" << metricData << "), defaulting to WAIT_TIME.");
|
|
}
|
|
mCongestionManager = new GeneralCongestionManager(
|
|
metric,
|
|
mProxyConfig->getConfigUnsignedLong("CongestionManagementTolerance", 200));
|
|
mSipStack->setCongestionManager(mCongestionManager);
|
|
}
|
|
|
|
// Create base thread to run stack in (note: stack may use other sub-threads, depending on configuration)
|
|
resip_assert(!mStackThread);
|
|
mStackThread = new EventStackThread(*mSipStack,
|
|
*dynamic_cast<EventThreadInterruptor*>(mAsyncProcessHandler),
|
|
*mFdPollGrp);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ReproRunner::createDatastore()
|
|
{
|
|
// Create Database access objects
|
|
resip_assert(!mAbstractDb);
|
|
resip_assert(!mRuntimeAbstractDb);
|
|
int defaultDatabaseIndex = mProxyConfig->getConfigInt("DefaultDatabase", -1);
|
|
if(defaultDatabaseIndex >= 0)
|
|
{
|
|
mAbstractDb = mProxyConfig->getDatabase(defaultDatabaseIndex);
|
|
if(!mAbstractDb)
|
|
{
|
|
CritLog(<<"Failed to get configuration database");
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
}
|
|
else // Try legacy configuration parameter names
|
|
{
|
|
#ifdef USE_MYSQL
|
|
Data mySQLServer;
|
|
mProxyConfig->getConfigValue("MySQLServer", mySQLServer);
|
|
if(!mySQLServer.empty())
|
|
{
|
|
WarningLog(<<"Using deprecated parameter MySQLServer, please update to indexed Database definitions.");
|
|
mAbstractDb = new MySqlDb(*mProxyConfig, mySQLServer,
|
|
mProxyConfig->getConfigData("MySQLUser", Data::Empty),
|
|
mProxyConfig->getConfigData("MySQLPassword", Data::Empty),
|
|
mProxyConfig->getConfigData("MySQLDatabaseName", Data::Empty),
|
|
mProxyConfig->getConfigUnsignedLong("MySQLPort", 0),
|
|
mProxyConfig->getConfigData("MySQLCustomUserAuthQuery", Data::Empty));
|
|
}
|
|
#endif
|
|
if (!mAbstractDb)
|
|
{
|
|
mAbstractDb = new BerkeleyDb(mProxyConfig->getConfigData("DatabasePath", "./", true));
|
|
}
|
|
}
|
|
int runtimeDatabaseIndex = mProxyConfig->getConfigInt("RuntimeDatabase", -1);
|
|
if(runtimeDatabaseIndex >= 0)
|
|
{
|
|
mRuntimeAbstractDb = mProxyConfig->getDatabase(runtimeDatabaseIndex);
|
|
if(!mRuntimeAbstractDb || !mRuntimeAbstractDb->isSane())
|
|
{
|
|
CritLog(<<"Failed to get runtime database");
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
}
|
|
#ifdef USE_MYSQL
|
|
else // Try legacy configuration parameter names
|
|
{
|
|
Data runtimeMySQLServer;
|
|
mProxyConfig->getConfigValue("RuntimeMySQLServer", runtimeMySQLServer);
|
|
if(!runtimeMySQLServer.empty())
|
|
{
|
|
WarningLog(<<"Using deprecated parameter RuntimeMySQLServer, please update to indexed Database definitions.");
|
|
mRuntimeAbstractDb = new MySqlDb(*mProxyConfig, runtimeMySQLServer,
|
|
mProxyConfig->getConfigData("RuntimeMySQLUser", Data::Empty),
|
|
mProxyConfig->getConfigData("RuntimeMySQLPassword", Data::Empty),
|
|
mProxyConfig->getConfigData("RuntimeMySQLDatabaseName", Data::Empty),
|
|
mProxyConfig->getConfigUnsignedLong("RuntimeMySQLPort", 0),
|
|
mProxyConfig->getConfigData("MySQLCustomUserAuthQuery", Data::Empty));
|
|
}
|
|
}
|
|
#endif
|
|
resip_assert(mAbstractDb);
|
|
if(!mAbstractDb->isSane())
|
|
{
|
|
CritLog(<<"Failed to open configuration database");
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
if(mRuntimeAbstractDb && !mRuntimeAbstractDb->isSane())
|
|
{
|
|
CritLog(<<"Failed to open runtime configuration database");
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
mProxyConfig->createDataStore(mAbstractDb, mRuntimeAbstractDb);
|
|
|
|
// Create ImMemory Registration Database
|
|
mRegSyncPort = mProxyConfig->getConfigInt("RegSyncPort", 0);
|
|
// We only need removed records to linger if we have reg sync enabled
|
|
if(!mRestarting) // If we are restarting then we left the InMemorySyncRegDb and InMemorySyncPubDb intact at restart - don't recreate
|
|
{
|
|
resip_assert(!mRegistrationPersistenceManager);
|
|
mRegistrationPersistenceManager = new InMemorySyncRegDb(mRegSyncPort ? 86400 /* 24 hours */ : 0 /* removeLingerSecs */); // !slg! could make linger time a setting
|
|
resip_assert(!mPublicationPersistenceManager);
|
|
mPublicationPersistenceManager = new InMemorySyncPubDb((mRegSyncPort && mProxyConfig->getConfigBool("EnablePublicationReplication", false)) ? true : false);
|
|
}
|
|
resip_assert(mRegistrationPersistenceManager);
|
|
resip_assert(mPublicationPersistenceManager);
|
|
|
|
// Copy contacts from the StaticRegStore to the RegistrationPersistanceManager
|
|
populateRegistrations();
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ReproRunner::createAuthenticatorFactory()
|
|
{
|
|
// TODO: let a plugin supply an instance of AuthenticatorFactory
|
|
// instead of our builtin ReproAuthenticatorFactory
|
|
mAuthFactory = new ReproAuthenticatorFactory(*mProxyConfig, *mSipStack, mDum);
|
|
}
|
|
|
|
void
|
|
ReproRunner::createDialogUsageManager()
|
|
{
|
|
// Create Profile settings for DUM Instance that handles ServerRegistration,
|
|
// and potentially certificate subscription server
|
|
auto profile = std::make_shared<MasterProfile>();
|
|
profile->setRportEnabled(InteropHelper::getRportEnabled());
|
|
profile->clearSupportedMethods();
|
|
profile->addSupportedMethod(resip::REGISTER);
|
|
#ifdef USE_SSL
|
|
profile->addSupportedScheme(Symbols::Sips);
|
|
#endif
|
|
if(InteropHelper::getOutboundSupported())
|
|
{
|
|
profile->addSupportedOptionTag(Token(Symbols::Outbound));
|
|
}
|
|
profile->addSupportedOptionTag(Token(Symbols::Path));
|
|
if(mProxyConfig->getConfigBool("AllowBadReg", false))
|
|
{
|
|
profile->allowBadRegistrationEnabled() = true;
|
|
}
|
|
#ifdef PACKAGE_VERSION
|
|
Data serverText(mProxyConfig->getConfigData("ServerText", "repro " PACKAGE_VERSION));
|
|
#else
|
|
Data serverText(mProxyConfig->getConfigData("ServerText", Data::Empty));
|
|
#endif
|
|
if(!serverText.empty())
|
|
{
|
|
profile->setUserAgent(serverText);
|
|
}
|
|
|
|
// Create DialogeUsageManager if Registrar or Certificate Server are enabled
|
|
resip_assert(!mRegistrar);
|
|
resip_assert(!mDum);
|
|
resip_assert(!mDumThread);
|
|
mRegistrar = new Registrar;
|
|
resip::MessageFilterRuleList ruleList;
|
|
bool registrarEnabled = !mProxyConfig->getConfigBool("DisableRegistrar", false);
|
|
bool certServerEnabled = mProxyConfig->getConfigBool("EnableCertServer", false);
|
|
bool presenceEnabled = mProxyConfig->getConfigBool("EnablePresenceServer", false);
|
|
if (registrarEnabled || certServerEnabled || presenceEnabled)
|
|
{
|
|
mDum = new DialogUsageManager(*mSipStack);
|
|
mDum->setMasterProfile(profile);
|
|
addDomains(*mDum);
|
|
}
|
|
|
|
// If registrar is enabled, configure DUM to handle REGISTER requests
|
|
if (registrarEnabled)
|
|
{
|
|
resip_assert(mDum);
|
|
resip_assert(mRegistrationPersistenceManager);
|
|
mDum->setServerRegistrationHandler(mRegistrar);
|
|
mDum->setRegistrationPersistenceManager(mRegistrationPersistenceManager);
|
|
|
|
// Install rules so that the registrar only gets REGISTERs
|
|
resip::MessageFilterRule::MethodList methodList;
|
|
methodList.push_back(resip::REGISTER);
|
|
ruleList.push_back(MessageFilterRule(resip::MessageFilterRule::SchemeList(),
|
|
resip::MessageFilterRule::DomainIsMe,
|
|
methodList) );
|
|
}
|
|
|
|
// If Certificate Server is enabled, configure DUM to handle SUBSCRIBE and
|
|
// PUBLISH requests for events: credential and certificate
|
|
resip_assert(!mCertServer);
|
|
if (certServerEnabled)
|
|
{
|
|
#if defined(USE_SSL)
|
|
mCertServer = new CertServer(*mDum);
|
|
|
|
// Install rules so that the cert server receives SUBSCRIBEs and PUBLISHs
|
|
resip::MessageFilterRule::MethodList methodList;
|
|
resip::MessageFilterRule::EventList eventList;
|
|
methodList.push_back(resip::SUBSCRIBE);
|
|
methodList.push_back(resip::PUBLISH);
|
|
eventList.push_back(resip::Symbols::Credential);
|
|
eventList.push_back(resip::Symbols::Certificate);
|
|
ruleList.push_back(MessageFilterRule(resip::MessageFilterRule::SchemeList(),
|
|
resip::MessageFilterRule::DomainIsMe,
|
|
methodList,
|
|
eventList));
|
|
#endif
|
|
}
|
|
|
|
if (presenceEnabled)
|
|
{
|
|
resip_assert(mDum);
|
|
resip_assert(mPublicationPersistenceManager);
|
|
|
|
// Set the publication persistence manager in dum
|
|
mDum->setPublicationPersistenceManager(mPublicationPersistenceManager);
|
|
|
|
// Configure DUM to handle SUBSCRIBE and PUBLISH requests for presence
|
|
mPresenceServer = new PresenceServer(*mDum, mAuthFactory->getDispatcher(),
|
|
mProxyConfig->getConfigBool("PresenceUsesRegistrationState", true),
|
|
mProxyConfig->getConfigBool("PresenceNotifyClosedStateForNonPublishedUsers", true));
|
|
|
|
// Install rules so that the cert server receives SUBSCRIBEs and PUBLISHs
|
|
MessageFilterRule::MethodList methodList;
|
|
MessageFilterRule::EventList eventList;
|
|
methodList.push_back(SUBSCRIBE);
|
|
methodList.push_back(PUBLISH);
|
|
eventList.push_back(Symbols::Presence);
|
|
ruleList.push_back(MessageFilterRule(MessageFilterRule::SchemeList(),
|
|
MessageFilterRule::DomainIsMe,
|
|
methodList,
|
|
eventList));
|
|
}
|
|
|
|
if (mDum)
|
|
{
|
|
resip_assert(mAuthFactory);
|
|
mAuthFactory->setDum(mDum);
|
|
|
|
if(mAuthFactory->certificateAuthEnabled())
|
|
{
|
|
// TODO: perhaps this should be initialised from the trusted node
|
|
// monkey? Or should the list of trusted TLS peers be independent
|
|
// from the trusted node list?
|
|
mDum->addIncomingFeature(mAuthFactory->getCertificateAuthManager());
|
|
}
|
|
|
|
Data wsCookieAuthSharedSecret = mProxyConfig->getConfigData("WSCookieAuthSharedSecret", Data::Empty);
|
|
if(!mAuthFactory->digestAuthEnabled() && !wsCookieAuthSharedSecret.empty())
|
|
{
|
|
auto cookieAuth = std::make_shared<WsCookieAuthManager>(*mDum, mDum->dumIncomingTarget());
|
|
mDum->addIncomingFeature(cookieAuth);
|
|
}
|
|
|
|
// If Authentication is enabled, then configure DUM to authenticate requests
|
|
if (mAuthFactory->digestAuthEnabled())
|
|
{
|
|
mDum->setServerAuthManager(mAuthFactory->getServerAuthManager());
|
|
}
|
|
|
|
// Set the MessageFilterRuleList on DUM and create a thread to run DUM in
|
|
mDum->setMessageFilterRuleList(ruleList);
|
|
mDumThread = new DumThread(*mDum);
|
|
}
|
|
}
|
|
|
|
bool
|
|
ReproRunner::createProxy()
|
|
{
|
|
// Create AsyncProcessorDispatcher thread pool that is shared by the processsors for
|
|
// any asynchronous tasks (ie: RequestFilter and MessageSilo processors)
|
|
int numAsyncProcessorWorkerThreads = mProxyConfig->getConfigInt("NumAsyncProcessorWorkerThreads", 2);
|
|
if(numAsyncProcessorWorkerThreads > 0)
|
|
{
|
|
resip_assert(!mAsyncProcessorDispatcher);
|
|
mAsyncProcessorDispatcher = new Dispatcher(std::unique_ptr<Worker>(new AsyncProcessorWorker),
|
|
mSipStack,
|
|
numAsyncProcessorWorkerThreads);
|
|
}
|
|
|
|
// Create proxy processor chains
|
|
/* Explanation: "Monkeys" are processors which operate on incoming requests
|
|
"Lemurs" are processors which operate on incoming responses
|
|
"Baboons" are processors which operate on a request for each target
|
|
as the request is about to be forwarded to that target */
|
|
// Make Monkeys
|
|
resip_assert(!mMonkeys);
|
|
mMonkeys = new ProcessorChain(Processor::REQUEST_CHAIN);
|
|
makeRequestProcessorChain(*mMonkeys);
|
|
InfoLog(<< *mMonkeys);
|
|
#ifdef DSO_PLUGINS
|
|
if(mPluginManager)
|
|
{
|
|
mPluginManager->onRequestProcessorChainPopulated(*mMonkeys);
|
|
}
|
|
#endif
|
|
|
|
// Make Lemurs
|
|
resip_assert(!mLemurs);
|
|
mLemurs = new ProcessorChain(Processor::RESPONSE_CHAIN);
|
|
makeResponseProcessorChain(*mLemurs);
|
|
InfoLog(<< *mLemurs);
|
|
#ifdef DSO_PLUGINS
|
|
if(mPluginManager)
|
|
{
|
|
mPluginManager->onResponseProcessorChainPopulated(*mLemurs);
|
|
}
|
|
#endif
|
|
|
|
// Make Baboons
|
|
resip_assert(!mBaboons);
|
|
mBaboons = new ProcessorChain(Processor::TARGET_CHAIN);
|
|
makeTargetProcessorChain(*mBaboons);
|
|
InfoLog(<< *mBaboons);
|
|
#ifdef DSO_PLUGINS
|
|
if(mPluginManager)
|
|
{
|
|
mPluginManager->onTargetProcessorChainPopulated(*mBaboons);
|
|
}
|
|
#endif
|
|
|
|
// Create main Proxy class
|
|
resip_assert(!mProxy);
|
|
mProxy = new Proxy(*mSipStack,
|
|
*mProxyConfig,
|
|
*mMonkeys,
|
|
*mLemurs,
|
|
*mBaboons);
|
|
addDomains(*mProxy);
|
|
mHttpRealm = mProxyConfig->getConfigData("HttpAdminRealm", mDefaultRealm);
|
|
|
|
// Set Server Text
|
|
#ifdef PACKAGE_VERSION
|
|
Data serverText(mProxyConfig->getConfigData("ServerText", "repro " PACKAGE_VERSION));
|
|
#else
|
|
Data serverText(mProxyConfig->getConfigData("ServerText", Data::Empty));
|
|
#endif
|
|
if(!serverText.empty())
|
|
{
|
|
mProxy->setServerText(serverText);
|
|
}
|
|
|
|
// Register the Proxy class a stack transaction user
|
|
// Note: This is done after creating the DialogUsageManager so that it acts
|
|
// like a catchall and will handle all requests the DUM does not
|
|
mSipStack->registerTransactionUser(*mProxy);
|
|
|
|
// Map the Registrar to the Proxy
|
|
if(mRegistrar)
|
|
{
|
|
mRegistrar->setProxy(mProxy);
|
|
}
|
|
|
|
// Add the transport specific RecordRoutes that were stored in addTransports to the Proxy
|
|
for(TransportRecordRouteMap::iterator it = mStartupTransportRecordRoutes.begin();
|
|
it != mStartupTransportRecordRoutes.end(); it++)
|
|
{
|
|
mProxy->addTransportRecordRoute(it->first, it->second);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ReproRunner::populateRegistrations()
|
|
{
|
|
resip_assert(mRegistrationPersistenceManager);
|
|
resip_assert(mProxyConfig);
|
|
resip_assert(mProxyConfig->getDataStore());
|
|
|
|
// Copy contacts from the StaticRegStore to the RegistrationPersistanceManager
|
|
StaticRegStore::StaticRegRecordMap& staticRegList = mProxyConfig->getDataStore()->mStaticRegStore.getStaticRegList();
|
|
StaticRegStore::StaticRegRecordMap::iterator it = staticRegList.begin();
|
|
for(; it != staticRegList.end(); it++)
|
|
{
|
|
try
|
|
{
|
|
Uri aor(it->second.mAor);
|
|
|
|
ContactInstanceRecord rec;
|
|
rec.mContact = NameAddr(it->second.mContact);
|
|
rec.mSipPath = NameAddrs(it->second.mPath);
|
|
rec.mRegExpires = NeverExpire;
|
|
rec.mSyncContact = true; // Tag this permanent contact as being a synchronized contact so that it will
|
|
// not be synchronized to a paired server (this is actually configuration information)
|
|
mRegistrationPersistenceManager->updateContact(aor, rec);
|
|
}
|
|
catch(resip::ParseBuffer::Exception& e)
|
|
{
|
|
// This should never happen, since the format should be verified before writing to DB
|
|
ErrLog(<<"Failed to apply a static registration due to parse error: " << e);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
ReproRunner::createWebAdmin()
|
|
{
|
|
resip_assert(mWebAdminList.empty());
|
|
resip_assert(!mWebAdminThread);
|
|
|
|
std::vector<resip::Data> httpServerBindAddresses;
|
|
mProxyConfig->getConfigValue("HttpBindAddress", httpServerBindAddresses);
|
|
int httpPort = mProxyConfig->getConfigInt("HttpPort", 5080);
|
|
|
|
if(httpPort)
|
|
{
|
|
if(httpServerBindAddresses.empty())
|
|
{
|
|
if(mUseV4)
|
|
{
|
|
httpServerBindAddresses.push_back("0.0.0.0");
|
|
}
|
|
if(mUseV6)
|
|
{
|
|
httpServerBindAddresses.push_back("::");
|
|
}
|
|
}
|
|
|
|
for(std::vector<resip::Data>::iterator it = httpServerBindAddresses.begin(); it != httpServerBindAddresses.end(); it++)
|
|
{
|
|
if(mUseV4 && DnsUtil::isIpV4Address(*it))
|
|
{
|
|
WebAdmin* webAdminV4 = 0;
|
|
|
|
try
|
|
{
|
|
webAdminV4 = new WebAdmin(*mProxy,
|
|
*mRegistrationPersistenceManager,
|
|
*mPublicationPersistenceManager,
|
|
mHttpRealm,
|
|
httpPort,
|
|
V4,
|
|
*it);
|
|
}
|
|
catch(WebAdmin::ConfigException& ex)
|
|
{
|
|
ErrLog(<<"Exception when starting WebAdmin: " << ex.getMessage());
|
|
webAdminV4 = 0;
|
|
}
|
|
|
|
if (!webAdminV4 || !webAdminV4->isSane())
|
|
{
|
|
CritLog(<<"Failed to start WebAdminV4");
|
|
delete webAdminV4;
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
|
|
mWebAdminList.push_back(webAdminV4);
|
|
}
|
|
|
|
if(mUseV6 && DnsUtil::isIpV6Address(*it))
|
|
{
|
|
WebAdmin* webAdminV6 = 0;
|
|
|
|
try
|
|
{
|
|
webAdminV6 = new WebAdmin(*mProxy,
|
|
*mRegistrationPersistenceManager,
|
|
*mPublicationPersistenceManager,
|
|
mHttpRealm,
|
|
httpPort,
|
|
V6,
|
|
*it);
|
|
}
|
|
catch(WebAdmin::ConfigException& ex)
|
|
{
|
|
ErrLog(<<"Exception when starting WebAdmin: " << ex.getMessage());
|
|
webAdminV6 = 0;
|
|
}
|
|
|
|
if (!webAdminV6 || !webAdminV6->isSane())
|
|
{
|
|
CritLog(<<"Failed to start WebAdminV6");
|
|
delete webAdminV6;
|
|
cleanupObjects();
|
|
return false;
|
|
}
|
|
|
|
mWebAdminList.push_back(webAdminV6);
|
|
}
|
|
}
|
|
|
|
// This shouldn't happen because it would return false before
|
|
// it reached this point
|
|
if(!mWebAdminList.empty())
|
|
{
|
|
mWebAdminThread = new WebAdminThread(mWebAdminList);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
CritLog(<<"Failed to start any WebAdmin");
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ReproRunner::createRegSync()
|
|
{
|
|
resip_assert(!mRegSyncClient);
|
|
resip_assert(!mRegSyncServerV4);
|
|
resip_assert(!mRegSyncServerV6);
|
|
resip_assert(!mRegSyncServerAMQP);
|
|
resip_assert(!mRegSyncServerThread);
|
|
bool enablePublicationReplication = mProxyConfig->getConfigBool("EnablePublicationReplication", false);
|
|
std::list<RegSyncServer*> regSyncServerList;
|
|
Data regSyncBrokerTopic = mProxyConfig->getConfigData("RegSyncBrokerTopic", Data::Empty);
|
|
if(!regSyncBrokerTopic.empty())
|
|
{
|
|
#ifdef BUILD_QPID_PROTON
|
|
mRegSyncServerAMQP = new RegSyncServer(dynamic_cast<InMemorySyncRegDb*>(mRegistrationPersistenceManager),
|
|
regSyncBrokerTopic,
|
|
enablePublicationReplication ? dynamic_cast<InMemorySyncPubDb*>(mPublicationPersistenceManager) : 0);
|
|
regSyncServerList.push_back(mRegSyncServerAMQP);
|
|
#endif
|
|
}
|
|
if(mRegSyncPort != 0 || regSyncServerList.size() > 0)
|
|
{
|
|
if(mUseV4)
|
|
{
|
|
mRegSyncServerV4 = new RegSyncServer(dynamic_cast<InMemorySyncRegDb*>(mRegistrationPersistenceManager),
|
|
mRegSyncPort, V4,
|
|
enablePublicationReplication ? dynamic_cast<InMemorySyncPubDb*>(mPublicationPersistenceManager) : 0);
|
|
regSyncServerList.push_back(mRegSyncServerV4);
|
|
}
|
|
if(mUseV6)
|
|
{
|
|
mRegSyncServerV6 = new RegSyncServer(dynamic_cast<InMemorySyncRegDb*>(mRegistrationPersistenceManager),
|
|
mRegSyncPort, V6,
|
|
enablePublicationReplication ? dynamic_cast<InMemorySyncPubDb*>(mPublicationPersistenceManager) : 0);
|
|
regSyncServerList.push_back(mRegSyncServerV6);
|
|
}
|
|
if(!regSyncServerList.empty())
|
|
{
|
|
mRegSyncServerThread = new RegSyncServerThread(regSyncServerList);
|
|
}
|
|
Data regSyncPeerAddress(mProxyConfig->getConfigData("RegSyncPeer", ""));
|
|
if(!regSyncPeerAddress.empty())
|
|
{
|
|
int remoteRegSyncPort = mProxyConfig->getConfigInt("RemoteRegSyncPort", 0);
|
|
if (remoteRegSyncPort == 0)
|
|
{
|
|
remoteRegSyncPort = mRegSyncPort;
|
|
}
|
|
if(remoteRegSyncPort != 0)
|
|
{
|
|
mRegSyncClient = new RegSyncClient(dynamic_cast<InMemorySyncRegDb*>(mRegistrationPersistenceManager),
|
|
regSyncPeerAddress, remoteRegSyncPort,
|
|
enablePublicationReplication ? dynamic_cast<InMemorySyncPubDb*>(mPublicationPersistenceManager) : 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ReproRunner::createCommandServer()
|
|
{
|
|
resip_assert(mCommandServerList.empty());
|
|
resip_assert(!mCommandServerThread);
|
|
|
|
std::vector<resip::Data> commandServerBindAddresses;
|
|
mProxyConfig->getConfigValue("CommandBindAddress", commandServerBindAddresses);
|
|
int commandPort = mProxyConfig->getConfigInt("CommandPort", 5081);
|
|
|
|
WebAdmin* webAdmin = 0;
|
|
if(!mWebAdminList.empty())
|
|
{
|
|
webAdmin = mWebAdminList.front();
|
|
}
|
|
|
|
if(commandPort != 0)
|
|
{
|
|
if(commandServerBindAddresses.empty())
|
|
{
|
|
if(mUseV4)
|
|
{
|
|
commandServerBindAddresses.push_back("0.0.0.0");
|
|
}
|
|
if(mUseV6)
|
|
{
|
|
commandServerBindAddresses.push_back("::");
|
|
}
|
|
}
|
|
|
|
for(std::vector<resip::Data>::iterator it = commandServerBindAddresses.begin(); it != commandServerBindAddresses.end(); it++)
|
|
{
|
|
if(mUseV4 && DnsUtil::isIpV4Address(*it))
|
|
{
|
|
CommandServer* pCommandServerV4 = new CommandServer(*this, *it, commandPort, V4, webAdmin);
|
|
|
|
if(pCommandServerV4->isSane())
|
|
{
|
|
mCommandServerList.push_back(pCommandServerV4);
|
|
}
|
|
else
|
|
{
|
|
CritLog(<<"Failed to start CommandServerV4");
|
|
delete pCommandServerV4;
|
|
}
|
|
}
|
|
|
|
if(mUseV6 && DnsUtil::isIpV6Address(*it))
|
|
{
|
|
CommandServer* pCommandServerV6 = new CommandServer(*this, *it, commandPort, V6, webAdmin);
|
|
|
|
if(pCommandServerV6->isSane())
|
|
{
|
|
mCommandServerList.push_back(pCommandServerV6);
|
|
}
|
|
else
|
|
{
|
|
CritLog(<<"Failed to start CommandServerV6");
|
|
delete pCommandServerV6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef BUILD_QPID_PROTON
|
|
std::vector<resip::Data> commandServerQueues;
|
|
mProxyConfig->getConfigValue("CommandQueue", commandServerQueues);
|
|
for(Data& brokerUrl : commandServerQueues)
|
|
{
|
|
CommandServer* pCommandServerQueue = new CommandServer(*this, brokerUrl, false, webAdmin);
|
|
if(pCommandServerQueue->isSane())
|
|
{
|
|
mCommandServerList.push_back(pCommandServerQueue);
|
|
}
|
|
else
|
|
{
|
|
CritLog(<<"Failed to start CommandServerQueue");
|
|
delete pCommandServerQueue;
|
|
}
|
|
}
|
|
|
|
std::vector<resip::Data> commandServerTopics;
|
|
mProxyConfig->getConfigValue("CommandEventTopic", commandServerTopics);
|
|
for(Data& brokerUrl : commandServerTopics)
|
|
{
|
|
CommandServer* pCommandServerTopic = new CommandServer(*this, brokerUrl, true, webAdmin);
|
|
if(pCommandServerTopic->isSane())
|
|
{
|
|
mCommandServerList.push_back(pCommandServerTopic);
|
|
}
|
|
else
|
|
{
|
|
CritLog(<<"Failed to start CommandServerTopic");
|
|
delete pCommandServerTopic;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(!mCommandServerList.empty())
|
|
{
|
|
mCommandServerThread = new CommandServerThread(mCommandServerList);
|
|
}
|
|
}
|
|
|
|
void
|
|
ReproRunner::initDomainMatcher()
|
|
{
|
|
resip_assert(mProxyConfig);
|
|
|
|
auto matcher = std::make_shared<ExtendedDomainMatcher>();
|
|
mDomainMatcher = matcher;
|
|
|
|
std::vector<Data> configDomains;
|
|
if(mProxyConfig->getConfigValue("Domains", configDomains))
|
|
{
|
|
for (std::vector<Data>::const_iterator i=configDomains.begin();
|
|
i != configDomains.end(); ++i)
|
|
{
|
|
InfoLog (<< "Adding domain " << *i << " from command line");
|
|
matcher->addDomain(*i);
|
|
if ( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = *i;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<Data> configDomainSuffixes;
|
|
if(mProxyConfig->getConfigValue("DomainSuffixes", configDomainSuffixes))
|
|
{
|
|
for (std::vector<Data>::const_iterator i=configDomainSuffixes.begin();
|
|
i != configDomainSuffixes.end(); ++i)
|
|
{
|
|
InfoLog (<< "Adding domain suffix " << *i << " from command line");
|
|
matcher->addDomainSuffix(*i);
|
|
if ( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = *i;
|
|
}
|
|
}
|
|
}
|
|
|
|
const ConfigStore::ConfigData& dList = mProxyConfig->getDataStore()->mConfigStore.getConfigs();
|
|
for (ConfigStore::ConfigData::const_iterator i=dList.begin();
|
|
i != dList.end(); ++i)
|
|
{
|
|
InfoLog (<< "Adding domain " << i->second.mDomain << " from config");
|
|
matcher->addDomain( i->second.mDomain );
|
|
if ( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = i->second.mDomain;
|
|
}
|
|
}
|
|
|
|
/* All of this logic has been commented out - the sysadmin must explicitly
|
|
add any of the items below to the Domains config option in repro.config
|
|
|
|
Data localhostname(DnsUtil::getLocalHostName());
|
|
InfoLog (<< "Adding local hostname domain " << localhostname );
|
|
matcher->addDomain(localhostname);
|
|
if ( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = localhostname;
|
|
}
|
|
|
|
InfoLog (<< "Adding localhost domain.");
|
|
matcher->addDomain("localhost");
|
|
if ( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = "localhost";
|
|
}
|
|
|
|
list<pair<Data,Data> > ips = DnsUtil::getInterfaces();
|
|
for ( list<pair<Data,Data> >::const_iterator i=ips.begin(); i!=ips.end(); i++)
|
|
{
|
|
InfoLog( << "Adding domain for IP " << i->second << " from interface " << i->first );
|
|
matcher->addDomain(i->second);
|
|
}
|
|
|
|
InfoLog (<< "Adding 127.0.0.1 domain.");
|
|
matcher->addDomain("127.0.0.1"); */
|
|
|
|
if( mDefaultRealm.empty() )
|
|
{
|
|
mDefaultRealm = "Unconfigured";
|
|
}
|
|
}
|
|
|
|
void
|
|
ReproRunner::addDomains(TransactionUser& tu)
|
|
{
|
|
if (!mDomainMatcher)
|
|
{
|
|
initDomainMatcher();
|
|
}
|
|
tu.setDomainMatcher(mDomainMatcher);
|
|
}
|
|
|
|
bool
|
|
ReproRunner::addTransports(bool& allTransportsSpecifyRecordRoute)
|
|
{
|
|
resip_assert(mProxyConfig);
|
|
resip_assert(mSipStack);
|
|
|
|
allTransportsSpecifyRecordRoute=false;
|
|
mStartupTransportRecordRoutes.clear();
|
|
|
|
bool useEmailAsSIP = mProxyConfig->getConfigBool("TLSUseEmailAsSIP", false);
|
|
Data wsCookieAuthSharedSecret = mProxyConfig->getConfigData("WSCookieAuthSharedSecret", Data::Empty);
|
|
std::shared_ptr<BasicWsConnectionValidator> basicWsConnectionValidator;
|
|
std::shared_ptr<WsCookieContextFactory> wsCookieContextFactory;
|
|
if(!wsCookieAuthSharedSecret.empty())
|
|
{
|
|
basicWsConnectionValidator = std::make_shared<BasicWsConnectionValidator>(wsCookieAuthSharedSecret);
|
|
Data infoCookieName = mProxyConfig->getConfigData("WSCookieNameInfo", Data::Empty);
|
|
Data extraCookieName = mProxyConfig->getConfigData("WSCookieNameExtra", Data::Empty);
|
|
Data macCookieName = mProxyConfig->getConfigData("WSCookieNameMac", Data::Empty);
|
|
|
|
wsCookieContextFactory = std::make_shared<BasicWsCookieContextFactory>(infoCookieName, extraCookieName, macCookieName);
|
|
}
|
|
|
|
try
|
|
{
|
|
// Check if advanced transport settings are provided
|
|
ConfigParse::NestedConfigMap m = mProxyConfig->getConfigNested("Transport");
|
|
DebugLog(<<"Found " << m.size() << " interface(s) defined in the advanced format");
|
|
if(!m.empty())
|
|
{
|
|
// Sample config file format for advanced transport settings
|
|
// Transport1Interface = 192.168.1.106:5061
|
|
// Transport1Type = TLS
|
|
// Transport1TlsDomain = sipdomain.com
|
|
// Transport1TlsCertificate = /etc/ssl/crt/sipdomain.com.pem
|
|
// Transport1TlsPrivateKey = /etc/ssl/private/sipdomain.com.pem
|
|
// Transport1TlsPrivateKeyPassPhrase = <pwd>
|
|
// Transport1TlsClientVerification = None
|
|
// Transport1RecordRouteUri = sip:sipdomain.com;transport=TLS
|
|
// Transport1RcvBufLen = 2000
|
|
|
|
allTransportsSpecifyRecordRoute = true;
|
|
|
|
const char *anchor;
|
|
for(ConfigParse::NestedConfigMap::iterator it = m.begin();
|
|
it != m.end();
|
|
it++)
|
|
{
|
|
int idx = it->first;
|
|
SipConfigParse tc(it->second);
|
|
Data transportPrefix = "Transport" + Data(idx);
|
|
DebugLog(<< "checking values for transport: " << idx);
|
|
Data interfaceSettings = tc.getConfigData("Interface", Data::Empty, true);
|
|
|
|
// Parse out interface settings
|
|
ParseBuffer pb(interfaceSettings);
|
|
anchor = pb.position();
|
|
pb.skipToEnd();
|
|
pb.skipBackToChar(':'); // For IPv6 the last : should be the port
|
|
pb.skipBackChar();
|
|
if(!pb.eof())
|
|
{
|
|
Data ipAddr;
|
|
Data portData;
|
|
pb.data(ipAddr, anchor);
|
|
pb.skipChar();
|
|
anchor = pb.position();
|
|
pb.skipToEnd();
|
|
pb.data(portData, anchor);
|
|
if(!DnsUtil::isIpAddress(ipAddr))
|
|
{
|
|
CritLog(<< "Malformed IP-address found in " << transportPrefix << "Interface setting: " << ipAddr);
|
|
}
|
|
int port = portData.convertInt();
|
|
if(port == 0)
|
|
{
|
|
CritLog(<< "Invalid port found in " << transportPrefix << " setting: " << port);
|
|
}
|
|
TransportType tt = Tuple::toTransport(tc.getConfigData("Type", "UDP"));
|
|
if(tt == UNKNOWN_TRANSPORT)
|
|
{
|
|
CritLog(<< "Unknown transport type found in " << transportPrefix << "Type setting: " << tc.getConfigData("Type", "UDP"));
|
|
}
|
|
Data tlsDomain = tc.getConfigData("TlsDomain", Data::Empty);
|
|
Data tlsCertificate = tc.getConfigData("TlsCertificate", Data::Empty);
|
|
Data tlsPrivateKey = tc.getConfigData("TlsPrivateKey", Data::Empty);
|
|
Data tlsPrivateKeyPassPhrase = tc.getConfigData("TlsPrivateKeyPassPhrase", Data::Empty);
|
|
SecurityTypes::TlsClientVerificationMode cvm = tc.getConfigClientVerificationMode("TlsClientVerification", SecurityTypes::None);
|
|
SecurityTypes::SSLType sslType = SecurityTypes::NoSSL;
|
|
#ifdef USE_SSL
|
|
sslType = tc.getConfigSSLType("TlsConnectionMethod", DEFAULT_TLS_METHOD);
|
|
#endif
|
|
|
|
#ifdef USE_SSL
|
|
// Make sure certificate material available before trying to instantiate Transport
|
|
if(isSecure(tt))
|
|
{
|
|
Security* security = mSipStack->getSecurity();
|
|
resip_assert(security != 0);
|
|
// FIXME: see comments about CertificatePath
|
|
if(!tlsCertificate.empty())
|
|
{
|
|
security->addDomainCertPEM(tlsDomain, Data::fromFile(tlsCertificate));
|
|
}
|
|
if(!tlsPrivateKey.empty())
|
|
{
|
|
security->addDomainPrivateKeyPEM(tlsDomain, Data::fromFile(tlsPrivateKey), tlsPrivateKeyPassPhrase);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Transport *t = mSipStack->addTransport(tt,
|
|
port,
|
|
DnsUtil::isIpV6Address(ipAddr) ? V6 : V4,
|
|
StunEnabled,
|
|
ipAddr, // interface to bind to
|
|
tlsDomain,
|
|
tlsPrivateKeyPassPhrase, // private key passphrase
|
|
sslType, // sslType
|
|
0, // transport flags
|
|
tlsCertificate, tlsPrivateKey,
|
|
cvm, // tls client verification mode
|
|
useEmailAsSIP,
|
|
basicWsConnectionValidator, wsCookieContextFactory);
|
|
|
|
if (t)
|
|
{
|
|
int rcvBufLen = tc.getConfigInt("RcvBufLen", 0);
|
|
if (rcvBufLen >0 )
|
|
{
|
|
#if defined(RESIP_SIPSTACK_HAVE_FDPOLL)
|
|
// this new method is part of the epoll changeset,
|
|
// which isn't commited yet.
|
|
t->setRcvBufLen(rcvBufLen);
|
|
#else
|
|
resip_assert(0);
|
|
#endif
|
|
}
|
|
|
|
Data recordRouteUri = tc.getConfigData("RecordRouteUri", Data::Empty);
|
|
if(!recordRouteUri.empty())
|
|
{
|
|
try
|
|
{
|
|
if(isEqualNoCase(recordRouteUri, "auto")) // auto generated record route uri
|
|
{
|
|
NameAddr rr;
|
|
if(isSecure(tt))
|
|
{
|
|
rr.uri().host()=tlsDomain;
|
|
}
|
|
else
|
|
{
|
|
rr.uri().host()=ipAddr;
|
|
}
|
|
rr.uri().port()=port;
|
|
rr.uri().param(resip::p_transport)=resip::Tuple::toDataLower(tt);
|
|
mStartupTransportRecordRoutes[t->getKey()] = rr; // Store to be added to Proxy after it is created
|
|
InfoLog (<< "Transport specific record-route enabled (generated): " << rr);
|
|
}
|
|
else
|
|
{
|
|
NameAddr rr(recordRouteUri);
|
|
mStartupTransportRecordRoutes[t->getKey()] = rr; // Store to be added to Proxy after it is created
|
|
InfoLog (<< "Transport specific record-route enabled: " << rr);
|
|
}
|
|
}
|
|
catch(BaseException& e)
|
|
{
|
|
ErrLog (<< "Invalid uri provided in " << transportPrefix << "RecordRouteUri setting (ignoring): " << e);
|
|
allTransportsSpecifyRecordRoute = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
allTransportsSpecifyRecordRoute = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CritLog(<< "Port not specified in " << transportPrefix << " setting: expected format is <IPAddress>:<Port>");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Data ipAddress = mProxyConfig->getConfigData("IPAddress", Data::Empty, true);
|
|
bool isV4Address = DnsUtil::isIpV4Address(ipAddress);
|
|
bool isV6Address = DnsUtil::isIpV6Address(ipAddress);
|
|
if(!isV4Address && !isV6Address)
|
|
{
|
|
if (!ipAddress.empty())
|
|
{
|
|
ErrLog(<< "Malformed IP-address found in IPAddress setting, ignoring (binding to all interfaces): " << ipAddress);
|
|
}
|
|
ipAddress = Data::Empty;
|
|
isV4Address = true;
|
|
isV6Address = true;
|
|
}
|
|
int udpPort = mProxyConfig->getConfigInt("UDPPort", 5060);
|
|
int tcpPort = mProxyConfig->getConfigInt("TCPPort", 5060);
|
|
int tlsPort = mProxyConfig->getConfigInt("TLSPort", 5061);
|
|
int wsPort = mProxyConfig->getConfigInt("WSPort", 80);
|
|
int wssPort = mProxyConfig->getConfigInt("WSSPort", 443);
|
|
int dtlsPort = mProxyConfig->getConfigInt("DTLSPort", 0);
|
|
Data tlsDomain = mProxyConfig->getConfigData("TLSDomainName", Data::Empty);
|
|
Data tlsCertificate = mProxyConfig->getConfigData("TLSCertificate", Data::Empty);
|
|
Data tlsPrivateKey = mProxyConfig->getConfigData("TLSPrivateKey", Data::Empty);
|
|
Data tlsPrivateKeyPassPhrase = mProxyConfig->getConfigData("TlsPrivateKeyPassPhrase", Data::Empty);
|
|
SecurityTypes::TlsClientVerificationMode cvm = mProxyConfig->getConfigClientVerificationMode("TLSClientVerification", SecurityTypes::None);
|
|
SecurityTypes::SSLType sslType = SecurityTypes::NoSSL;
|
|
#ifdef USE_SSL
|
|
sslType = mProxyConfig->getConfigSSLType("TLSConnectionMethod", DEFAULT_TLS_METHOD);
|
|
#endif
|
|
|
|
#ifdef USE_SSL
|
|
// Make sure certificate material available before trying to instantiate Transport
|
|
if (tlsPort || wssPort || dtlsPort)
|
|
{
|
|
Security* security = mSipStack->getSecurity();
|
|
resip_assert(security != 0);
|
|
// FIXME: should check that EITHER CertificatePath was set or both of these
|
|
// are supplied
|
|
// In any case, it will still give a helpful error when it fails to
|
|
// create the transport
|
|
if(!tlsCertificate.empty())
|
|
{
|
|
security->addDomainCertPEM(tlsDomain, Data::fromFile(tlsCertificate));
|
|
}
|
|
if(!tlsPrivateKey.empty())
|
|
{
|
|
security->addDomainPrivateKeyPEM(tlsDomain, Data::fromFile(tlsPrivateKey));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (udpPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(UDP, udpPort, V4, StunEnabled, ipAddress);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(UDP, udpPort, V6, StunEnabled, ipAddress);
|
|
}
|
|
if (tcpPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(TCP, tcpPort, V4, StunEnabled, ipAddress);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(TCP, tcpPort, V6, StunEnabled, ipAddress);
|
|
}
|
|
if (tlsPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(TLS, tlsPort, V4, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey, cvm, useEmailAsSIP);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(TLS, tlsPort, V6, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey, cvm, useEmailAsSIP);
|
|
}
|
|
if (wsPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(WS, wsPort, V4, StunEnabled, ipAddress, Data::Empty, Data::Empty, SecurityTypes::NoSSL, 0, Data::Empty, Data::Empty, SecurityTypes::None, false, basicWsConnectionValidator, wsCookieContextFactory);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(WS, wsPort, V6, StunEnabled, ipAddress, Data::Empty, Data::Empty, SecurityTypes::NoSSL, 0, Data::Empty, Data::Empty, SecurityTypes::None, false, basicWsConnectionValidator, wsCookieContextFactory);
|
|
}
|
|
if (wssPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(WSS, wssPort, V4, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey, cvm, useEmailAsSIP, basicWsConnectionValidator, wsCookieContextFactory);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(WSS, wssPort, V6, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey, cvm, useEmailAsSIP, basicWsConnectionValidator, wsCookieContextFactory);
|
|
}
|
|
if (dtlsPort)
|
|
{
|
|
if (mUseV4 && isV4Address) mSipStack->addTransport(DTLS, dtlsPort, V4, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey);
|
|
if (mUseV6 && isV6Address) mSipStack->addTransport(DTLS, dtlsPort, V6, StunEnabled, ipAddress, tlsDomain, tlsPrivateKeyPassPhrase, sslType, 0, tlsCertificate, tlsPrivateKey);
|
|
}
|
|
}
|
|
}
|
|
catch (BaseException& e)
|
|
{
|
|
std::cerr << "Likely a port is already in use" << endl;
|
|
InfoLog (<< "Caught: " << e);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ReproRunner::addProcessor(repro::ProcessorChain& chain, std::unique_ptr<Processor> processor)
|
|
{
|
|
chain.addProcessor(std::move(processor));
|
|
}
|
|
|
|
void // Monkeys
|
|
ReproRunner::makeRequestProcessorChain(ProcessorChain& chain)
|
|
{
|
|
resip_assert(mProxyConfig);
|
|
resip_assert(mRegistrationPersistenceManager);
|
|
|
|
// Add strict route fixup monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new StrictRouteFixup));
|
|
|
|
// Add is trusted node monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new IsTrustedNode(*mProxyConfig)));
|
|
|
|
// Add Certificate Authenticator - if required
|
|
resip_assert(mAuthFactory);
|
|
if(mAuthFactory->certificateAuthEnabled())
|
|
{
|
|
// TODO: perhaps this should be initialised from the trusted node
|
|
// monkey? Or should the list of trusted TLS peers be independent
|
|
// from the trusted node list?
|
|
// Should we used the same trustedPeers object that was
|
|
// passed to TlsPeerAuthManager perhaps?
|
|
addProcessor(chain, mAuthFactory->getCertificateAuthenticator());
|
|
}
|
|
|
|
Data wsCookieAuthSharedSecret = mProxyConfig->getConfigData("WSCookieAuthSharedSecret", Data::Empty);
|
|
Data wsCookieExtraHeaderName = mProxyConfig->getConfigData("WSCookieExtraHeaderName", "X-WS-Session-Extra");
|
|
if(!mAuthFactory->digestAuthEnabled() && !wsCookieAuthSharedSecret.empty())
|
|
{
|
|
addProcessor(chain, std::unique_ptr<Processor>(new CookieAuthenticator(wsCookieAuthSharedSecret, wsCookieExtraHeaderName, mSipStack)));
|
|
}
|
|
|
|
// Add digest authenticator monkey - if required
|
|
if (mAuthFactory->digestAuthEnabled())
|
|
{
|
|
addProcessor(chain, mAuthFactory->getDigestAuthenticator());
|
|
}
|
|
|
|
// Add am I responsible monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new AmIResponsible(mProxyConfig->getConfigBool("AlwaysAllowRelaying", false))));
|
|
|
|
// Add RequestFilter monkey
|
|
if(!mProxyConfig->getConfigBool("DisableRequestFilterProcessor", false))
|
|
{
|
|
if(mAsyncProcessorDispatcher)
|
|
{
|
|
addProcessor(chain, std::unique_ptr<Processor>(new RequestFilter(*mProxyConfig, mAsyncProcessorDispatcher)));
|
|
}
|
|
else
|
|
{
|
|
WarningLog(<< "Could not start RequestFilter Processor due to no worker thread pool (NumAsyncProcessorWorkerThreads=0)");
|
|
}
|
|
}
|
|
|
|
// [TODO] support for GRUU is on roadmap. When it is added the GruuMonkey will go here
|
|
|
|
// [TODO] support for Manipulating Tel URIs is on the roadmap.
|
|
// When added, the telUriMonkey will go here
|
|
|
|
std::vector<Data> routeSet;
|
|
mProxyConfig->getConfigValue("Routes", routeSet);
|
|
if (routeSet.empty())
|
|
{
|
|
// add static route monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new StaticRoute(*mProxyConfig)));
|
|
}
|
|
else
|
|
{
|
|
// add simple static route monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new SimpleStaticRoute(*mProxyConfig)));
|
|
}
|
|
|
|
// Add location server monkey
|
|
addProcessor(chain, std::unique_ptr<Processor>(new LocationServer(*mProxyConfig, *mRegistrationPersistenceManager, mAuthFactory->getDispatcher())));
|
|
|
|
// Add message silo monkey
|
|
if(mProxyConfig->getConfigBool("MessageSiloEnabled", false))
|
|
{
|
|
if(mAsyncProcessorDispatcher && mRegistrar)
|
|
{
|
|
MessageSilo* silo = new MessageSilo(*mProxyConfig, mAsyncProcessorDispatcher);
|
|
mRegistrar->addRegistrarHandler(silo);
|
|
addProcessor(chain, std::unique_ptr<Processor>(silo));
|
|
}
|
|
else
|
|
{
|
|
WarningLog(<< "Could not start MessageSilo Processor due to no worker thread pool (NumAsyncProcessorWorkerThreads=0) or Registrar");
|
|
}
|
|
}
|
|
}
|
|
|
|
void // Lemurs
|
|
ReproRunner::makeResponseProcessorChain(ProcessorChain& chain)
|
|
{
|
|
resip_assert(mProxyConfig);
|
|
resip_assert(mRegistrationPersistenceManager);
|
|
|
|
// Add outbound target handler lemur
|
|
addProcessor(chain, std::unique_ptr<Processor>(new OutboundTargetHandler(*mRegistrationPersistenceManager)));
|
|
|
|
if (mProxyConfig->getConfigBool("RecursiveRedirect", false))
|
|
{
|
|
// Add recursive redirect lemur
|
|
addProcessor(chain, std::unique_ptr<Processor>(new RecursiveRedirect));
|
|
}
|
|
}
|
|
|
|
void // Baboons
|
|
ReproRunner::makeTargetProcessorChain(ProcessorChain& chain)
|
|
{
|
|
resip_assert(mProxyConfig);
|
|
|
|
#ifndef RESIP_FIXED_POINT
|
|
if(mProxyConfig->getConfigBool("GeoProximityTargetSorting", false))
|
|
{
|
|
addProcessor(chain, std::unique_ptr<Processor>(new GeoProximityTargetSorter(*mProxyConfig)));
|
|
}
|
|
#endif
|
|
|
|
if(mProxyConfig->getConfigBool("QValue", true))
|
|
{
|
|
// Add q value target handler baboon
|
|
addProcessor(chain, std::unique_ptr<Processor>(new QValueTargetHandler(*mProxyConfig)));
|
|
}
|
|
|
|
// Add simple target handler baboon
|
|
addProcessor(chain, std::unique_ptr<Processor>(new SimpleTargetHandler));
|
|
}
|
|
|
|
bool
|
|
ReproRunner::operator()(resip::StatisticsMessage &statsMessage)
|
|
{
|
|
// Dispatch to each command server
|
|
for(std::list<CommandServer*>::iterator it = mCommandServerList.begin(); it != mCommandServerList.end(); it++)
|
|
{
|
|
(*it)->handleStatisticsMessage(statsMessage);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* ====================================================================
|
|
* The Vovida Software License, Version 1.0
|
|
*
|
|
* Copyright (c) 2022, Software Freedom Institute https://softwarefreedom.institute
|
|
* Copyright (c) 2022, Daniel Pocock https://danielpocock.com
|
|
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* ====================================================================
|
|
*/
|