mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
This change moves the QSslSocket::supportsSsl() check from MetaParams:read() to main.cpp. This also ensures that the SSL check happens very early in Murmur's initialization process, rather than after we might have already attempted to call some SSL-related APIs. This change also moves the log message that prints the OpenSSL version to main.cpp, and integrates it with the QSslSocket::supportsSsl() check.
668 lines
22 KiB
C++
668 lines
22 KiB
C++
// Copyright 2005-2017 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "murmur_pch.h"
|
|
|
|
#include "Meta.h"
|
|
|
|
#include "Connection.h"
|
|
#include "Net.h"
|
|
#include "ServerDB.h"
|
|
#include "Server.h"
|
|
#include "OSInfo.h"
|
|
#include "Version.h"
|
|
#include "SSL.h"
|
|
|
|
#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
|
|
# include <QSslDiffieHellmanParameters>
|
|
#endif
|
|
|
|
MetaParams Meta::mp;
|
|
|
|
#ifdef Q_OS_WIN
|
|
HANDLE Meta::hQoS = NULL;
|
|
#endif
|
|
|
|
MetaParams::MetaParams() {
|
|
qsPassword = QString();
|
|
usPort = DEFAULT_MUMBLE_PORT;
|
|
iTimeout = 30;
|
|
iMaxBandwidth = 72000;
|
|
iMaxUsers = 1000;
|
|
iMaxUsersPerChannel = 0;
|
|
iMaxTextMessageLength = 5000;
|
|
iMaxImageMessageLength = 131072;
|
|
legacyPasswordHash = false;
|
|
kdfIterations = -1;
|
|
bAllowHTML = true;
|
|
iDefaultChan = 0;
|
|
bRememberChan = true;
|
|
qsWelcomeText = QString();
|
|
qsDatabase = QString();
|
|
iDBPort = 0;
|
|
qsDBusService = "net.sourceforge.mumble.murmur";
|
|
qsDBDriver = "QSQLITE";
|
|
qsLogfile = "murmur.log";
|
|
|
|
iLogDays = 31;
|
|
|
|
iObfuscate = 0;
|
|
bSendVersion = true;
|
|
bBonjour = true;
|
|
bAllowPing = true;
|
|
bCertRequired = false;
|
|
bForceExternalAuth = false;
|
|
|
|
iBanTries = 10;
|
|
iBanTimeframe = 120;
|
|
iBanTime = 300;
|
|
|
|
#ifdef Q_OS_UNIX
|
|
uiUid = uiGid = 0;
|
|
#endif
|
|
|
|
iOpusThreshold = 100;
|
|
|
|
iChannelNestingLimit = 10;
|
|
|
|
qrUserName = QRegExp(QLatin1String("[-=\\w\\[\\]\\{\\}\\(\\)\\@\\|\\.]+"));
|
|
qrChannelName = QRegExp(QLatin1String("[ \\-=\\w\\#\\[\\]\\{\\}\\(\\)\\@\\|]+"));
|
|
|
|
qsCiphers = MumbleSSL::defaultOpenSSLCipherString();
|
|
|
|
qsSettings = NULL;
|
|
}
|
|
|
|
MetaParams::~MetaParams() {
|
|
delete qsSettings;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a qsSettings config variable named 'name' was set to a valid value for the type
|
|
* we want to convert it to. Otherwise a error message is logged and 'defaultValue' is returned.
|
|
*
|
|
* E.g. checkedFromSettings<QString>("blub", bla) would output an error message and leave bla unchanged
|
|
* if the user had set the ini parameter to "blub = has, commas, in, it" which QSettings will interpret
|
|
* not as a String but as a list of strings.
|
|
*
|
|
* @param T Conversion target type (type of 'defaultValue', auto inducable)
|
|
* @param name qsSettings variable name
|
|
* @param defaultValue Default value for 'name'
|
|
* @param settings The QSettings object to read from. If null, the Meta's qsSettings will be used.
|
|
* @return Setting if valid, default if not or setting not set.
|
|
*/
|
|
template <class T>
|
|
T MetaParams::typeCheckedFromSettings(const QString &name, const T &defaultValue, QSettings *settings) {
|
|
// Use qsSettings unless a specific QSettings instance
|
|
// is requested.
|
|
if (settings == NULL) {
|
|
settings = qsSettings;
|
|
}
|
|
|
|
QVariant cfgVariable = settings->value(name, defaultValue);
|
|
|
|
if (!cfgVariable.convert(QVariant(defaultValue).type())) { // Bit convoluted as canConvert<T>() only does a static check without considering whether say a string like "blub" is actually a valid double (which convert does).
|
|
qCritical() << "Configuration variable" << name << "is of invalid format. Set to default value of" << defaultValue << ".";
|
|
return defaultValue;
|
|
}
|
|
|
|
return cfgVariable.value<T>();
|
|
}
|
|
|
|
void MetaParams::read(QString fname) {
|
|
qmConfig.clear();
|
|
|
|
if (fname.isEmpty()) {
|
|
QStringList datapaths;
|
|
|
|
#if defined(Q_OS_WIN)
|
|
#if QT_VERSION >= 0x050000
|
|
datapaths << QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
|
#else
|
|
datapaths << QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
|
#endif
|
|
|
|
size_t reqSize;
|
|
_wgetenv_s(&reqSize, NULL, 0, L"APPDATA");
|
|
if (reqSize > 0) {
|
|
STACKVAR(wchar_t, buff, reqSize+1);
|
|
_wgetenv_s(&reqSize, buff, reqSize, L"APPDATA");
|
|
|
|
QDir appdir = QDir(QDir::fromNativeSeparators(QString::fromWCharArray(buff)));
|
|
|
|
datapaths << appdir.absolutePath() + QLatin1String("/Mumble");
|
|
}
|
|
#else
|
|
datapaths << QDir::homePath() + QLatin1String("/.murmurd");
|
|
datapaths << QDir::homePath() + QLatin1String("/.config/Mumble");
|
|
#endif
|
|
|
|
#if defined(Q_OS_MAC)
|
|
/* Old Mac data path. */
|
|
datapaths << QDir::homePath() + QLatin1String("/Library/Preferences/Mumble/");
|
|
#endif
|
|
|
|
datapaths << QDir::homePath();
|
|
datapaths << QDir::currentPath();
|
|
datapaths << QCoreApplication::instance()->applicationDirPath();
|
|
|
|
foreach(const QString &p, datapaths) {
|
|
if (! p.isEmpty()) {
|
|
QFileInfo fi(p, "murmur.ini");
|
|
if (fi.exists() && fi.isReadable()) {
|
|
qdBasePath = QDir(p);
|
|
qsAbsSettingsFilePath = fi.absoluteFilePath();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (qsAbsSettingsFilePath.isEmpty()) {
|
|
QDir::root().mkpath(qdBasePath.absolutePath());
|
|
qdBasePath = QDir(datapaths.at(0));
|
|
qsAbsSettingsFilePath = qdBasePath.absolutePath() + QLatin1String("/murmur.ini");
|
|
}
|
|
} else {
|
|
QFile f(fname);
|
|
if (! f.open(QIODevice::ReadOnly)) {
|
|
qFatal("Specified ini file %s could not be opened", qPrintable(fname));
|
|
}
|
|
qdBasePath = QFileInfo(f).absoluteDir();
|
|
qsAbsSettingsFilePath = QFileInfo(f).absoluteFilePath();
|
|
f.close();
|
|
}
|
|
QDir::setCurrent(qdBasePath.absolutePath());
|
|
qsSettings = new QSettings(qsAbsSettingsFilePath, QSettings::IniFormat);
|
|
#if QT_VERSION >= 0x040500
|
|
qsSettings->setIniCodec("UTF-8");
|
|
#endif
|
|
qWarning("Initializing settings from %s (basepath %s)", qPrintable(qsSettings->fileName()), qPrintable(qdBasePath.absolutePath()));
|
|
|
|
QString qsHost = qsSettings->value("host", QString()).toString();
|
|
if (! qsHost.isEmpty()) {
|
|
foreach(const QString &host, qsHost.split(QRegExp(QLatin1String("\\s+")), QString::SkipEmptyParts)) {
|
|
QHostAddress qhaddr;
|
|
if (qhaddr.setAddress(host)) {
|
|
qlBind << qhaddr;
|
|
} else {
|
|
bool found = false;
|
|
QHostInfo hi = QHostInfo::fromName(host);
|
|
foreach(QHostAddress qha, hi.addresses()) {
|
|
if ((qha.protocol() == QAbstractSocket::IPv4Protocol) || (qha.protocol() == QAbstractSocket::IPv6Protocol)) {
|
|
qlBind << qha;
|
|
found = true;
|
|
}
|
|
}
|
|
if (! found) {
|
|
qFatal("Lookup of bind hostname %s failed", qPrintable(host));
|
|
}
|
|
}
|
|
}
|
|
foreach(const QHostAddress &qha, qlBind)
|
|
qWarning("Binding to address %s", qPrintable(qha.toString()));
|
|
}
|
|
|
|
if (qlBind.isEmpty()) {
|
|
bool hasipv6 = false;
|
|
bool hasipv4 = false;
|
|
|
|
foreach(const QNetworkInterface &qni, QNetworkInterface::allInterfaces()) {
|
|
if (!(qni.flags() & QNetworkInterface::IsUp))
|
|
continue;
|
|
if (!(qni.flags() & QNetworkInterface::IsRunning))
|
|
continue;
|
|
if (qni.flags() & QNetworkInterface::IsLoopBack)
|
|
continue;
|
|
|
|
foreach(const QNetworkAddressEntry &qna, qni.addressEntries()) {
|
|
const QHostAddress &qha = qna.ip();
|
|
switch (qha.protocol()) {
|
|
case QAbstractSocket::IPv4Protocol:
|
|
hasipv4 = true;
|
|
break;
|
|
case QAbstractSocket::IPv6Protocol:
|
|
hasipv6 = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if QT_VERSION >= 0x050000
|
|
if (hasipv6) {
|
|
if (SslServer::hasDualStackSupport() && hasipv4) {
|
|
qlBind << QHostAddress(QHostAddress::Any);
|
|
hasipv4 = false; // No need to add a separate ipv4 socket
|
|
} else {
|
|
qlBind << QHostAddress(QHostAddress::AnyIPv6);
|
|
}
|
|
}
|
|
|
|
if (hasipv4) {
|
|
qlBind << QHostAddress(QHostAddress::AnyIPv4);
|
|
}
|
|
#else // QT_VERSION < 0x050000
|
|
// For Qt 4 AnyIPv6 resulted in a dual stack socket on dual stack
|
|
// capable systems while Any resulted in an IPv4 only socket. For
|
|
// Qt 5 this has been reworked so that AnyIPv6/v4 are now exclusive
|
|
// IPv6/4 sockets while Any is the dual stack socket.
|
|
|
|
if (hasipv6) {
|
|
qlBind << QHostAddress(QHostAddress::AnyIPv6);
|
|
if (SslServer::hasDualStackSupport() && hasipv4) {
|
|
hasipv4 = false; // No need to add a separate ipv4 socket
|
|
}
|
|
}
|
|
|
|
if (hasipv4) {
|
|
qlBind << QHostAddress(QHostAddress::Any);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
qsPassword = typeCheckedFromSettings("serverpassword", qsPassword);
|
|
usPort = static_cast<unsigned short>(typeCheckedFromSettings("port", static_cast<uint>(usPort)));
|
|
iTimeout = typeCheckedFromSettings("timeout", iTimeout);
|
|
iMaxTextMessageLength = typeCheckedFromSettings("textmessagelength", iMaxTextMessageLength);
|
|
iMaxImageMessageLength = typeCheckedFromSettings("imagemessagelength", iMaxImageMessageLength);
|
|
legacyPasswordHash = typeCheckedFromSettings("legacypasswordhash", legacyPasswordHash);
|
|
kdfIterations = typeCheckedFromSettings("kdfiterations", -1);
|
|
bAllowHTML = typeCheckedFromSettings("allowhtml", bAllowHTML);
|
|
iMaxBandwidth = typeCheckedFromSettings("bandwidth", iMaxBandwidth);
|
|
iDefaultChan = typeCheckedFromSettings("defaultchannel", iDefaultChan);
|
|
bRememberChan = typeCheckedFromSettings("rememberchannel", bRememberChan);
|
|
iMaxUsers = typeCheckedFromSettings("users", iMaxUsers);
|
|
iMaxUsersPerChannel = typeCheckedFromSettings("usersperchannel", iMaxUsersPerChannel);
|
|
qsWelcomeText = typeCheckedFromSettings("welcometext", qsWelcomeText);
|
|
bCertRequired = typeCheckedFromSettings("certrequired", bCertRequired);
|
|
bForceExternalAuth = typeCheckedFromSettings("forceExternalAuth", bForceExternalAuth);
|
|
|
|
qsDatabase = typeCheckedFromSettings("database", qsDatabase);
|
|
|
|
qsDBDriver = typeCheckedFromSettings("dbDriver", qsDBDriver);
|
|
qsDBUserName = typeCheckedFromSettings("dbUsername", qsDBUserName);
|
|
qsDBPassword = typeCheckedFromSettings("dbPassword", qsDBPassword);
|
|
qsDBHostName = typeCheckedFromSettings("dbHost", qsDBHostName);
|
|
qsDBPrefix = typeCheckedFromSettings("dbPrefix", qsDBPrefix);
|
|
qsDBOpts = typeCheckedFromSettings("dbOpts", qsDBOpts);
|
|
iDBPort = typeCheckedFromSettings("dbPort", iDBPort);
|
|
|
|
qsIceEndpoint = typeCheckedFromSettings("ice", qsIceEndpoint);
|
|
qsIceSecretRead = typeCheckedFromSettings("icesecret", qsIceSecretRead);
|
|
qsIceSecretRead = typeCheckedFromSettings("icesecretread", qsIceSecretRead);
|
|
qsIceSecretWrite = typeCheckedFromSettings("icesecretwrite", qsIceSecretRead);
|
|
|
|
qsGRPCAddress = typeCheckedFromSettings("grpc", qsGRPCAddress);
|
|
qsGRPCCert = typeCheckedFromSettings("grpccert", qsGRPCCert);
|
|
qsGRPCKey = typeCheckedFromSettings("grpckey", qsGRPCKey);
|
|
|
|
iLogDays = typeCheckedFromSettings("logdays", iLogDays);
|
|
|
|
qsDBus = typeCheckedFromSettings("dbus", qsDBus);
|
|
qsDBusService = typeCheckedFromSettings("dbusservice", qsDBusService);
|
|
qsLogfile = typeCheckedFromSettings("logfile", qsLogfile);
|
|
qsPid = typeCheckedFromSettings("pidfile", qsPid);
|
|
|
|
qsRegName = typeCheckedFromSettings("registerName", qsRegName);
|
|
qsRegPassword = typeCheckedFromSettings("registerPassword", qsRegPassword);
|
|
qsRegHost = typeCheckedFromSettings("registerHostname", qsRegHost);
|
|
qsRegLocation = typeCheckedFromSettings("registerLocation", qsRegLocation);
|
|
qurlRegWeb = QUrl(typeCheckedFromSettings("registerUrl", qurlRegWeb).toString());
|
|
bBonjour = typeCheckedFromSettings("bonjour", bBonjour);
|
|
|
|
iBanTries = typeCheckedFromSettings("autobanAttempts", iBanTries);
|
|
iBanTimeframe = typeCheckedFromSettings("autobanTimeframe", iBanTimeframe);
|
|
iBanTime = typeCheckedFromSettings("autobanTime", iBanTime);
|
|
|
|
qvSuggestVersion = MumbleVersion::getRaw(qsSettings->value("suggestVersion").toString());
|
|
if (qvSuggestVersion.toUInt() == 0)
|
|
qvSuggestVersion = QVariant();
|
|
|
|
qvSuggestPositional = qsSettings->value("suggestPositional");
|
|
if (qvSuggestPositional.toString().trimmed().isEmpty())
|
|
qvSuggestPositional = QVariant();
|
|
|
|
qvSuggestPushToTalk = qsSettings->value("suggestPushToTalk");
|
|
if (qvSuggestPushToTalk.toString().trimmed().isEmpty())
|
|
qvSuggestPushToTalk = QVariant();
|
|
|
|
iOpusThreshold = typeCheckedFromSettings("opusthreshold", iOpusThreshold);
|
|
|
|
iChannelNestingLimit = typeCheckedFromSettings("channelnestinglimit", iChannelNestingLimit);
|
|
|
|
#ifdef Q_OS_UNIX
|
|
qsName = qsSettings->value("uname").toString();
|
|
if (geteuid() == 0) {
|
|
// TODO: remove this silent fallback to enforce running as non-root
|
|
bool requested = true;
|
|
if (qsName.isEmpty()) {
|
|
// default server user name
|
|
qsName = "mumble-server";
|
|
requested = false;
|
|
}
|
|
struct passwd *pw = getpwnam(qPrintable(qsName));
|
|
if (pw) {
|
|
uiUid = pw->pw_uid;
|
|
uiGid = pw->pw_gid;
|
|
qsHome = pw->pw_dir;
|
|
} else if (requested) {
|
|
qFatal("Cannot find username %s", qPrintable(qsName));
|
|
}
|
|
endpwent();
|
|
}
|
|
#endif
|
|
|
|
qrUserName = QRegExp(typeCheckedFromSettings("username", qrUserName.pattern()));
|
|
qrChannelName = QRegExp(typeCheckedFromSettings("channelname", qrChannelName.pattern()));
|
|
|
|
bool bObfuscate = typeCheckedFromSettings("obfuscate", false);
|
|
if (bObfuscate) {
|
|
qWarning("IP address obfuscation enabled.");
|
|
iObfuscate = qrand();
|
|
}
|
|
bSendVersion = typeCheckedFromSettings("sendversion", bSendVersion);
|
|
bAllowPing = typeCheckedFromSettings("allowping", bAllowPing);
|
|
|
|
qsCiphers = typeCheckedFromSettings("sslCiphers", qsCiphers);
|
|
|
|
QString qsSSLCert = qsSettings->value("sslCert").toString();
|
|
QString qsSSLKey = qsSettings->value("sslKey").toString();
|
|
QString qsSSLCA = qsSettings->value("sslCA").toString();
|
|
QString qsSSLDHParams = qsSettings->value("sslDHParams").toString();
|
|
|
|
qbaPassPhrase = qsSettings->value("sslPassPhrase").toByteArray();
|
|
|
|
if (! qsSSLCA.isEmpty()) {
|
|
QFile pem(qsSSLCA);
|
|
if (pem.open(QIODevice::ReadOnly)) {
|
|
QByteArray qba = pem.readAll();
|
|
pem.close();
|
|
QList<QSslCertificate> ql = QSslCertificate::fromData(qba);
|
|
if (ql.isEmpty()) {
|
|
qCritical("Failed to parse any CA certificates from %s", qPrintable(qsSSLCA));
|
|
} else {
|
|
qlCA = ql;
|
|
}
|
|
} else {
|
|
qCritical("Failed to read %s", qPrintable(qsSSLCA));
|
|
}
|
|
}
|
|
|
|
QByteArray crt, key, dhparams;
|
|
|
|
if (! qsSSLCert.isEmpty()) {
|
|
QFile pem(qsSSLCert);
|
|
if (pem.open(QIODevice::ReadOnly)) {
|
|
crt = pem.readAll();
|
|
pem.close();
|
|
} else {
|
|
qCritical("Failed to read %s", qPrintable(qsSSLCert));
|
|
}
|
|
}
|
|
if (! qsSSLKey.isEmpty()) {
|
|
QFile pem(qsSSLKey);
|
|
if (pem.open(QIODevice::ReadOnly)) {
|
|
key = pem.readAll();
|
|
pem.close();
|
|
} else {
|
|
qCritical("Failed to read %s", qPrintable(qsSSLKey));
|
|
}
|
|
}
|
|
|
|
if (! key.isEmpty() || ! crt.isEmpty()) {
|
|
if (! key.isEmpty()) {
|
|
qskKey = Server::privateKeyFromPEM(key, qbaPassPhrase);
|
|
}
|
|
if (qskKey.isNull() && ! crt.isEmpty()) {
|
|
qskKey = Server::privateKeyFromPEM(crt, qbaPassPhrase);
|
|
if (! qskKey.isNull())
|
|
qCritical("Using private key found in certificate file.");
|
|
}
|
|
if (qskKey.isNull())
|
|
qFatal("No private key found in certificate or key file.");
|
|
|
|
QList<QSslCertificate> ql = QSslCertificate::fromData(crt);
|
|
ql << QSslCertificate::fromData(key);
|
|
for (int i=0;i<ql.size(); ++i) {
|
|
const QSslCertificate &c = ql.at(i);
|
|
if (Server::isKeyForCert(qskKey, c)) {
|
|
qscCert = c;
|
|
ql.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
if (qscCert.isNull()) {
|
|
qFatal("Failed to find certificate matching private key.");
|
|
}
|
|
if (ql.size() > 0) {
|
|
qlIntermediates = ql;
|
|
qCritical("Adding %d intermediate certificates from certificate file.", ql.size());
|
|
}
|
|
}
|
|
|
|
#if defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
|
|
if (! qsSSLDHParams.isEmpty()) {
|
|
QFile pem(qsSSLDHParams);
|
|
if (pem.open(QIODevice::ReadOnly)) {
|
|
dhparams = pem.readAll();
|
|
pem.close();
|
|
} else {
|
|
qCritical("Failed to read %s", qPrintable(qsSSLDHParams));
|
|
}
|
|
}
|
|
|
|
if (! dhparams.isEmpty()) {
|
|
QSslDiffieHellmanParameters qdhp = QSslDiffieHellmanParameters::fromEncoded(dhparams);
|
|
if (qdhp.isValid()) {
|
|
qbaDHParams = dhparams;
|
|
} else {
|
|
qFatal("Unable to use specified Diffie-Hellman parameters: %s", qPrintable(qdhp.errorString()));
|
|
}
|
|
}
|
|
#else
|
|
if (! qsSSLDHParams.isEmpty()) {
|
|
qFatal("This version of Murmur does not support Diffie-Hellman parameters (sslDHParams). Murmur will not start unless you remove the option from your murmur.ini file.");
|
|
}
|
|
#endif
|
|
|
|
{
|
|
QList<QSslCipher> ciphers = MumbleSSL::ciphersFromOpenSSLCipherString(qsCiphers);
|
|
if (ciphers.isEmpty()) {
|
|
qFatal("Invalid sslCiphers option. Either the cipher string is invalid or none of the ciphers are available: \"%s\"", qPrintable(qsCiphers));
|
|
}
|
|
|
|
// If the version of Qt we're building against doesn't support
|
|
// QSslDiffieHellmanParameters, then we must filter out Diffie-
|
|
// Hellman cipher suites in order to guarantee that we do not
|
|
// use Qt's default Diffie-Hellman parameters.
|
|
QList<QSslCipher> filtered;
|
|
#if !defined(USE_QSSLDIFFIEHELLMANPARAMETERS)
|
|
foreach (QSslCipher c, ciphers) {
|
|
if (c.keyExchangeMethod() == QLatin1String("DH")) {
|
|
continue;
|
|
}
|
|
filtered << c;
|
|
}
|
|
if (ciphers.size() != filtered.size()) {
|
|
qWarning("Warning: all cipher suites in sslCiphers using Diffie-Hellman key exchange "
|
|
"have been removed. Qt %s does not support custom Diffie-Hellman parameters.",
|
|
qVersion());
|
|
}
|
|
#else
|
|
filtered = ciphers;
|
|
#endif
|
|
|
|
QSslSocket::setDefaultCiphers(filtered);
|
|
|
|
QStringList pref;
|
|
foreach (QSslCipher c, filtered) {
|
|
pref << c.name();
|
|
}
|
|
qWarning("Meta: TLS cipher preference is \"%s\"", qPrintable(pref.join(QLatin1String(":"))));
|
|
}
|
|
|
|
QStringList hosts;
|
|
foreach(const QHostAddress &qha, qlBind) {
|
|
hosts << qha.toString();
|
|
}
|
|
qmConfig.insert(QLatin1String("host"),hosts.join(" "));
|
|
qmConfig.insert(QLatin1String("password"),qsPassword);
|
|
qmConfig.insert(QLatin1String("port"),QString::number(usPort));
|
|
qmConfig.insert(QLatin1String("timeout"),QString::number(iTimeout));
|
|
qmConfig.insert(QLatin1String("textmessagelength"), QString::number(iMaxTextMessageLength));
|
|
qmConfig.insert(QLatin1String("legacypasswordhash"), legacyPasswordHash ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("kdfiterations"), QString::number(kdfIterations));
|
|
qmConfig.insert(QLatin1String("allowhtml"), bAllowHTML ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("bandwidth"),QString::number(iMaxBandwidth));
|
|
qmConfig.insert(QLatin1String("users"),QString::number(iMaxUsers));
|
|
qmConfig.insert(QLatin1String("defaultchannel"),QString::number(iDefaultChan));
|
|
qmConfig.insert(QLatin1String("rememberchannel"),bRememberChan ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("welcometext"),qsWelcomeText);
|
|
qmConfig.insert(QLatin1String("registername"),qsRegName);
|
|
qmConfig.insert(QLatin1String("registerpassword"),qsRegPassword);
|
|
qmConfig.insert(QLatin1String("registerhostname"),qsRegHost);
|
|
qmConfig.insert(QLatin1String("registerlocation"), qsRegLocation);
|
|
qmConfig.insert(QLatin1String("registerurl"),qurlRegWeb.toString());
|
|
qmConfig.insert(QLatin1String("bonjour"), bBonjour ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("certificate"),qscCert.toPem());
|
|
qmConfig.insert(QLatin1String("key"),qskKey.toPem());
|
|
qmConfig.insert(QLatin1String("obfuscate"),bObfuscate ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("username"),qrUserName.pattern());
|
|
qmConfig.insert(QLatin1String("channelname"),qrChannelName.pattern());
|
|
qmConfig.insert(QLatin1String("certrequired"), bCertRequired ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("forceExternalAuth"), bForceExternalAuth ? QLatin1String("true") : QLatin1String("false"));
|
|
qmConfig.insert(QLatin1String("suggestversion"), qvSuggestVersion.isNull() ? QString() : qvSuggestVersion.toString());
|
|
qmConfig.insert(QLatin1String("suggestpositional"), qvSuggestPositional.isNull() ? QString() : qvSuggestPositional.toString());
|
|
qmConfig.insert(QLatin1String("suggestpushtotalk"), qvSuggestPushToTalk.isNull() ? QString() : qvSuggestPushToTalk.toString());
|
|
qmConfig.insert(QLatin1String("opusthreshold"), QString::number(iOpusThreshold));
|
|
qmConfig.insert(QLatin1String("channelnestinglimit"), QString::number(iChannelNestingLimit));
|
|
qmConfig.insert(QLatin1String("sslCiphers"), qsCiphers);
|
|
qmConfig.insert(QLatin1String("sslDHParams"), QString::fromLatin1(qbaDHParams.constData()));
|
|
}
|
|
|
|
Meta::Meta() {
|
|
#ifdef Q_OS_WIN
|
|
QOS_VERSION qvVer;
|
|
qvVer.MajorVersion = 1;
|
|
qvVer.MinorVersion = 0;
|
|
|
|
hQoS = NULL;
|
|
|
|
HMODULE hLib = LoadLibrary(L"qWave.dll");
|
|
if (hLib == NULL) {
|
|
qWarning("Meta: Failed to load qWave.dll, no QoS available");
|
|
} else {
|
|
FreeLibrary(hLib);
|
|
if (! QOSCreateHandle(&qvVer, &hQoS))
|
|
qWarning("Meta: Failed to create QOS2 handle");
|
|
else
|
|
Connection::setQoS(hQoS);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Meta::~Meta() {
|
|
#ifdef Q_OS_WIN
|
|
if (hQoS) {
|
|
QOSCloseHandle(hQoS);
|
|
Connection::setQoS(NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Meta::getOSInfo() {
|
|
qsOS = OSInfo::getOS();
|
|
qsOSVersion = OSInfo::getOSDisplayableVersion();
|
|
}
|
|
|
|
void Meta::bootAll() {
|
|
QList<int> ql = ServerDB::getBootServers();
|
|
foreach(int snum, ql)
|
|
boot(snum);
|
|
}
|
|
|
|
bool Meta::boot(int srvnum) {
|
|
if (qhServers.contains(srvnum))
|
|
return false;
|
|
if (! ServerDB::serverExists(srvnum))
|
|
return false;
|
|
Server *s = new Server(srvnum, this);
|
|
if (! s->bValid) {
|
|
delete s;
|
|
return false;
|
|
}
|
|
qhServers.insert(srvnum, s);
|
|
emit started(s);
|
|
|
|
#ifdef Q_OS_UNIX
|
|
unsigned int sockets = 19; // Base
|
|
foreach(s, qhServers) {
|
|
sockets += 11; // Listen sockets, signal pipes etc.
|
|
sockets += s->iMaxUsers; // One per user
|
|
}
|
|
|
|
struct rlimit r;
|
|
if (getrlimit(RLIMIT_NOFILE, &r) == 0) {
|
|
if (r.rlim_cur < sockets) {
|
|
r.rlim_cur = sockets;
|
|
if (r.rlim_max < sockets)
|
|
r.rlim_max = sockets;
|
|
if (setrlimit(RLIMIT_NOFILE, &r) != 0) {
|
|
getrlimit(RLIMIT_NOFILE, &r);
|
|
if (r.rlim_cur < r.rlim_max) {
|
|
r.rlim_cur = r.rlim_max;
|
|
setrlimit(RLIMIT_NOFILE, &r);
|
|
getrlimit(RLIMIT_NOFILE, &r);
|
|
}
|
|
}
|
|
}
|
|
if (r.rlim_cur < sockets)
|
|
qCritical("Current booted servers require minimum %d file descriptors when all slots are full, but only %lu file descriptors are allowed for this process. Your server will crash and burn; read the FAQ for details.", sockets, static_cast<unsigned long>(r.rlim_cur));
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void Meta::kill(int srvnum) {
|
|
Server *s = qhServers.take(srvnum);
|
|
if (!s)
|
|
return;
|
|
emit stopped(s);
|
|
delete s;
|
|
}
|
|
|
|
void Meta::killAll() {
|
|
foreach(Server *s, qhServers) {
|
|
emit stopped(s);
|
|
delete s;
|
|
}
|
|
qhServers.clear();
|
|
}
|
|
|
|
bool Meta::banCheck(const QHostAddress &addr) {
|
|
if ((mp.iBanTries == 0) || (mp.iBanTimeframe == 0))
|
|
return false;
|
|
|
|
if (qhBans.contains(addr)) {
|
|
Timer t = qhBans.value(addr);
|
|
if (t.elapsed() < (1000000ULL * mp.iBanTime))
|
|
return true;
|
|
qhBans.remove(addr);
|
|
}
|
|
|
|
QList<Timer> &ql = qhAttempts[addr];
|
|
|
|
ql.append(Timer());
|
|
while (! ql.isEmpty() && (ql.at(0).elapsed() > (1000000ULL * mp.iBanTimeframe)))
|
|
ql.removeFirst();
|
|
|
|
if (ql.count() > mp.iBanTries) {
|
|
qhBans.insert(addr, Timer());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|