// Copyright 2007-2023 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 . #include "Server.h" #include "ACL.h" #include "Channel.h" #include "ClientType.h" #include "Connection.h" #include "EnvUtils.h" #include "Group.h" #include "HTMLFilter.h" #include "HostAddress.h" #include "Meta.h" #include "MumbleProtocol.h" #include "ProtoUtils.h" #include "QtUtils.h" #include "ServerDB.h" #include "ServerUser.h" #include "User.h" #include "Version.h" #ifdef USE_ZEROCONF # include "Zeroconf.h" #endif #include "Utils.h" #include #include #include #include #include #include #include #include "TracyConstants.h" #include #include #include #include #include #ifdef Q_OS_WIN # include # include #else # include # include #endif ExecEvent::ExecEvent(boost::function< void() > f) : QEvent(static_cast< QEvent::Type >(EXEC_QEVENT)) { func = f; } void ExecEvent::execute() { func(); } SslServer::SslServer(QObject *p) : QTcpServer(p) { } void SslServer::incomingConnection(qintptr v) { QSslSocket *s = new QSslSocket(this); s->setSocketDescriptor(v); qlSockets.append(s); } QSslSocket *SslServer::nextPendingSSLConnection() { if (qlSockets.isEmpty()) return nullptr; return qlSockets.takeFirst(); } Server::Server(int snum, QObject *p) : QThread(p) { tracy::SetThreadName("Main"); bValid = true; iServerNum = snum; #ifdef USE_ZEROCONF zeroconf = nullptr; #endif bUsingMetaCert = false; #ifdef Q_OS_UNIX aiNotify[0] = aiNotify[1] = -1; #else hNotify = nullptr; #endif qtTimeout = new QTimer(this); iCodecAlpha = iCodecBeta = 0; bPreferAlpha = false; bOpus = true; qnamNetwork = nullptr; readParams(); initialize(); foreach (const QHostAddress &qha, qlBind) { SslServer *ss = new SslServer(this); connect(ss, SIGNAL(newConnection()), this, SLOT(newClient()), Qt::QueuedConnection); if (!ss->listen(qha, usPort)) { log(QString("Server: TCP Listen on %1 failed: %2").arg(addressToString(qha, usPort), ss->errorString())); bValid = false; } else { log(QString("Server listening on %1").arg(addressToString(qha, usPort))); } qlServer << ss; } if (!bValid) return; foreach (SslServer *ss, qlServer) { sockaddr_storage addr; #ifdef Q_OS_UNIX int tcpsock = static_cast< int >(ss->socketDescriptor()); socklen_t len = sizeof(addr); #else SOCKET tcpsock = ss->socketDescriptor(); int len = sizeof(addr); #endif memset(&addr, 0, sizeof(addr)); getsockname(tcpsock, reinterpret_cast< struct sockaddr * >(&addr), &len); #ifdef Q_OS_UNIX int sock = ::socket(addr.ss_family, SOCK_DGRAM, 0); # ifdef Q_OS_LINUX int sockopt = 1; if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &sockopt, sizeof(sockopt))) log(QString("Failed to set IP_PKTINFO for %1").arg(addressToString(ss->serverAddress(), usPort))); sockopt = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt, sizeof(sockopt))) log(QString("Failed to set IPV6_RECVPKTINFO for %1").arg(addressToString(ss->serverAddress(), usPort))); # endif #else # ifndef SIO_UDP_CONNRESET # define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) # endif SOCKET sock = ::WSASocket(addr.ss_family, SOCK_DGRAM, IPPROTO_UDP, nullptr, 0, WSA_FLAG_OVERLAPPED); DWORD dwBytesReturned = 0; BOOL bNewBehaviour = FALSE; if (WSAIoctl(sock, SIO_UDP_CONNRESET, &bNewBehaviour, sizeof(bNewBehaviour), nullptr, 0, &dwBytesReturned, nullptr, nullptr) == SOCKET_ERROR) { log(QString("Failed to set SIO_UDP_CONNRESET: %1").arg(WSAGetLastError())); } #endif if (sock == INVALID_SOCKET) { log("Failed to create UDP Socket"); bValid = false; return; } else { if (addr.ss_family == AF_INET6) { // Copy IPV6_V6ONLY attribute from tcp socket, it defaults to nonzero on Windows // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms738574%28v=vs.85%29.aspx // This will fail for WindowsXP which is ok. Our TCP code will have split that up // into two sockets. int ipv6only = 0; socklen_t optlen = sizeof(ipv6only); if (::getsockopt(tcpsock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast< char * >(&ipv6only), &optlen) == 0) { if (::setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast< const char * >(&ipv6only), optlen) == SOCKET_ERROR) { log(QString("Failed to copy IPV6_V6ONLY socket attribute from tcp to udp socket")); } } } if (::bind(sock, reinterpret_cast< sockaddr * >(&addr), len) == SOCKET_ERROR) { log(QString("Failed to bind UDP Socket to %1").arg(addressToString(ss->serverAddress(), usPort))); } else { #ifdef Q_OS_UNIX int val = 0xe0; if (setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val))) { val = 0x80; if (setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val))) log("Server: Failed to set TOS for UDP Socket"); } # if defined(SO_PRIORITY) socklen_t optlen = sizeof(val); if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, &val, &optlen) == 0) { if (val == 0) { val = 6; setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)); } } # endif #endif } QSocketNotifier *qsn = new QSocketNotifier(sock, QSocketNotifier::Read, this); connect(qsn, SIGNAL(activated(int)), this, SLOT(udpActivated(int))); qlUdpSocket << sock; qlUdpNotifier << qsn; } } bValid = bValid && (qlServer.count() == qlBind.count()) && (qlUdpSocket.count() == qlBind.count()); if (!bValid) return; #ifdef Q_OS_UNIX if (socketpair(AF_UNIX, SOCK_STREAM, 0, aiNotify) != 0) { log("Failed to create notify socket"); bValid = false; return; } #else hNotify = CreateEvent(nullptr, FALSE, FALSE, nullptr); #endif connect(this, SIGNAL(tcpTransmit(QByteArray, unsigned int)), this, SLOT(tcpTransmitData(QByteArray, unsigned int)), Qt::QueuedConnection); connect(this, SIGNAL(reqSync(unsigned int)), this, SLOT(doSync(unsigned int))); for (int i = 1; i < iMaxUsers * 2; ++i) qqIds.enqueue(i); connect(qtTimeout, SIGNAL(timeout()), this, SLOT(checkTimeout())); getBans(); readChannels(); readLinks(); initializeCert(); if (bValid) { #ifdef USE_ZEROCONF if (bBonjour) initZeroconf(); #endif initRegister(); } } void Server::startThread() { if (!isRunning()) { log("Starting voice thread"); bRunning = true; foreach (QSocketNotifier *qsn, qlUdpNotifier) qsn->setEnabled(false); start(QThread::HighestPriority); #ifdef Q_OS_LINUX // QThread::HighestPriority == Same as everything else... int policy; struct sched_param param; if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { if (policy == SCHED_OTHER) { policy = SCHED_FIFO; param.sched_priority = 1; pthread_setschedparam(pthread_self(), policy, ¶m); } } #endif } if (!qtTimeout->isActive()) qtTimeout->start(15500); } void Server::stopThread() { bRunning = false; if (isRunning()) { log("Ending voice thread"); #ifdef Q_OS_UNIX unsigned char val = 0; if (::write(aiNotify[1], &val, 1) != 1) log("Failed to signal voice thread"); #else SetEvent(hNotify); #endif wait(); foreach (QSocketNotifier *qsn, qlUdpNotifier) qsn->setEnabled(true); } qtTimeout->stop(); } Server::~Server() { #ifdef USE_ZEROCONF removeZeroconf(); #endif stopThread(); foreach (QSocketNotifier *qsn, qlUdpNotifier) delete qsn; #ifdef Q_OS_UNIX foreach (int s, qlUdpSocket) close(s); if (aiNotify[0] >= 0) close(aiNotify[0]); if (aiNotify[1] >= 0) close(aiNotify[1]); #else foreach (SOCKET s, qlUdpSocket) closesocket(s); if (hNotify) CloseHandle(hNotify); #endif clearACLCache(); log("Stopped"); } void Server::readParams() { qsPassword = Meta::mp.qsPassword; usPort = static_cast< unsigned short >(Meta::mp.usPort + iServerNum - 1); iTimeout = Meta::mp.iTimeout; iMaxBandwidth = Meta::mp.iMaxBandwidth; iMaxUsers = Meta::mp.iMaxUsers; iMaxUsersPerChannel = Meta::mp.iMaxUsersPerChannel; iMaxTextMessageLength = Meta::mp.iMaxTextMessageLength; iMaxImageMessageLength = Meta::mp.iMaxImageMessageLength; bAllowHTML = Meta::mp.bAllowHTML; iDefaultChan = Meta::mp.iDefaultChan; bRememberChan = Meta::mp.bRememberChan; iRememberChanDuration = Meta::mp.iRememberChanDuration; qsWelcomeText = Meta::mp.qsWelcomeText; qsWelcomeTextFile = Meta::mp.qsWelcomeTextFile; qlBind = Meta::mp.qlBind; qsRegName = Meta::mp.qsRegName; qsRegPassword = Meta::mp.qsRegPassword; qsRegHost = Meta::mp.qsRegHost; qsRegLocation = Meta::mp.qsRegLocation; qurlRegWeb = Meta::mp.qurlRegWeb; bBonjour = Meta::mp.bBonjour; bAllowPing = Meta::mp.bAllowPing; allowRecording = Meta::mp.allowRecording; bCertRequired = Meta::mp.bCertRequired; bForceExternalAuth = Meta::mp.bForceExternalAuth; qrUserName = Meta::mp.qrUserName; qrChannelName = Meta::mp.qrChannelName; iMessageLimit = Meta::mp.iMessageLimit; iMessageBurst = Meta::mp.iMessageBurst; iPluginMessageLimit = Meta::mp.iPluginMessageLimit; iPluginMessageBurst = Meta::mp.iPluginMessageBurst; broadcastListenerVolumeAdjustments = Meta::mp.broadcastListenerVolumeAdjustments; m_suggestVersion = Meta::mp.m_suggestVersion; qvSuggestPositional = Meta::mp.qvSuggestPositional; qvSuggestPushToTalk = Meta::mp.qvSuggestPushToTalk; iOpusThreshold = Meta::mp.iOpusThreshold; iChannelNestingLimit = Meta::mp.iChannelNestingLimit; iChannelCountLimit = Meta::mp.iChannelCountLimit; QString qsHost = getConf("host", QString()).toString(); if (!qsHost.isEmpty()) { qlBind.clear(); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) foreach (const QString &host, qsHost.split(QRegExp(QLatin1String("\\s+")), Qt::SkipEmptyParts)) { #else // Qt 5.14 introduced the Qt::SplitBehavior flags deprecating the QString fields foreach (const QString &host, qsHost.split(QRegExp(QLatin1String("\\s+")), QString::SkipEmptyParts)) { #endif QHostAddress qhaddr; if (qhaddr.setAddress(qsHost)) { 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) { log(QString("Lookup of bind hostname %1 failed").arg(host)); } } } foreach (const QHostAddress &qha, qlBind) log(QString("Binding to address %1").arg(qha.toString())); if (qlBind.isEmpty()) qlBind = Meta::mp.qlBind; } qsPassword = getConf("password", qsPassword).toString(); usPort = static_cast< unsigned short >(getConf("port", usPort).toUInt()); iTimeout = getConf("timeout", iTimeout).toInt(); iMaxBandwidth = getConf("bandwidth", iMaxBandwidth).toInt(); iMaxUsers = getConf("users", iMaxUsers).toInt(); iMaxUsersPerChannel = getConf("usersperchannel", iMaxUsersPerChannel).toInt(); iMaxTextMessageLength = getConf("textmessagelength", iMaxTextMessageLength).toInt(); iMaxImageMessageLength = getConf("imagemessagelength", iMaxImageMessageLength).toInt(); bAllowHTML = getConf("allowhtml", bAllowHTML).toBool(); iDefaultChan = getConf("defaultchannel", iDefaultChan).toInt(); bRememberChan = getConf("rememberchannel", bRememberChan).toBool(); iRememberChanDuration = getConf("rememberchannelduration", iRememberChanDuration).toInt(); qsWelcomeText = getConf("welcometext", qsWelcomeText).toString(); qsWelcomeTextFile = getConf("welcometextfile", qsWelcomeTextFile).toString(); if (!qsWelcomeTextFile.isEmpty()) { if (qsWelcomeText.isEmpty()) { QFile f(qsWelcomeTextFile); if (f.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&f); qsWelcomeText = in.readAll(); f.close(); } else { log(QString("Failed to open welcome text file %1").arg(qsWelcomeTextFile)); } } else { log(QString("Ignoring welcometextfile %1 because welcometext is defined").arg(qsWelcomeTextFile)); } } qsRegName = getConf("registername", qsRegName).toString(); qsRegPassword = getConf("registerpassword", qsRegPassword).toString(); qsRegHost = getConf("registerhostname", qsRegHost).toString(); qsRegLocation = getConf("registerlocation", qsRegLocation).toString(); qurlRegWeb = QUrl(getConf("registerurl", qurlRegWeb.toString()).toString()); bBonjour = getConf("bonjour", bBonjour).toBool(); bAllowPing = getConf("allowping", bAllowPing).toBool(); bCertRequired = getConf("certrequired", bCertRequired).toBool(); bForceExternalAuth = getConf("forceExternalAuth", bForceExternalAuth).toBool(); m_suggestVersion = Version::fromString(getConf("suggestversion", Version::toConfigString(m_suggestVersion)).toString()); qvSuggestPositional = getConf("suggestpositional", qvSuggestPositional); if (qvSuggestPositional.toString().trimmed().isEmpty()) qvSuggestPositional = QVariant(); qvSuggestPushToTalk = getConf("suggestpushtotalk", qvSuggestPushToTalk); if (qvSuggestPushToTalk.toString().trimmed().isEmpty()) qvSuggestPushToTalk = QVariant(); iOpusThreshold = getConf("opusthreshold", iOpusThreshold).toInt(); iChannelNestingLimit = getConf("channelnestinglimit", iChannelNestingLimit).toInt(); iChannelCountLimit = getConf("channelcountlimit", iChannelCountLimit).toInt(); qrUserName = QRegExp(getConf("username", qrUserName.pattern()).toString()); qrChannelName = QRegExp(getConf("channelname", qrChannelName.pattern()).toString()); iMessageLimit = getConf("messagelimit", iMessageLimit).toUInt(); if (iMessageLimit < 1) { // Prevent disabling messages entirely iMessageLimit = 1; } iMessageBurst = getConf("messageburst", iMessageBurst).toUInt(); if (iMessageBurst < 1) { // Prevent disabling messages entirely iMessageBurst = 1; } iPluginMessageLimit = getConf("mpluginessagelimit", iPluginMessageLimit).toUInt(); if (iPluginMessageLimit < 1) { // Prevent disabling messages entirely iPluginMessageLimit = 1; } iPluginMessageBurst = getConf("pluginmessageburst", iPluginMessageBurst).toUInt(); if (iPluginMessageBurst < 1) { // Prevent disabling messages entirely iPluginMessageBurst = 1; } broadcastListenerVolumeAdjustments = getConf("broadcastlistenervolumeadjustments", broadcastListenerVolumeAdjustments).toBool(); } void Server::setLiveConf(const QString &key, const QString &value) { QString v = value.trimmed().isEmpty() ? QString() : value; int i = v.toInt(); if ((key == "password") || (key == "serverpassword")) qsPassword = !v.isNull() ? v : Meta::mp.qsPassword; else if (key == "timeout") iTimeout = i ? i : Meta::mp.iTimeout; else if (key == "bandwidth") { int length = i ? i : Meta::mp.iMaxBandwidth; if (length != iMaxBandwidth) { iMaxBandwidth = length; MumbleProto::ServerConfig mpsc; mpsc.set_max_bandwidth(length); sendAll(mpsc); } } else if (key == "users") { int newmax = i ? i : Meta::mp.iMaxUsers; if (iMaxUsers == newmax) return; iMaxUsers = newmax; qqIds.clear(); for (int id = 1; id < iMaxUsers * 2; ++id) if (!qhUsers.contains(id)) qqIds.enqueue(id); MumbleProto::ServerConfig mpsc; mpsc.set_max_users(iMaxUsers); sendAll(mpsc); } else if (key == "usersperchannel") iMaxUsersPerChannel = i ? i : Meta::mp.iMaxUsersPerChannel; else if (key == "textmessagelength") { int length = i ? i : Meta::mp.iMaxTextMessageLength; if (length != iMaxTextMessageLength) { iMaxTextMessageLength = length; MumbleProto::ServerConfig mpsc; mpsc.set_message_length(length); sendAll(mpsc); } } else if (key == "imagemessagelength") { int length = i ? i : Meta::mp.iMaxImageMessageLength; if (length != iMaxImageMessageLength) { iMaxImageMessageLength = length; MumbleProto::ServerConfig mpsc; mpsc.set_image_message_length(length); sendAll(mpsc); } } else if (key == "allowhtml") { bool allow = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bAllowHTML; if (allow != bAllowHTML) { bAllowHTML = allow; MumbleProto::ServerConfig mpsc; mpsc.set_allow_html(bAllowHTML); sendAll(mpsc); } } else if (key == "defaultchannel") iDefaultChan = i ? i : Meta::mp.iDefaultChan; else if (key == "rememberchannel") bRememberChan = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bRememberChan; else if (key == "rememberchannelduration") { iRememberChanDuration = !v.isNull() ? v.toInt() : Meta::mp.iRememberChanDuration; if (iRememberChanDuration < 0) { iRememberChanDuration = 0; } } else if (key == "welcometext") { QString text = !v.isNull() ? v : Meta::mp.qsWelcomeText; if (text != qsWelcomeText) { qsWelcomeText = text; } } else if (key == "registername") { QString text = !v.isNull() ? v : Meta::mp.qsRegName; if (text != qsRegName) { qsRegName = text; if (!qsRegName.isEmpty()) { MumbleProto::ChannelState mpcs; mpcs.set_channel_id(0); mpcs.set_name(u8(qsRegName)); sendAll(mpcs); } } } else if (key == "registerpassword") qsRegPassword = !v.isNull() ? v : Meta::mp.qsRegPassword; else if (key == "registerhostname") qsRegHost = !v.isNull() ? v : Meta::mp.qsRegHost; else if (key == "registerlocation") qsRegLocation = !v.isNull() ? v : Meta::mp.qsRegLocation; else if (key == "registerurl") qurlRegWeb = !v.isNull() ? v : Meta::mp.qurlRegWeb; else if (key == "certrequired") bCertRequired = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bCertRequired; else if (key == "forceExternalAuth") bForceExternalAuth = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bForceExternalAuth; else if (key == "bonjour") { bBonjour = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bBonjour; #ifdef USE_ZEROCONF if (bBonjour && !zeroconf) { initZeroconf(); } else if (!bBonjour && zeroconf) { removeZeroconf(); } #endif } else if (key == "allowping") bAllowPing = !v.isNull() ? QVariant(v).toBool() : Meta::mp.bAllowPing; else if (key == "allowrecording") allowRecording = !v.isNull() ? QVariant(v).toBool() : Meta::mp.allowRecording; else if (key == "username") qrUserName = !v.isNull() ? QRegExp(v) : Meta::mp.qrUserName; else if (key == "channelname") qrChannelName = !v.isNull() ? QRegExp(v) : Meta::mp.qrChannelName; else if (key == "suggestversion") m_suggestVersion = !v.isNull() ? Version::fromConfig(v) : Meta::mp.m_suggestVersion; else if (key == "suggestpositional") qvSuggestPositional = !v.isNull() ? (v.isEmpty() ? QVariant() : v) : Meta::mp.qvSuggestPositional; else if (key == "suggestpushtotalk") qvSuggestPushToTalk = !v.isNull() ? (v.isEmpty() ? QVariant() : v) : Meta::mp.qvSuggestPushToTalk; else if (key == "opusthreshold") iOpusThreshold = (i >= 0 && !v.isNull()) ? qBound(0, i, 100) : Meta::mp.iOpusThreshold; else if (key == "channelnestinglimit") iChannelNestingLimit = (i >= 0 && !v.isNull()) ? i : Meta::mp.iChannelNestingLimit; else if (key == "channelcountlimit") iChannelCountLimit = (i >= 0 && !v.isNull()) ? i : Meta::mp.iChannelCountLimit; else if (key == "messagelimit") { iMessageLimit = (!v.isNull()) ? v.toUInt() : Meta::mp.iMessageLimit; if (iMessageLimit < 1) { iMessageLimit = 1; } } else if (key == "messageburst") { iMessageBurst = (!v.isNull()) ? v.toUInt() : Meta::mp.iMessageBurst; if (iMessageBurst < 1) { iMessageBurst = 1; } } else if (key == "broadcastlistenervolumeadjustments") { broadcastListenerVolumeAdjustments = (!v.isNull() ? QVariant(v).toBool() : Meta::mp.broadcastListenerVolumeAdjustments); } } #ifdef USE_ZEROCONF void Server::initZeroconf() { zeroconf = new Zeroconf(); if (zeroconf->isOk()) { log("Registering zeroconf service..."); zeroconf->registerService(BonjourRecord(qsRegName, "_mumble._tcp", ""), usPort); return; } delete zeroconf; zeroconf = nullptr; } void Server::removeZeroconf() { if (!zeroconf) { return; } if (zeroconf->isOk()) { log("Unregistering zeroconf service..."); } delete zeroconf; zeroconf = nullptr; } #endif gsl::span< const Mumble::Protocol::byte > Server::handlePing(const Mumble::Protocol::UDPDecoder< Mumble::Protocol::Role::Server > &decoder, Mumble::Protocol::UDPPingEncoder< Mumble::Protocol::Role::Server > &encoder, bool expectExtended) { Mumble::Protocol::PingData pingData = decoder.getPingData(); if (pingData.requestAdditionalInformation) { pingData.requestAdditionalInformation = false; pingData.serverVersion = Version::get(); pingData.userCount = qhUsers.size() - m_botCount; pingData.maxUserCount = iMaxUsers; pingData.maxBandwidthPerUser = iMaxBandwidth; pingData.containsAdditionalInformation = true; } else if (expectExtended) { // Return zero-length span return {}; } // Encode in the same protocol version that we decoded with encoder.setProtocolVersion(decoder.getProtocolVersion()); return encoder.encodePingPacket(pingData); } void Server::customEvent(QEvent *evt) { if (evt->type() == EXEC_QEVENT) static_cast< ExecEvent * >(evt)->execute(); } void Server::udpActivated(int socket) { // At this part we are only expecting pings of clients we don't know yet -> thus we also don't know which protocol // version they are using. m_udpDecoder.setProtocolVersion(Version::UNKNOWN); qint32 len; sockaddr_storage from; #ifdef Q_OS_UNIX # ifdef Q_OS_LINUX struct msghdr msg; struct iovec iov[1]; iov[0].iov_base = m_udpDecoder.getBuffer().data(); iov[0].iov_len = m_udpDecoder.getBuffer().size(); uint8_t controldata[CMSG_SPACE(std::max(sizeof(struct in6_pktinfo), sizeof(struct in_pktinfo)))]; memset(&msg, 0, sizeof(msg)); msg.msg_name = reinterpret_cast< struct sockaddr * >(&from); msg.msg_namelen = sizeof(from); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = controldata; msg.msg_controllen = sizeof(controldata); int &sock = socket; len = static_cast< quint32 >(::recvmsg(sock, &msg, MSG_TRUNC)); # else socklen_t fromlen = sizeof(from); int &sock = socket; len = static_cast< qint32 >(::recvfrom(sock, m_udpDecoder.getBuffer().data(), m_udpDecoder.getBuffer().size(), MSG_TRUNC, reinterpret_cast< struct sockaddr * >(&from), &fromlen)); # endif #else int fromlen = sizeof(from); SOCKET sock = static_cast< SOCKET >(socket); len = ::recvfrom(sock, reinterpret_cast< char * >(m_udpDecoder.getBuffer().data()), m_udpDecoder.getBuffer().size(), 0, reinterpret_cast< struct sockaddr * >(&from), &fromlen); #endif gsl::span< Mumble::Protocol::byte > inputData(&m_udpDecoder.getBuffer()[0], len); if (bAllowPing && m_udpDecoder.decodePing(inputData) && m_udpDecoder.getMessageType() == Mumble::Protocol::UDPMessageType::Ping) { gsl::span< const Mumble::Protocol::byte > encodedPing = handlePing(m_udpDecoder, m_udpPingEncoder, true); if (!encodedPing.empty()) { #ifdef Q_OS_LINUX // There will be space for only one header, and the only data we have asked for is the incoming // address. So we can reuse most of the same msg and control data. iov[0].iov_len = encodedPing.size(); iov[0].iov_base = const_cast< Mumble::Protocol::byte * >(encodedPing.data()); ::sendmsg(sock, &msg, 0); #else ::sendto(sock, reinterpret_cast< const char * >(encodedPing.data()), encodedPing.size(), 0, reinterpret_cast< struct sockaddr * >(&from), fromlen); #endif } } } void Server::run() { tracy::SetThreadName("Audio"); qint32 len; #if defined(__LP64__) unsigned char encbuff[Mumble::Protocol::MAX_UDP_PACKET_SIZE + 8]; unsigned char *encrypt = encbuff + 4; #else unsigned char encrypt[Mumble::Protocol::MAX_UDP_PACKET_SIZE]; #endif unsigned char buffer[Mumble::Protocol::MAX_UDP_PACKET_SIZE]; sockaddr_storage from; int nfds = qlUdpSocket.count(); #ifdef Q_OS_UNIX socklen_t fromlen; STACKVAR(struct pollfd, fds, nfds + 1); for (int i = 0; i < nfds; ++i) { fds[i].fd = qlUdpSocket.at(i); fds[i].events = POLLIN; fds[i].revents = 0; } fds[nfds].fd = aiNotify[0]; fds[nfds].events = POLLIN; fds[nfds].revents = 0; #else int fromlen; STACKVAR(SOCKET, fds, nfds); STACKVAR(HANDLE, events, nfds + 1); for (int i = 0; i < nfds; ++i) { fds[i] = qlUdpSocket.at(i); events[i] = CreateEvent(nullptr, FALSE, FALSE, nullptr); ::WSAEventSelect(fds[i], events[i], FD_READ); } events[nfds] = hNotify; #endif ++nfds; while (bRunning) { FrameMarkNamed(TracyConstants::UDP_FRAME); #ifdef Q_OS_UNIX int pret = poll(fds, nfds, -1); if (pret <= 0) { if (errno == EINTR) continue; qCritical("poll failure"); bRunning = false; break; } if (fds[nfds - 1].revents) { // Drain pipe unsigned char val; while (::recv(aiNotify[0], &val, 1, MSG_DONTWAIT) == 1) { }; break; } for (int i = 0; i < nfds - 1; ++i) { if (fds[i].revents) { if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) { qCritical("poll event failure"); bRunning = false; break; } int sock = fds[i].fd; #else for (int i = 0; i < 1; ++i) { { DWORD ret = WaitForMultipleObjects(nfds, events, FALSE, INFINITE); if (ret == (WAIT_OBJECT_0 + nfds - 1)) { break; } if (ret == WAIT_FAILED) { qCritical("UDP wait failed"); bRunning = false; break; } SOCKET sock = fds[ret - WAIT_OBJECT_0]; #endif fromlen = sizeof(from); #ifdef Q_OS_WIN len = ::recvfrom(sock, reinterpret_cast< char * >(encrypt), Mumble::Protocol::MAX_UDP_PACKET_SIZE, 0, reinterpret_cast< struct sockaddr * >(&from), &fromlen); #else # ifdef Q_OS_LINUX struct msghdr msg; struct iovec iov[1]; iov[0].iov_base = encrypt; iov[0].iov_len = Mumble::Protocol::MAX_UDP_PACKET_SIZE; uint8_t controldata[CMSG_SPACE(std::max(sizeof(struct in6_pktinfo), sizeof(struct in_pktinfo)))]; memset(&msg, 0, sizeof(msg)); msg.msg_name = reinterpret_cast< struct sockaddr * >(&from); msg.msg_namelen = sizeof(from); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = controldata; msg.msg_controllen = sizeof(controldata); len = static_cast< quint32 >(::recvmsg(sock, &msg, MSG_TRUNC)); Q_UNUSED(fromlen); # else len = static_cast< qint32 >(::recvfrom(sock, encrypt, Mumble::Protocol::MAX_UDP_PACKET_SIZE, MSG_TRUNC, reinterpret_cast< struct sockaddr * >(&from), &fromlen)); # endif #endif // Capture only the processing without the polling ZoneScopedN(TracyConstants::UDP_PACKET_PROCESSING_ZONE); if (len == 0) { break; } else if (len == SOCKET_ERROR) { break; } else if (len < 5) { // 4 bytes crypt header + type + session continue; } else if (static_cast< unsigned int >(len) > Mumble::Protocol::MAX_UDP_PACKET_SIZE) { // This will also catch the len == -1 case (indicating error) static_assert(static_cast< unsigned int >(-1) > Mumble::Protocol::MAX_UDP_PACKET_SIZE, "Invalid assumption"); continue; } QReadLocker rl(&qrwlVoiceThread); quint16 port = (from.ss_family == AF_INET6) ? (reinterpret_cast< sockaddr_in6 * >(&from)->sin6_port) : (reinterpret_cast< sockaddr_in * >(&from)->sin_port); const HostAddress &ha = HostAddress(from); const QPair< HostAddress, quint16 > &key = QPair< HostAddress, quint16 >(ha, port); ServerUser *u = qhPeerUsers.value(key); if (u) { m_udpDecoder.setProtocolVersion(u->m_version); } else { m_udpDecoder.setProtocolVersion(Version::UNKNOWN); } // This may be a general ping requesting server details, unencrypted. if (bAllowPing && m_udpDecoder.decodePing(gsl::span< Mumble::Protocol::byte >(encrypt, len)) && m_udpDecoder.getMessageType() == Mumble::Protocol::UDPMessageType::Ping) { ZoneScopedN(TracyConstants::PING_PROCESSING_ZONE); gsl::span< const Mumble::Protocol::byte > encodedPing = handlePing(m_udpDecoder, m_udpPingEncoder, true); if (!encodedPing.empty()) { #ifdef Q_OS_LINUX // We are only reading from the buffer and thus the const_cast should be fine iov[0].iov_base = const_cast< Mumble::Protocol::byte * >(encodedPing.data()); iov[0].iov_len = encodedPing.size(); ::sendmsg(sock, &msg, 0); #else ::sendto(sock, reinterpret_cast< const char * >(encodedPing.data()), encodedPing.size(), 0, reinterpret_cast< struct sockaddr * >(&from), fromlen); #endif } continue; } if (u) { if (!checkDecrypt(u, encrypt, buffer, len)) { continue; } } else { ZoneScopedN(TracyConstants::DECRYPT_UNKNOWN_PEER_ZONE); // Unknown peer foreach (ServerUser *usr, qhHostUsers.value(ha)) { if (checkDecrypt(usr, encrypt, buffer, len)) { // checkDecrypt takes the User's qrwlCrypt lock. // Every time we relock, reverify users' existence. // The main thread might delete the user while the lock isn't held. unsigned int uiSession = usr->uiSession; rl.unlock(); qrwlVoiceThread.lockForWrite(); if (qhUsers.contains(uiSession)) { u = usr; u->sUdpSocket = sock; memcpy(&u->saiUdpAddress, &from, sizeof(from)); qhHostUsers[from].remove(u); qhPeerUsers.insert(key, u); } qrwlVoiceThread.unlock(); rl.relock(); if (u && !qhUsers.contains(uiSession)) u = nullptr; break; } } if (!u) { continue; } } len -= 4; if (m_udpDecoder.decode(gsl::span< Mumble::Protocol::byte >(buffer, len))) { switch (m_udpDecoder.getMessageType()) { case Mumble::Protocol::UDPMessageType::Audio: { Mumble::Protocol::AudioData audioData = m_udpDecoder.getAudioData(); // Allow all voice packets through by default. bool ok = true; // ...Unless we're in Opus mode. In Opus mode, only Opus packets are allowed. if (bOpus && audioData.usedCodec != Mumble::Protocol::AudioCodec::Opus) { ok = false; } if (ok) { u->aiUdpFlag = 1; // Add session id audioData.senderSession = u->uiSession; processMsg(u, audioData, m_udpAudioReceivers, m_udpAudioEncoder); } break; } case Mumble::Protocol::UDPMessageType::Ping: { ZoneScopedN(TracyConstants::UDP_PING_PROCESSING_ZONE); Mumble::Protocol::PingData pingData = m_udpDecoder.getPingData(); if (!pingData.requestAdditionalInformation && !pingData.containsAdditionalInformation) { // At this point here, we only want to handle connectivity pings gsl::span< const Mumble::Protocol::byte > encodedPing = handlePing(m_udpDecoder, m_udpPingEncoder, false); QByteArray cache; sendMessage(*u, encodedPing.data(), encodedPing.size(), cache, true); } break; } } } #ifdef Q_OS_UNIX fds[i].revents = 0; #endif } } } #ifdef Q_OS_WIN for (int i = 0; i < nfds - 1; ++i) { ::WSAEventSelect(fds[i], nullptr, 0); CloseHandle(events[i]); } #endif } bool Server::checkDecrypt(ServerUser *u, const unsigned char *encrypt, unsigned char *plain, unsigned int len) { ZoneScoped; QMutexLocker l(&u->qmCrypt); if (u->csCrypt->isValid() && u->csCrypt->decrypt(encrypt, plain, len)) { return true; } if (u->csCrypt->tLastGood.elapsed() > 5000000ULL) { if (u->csCrypt->tLastRequest.elapsed() > 5000000ULL) { u->csCrypt->tLastRequest.restart(); emit reqSync(u->uiSession); } } return false; } void Server::sendMessage(ServerUser &u, const unsigned char *data, int len, QByteArray &cache, bool force) { ZoneScoped; #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) if ((u.aiUdpFlag.loadRelaxed() == 1 || force) && (u.sUdpSocket != INVALID_SOCKET)) { #else // Qt 5.14 introduced QAtomicInteger::loadRelaxed() which deprecates QAtomicInteger::load() if ((u.aiUdpFlag.load() == 1 || force) && (u.sUdpSocket != INVALID_SOCKET)) { #endif #if defined(__LP64__) STACKVAR(char, ebuffer, len + 4 + 16); char *buffer = reinterpret_cast< char * >(((reinterpret_cast< quint64 >(ebuffer) + 8) & ~7) + 4); #else STACKVAR(char, buffer, len + 4); #endif { QMutexLocker wl(&u.qmCrypt); if (!u.csCrypt->isValid()) { return; } if (!u.csCrypt->encrypt(reinterpret_cast< const unsigned char * >(data), reinterpret_cast< unsigned char * >(buffer), len)) { return; } } #ifdef Q_OS_WIN DWORD dwFlow = 0; if (Meta::hQoS) QOSAddSocketToFlow(Meta::hQoS, u.sUdpSocket, reinterpret_cast< struct sockaddr * >(&u.saiUdpAddress), QOSTrafficTypeVoice, QOS_NON_ADAPTIVE_FLOW, reinterpret_cast< PQOS_FLOWID >(&dwFlow)); #endif #ifdef Q_OS_LINUX struct msghdr msg; struct iovec iov[1]; iov[0].iov_base = buffer; iov[0].iov_len = len + 4; uint8_t controldata[CMSG_SPACE(std::max(sizeof(struct in6_pktinfo), sizeof(struct in_pktinfo)))]; memset(controldata, 0, sizeof(controldata)); memset(&msg, 0, sizeof(msg)); msg.msg_name = reinterpret_cast< struct sockaddr * >(&u.saiUdpAddress); msg.msg_namelen = static_cast< socklen_t >( (u.saiUdpAddress.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = controldata; msg.msg_controllen = CMSG_SPACE((u.saiUdpAddress.ss_family == AF_INET6) ? sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo)); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); HostAddress tcpha(u.saiTcpLocalAddress); if (u.saiUdpAddress.ss_family == AF_INET6) { cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); struct in6_pktinfo *pktinfo = reinterpret_cast< struct in6_pktinfo * >(CMSG_DATA(cmsg)); memset(pktinfo, 0, sizeof(*pktinfo)); memcpy(&pktinfo->ipi6_addr.s6_addr[0], &tcpha.qip6.c[0], sizeof(pktinfo->ipi6_addr.s6_addr)); } else { cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); struct in_pktinfo *pktinfo = reinterpret_cast< struct in_pktinfo * >(CMSG_DATA(cmsg)); memset(pktinfo, 0, sizeof(*pktinfo)); if (tcpha.isV6()) return; pktinfo->ipi_spec_dst.s_addr = tcpha.hash[3]; } ::sendmsg(u.sUdpSocket, &msg, 0); #else ::sendto(u.sUdpSocket, buffer, len + 4, 0, reinterpret_cast< struct sockaddr * >(&u.saiUdpAddress), (u.saiUdpAddress.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); #endif #ifdef Q_OS_WIN if (Meta::hQoS && dwFlow) QOSRemoveSocketFromFlow(Meta::hQoS, 0, dwFlow, 0); #else #endif } else { if (cache.isEmpty()) cache = QByteArray(reinterpret_cast< const char * >(data), len); emit tcpTransmit(cache, u.uiSession); } } void Server::addListener(QHash< ServerUser *, VolumeAdjustment > &listeners, ServerUser &user, const Channel &channel) { const VolumeAdjustment &volumeAdjustment = m_channelListenerManager.getListenerVolumeAdjustment(user.uiSession, channel.iId); auto it = listeners.find(&user); if (it == listeners.end() || it->factor < volumeAdjustment.factor) { listeners[&user] = volumeAdjustment; } } void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, AudioReceiverBuffer &buffer, Mumble::Protocol::UDPAudioEncoder< Mumble::Protocol::Role::Server > &encoder) { ZoneScoped; // Note that in this function we never have to acquire a read-lock on qrwlVoiceThread // as all places that call this function will hold that lock at the point of calling // this function. // This function is currently called from Server::msgUDPTunnel, Server::run and // Server::message if (u->sState != ServerUser::Authenticated || u->bMute || u->bSuppress || u->bSelfMute) return; // Check the voice data rate limit. { BandwidthRecord *bw = &u->bwr; // IP + UDP + Crypt + Data const int packetsize = 20 + 8 + 4 + audioData.payload.size(); if (!bw->addFrame(packetsize, iMaxBandwidth / 8)) { // Suppress packet. return; } } buffer.clear(); if (audioData.targetOrContext == Mumble::Protocol::ReservedTargetIDs::SERVER_LOOPBACK) { buffer.forceAddReceiver(*u, Mumble::Protocol::AudioContext::NORMAL, audioData.containsPositionalData); } else if (audioData.targetOrContext == Mumble::Protocol::ReservedTargetIDs::REGULAR_SPEECH) { Channel *c = u->cChannel; // Send audio to all users that are listening to the channel foreach (unsigned int currentSession, m_channelListenerManager.getListenersForChannel(c->iId)) { ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession)); if (pDst) { buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData, m_channelListenerManager.getListenerVolumeAdjustment(pDst->uiSession, c->iId)); } } // Send audio to all users in the same channel for (User *p : c->qlUsers) { ServerUser *pDst = static_cast< ServerUser * >(p); buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::NORMAL, audioData.containsPositionalData); } // Send audio to all linked channels the user has speak-permission if (!c->qhLinks.isEmpty()) { QSet< Channel * > chans = c->allLinks(); chans.remove(c); QMutexLocker qml(&qmCache); for (Channel *l : chans) { if (ChanACL::hasPermission(u, l, ChanACL::Speak, &acCache)) { // Send the audio stream to all users that are listening to the linked channel for (unsigned int currentSession : m_channelListenerManager.getListenersForChannel(l->iId)) { ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession)); if (pDst) { buffer.addReceiver( *u, *pDst, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData, m_channelListenerManager.getListenerVolumeAdjustment(pDst->uiSession, l->iId)); } } // Send audio to users in the linked channel for (User *p : l->qlUsers) { ServerUser *pDst = static_cast< ServerUser * >(p); buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::NORMAL, audioData.containsPositionalData); } } } } } else if (u->qmTargets.contains(audioData.targetOrContext)) { // Whisper/Shout QSet< ServerUser * > channel; QSet< ServerUser * > direct; QHash< ServerUser *, VolumeAdjustment > cachedListeners; if (u->qmTargetCache.contains(audioData.targetOrContext)) { ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_STORE); const WhisperTargetCache &cache = u->qmTargetCache.value(audioData.targetOrContext); channel = cache.channelTargets; direct = cache.directTargets; cachedListeners = cache.listeningTargets; } else { ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_CREATE); const WhisperTarget &wt = u->qmTargets.value(audioData.targetOrContext); if (!wt.qlChannels.isEmpty()) { QMutexLocker qml(&qmCache); foreach (const WhisperTarget::Channel &wtc, wt.qlChannels) { Channel *wc = qhChannels.value(wtc.iId); if (wc) { bool link = wtc.bLinks && !wc->qhLinks.isEmpty(); bool dochildren = wtc.bChildren && !wc->qlChannels.isEmpty(); bool group = !wtc.qsGroup.isEmpty(); if (!link && !dochildren && !group) { // Common case if (ChanACL::hasPermission(u, wc, ChanACL::Whisper, &acCache)) { foreach (User *p, wc->qlUsers) { channel.insert(static_cast< ServerUser * >(p)); } foreach (unsigned int currentSession, m_channelListenerManager.getListenersForChannel(wc->iId)) { ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession)); if (pDst) { addListener(cachedListeners, *pDst, *wc); } } } } else { QSet< Channel * > channels; if (link) channels = wc->allLinks(); else channels.insert(wc); if (dochildren) channels.unite(wc->allChildren()); const QString &redirect = u->qmWhisperRedirect.value(wtc.qsGroup); const QString &qsg = redirect.isEmpty() ? wtc.qsGroup : redirect; foreach (Channel *tc, channels) { if (ChanACL::hasPermission(u, tc, ChanACL::Whisper, &acCache)) { foreach (User *p, tc->qlUsers) { ServerUser *su = static_cast< ServerUser * >(p); if (!group || Group::appliesToUser(*tc, *tc, qsg, *su)) { channel.insert(su); } } foreach (unsigned int currentSession, m_channelListenerManager.getListenersForChannel(tc->iId)) { ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession)); if (pDst && (!group || Group::appliesToUser(*tc, *tc, qsg, *pDst))) { // Only send audio to listener if the user exists and it is in the group the // speech is directed at (if any) addListener(cachedListeners, *pDst, *tc); } } } } } } } } { QMutexLocker qml(&qmCache); foreach (unsigned int id, wt.qlSessions) { ServerUser *pDst = qhUsers.value(id); if (pDst && ChanACL::hasPermission(u, pDst->cChannel, ChanACL::Whisper, &acCache) && !channel.contains(pDst)) direct.insert(pDst); } } int uiSession = u->uiSession; qrwlVoiceThread.unlock(); qrwlVoiceThread.lockForWrite(); if (qhUsers.contains(uiSession)) u->qmTargetCache.insert(audioData.targetOrContext, { channel, direct, cachedListeners }); qrwlVoiceThread.unlock(); qrwlVoiceThread.lockForRead(); if (!qhUsers.contains(uiSession)) return; } // These users receive the audio because someone is shouting to their channel for (ServerUser *pDst : channel) { buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::SHOUT, audioData.containsPositionalData); } // These users receive audio because someone is whispering to them for (ServerUser *pDst : direct) { buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::WHISPER, audioData.containsPositionalData); } // These users receive audio because someone is sending audio to one of their listeners QHashIterator< ServerUser *, VolumeAdjustment > it(cachedListeners); while (it.hasNext()) { it.next(); ServerUser *user = it.key(); const VolumeAdjustment &volumeAdjustment = it.value(); buffer.addReceiver(*u, *user, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData, volumeAdjustment); } } ZoneNamedN(__tracy_scoped_zone2, TracyConstants::AUDIO_SENDOUT_ZONE, true); buffer.preprocessBuffer(); bool isFirstIteration = true; QByteArray tcpCache; for (bool includePositionalData : { true, false }) { std::vector< AudioReceiver > &receiverList = buffer.getReceivers(includePositionalData); audioData.containsPositionalData = includePositionalData && audioData.containsPositionalData; if (!audioData.containsPositionalData) { encoder.dropPositionalData(); } // Note: The receiver-ranges are determined in such a way, that they are all going to receive the exact // same audio packet. ReceiverRange< std::vector< AudioReceiver >::iterator > currentRange = AudioReceiverBuffer::getReceiverRange(receiverList.begin(), receiverList.end()); while (currentRange.begin != currentRange.end) { // Setup encoder for this range if (isFirstIteration || !Mumble::Protocol::protocolVersionsAreCompatible(encoder.getProtocolVersion(), currentRange.begin->getReceiver().m_version)) { ZoneScopedN(TracyConstants::AUDIO_ENCODE); encoder.setProtocolVersion(currentRange.begin->getReceiver().m_version); // We have to re-encode the "fixed" part of the audio message encoder.prepareAudioPacket(audioData); if (audioData.containsPositionalData) { encoder.addPositionalData(audioData); } isFirstIteration = false; } audioData.targetOrContext = currentRange.begin->getContext(); audioData.volumeAdjustment = currentRange.begin->getVolumeAdjustment(); // Update data TracyCZoneN(__tracy_zone, TracyConstants::AUDIO_UPDATE, true); gsl::span< const Mumble::Protocol::byte > encodedPacket = encoder.updateAudioPacket(audioData); TracyCZoneEnd(__tracy_zone); // Clear TCP cache tcpCache.clear(); // Send encoded packet to all receivers of this range for (auto it = currentRange.begin; it != currentRange.end; ++it) { sendMessage(it->getReceiver(), encodedPacket.data(), encodedPacket.size(), tcpCache); } // Find next range currentRange = AudioReceiverBuffer::getReceiverRange(currentRange.end, receiverList.end()); } } } void Server::log(ServerUser *u, const QString &str) const { QString msg = QString("<%1:%2(%3)> %4").arg(QString::number(u->uiSession), u->qsName, QString::number(u->iId), str); log(msg); } void Server::log(const QString &msg) const { dblog(msg); qWarning("%d => %s", iServerNum, msg.toUtf8().constData()); } void Server::newClient() { SslServer *ss = qobject_cast< SslServer * >(sender()); if (!ss) return; forever { QSslSocket *sock = ss->nextPendingSSLConnection(); if (!sock) return; QHostAddress adr = sock->peerAddress(); if (meta->banCheck(adr)) { log(QString("Ignoring connection: %1 (Global ban)") .arg(addressToString(sock->peerAddress(), sock->peerPort()))); sock->disconnectFromHost(); sock->deleteLater(); return; } HostAddress ha(adr); QList< Ban > tmpBans = qlBans; foreach (const Ban &ban, qlBans) { if (ban.isExpired()) tmpBans.removeOne(ban); } if (qlBans.count() != tmpBans.count()) { qlBans = tmpBans; saveBans(); } foreach (const Ban &ban, qlBans) { if (ban.haAddress.match(ha, ban.iMask)) { log(QString("Ignoring connection: %1, Reason: %2, Username: %3, Hash: %4 (Server ban)") .arg(addressToString(sock->peerAddress(), sock->peerPort()), ban.qsReason, ban.qsUsername, ban.qsHash)); sock->disconnectFromHost(); sock->deleteLater(); return; } } #ifdef Q_OS_MAC // One unexpected behavior of Qt's SSL backend is: it will add the key pair // it uses in a connection into the default keychain, and when access the private // key afterwards, a pop up will show up asking for user's permission. // In some case (OS X 10.15.5), this pop up will be suppressed somehow and no private // key is returned. // This env variable will avoid Qt directly adding the key pair into the default keychain, // using a temporary keychain instead. // See #4298 and https://codereview.qt-project.org/c/qt/qtbase/+/184243 EnvUtils::setenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1"); #endif sock->setPrivateKey(qskKey); sock->setLocalCertificate(qscCert); QSslConfiguration config; #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) config = sock->sslConfiguration(); // Qt 5.15 introduced QSslConfiguration::addCaCertificate(s) that should be preferred over the functions in // QSslSocket // Treat the leaf certificate as a root. // This shouldn't strictly be necessary, // and is a left-over from early on. // Perhaps it is necessary for self-signed // certs? config.addCaCertificate(qscCert); // Add CA certificates specified via // murmur.ini's sslCA option. config.addCaCertificates(Meta::mp.qlCA); // Add intermediate CAs found in the PEM // bundle used for this server's certificate. config.addCaCertificates(qlIntermediates); #else // Treat the leaf certificate as a root. // This shouldn't strictly be necessary, // and is a left-over from early on. // Perhaps it is necessary for self-signed // certs? sock->addCaCertificate(qscCert); // Add CA certificates specified via // murmur.ini's sslCA option. sock->addCaCertificates(Meta::mp.qlCA); // Add intermediate CAs found in the PEM // bundle used for this server's certificate. sock->addCaCertificates(qlIntermediates); // Must not get config from socket before setting CA certificates config = sock->sslConfiguration(); #endif config.setCiphers(Meta::mp.qlCiphers); #if defined(USE_QSSLDIFFIEHELLMANPARAMETERS) config.setDiffieHellmanParameters(qsdhpDHParams); #endif sock->setSslConfiguration(config); if (qqIds.isEmpty()) { log(QString("Session ID pool (%1) empty, rejecting connection").arg(iMaxUsers)); sock->disconnectFromHost(); sock->deleteLater(); return; } ServerUser *u = new ServerUser(this, sock); u->haAddress = ha; HostAddress(sock->localAddress()).toSockaddr(&u->saiTcpLocalAddress); connect(u, &ServerUser::connectionClosed, this, &Server::connectionClosed); connect(u, SIGNAL(message(Mumble::Protocol::TCPMessageType, const QByteArray &)), this, SLOT(message(Mumble::Protocol::TCPMessageType, const QByteArray &))); connect(u, &ServerUser::handleSslErrors, this, &Server::sslError); connect(u, &ServerUser::encrypted, this, &Server::encrypted); log(u, QString("New connection: %1").arg(addressToString(sock->peerAddress(), sock->peerPort()))); u->setToS(); #if QT_VERSION >= 0x050500 sock->setProtocol(QSsl::TlsV1_0OrLater); #elif QT_VERSION >= 0x050400 // In Qt 5.4, QSsl::SecureProtocols is equivalent // to "TLSv1.0 or later", which we require. sock->setProtocol(QSsl::SecureProtocols); #else sock->setProtocol(QSsl::TlsV1_0); #endif sock->startServerEncryption(); meta->successfulConnectionFrom(adr); } } void Server::encrypted() { ServerUser *uSource = qobject_cast< ServerUser * >(sender()); MumbleProto::Version mpv; MumbleProto::setVersion(mpv, Version::get()); if (Meta::mp.bSendVersion) { mpv.set_release(u8(Version::getRelease())); mpv.set_os(u8(meta->qsOS)); mpv.set_os_version(u8(meta->qsOSVersion)); } sendMessage(uSource, mpv); QList< QSslCertificate > certs = uSource->peerCertificateChain(); if (!certs.isEmpty()) { // Get the client's immediate SSL certificate const QSslCertificate &cert = certs.first(); uSource->qslEmail = cert.subjectAlternativeNames().values(QSsl::EmailEntry); uSource->qsHash = QString::fromLatin1(cert.digest(QCryptographicHash::Sha1).toHex()); if (!uSource->qslEmail.isEmpty() && uSource->bVerified) { QString subject; QString issuer; QStringList subjectList = cert.subjectInfo(QSslCertificate::CommonName); if (!subjectList.isEmpty()) { subject = subjectList.first(); } QStringList issuerList = certs.first().issuerInfo(QSslCertificate::CommonName); if (!issuerList.isEmpty()) { issuer = issuerList.first(); } log(uSource, QString::fromUtf8("Strong certificate for %1 <%2> (signed by %3)") .arg(subject) .arg(uSource->qslEmail.join(", ")) .arg(issuer)); } foreach (const Ban &ban, qlBans) { if (ban.qsHash == uSource->qsHash) { log(uSource, QString("Certificate hash is banned: %1, Username: %2, Reason: %3.") .arg(ban.qsHash, ban.qsUsername, ban.qsReason)); uSource->disconnectSocket(); } } } } void Server::sslError(const QList< QSslError > &errors) { ServerUser *u = qobject_cast< ServerUser * >(sender()); if (!u) return; bool ok = true; foreach (QSslError e, errors) { switch (e.error()) { case QSslError::InvalidPurpose: // Allow email certificates. break; case QSslError::NoPeerCertificate: case QSslError::SelfSignedCertificate: case QSslError::SelfSignedCertificateInChain: case QSslError::UnableToGetLocalIssuerCertificate: case QSslError::UnableToVerifyFirstCertificate: case QSslError::HostNameMismatch: case QSslError::CertificateNotYetValid: case QSslError::CertificateExpired: u->bVerified = false; break; default: log(u, QString("SSL Error: %1").arg(e.errorString())); ok = false; } } if (ok) { u->proceedAnyway(); } else { // Due to a regression in Qt 5 (QTBUG-53906), // we can't 'force' disconnect (which calls // QAbstractSocket->abort()) when built against Qt 5. // // The bug is that Qt doesn't update the // QSslSocket's socket state when QSslSocket->abort() // is called. // // Our call to abort() happens when QSslSocket is inside // startHandshake(). That is, a handshake is in progress. // // After emitting the peerVerifyError/sslErrors signals, // startHandshake() checks whether the connection is still // in QAbstractSocket::ConectedState. // // Unfortunately, because abort() doesn't update the socket's // state to signal that it is no longer connected, startHandshake() // still thinks the socket is connected and will continue to // attempt to finish the handshake. // // Because abort() tears down a lot of internal state // of the QSslSocket, including the 'SSL *' object // associated with the socket, this is fatal and leads // to crashes, such as attempting to derefernce a nullptr // 'SSL *' object. // // To avoid this, we use a non-forceful disconnect // until this is fixed upstream. // // See // https://bugreports.qt.io/browse/QTBUG-53906 // https://github.com/mumble-voip/mumble/issues/2334 u->disconnectSocket(); } } void Server::connectionClosed(QAbstractSocket::SocketError err, const QString &reason) { if (reason.contains(QLatin1String("140E0197"))) { // A severe bug was introduced in qt/qtbase@93a803a6de27d9eb57931c431b5f3d074914f693. // q_SSL_shutdown() causes Qt to emit "error()" from unrelated QSslSocket(s), in addition to the correct // one. The issue causes this function to disconnect random authenticated clients. // // The workaround consists in ignoring a specific OpenSSL error: // "Error while reading: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init [20]" // // Definitely not ideal, but it fixes a critical vulnerability. qWarning("Ignored OpenSSL error 140E0197 for %p", static_cast< void * >(sender())); return; } Connection *c = qobject_cast< Connection * >(sender()); if (!c) return; if (c->bDisconnectedEmitted) return; c->bDisconnectedEmitted = true; ServerUser *u = static_cast< ServerUser * >(c); log(u, QString("Connection closed: %1 [%2]").arg(reason).arg(err)); setLastDisconnect(u); if (u->sState == ServerUser::Authenticated) { if (m_channelListenerManager.isListeningToAny(u->uiSession)) { foreach (int channelID, m_channelListenerManager.getListenedChannelsForUser(u->uiSession)) { // Remove the client from the list on the server m_channelListenerManager.removeListener(u->uiSession, channelID); } } MumbleProto::UserRemove mpur; mpur.set_session(u->uiSession); sendExcept(u, mpur); if (u->m_clientType == ClientType::BOT) { m_botCount--; } emit userDisconnected(u); } Channel *old = u->cChannel; { QWriteLocker wl(&qrwlVoiceThread); qhUsers.remove(u->uiSession); qhHostUsers[u->haAddress].remove(u); quint16 port = (u->saiUdpAddress.ss_family == AF_INET6) ? (reinterpret_cast< sockaddr_in6 * >(&u->saiUdpAddress)->sin6_port) : (reinterpret_cast< sockaddr_in * >(&u->saiUdpAddress)->sin_port); const QPair< HostAddress, quint16 > &key = QPair< HostAddress, quint16 >(u->haAddress, port); qhPeerUsers.remove(key); if (old) old->removeUser(u); } if (old && old->bTemporary && old->qlUsers.isEmpty()) QCoreApplication::instance()->postEvent(this, new ExecEvent(boost::bind(&Server::removeChannel, this, old->iId))); if (u->uiSession > 0 && static_cast< int >(u->uiSession) < iMaxUsers * 2) qqIds.enqueue(u->uiSession); // Reinsert session id into pool if (u->sState == ServerUser::Authenticated) { clearTempGroups(u); // Also clears ACL cache recheckCodecVersions(); // Maybe can choose a better codec now } u->deleteLater(); if (qhUsers.isEmpty()) stopThread(); } void Server::message(Mumble::Protocol::TCPMessageType type, const QByteArray &qbaMsg, ServerUser *u) { ZoneScopedN(TracyConstants::TCP_PACKET_PROCESSING_ZONE); if (!u) { u = static_cast< ServerUser * >(sender()); } if (u->sState == ServerUser::Authenticated) { u->resetActivityTime(); } if (type == Mumble::Protocol::TCPMessageType::UDPTunnel) { int len = qbaMsg.size(); if (len < 2 || static_cast< unsigned int >(len) > Mumble::Protocol::MAX_UDP_PACKET_SIZE) { // Drop messages that are too small to be senseful or that are bigger than allowed return; } QReadLocker rl(&qrwlVoiceThread); u->aiUdpFlag = 0; m_tcpTunnelDecoder.setProtocolVersion(u->m_version); if (m_tcpTunnelDecoder.decode(gsl::span< const Mumble::Protocol::byte >( reinterpret_cast< const Mumble::Protocol::byte * >(qbaMsg.constData()), qbaMsg.size()))) { if (m_tcpTunnelDecoder.getMessageType() == Mumble::Protocol::UDPMessageType::Audio) { Mumble::Protocol::AudioData audioData = m_tcpTunnelDecoder.getAudioData(); // Allow all voice packets through by default. bool ok = true; // ...Unless we're in Opus mode. In Opus mode, only Opus packets are allowed. if (bOpus && audioData.usedCodec != Mumble::Protocol::AudioCodec::Opus) { ok = false; } if (ok) { // Add session id audioData.senderSession = u->uiSession; processMsg(u, std::move(audioData), m_tcpAudioReceivers, m_tcpAudioEncoder); } } } return; } #ifdef QT_NO_DEBUG # define PROCESS_MUMBLE_TCP_MESSAGE(name, value) \ case Mumble::Protocol::TCPMessageType::name: { \ MumbleProto::name msg; \ if (msg.ParseFromArray(qbaMsg.constData(), qbaMsg.size())) { \ msg.DiscardUnknownFields(); \ msg##name(u, msg); \ } \ break; \ } #else # define PROCESS_MUMBLE_TCP_MESSAGE(name, value) \ case Mumble::Protocol::TCPMessageType::name: { \ MumbleProto::name msg; \ if (msg.ParseFromArray(qbaMsg.constData(), qbaMsg.size())) { \ if (type != Mumble::Protocol::TCPMessageType::Ping) { \ printf("== %s:\n", #name); \ msg.PrintDebugString(); \ } \ msg.DiscardUnknownFields(); \ msg##name(u, msg); \ } \ break; \ } #endif switch (type) { MUMBLE_ALL_TCP_MESSAGES } #undef PROCESS_MUMBLE_TCP_MESSAGE } void Server::checkTimeout() { QList< ServerUser * > qlClose; qrwlVoiceThread.lockForRead(); foreach (ServerUser *u, qhUsers) { if (u->activityTime() > (iTimeout * 1000)) { log(u, "Timeout"); qlClose.append(u); } } qrwlVoiceThread.unlock(); foreach (ServerUser *u, qlClose) u->disconnectSocket(true); } void Server::tcpTransmitData(QByteArray a, unsigned int id) { Connection *c = qhUsers.value(id); if (c) { QByteArray qba; int len = a.size(); qba.resize(len + 6); unsigned char *uc = reinterpret_cast< unsigned char * >(qba.data()); *reinterpret_cast< quint16 * >(&uc[0]) = qToBigEndian(static_cast< quint16 >(Mumble::Protocol::TCPMessageType::UDPTunnel)); *reinterpret_cast< quint32 * >(&uc[2]) = qToBigEndian(static_cast< quint32 >(len)); memcpy(uc + 6, a.constData(), len); c->sendMessage(qba); c->forceFlush(); } } void Server::doSync(unsigned int id) { ServerUser *u = qhUsers.value(id); if (u) { log(u, "Requesting crypt-nonce resync"); MumbleProto::CryptSetup mpcs; sendMessage(u, mpcs); } } void Server::sendProtoMessage(ServerUser *u, const ::google::protobuf::Message &msg, Mumble::Protocol::TCPMessageType msgType) { QByteArray cache; u->sendMessage(msg, msgType, cache); } void Server::sendProtoAll(const ::google::protobuf::Message &msg, Mumble::Protocol::TCPMessageType msgType, Version::full_t version, Version::CompareMode mode) { sendProtoExcept(nullptr, msg, msgType, version, mode); } void Server::sendProtoExcept(ServerUser *u, const ::google::protobuf::Message &msg, Mumble::Protocol::TCPMessageType msgType, Version::full_t version, Version::CompareMode mode) { QByteArray cache; foreach (ServerUser *usr, qhUsers) if ((usr != u) && (usr->sState == ServerUser::Authenticated)) { assert(mode == Version::CompareMode::AtLeast || mode == Version::CompareMode::LessThan); const bool isUnknown = version == Version::UNKNOWN; const bool fulfillsVersionRequirement = mode == Version::CompareMode::AtLeast ? usr->m_version >= version : usr->m_version < version; if (isUnknown || fulfillsVersionRequirement) { usr->sendMessage(msg, msgType, cache); } } } void Server::removeChannel(int id) { Channel *c = qhChannels.value(id); if (c) removeChannel(c); } void Server::removeChannel(Channel *chan, Channel *dest) { Channel *c; User *p; if (!dest) dest = chan->cParent; { QWriteLocker wl(&qrwlVoiceThread); chan->unlink(nullptr); } foreach (c, chan->qlChannels) { removeChannel(c, dest); } foreach (p, chan->qlUsers) { { QWriteLocker wl(&qrwlVoiceThread); chan->removeUser(p); } Channel *target = dest; while (target->cParent && (!hasPermission(static_cast< ServerUser * >(p), target, ChanACL::Enter) || isChannelFull(target, static_cast< ServerUser * >(p)))) target = target->cParent; MumbleProto::UserState mpus; mpus.set_session(p->uiSession); mpus.set_channel_id(target->iId); userEnterChannel(p, target, mpus); sendAll(mpus); emit userStateChanged(p); } foreach (unsigned int userSession, m_channelListenerManager.getListenersForChannel(chan->iId)) { const ServerUser *user = qhUsers.value(userSession); if (!user) { continue; } deleteChannelListener(*user, *chan); // Notify that all clients that have been listening to this channel, will do so no more MumbleProto::UserState mpus; mpus.set_session(user->uiSession); mpus.add_listening_channel_remove(chan->iId); sendAll(mpus); } MumbleProto::ChannelRemove mpcr; mpcr.set_channel_id(chan->iId); sendAll(mpcr); removeChannelDB(chan); emit channelRemoved(chan); if (chan->cParent) { QWriteLocker wl(&qrwlVoiceThread); chan->cParent->removeChannel(chan); } delete chan; } bool Server::unregisterUser(int id) { if (!unregisterUserDB(id)) return false; { QMutexLocker lock(&qmCache); foreach (Channel *c, qhChannels) { bool write = false; QList< ChanACL * > ql = c->qlACL; foreach (ChanACL *acl, ql) { if (acl->iUserId == id) { c->qlACL.removeAll(acl); write = true; } } foreach (Group *g, c->qhGroups) { bool addrem = g->qsAdd.remove(id); bool remrem = g->qsRemove.remove(id); write = write || addrem || remrem; } if (write) updateChannel(c); } } foreach (ServerUser *u, qhUsers) { if (u->iId == id) { clearACLCache(u); MumbleProto::UserState mpus; mpus.set_session(u->uiSession); mpus.set_user_id(-1); sendAll(mpus); u->iId = -1; break; } } return true; } void Server::userEnterChannel(User *p, Channel *c, MumbleProto::UserState &mpus) { if (p->cChannel == c) return; Channel *old = p->cChannel; { QWriteLocker wl(&qrwlVoiceThread); c->addUser(p); bool mayspeak = ChanACL::hasPermission(static_cast< ServerUser * >(p), c, ChanACL::Speak, nullptr); bool sup = p->bSuppress; if (mayspeak == sup) { // Ok, he can speak and was suppressed, or vice versa p->bSuppress = !mayspeak; mpus.set_suppress(p->bSuppress); } if (p->bPrioritySpeaker) { // Clear priority speaker flag when switching channels p->bPrioritySpeaker = false; mpus.set_priority_speaker(p->bPrioritySpeaker); } } clearACLCache(p); setLastChannel(p); if (old && old->bTemporary && old->qlUsers.isEmpty()) { QCoreApplication::instance()->postEvent(this, new ExecEvent(boost::bind(&Server::removeChannel, this, old->iId))); } sendClientPermission(static_cast< ServerUser * >(p), c); if (c->cParent) sendClientPermission(static_cast< ServerUser * >(p), c->cParent); } bool Server::hasPermission(ServerUser *p, Channel *c, QFlags< ChanACL::Perm > perm) { QMutexLocker qml(&qmCache); return ChanACL::hasPermission(p, c, perm, &acCache); } QFlags< ChanACL::Perm > Server::effectivePermissions(ServerUser *p, Channel *c) { QMutexLocker qml(&qmCache); return ChanACL::effectivePermissions(p, c, &acCache); } void Server::sendClientPermission(ServerUser *u, Channel *c, bool explicitlyRequested) { unsigned int perm; if (u->iId == 0) return; { QMutexLocker qml(&qmCache); // Abuse that hasPermission will update acCache with the latest permissions (all of them, // not only the requested one) so that we can pull this information out of it afterwards. ChanACL::hasPermission(u, c, ChanACL::Enter, &acCache); perm = acCache.value(u)->value(c); } if (explicitlyRequested) { // Store the last channel the client showed explicit interest in u->iLastPermissionCheck = c->iId; } if (explicitlyRequested || u->qmPermissionSent.value(c->iId) != perm) { // Send the permission info only if the client has explicitly asked for it // or if the permissions have changed since the last time the client has // been informed about permission for this channel. u->qmPermissionSent.insert(c->iId, perm); MumbleProto::PermissionQuery mppq; mppq.set_channel_id(c->iId); mppq.set_permissions(perm); sendMessage(u, mppq); } } /* This function is a helper for clearACLCache and assumes qmCache is held. * First, check if anything actually changed, or if the list is getting awfully large, * because this function is potentially quite expensive. * If all the items are still valid; great. If they aren't, send off the last channel * the client expliticly asked for -- this may not be what it wants, but it's our best * guess. */ void Server::flushClientPermissionCache(ServerUser *u, MumbleProto::PermissionQuery &mppq) { QMap< int, unsigned int >::const_iterator i; bool match = (u->qmPermissionSent.count() < 20); for (i = u->qmPermissionSent.constBegin(); (match && (i != u->qmPermissionSent.constEnd())); ++i) { Channel *c = qhChannels.value(i.key()); if (!c) { match = false; } else { ChanACL::hasPermission(u, c, ChanACL::Enter, &acCache); unsigned int perm = acCache.value(u)->value(c); if (perm != i.value()) match = false; } } if (match) return; u->qmPermissionSent.clear(); Channel *c = qhChannels.value(u->iLastPermissionCheck); if (!c) { c = u->cChannel; u->iLastPermissionCheck = c->iId; } ChanACL::hasPermission(u, c, ChanACL::Enter, &acCache); unsigned int perm = acCache.value(u)->value(c); u->qmPermissionSent.insert(c->iId, perm); mppq.Clear(); mppq.set_channel_id(c->iId); mppq.set_permissions(perm); mppq.set_flush(true); sendMessage(u, mppq); } void Server::clearACLCache(User *p) { MumbleProto::PermissionQuery mppq; { QMutexLocker qml(&qmCache); if (p) { ChanACL::ChanCache *h = acCache.take(p); delete h; flushClientPermissionCache(static_cast< ServerUser * >(p), mppq); } else { foreach (ChanACL::ChanCache *h, acCache) delete h; acCache.clear(); foreach (ServerUser *u, qhUsers) if (u->sState == ServerUser::Authenticated) flushClientPermissionCache(u, mppq); } // A change in ACLs could also change a user's suppression state MumbleProto::UserState mpus; auto processingFunction = [&](ServerUser *user) { bool maySpeak = ChanACL::hasPermission(user, user->cChannel, ChanACL::Speak, &acCache); if (maySpeak == user->bSuppress) { // Mirror a user's ability to speak in the current channel (by means of the ACLs) in the suppress // property (not being allowed to speak -> suppressed and vice versa) user->bSuppress = !maySpeak; mpus.Clear(); mpus.set_session(user->uiSession); mpus.set_suppress(true); sendAll(mpus); } }; if (p) { processingFunction(static_cast< ServerUser * >(p)); } else { for (ServerUser *currentUser : qhUsers) { processingFunction(currentUser); } } } // A change in ACLs means that the user might be able to whisper // to users it didn't have permission to do before (or vice versa) clearWhisperTargetCache(); } void Server::clearWhisperTargetCache() { QWriteLocker lock(&qrwlVoiceThread); foreach (ServerUser *u, qhUsers) { u->qmTargetCache.clear(); } } QString Server::addressToString(const QHostAddress &adr, unsigned short port) { HostAddress ha(adr); if ((Meta::mp.iObfuscate != 0)) { QCryptographicHash h(QCryptographicHash::Sha1); h.addData(reinterpret_cast< const char * >(&Meta::mp.iObfuscate), sizeof(Meta::mp.iObfuscate)); if (adr.protocol() == QAbstractSocket::IPv4Protocol) { quint32 num = adr.toIPv4Address(); h.addData(reinterpret_cast< const char * >(&num), sizeof(num)); } else if (adr.protocol() == QAbstractSocket::IPv6Protocol) { Q_IPV6ADDR num = adr.toIPv6Address(); h.addData(reinterpret_cast< const char * >(num.c), sizeof(num.c)); } return QString("<<%1:%2>>").arg(QString::fromLatin1(h.result().toHex()), QString::number(port)); } return QString("%1:%2").arg(ha.toString(), QString::number(port)); } bool Server::validateUserName(const QString &name) { // We expect the name passed to this function to be fully trimmed already. This way we // prevent "empty" names (at least with the default username restriction). return (name.trimmed().length() == name.length() && qrUserName.exactMatch(name) && (name.length() <= 512)); } bool Server::validateChannelName(const QString &name) { return (qrChannelName.exactMatch(name) && (name.length() <= 512)); } void Server::recheckCodecVersions(ServerUser *connectingUser) { QMap< int, int > qmCodecUsercount; QMap< int, int >::const_iterator i; int users = 0; int opus = 0; // Count how many users use which codec foreach (ServerUser *u, qhUsers) { if (u->qlCodecs.isEmpty() && !u->bOpus) continue; ++users; if (u->bOpus) ++opus; foreach (int version, u->qlCodecs) ++qmCodecUsercount[version]; } if (!users) return; // Enable Opus if the number of users with Opus is higher than the threshold bool enableOpus = ((opus * 100 / users) >= iOpusThreshold); // Find the best possible codec most users support int version = 0; int maximum_users = 0; i = qmCodecUsercount.constEnd(); do { --i; if (i.value() > maximum_users) { version = i.key(); maximum_users = i.value(); } } while (i != qmCodecUsercount.constBegin()); int current_version = bPreferAlpha ? iCodecAlpha : iCodecBeta; // If we don't already use the compat bitstream version set // it as alpha and announce it. If another codec now got the // majority set it as the opposite of the currently valid bPreferAlpha // and announce it. if (current_version != version) { if (version == static_cast< qint32 >(0x8000000b)) bPreferAlpha = true; else bPreferAlpha = !bPreferAlpha; if (bPreferAlpha) iCodecAlpha = version; else iCodecBeta = version; } else if (bOpus == enableOpus) { if (bOpus && connectingUser && !connectingUser->bOpus) { sendTextMessage( nullptr, connectingUser, false, QLatin1String( "WARNING: Your client doesn't support the Opus codec the server is using, you " "won't be able to talk or hear anyone. Please upgrade to a client with Opus support.")); } return; } bOpus = enableOpus; MumbleProto::CodecVersion mpcv; mpcv.set_alpha(iCodecAlpha); mpcv.set_beta(iCodecBeta); mpcv.set_prefer_alpha(bPreferAlpha); mpcv.set_opus(bOpus); sendAll(mpcv); if (bOpus) { foreach (ServerUser *u, qhUsers) { // Prevent connected users that could not yet declare their opus capability during msgAuthenticate from // being spammed. Only authenticated users and the currently connecting user (if recheck is called in // that context) have a reliable u->bOpus. if ((u->sState == ServerUser::Authenticated || u == connectingUser) && !u->bOpus) { sendTextMessage(nullptr, u, false, QLatin1String("WARNING: Your client doesn't support the Opus " "codec the server is switching " "to, you won't be able to talk or hear anyone. Please upgrade to a " "client with Opus support.")); } } } log(QString::fromLatin1("CELT codec switch %1 %2 (prefer %3) (Opus %4)") .arg(iCodecAlpha, 0, 16) .arg(iCodecBeta, 0, 16) .arg(bPreferAlpha ? iCodecAlpha : iCodecBeta, 0, 16) .arg(bOpus)); } void Server::hashAssign(QString &dest, QByteArray &hash, const QString &src) { dest = src; if (src.length() >= 128) hash = sha1(src); else hash = QByteArray(); } void Server::hashAssign(QByteArray &dest, QByteArray &hash, const QByteArray &src) { dest = src; if (src.length() >= 128) hash = sha1(src); else hash = QByteArray(); } bool Server::isTextAllowed(QString &text, bool &changed) { changed = false; if (!bAllowHTML) { QString out; if (HTMLFilter::filter(text, out)) { changed = true; text = out; } return ((iMaxTextMessageLength == 0) || (text.length() <= iMaxTextMessageLength)); } else { int length = text.length(); // No limits if ((iMaxTextMessageLength == 0) && (iMaxImageMessageLength == 0)) return true; // Over Image limit? (If so, always fail) if ((iMaxImageMessageLength != 0) && (length > iMaxImageMessageLength)) return false; // Under textlength? if ((iMaxTextMessageLength == 0) || (length <= iMaxTextMessageLength)) return true; // Over textlength, under imagelength. If no XML, this is a fail. if (!text.contains(QLatin1Char('<'))) return false; // Strip value from s src attributes to check text-length only - // we already ensured the img-length requirement is met QString qsOut; QXmlStreamReader qxsr(QString::fromLatin1("%1").arg(text)); QXmlStreamWriter qxsw(&qsOut); while (!qxsr.atEnd()) { switch (qxsr.readNext()) { case QXmlStreamReader::Invalid: return false; case QXmlStreamReader::StartElement: { if (qxsr.name() == QLatin1String("img")) { qxsw.writeStartElement(qxsr.namespaceUri().toString(), qxsr.name().toString()); foreach (const QXmlStreamAttribute &a, qxsr.attributes()) if (a.name() != QLatin1String("src")) qxsw.writeAttribute(a); } else { qxsw.writeCurrentToken(qxsr); } } break; default: qxsw.writeCurrentToken(qxsr); break; } } length = qsOut.length(); return (length <= iMaxTextMessageLength); } } bool Server::isChannelFull(Channel *c, ServerUser *u) { if (u && hasPermission(u, c, ChanACL::Write)) { return false; } if (c->uiMaxUsers) { return static_cast< unsigned int >(c->qlUsers.count()) >= c->uiMaxUsers; } if (iMaxUsersPerChannel) { return c->qlUsers.count() >= iMaxUsersPerChannel; } return false; } bool Server::canNest(Channel *newParent, Channel *channel) const { const int parentLevel = newParent ? static_cast< int >(newParent->getLevel()) : -1; const int channelDepth = channel ? static_cast< int >(channel->getDepth()) : 0; return (parentLevel + channelDepth) < iChannelNestingLimit; } #undef SIO_UDP_CONNRESET #undef SENDTO