Files
resiprocate/repro/ReproRunner.cxx
2023-05-04 16:31:48 +02:00

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.
*
* ====================================================================
*/