diff options
-rw-r--r-- | scripts/murmur.ini | 39 | ||||
-rw-r--r-- | src/ACL.cpp | 21 | ||||
-rw-r--r-- | src/ACL.h | 4 | ||||
-rw-r--r-- | src/Connection.cpp | 4 | ||||
-rw-r--r-- | src/Timer.cpp | 2 | ||||
-rw-r--r-- | src/Timer.h | 2 | ||||
-rw-r--r-- | src/murmur/Cert.cpp | 16 | ||||
-rw-r--r-- | src/murmur/DBus.cpp | 16 | ||||
-rw-r--r-- | src/murmur/DBus_fake.h | 2 | ||||
-rw-r--r-- | src/murmur/Messages.cpp | 44 | ||||
-rw-r--r-- | src/murmur/Meta.cpp | 46 | ||||
-rw-r--r-- | src/murmur/Meta.h | 9 | ||||
-rw-r--r-- | src/murmur/Register.cpp | 8 | ||||
-rw-r--r-- | src/murmur/Server.cpp | 125 | ||||
-rw-r--r-- | src/murmur/Server.h | 54 | ||||
-rw-r--r-- | src/murmur/ServerDB.cpp | 20 | ||||
-rw-r--r-- | src/murmur/murmur.cpp | 10 |
17 files changed, 247 insertions, 175 deletions
diff --git a/scripts/murmur.ini b/scripts/murmur.ini index 0d5f1ac6e..147ed7857 100644 --- a/scripts/murmur.ini +++ b/scripts/murmur.ini @@ -1,13 +1,3 @@ -# Welcome message sent to users -welcometext="<br />Welcome to this server running <b>Murmur</b>.<br />Enjoy your stay!<br />" - -# Port to bind TCP and UDP sockets to -port=64738 - -# Specific IP or hostname to bind to. -# If this is left blank (default), murmur will bind to all available addresses. -#host= - # Path to database. If blank, will search for # murmur.sqlite in default locations. database= @@ -27,10 +17,33 @@ database= # #dbus=session +# How many login attempts do we tolerate from one IP +# inside a given timeframe before we ban the connection? +# Note that this is global (shared between all virtual servers), and that +# it counts both successfull and unsuccessfull connection attempts. +# Set either Attempts or Timeframe to 0 to disable. +#autobanAttempts = 10 +#autobanTimeframe = 120 +#autobanTime = 300 + # Murmur default to logging to murmur.log. If you leave this blank, # murmur will log to the console (linux) or through message boxes (win32). #logfile=murmur.log +# The below will be used as defaults for new configured servers. +# If you're just running one server (the default), it's easier to +# configure it here than through DBus/SQL. +# +# Welcome message sent to users +welcometext="<br />Welcome to this server running <b>Murmur</b>.<br />Enjoy your stay!<br />" + +# Port to bind TCP and UDP sockets to +port=64738 + +# Specific IP or hostname to bind to. +# If this is left blank (default), murmur will bind to all available addresses. +#host= + # Password to join server serverpassword= @@ -56,9 +69,3 @@ users=50 # If you have a proper SSL certificate, you can provide the filenames here. #sslCert= #sslKey= - -# If no key can be loaded, murmur will generate one for you, and store it -# in this file for later use. If no filename is specified, 'murmur.pem' -# will be used by default. -# sslStore= - diff --git a/src/ACL.cpp b/src/ACL.cpp index 58244f7e3..0ad73f6da 100644 --- a/src/ACL.cpp +++ b/src/ACL.cpp @@ -56,14 +56,21 @@ bool ChanACL::hasPermission(Player *p, Channel *chan, Perm perm, ACLCache &cache ChanACL *acl; // Superuser - if (p->iId == 0) - return true; + if (p->iId == 0) { + switch (perm) { + case Speak: + case AltSpeak: + return false; + default: + return true; + } + } Permissions granted = 0; - QHash<Player *, Permissions> *h = cache.value(chan); + QHash<Channel *, Permissions> *h = cache.value(p); if (h) - granted = h->value(p); + granted = h->value(chan); if (granted & Cached) { if ((perm != Speak) && (perm != AltSpeak)) return ((granted & (perm | Write)) != None); @@ -114,10 +121,10 @@ bool ChanACL::hasPermission(Player *p, Channel *chan, Perm perm, ACLCache &cache } } - if (! cache.contains(chan)) - cache.insert(chan, new QHash<Player *, Permissions>); + if (! cache.contains(p)) + cache.insert(p, new QHash<Channel *, Permissions>); - cache.value(chan)->insert(p, granted | Cached); + cache.value(p)->insert(chan, granted | Cached); if ((perm != Speak) && (perm != AltSpeak)) return ((granted & (perm | Write)) != None); @@ -56,8 +56,8 @@ class ChanACL : public QObject { Q_DECLARE_FLAGS(Permissions, Perm) - typedef QHash<Player *, Permissions> ChanCache; - typedef QHash<Channel *, ChanCache * > ACLCache; + typedef QHash<Channel *, Permissions> ChanCache; + typedef QHash<Player *, ChanCache * > ACLCache; // static QHash<Channel *, QHash<Player *, Permissions > > c_qhACLCache; diff --git a/src/Connection.cpp b/src/Connection.cpp index 9d39a751d..533f143c1 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -106,14 +106,10 @@ void Connection::socketError(QAbstractSocket::SocketError) { } void Connection::socketSslErrors(const QList<QSslError> &qlErr) { - foreach(QSslError e, qlErr) { - qDebug("Ssl Error: %s",qPrintable(e.errorString())); - } emit handleSslErrors(qlErr); } void Connection::proceedAnyway() { - qDebug("Allowing SSL connection to proceed."); qtsSocket->ignoreSslErrors(); } diff --git a/src/Timer.cpp b/src/Timer.cpp index 6de456490..ca25dd6a1 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -35,7 +35,7 @@ Timer::Timer() { uiStart = now();
}
-quint64 Timer::elapsed() {
+quint64 Timer::elapsed() const {
return now() - uiStart;
}
diff --git a/src/Timer.h b/src/Timer.h index f8f90cca9..fa6d5c582 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -39,7 +39,7 @@ protected: static quint64 now();
public:
Timer();
- quint64 elapsed();
+ quint64 elapsed() const;
quint64 restart();
};
diff --git a/src/murmur/Cert.cpp b/src/murmur/Cert.cpp index c2083c7e6..8a734835d 100644 --- a/src/murmur/Cert.cpp +++ b/src/murmur/Cert.cpp @@ -61,14 +61,14 @@ void Server::initializeCert() { if (! crt.isEmpty()) {
qscCert = QSslCertificate(crt);
if (qscCert.isNull()) {
- qWarning("Failed to parse certificate.");
+ log("Failed to parse certificate.");
}
}
if (! key.isEmpty() && qscCert.isNull()) {
qscCert = QSslCertificate(key);
if (! qscCert.isNull()) {
- qDebug("Using certificate from key file.");
+ log("Using certificate from key.");
}
}
@@ -78,14 +78,14 @@ void Server::initializeCert() { if (! key.isEmpty()) {
qskKey = QSslKey(key, alg);
if (qskKey.isNull()) {
- qWarning("Failed to parse key file.");
+ log("Failed to parse key.");
}
}
if (! crt.isEmpty() && qskKey.isNull()) {
qskKey = QSslKey(crt, alg);
if (! qskKey.isNull()) {
- qDebug("Using key from certificate file.");
+ log("Using key from certificate.");
}
}
@@ -93,12 +93,12 @@ void Server::initializeCert() { if (qscCert.isNull() || qskKey.isNull()) {
if (! key.isEmpty() || ! crt.isEmpty()) {
- qFatal("Certificate specified, but failed to load.");
+ log("Certificate specified, but failed to load.");
}
qskKey = Meta::mp.qskKey;
qscCert = Meta::mp.qscCert;
if (qscCert.isNull() || qskKey.isNull()) {
- qWarning("Generating new server certificate.");
+ log("Generating new server certificate.");
BIO *bio_err;
@@ -135,7 +135,7 @@ void Server::initializeCert() { qscCert = QSslCertificate(crt, QSsl::Der);
if (qscCert.isNull())
- qFatal("Certificate generation failed");
+ log("Certificate generation failed");
key.resize(i2d_PrivateKey(pkey, NULL));
dptr=reinterpret_cast<unsigned char *>(key.data());
@@ -143,7 +143,7 @@ void Server::initializeCert() { qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der);
if (qskKey.isNull())
- qFatal("Key generation failed");
+ log("Key generation failed");
setConf("certificate", qscCert.toPem());
setConf("key", qskKey.toPem());
diff --git a/src/murmur/DBus.cpp b/src/murmur/DBus.cpp index 2b1dcfc1b..484bfd650 100644 --- a/src/murmur/DBus.cpp +++ b/src/murmur/DBus.cpp @@ -153,7 +153,7 @@ QString MurmurDBus::mapIdToName(int id) { if (reply.isValid()) return reply.value(); else { - qWarning("Authenticator failed getUserName for %d: %s", id, qPrintable(reply.error().message())); + server->log("DBus Authenticator failed getUserName for %d: %s", id, qPrintable(reply.error().message())); qsAuthPath = QString(); qsAuthService = QString(); return QString(); @@ -183,7 +183,7 @@ int MurmurDBus::mapNameToId(const QString &name) { if (reply.isValid()) return reply.value(); else { - qWarning("Authenticator failed getUserId for %s: %s", qPrintable(name), qPrintable(reply.error().message())); + server->log("DBus Authenticator failed getUserId for %s: %s", qPrintable(name), qPrintable(reply.error().message())); qsAuthPath = QString(); qsAuthService = QString(); return -2; @@ -211,14 +211,14 @@ int MurmurDBus::authenticate(QString &uname, const QString &pw) { } } if (ok) { - qWarning("Authenticate success for %s: %d", qPrintable(uname),uid); + server->log("DBus Authenticate success for %s: %d", qPrintable(uname),uid); return uid; } else { - qWarning("Autenticator failed authenticate for %s: Invalid return type", qPrintable(uname)); + server->log("DBus Autenticator failed authenticate for %s: Invalid return type", qPrintable(uname)); return -2; } } else { - qWarning("Authenticator failed authenticate for %s: %s", qPrintable(uname), qPrintable(err.message())); + server->log("DBus Authenticator failed authenticate for %s: %s", qPrintable(uname), qPrintable(err.message())); qsAuthPath = QString(); qsAuthService = QString(); return -2; @@ -569,7 +569,7 @@ void MurmurDBus::getPlayerIds(const QStringList &names, const QDBusMessage &, QL void MurmurDBus::setAuthenticator(const QDBusObjectPath &path, const QDBusMessage &msg) { qsAuthPath = path.path(); qsAuthService = msg.service(); - qWarning("Authenticator set to %s %s",qPrintable(qsAuthService),qPrintable(qsAuthPath)); + server->log("DBus Authenticator set to %s %s",qPrintable(qsAuthService),qPrintable(qsAuthPath)); } void MurmurDBus::setTemporaryGroups(int channel, int playerid, const QStringList &groups, const QDBusMessage &msg) { @@ -588,7 +588,9 @@ void MurmurDBus::setTemporaryGroups(int channel, int playerid, const QStringList g->qsTemporary.insert(playerid); } - server->clearACLCache(); + Player *p = server->qhUsers.value(playerid); + if (p) + server->clearACLCache(p); } PlayerInfo::PlayerInfo(Player *p) { diff --git a/src/murmur/DBus_fake.h b/src/murmur/DBus_fake.h index c24ba0eb1..713e30d87 100644 --- a/src/murmur/DBus_fake.h +++ b/src/murmur/DBus_fake.h @@ -65,8 +65,6 @@ class MurmurDBus : public QObject { }; }; -extern MurmurDBus *dbus; - #else class MurmurDBus; #endif diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp index e984d27a9..215a9a1d1 100644 --- a/src/murmur/Messages.cpp +++ b/src/murmur/Messages.cpp @@ -53,7 +53,7 @@ #define PERM_DENIED(who, where, what) \ mpd.qsReason = QString("%1 not allowed to %2 in %3").arg(who->qsName).arg(ChanACL::permName(what)).arg(where->qsName); \ sendMessage(cCon, &mpd); \ - log(mpd.qsReason, cCon) + log(uSource, "%s", qPrintable(mpd.qsReason)) #define PERM_DENIED_TEXT(text) \ mpd.qsReason = text; \ sendMessage(cCon, &mpd) @@ -139,7 +139,7 @@ void Server::msgServerAuthenticate(Connection *cCon, MessageServerAuthenticate * } if (! ok) { - log(QString::fromLatin1("Rejected connection: %1").arg(msr.qsReason), cCon); + log(uSource, "Rejected connection: %s", qPrintable(msr.qsReason)); sendMessage(cCon, &msr); cCon->disconnectSocket(); return; @@ -147,7 +147,7 @@ void Server::msgServerAuthenticate(Connection *cCon, MessageServerAuthenticate * // Kick ghost if (uOld) { - log(QString::fromLatin1("Disconnecting ghost"), uOld); + log(uSource, "Disconnecting ghost"); uOld->disconnectSocket(); } @@ -246,7 +246,7 @@ void Server::msgServerAuthenticate(Connection *cCon, MessageServerAuthenticate * mssMsg.qsWelcomeText = qsWelcomeText; mssMsg.iMaxBandwidth = iMaxBandwidth; sendMessage(cCon, &mssMsg); - log(QString("Authenticated: %1").arg(msg->qsUsername), cCon); + log(uSource, "Authenticated"); dbus->playerConnected(uSource); playerEnterChannel(uSource, lc, false); @@ -268,7 +268,7 @@ void Server::msgServerBanList(Connection *cCon, MessageServerBanList *msg) { } else { qlBans = msg->qlBans; saveBans(); - log(QString("Updated banlist"), cCon); + log(uSource, "Updated banlist"); } } @@ -308,7 +308,7 @@ void Server::msgPlayerMute(Connection *cCon, MessagePlayerMute *msg) { if (! pDstUser) return; - if (! hasPermission(uSource, pDstUser->cChannel, ChanACL::MuteDeafen)) { + if ((pDstUser->iId ==0) || ! hasPermission(uSource, pDstUser->cChannel, ChanACL::MuteDeafen)) { PERM_DENIED(uSource, pDstUser->cChannel, ChanACL::MuteDeafen); return; } @@ -328,7 +328,7 @@ void Server::msgPlayerMute(Connection *cCon, MessagePlayerMute *msg) { dbus->playerStateChanged(pDstUser); - log(QString("Muted %1 (%2)").arg(pDstUser->qsName).arg(msg->bMute), cCon); + log(uSource, "Muted %s (%d)", qPrintable(pDstUser->qsName), msg->bMute); } void Server::msgPlayerDeaf(Connection *cCon, MessagePlayerDeaf *msg) { @@ -338,7 +338,7 @@ void Server::msgPlayerDeaf(Connection *cCon, MessagePlayerDeaf *msg) { if (! pDstUser) return; - if (! hasPermission(uSource, pDstUser->cChannel, ChanACL::MuteDeafen)) { + if ((pDstUser->iId ==0) || ! hasPermission(uSource, pDstUser->cChannel, ChanACL::MuteDeafen)) { PERM_DENIED(uSource, pDstUser->cChannel, ChanACL::MuteDeafen); return; } @@ -355,7 +355,7 @@ void Server::msgPlayerDeaf(Connection *cCon, MessagePlayerDeaf *msg) { dbus->playerStateChanged(pDstUser); - log(QString("Deafened %1 (%2)").arg(pDstUser->qsName).arg(msg->bDeaf), cCon); + log(uSource, "Deafened %s (%d)", qPrintable(pDstUser->qsName),msg->bDeaf); } void Server::msgPlayerKick(Connection *cCon, MessagePlayerKick *msg) { @@ -365,13 +365,13 @@ void Server::msgPlayerKick(Connection *cCon, MessagePlayerKick *msg) { if (! pDstUser) return; - if (! hasPermission(uSource, pDstUser->cChannel, ChanACL::MoveKick)) { + if ((pDstUser->iId ==0) || ! hasPermission(uSource, pDstUser->cChannel, ChanACL::MoveKick)) { PERM_DENIED(uSource, pDstUser->cChannel, ChanACL::MoveKick); return; } sendAll(msg); - log(QString("Kicked %1 (%2)").arg(pDstUser->qsName).arg(msg->qsReason), cCon); + log(uSource, "Kicked %s (%s)", qPrintable(pDstUser->qsName), qPrintable(msg->qsReason)); pDstUser->disconnectSocket(); } @@ -382,13 +382,13 @@ void Server::msgPlayerBan(Connection *cCon, MessagePlayerBan *msg) { if (! pDstUser) return; - if (! hasPermission(uSource, qhChannels.value(0), ChanACL::MoveKick)) { + if ((pDstUser->iId ==0) || ! hasPermission(uSource, qhChannels.value(0), ChanACL::MoveKick)) { PERM_DENIED(uSource, qhChannels.value(0), ChanACL::MoveKick); return; } sendAll(msg); - log(QString("Kickbanned %1 (%2)").arg(pDstUser->qsName).arg(msg->qsReason), cCon); + log(uSource, "Kickbanned %s (%s)", qPrintable(pDstUser->qsName), qPrintable(msg->qsReason)); QHostAddress adr = pDstUser->peerAddress(); quint32 base = adr.toIPv4Address(); @@ -418,7 +418,7 @@ void Server::msgPlayerMove(Connection *cCon, MessagePlayerMove *msg) { if (!c || (c == pDstUser->cChannel)) return; - if ((uSource != pDstUser) && ! hasPermission(uSource, pDstUser->cChannel, ChanACL::MoveKick)) { + if ((uSource != pDstUser) && ((pDstUser->iId ==0) || ! hasPermission(uSource, pDstUser->cChannel, ChanACL::MoveKick))) { PERM_DENIED(uSource, pDstUser->cChannel, ChanACL::MoveKick); return; } @@ -430,7 +430,7 @@ void Server::msgPlayerMove(Connection *cCon, MessagePlayerMove *msg) { sendAll(msg); playerEnterChannel(pDstUser, c); - log(QString("Moved to %1 (%2)").arg(c->qsName).arg(pDstUser->qsName), cCon); + log(uSource, "Moved %s to %s",qPrintable(pDstUser->qsName), qPrintable(c->qsName)); } void Server::msgChannelAdd(Connection *cCon, MessageChannelAdd *msg) { @@ -469,7 +469,7 @@ void Server::msgChannelAdd(Connection *cCon, MessageChannelAdd *msg) { msg->iId = c->iId; sendAll(msg); - log(QString("Added channel %1 (%2)").arg(c->qsName).arg(p->qsName), cCon); + log(uSource, "Added channel %s (%s)", qPrintable(c->qsName), qPrintable(p->qsName)); } void Server::msgChannelRemove(Connection *cCon, MessageChannelRemove *msg) { @@ -484,7 +484,7 @@ void Server::msgChannelRemove(Connection *cCon, MessageChannelRemove *msg) { return; } - log(QString("Removed channel %1").arg(c->qsName), cCon); + log(uSource, "Removed channel %s",qPrintable(c->qsName)); removeChannel(c, uSource); } @@ -518,7 +518,7 @@ void Server::msgChannelMove(Connection *cCon, MessageChannelMove *msg) { p = p->cParent; } - log(QString("Moved channel %1 (%2 -> %3)").arg(c->qsName).arg(c->cParent->qsName).arg(np->qsName), cCon); + log(uSource, "Moved channel %s from %s to %s", qPrintable(c->qsName), qPrintable(c->cParent->qsName), qPrintable(np->qsName)); c->cParent->removeChannel(c); np->addChannel(c); @@ -570,20 +570,20 @@ void Server::msgChannelLink(Connection *cCon, MessageChannelLink *msg) { c->unlink(NULL); removeLink(c, NULL); dbus->channelStateChanged(c); - log(QString("Unlinked all from channel %1").arg(c->qsName), cCon); + log(uSource, "Unlinked all from channel %s", qPrintable(c->qsName)); sendAll(msg); return; case MessageChannelLink::Link: c->link(l); addLink(c, l); dbus->channelStateChanged(c); - log(QString("Linked channel %1 and %2").arg(c->qsName).arg(l->qsName), cCon); + log(uSource, "Linked channel %s to %s", qPrintable(c->qsName),qPrintable(l->qsName)); break; case MessageChannelLink::Unlink: c->unlink(l); removeLink(c, l); dbus->channelStateChanged(c); - log(QString("Unlinked channel %1 and %2").arg(c->qsName).arg(l->qsName), cCon); + log(uSource, "Unlinked channel %s from %s", qPrintable(c->qsName), qPrintable(l->qsName)); break; case MessageChannelLink::PushLink: foreach(int tid, msg->qlTargets) { @@ -752,7 +752,7 @@ void Server::msgEditACL(Connection *cCon, MessageEditACL *msg) { } updateChannel(c); - log(QString("Updated ACL in channel %1(%2)").arg(c->qsName).arg(c->iId), cCon); + log(uSource, "Updated ACL in channel %s[%d]", qPrintable(c->qsName), c->iId); } } diff --git a/src/murmur/Meta.cpp b/src/murmur/Meta.cpp index 26dc400ff..ed24573dc 100644 --- a/src/murmur/Meta.cpp +++ b/src/murmur/Meta.cpp @@ -47,6 +47,10 @@ MetaParams::MetaParams() { qsDBDriver = "QSQLITE"; qsLogfile = "murmur.log"; qhaBind = QHostAddress(QHostAddress::Any); + + iBanTries = 10; + iBanTimeframe = 120; + iBanTime = 300; } void MetaParams::read(QString fname) { @@ -58,7 +62,7 @@ void MetaParams::read(QString fname) { } QSettings qs(fname, QSettings::IniFormat); - qDebug("Initializing settings from %s", qPrintable(qs.fileName())); + qWarning("Initializing settings from %s", qPrintable(qs.fileName())); QString qsHost = qs.value("host", QString()).toString(); if (! qsHost.isEmpty()) { @@ -75,7 +79,7 @@ void MetaParams::read(QString fname) { } } - qDebug("Binding to address %s", qPrintable(qhaBind.toString())); + qWarning("Binding to address %s", qPrintable(qhaBind.toString())); } qsPassword = qs.value("serverpassword", qsPassword).toString(); @@ -102,6 +106,10 @@ void MetaParams::read(QString fname) { qsRegHost = qs.value("registerHostname", qsRegHost).toString(); qurlRegWeb = QUrl(qs.value("registerUrl", qurlRegWeb.toString()).toString()); + iBanTries = qs.value("autobanAttempts", iBanTries).toInt(); + iBanTimeframe = qs.value("autobanTimeframe", iBanTimeframe).toInt(); + iBanTime = qs.value("autobanTime", iBanTime).toInt(); + QString qsSSLCert = qs.value("sslCert").toString(); QString qsSSLKey = qs.value("sslKey").toString(); @@ -136,7 +144,7 @@ void MetaParams::read(QString fname) { if (! key.isEmpty() && qscCert.isNull()) { qscCert = QSslCertificate(key); if (! qscCert.isNull()) { - qDebug("Using certificate from key file."); + qWarning("Using certificate from key file."); } } @@ -153,12 +161,18 @@ void MetaParams::read(QString fname) { if (! crt.isEmpty() && qskKey.isNull()) { qskKey = QSslKey(crt, alg); if (! qskKey.isNull()) { - qDebug("Using key from certificate file."); + qWarning("Using key from certificate file."); } } if (qskKey.isNull()) qscCert = QSslCertificate(); } + + if (qscCert.isNull() || qskKey.isNull()) { + if (! key.isEmpty() || ! crt.isEmpty()) { + qFatal("Certificate specified, but failed to load."); + } + } } Meta::Meta() { @@ -187,3 +201,27 @@ void Meta::kill(int srvnum) { return; delete s; } + +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; +} diff --git a/src/murmur/Meta.h b/src/murmur/Meta.h index 00bc30abc..b07ed134f 100644 --- a/src/murmur/Meta.h +++ b/src/murmur/Meta.h @@ -44,6 +44,10 @@ struct MetaParams { QString qsPassword; QString qsWelcomeText; + int iBanTries; + int iBanTimeframe; + int iBanTime; + QString qsDatabase; QString qsDBDriver; QString qsDBUserName; @@ -72,13 +76,18 @@ class Meta : public QObject { public: static MetaParams mp; QHash<int, Server *> qhServers; + QHash<QHostAddress, QList<Timer> > qhAttempts; + QHash<QHostAddress, Timer> qhBans; Meta(); void bootAll(); bool boot(int); + bool banCheck(const QHostAddress &); void kill(int); }; +extern Meta meta; + #else class Meta; #endif diff --git a/src/murmur/Register.cpp b/src/murmur/Register.cpp index eed480b6e..d8747eed2 100644 --- a/src/murmur/Register.cpp +++ b/src/murmur/Register.cpp @@ -40,9 +40,9 @@ void Server::initRegister() { if ((! qsRegName.isEmpty()) && (! qsRegPassword.isEmpty()) && (qurlRegWeb.isValid()) && (qsPassword.isEmpty())) qtTick.start(60000); else - qWarning("Registration needs nonempty name, password and url, and the server must not be password protected."); + log("Registration needs nonempty name, password and url, and the server must not be password protected."); } else { - qWarning("Not registering server as public"); + log("Not registering server as public"); } } @@ -113,10 +113,10 @@ void Server::update() { void Server::done(bool err) { if (err) { - qWarning("Regstration failed: %s", qPrintable(http->errorString())); + log("Regstration failed: %s", qPrintable(http->errorString())); } else { QByteArray qba = http->readAll(); - qWarning("Registration: %s", qPrintable(QString(QLatin1String(qba)))); + log("Registration: %s", qPrintable(QString(QLatin1String(qba)))); } abort(); } diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index b75011682..077bb7812 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -75,7 +75,6 @@ QSslSocket *SslServer::nextPendingSSLConnection() { } User::User(Server *p, QSslSocket *socket) : Connection(p, socket), Player() { - uiAddress = 0; usPort = 0; } @@ -90,9 +89,9 @@ Server::Server(int snum, QObject *p) : QThread(p) { connect(qtsServer, SIGNAL(newConnection()), this, SLOT(newClient()), Qt::QueuedConnection); if (! qtsServer->listen(qhaBind, iPort)) - qFatal("Server: TCP Listen on port %d failed",iPort); + log("Server: TCP Listen on port %d failed",iPort); - log(QString("Server listening on port %1").arg(iPort)); + log("Server listening on port %d",iPort); for (int i=1;i<5000;i++) qqIds.enqueue(i); @@ -139,12 +138,12 @@ void Server::readParams() { } } if ((qhaBind == QHostAddress::Any) || (qhaBind.isNull())) { - qWarning("Lookup of bind hostname %s failed", qPrintable(qsHost)); + log("Lookup of bind hostname %s failed", qPrintable(qsHost)); qhaBind = Meta::mp.qhaBind; } } - qDebug("Binding to address %s", qPrintable(qhaBind.toString())); + log("Binding to address %s", qPrintable(qhaBind.toString())); } qsPassword = getConf("password", qsPassword).toString(); @@ -186,15 +185,15 @@ int BandwidthRecord::bytesPerSec() { } void Server::run() { - qDebug("Starting UDP Thread"); + log("Starting UDP Thread"); qusUdp = new QUdpSocket(); if (! qusUdp->bind(qhaBind, iPort, QUdpSocket::DontShareAddress)) - qFatal("Server: UDP Bind to port %d failed",iPort); + log("Server: UDP Bind to port %d failed",iPort); #ifdef Q_OS_UNIX int val = IPTOS_PREC_FLASHOVERRIDE | IPTOS_LOWDELAY | IPTOS_THROUGHPUT; if (setsockopt(qusUdp->socketDescriptor(), IPPROTO_IP, IP_TOS, &val, sizeof(val))) - qWarning("Server: Failed to set TOS for UDP Socket"); + log("Server: Failed to set TOS for UDP Socket"); #endif connect(this, SIGNAL(tcpTransmit(QByteArray, unsigned int)), this, SLOT(tcpTransmitData(QByteArray, unsigned int)), Qt::QueuedConnection); @@ -206,8 +205,6 @@ void Server::run() { quint32 msgType; int uiSession; - qDebug("Entering UDP event loop"); - while (qusUdp->waitForReadyRead(-1)) { QReadLocker rl(&qrwlUsers); while (qusUdp->hasPendingDatagrams()) { @@ -222,26 +219,17 @@ void Server::run() { if (! pds.isValid()) continue; - Peer p(senderAddr.toIPv4Address(), senderPort); + User *u = qhUsers.value(uiSession); + if (! u) + continue; - if (p != qhPeers.value(uiSession)) { - Connection *source = qhUsers.value(uiSession); - if (! source || !(source->peerAddress() == senderAddr)) { + if ((senderAddr != u->qha) || (senderPort != u->usPort)) { + if (u->usPort != 0) continue; - } - // At any point after this, the connection might go away WHILE we're processing. That is "bad". - rl.unlock(); - { - QWriteLocker wl(&qrwlUsers); - if (qhUsers.contains(uiSession)) { - qhHosts[uiSession] = senderAddr; - qhPeers[uiSession] = p; - } - } - rl.relock(); - if (! qhUsers.contains(uiSession)) { + if (u->peerAddress() != senderAddr) continue; - } + u->qha = senderAddr; + u->usPort = senderPort; } if (msgType == Message::Ping) @@ -268,13 +256,13 @@ void Server::fakeUdpPacket(Message *msg, Connection *source) { processMsg(pds, source); } -void Server::sendMessage(unsigned int id, const char *data, int len, QByteArray &cache) { - if (qhPeers.contains(id)) { - qusUdp->writeDatagram(data, len, qhHosts[id], qhPeers[id].second); +void Server::sendMessage(User *u, const char *data, int len, QByteArray &cache) { + if (u->usPort != 0) { + qusUdp->writeDatagram(data, len, u->qha, u->usPort); } else { if (cache.isEmpty()) cache = QByteArray(data, len); - emit tcpTransmit(cache,id); + emit tcpTransmit(cache,u->uiSession); } } @@ -313,13 +301,13 @@ void Server::processMsg(PacketDataStream &pds, Connection *cCon) { int len = pds.left(); if (flags & MessageSpeex::LoopBack) { - sendMessage(u->uiSession, data, len, qba); + sendMessage(u, data, len, qba); return; } foreach(p, c->qlPlayers) { if (! p->bDeaf && ! p->bSelfDeaf && (p != static_cast<Player *>(u))) - sendMessage(p->uiSession, data, len, qba); + sendMessage(static_cast<User *>(p), data, len, qba); } if (! c->qhLinks.isEmpty()) { @@ -332,29 +320,36 @@ void Server::processMsg(PacketDataStream &pds, Connection *cCon) { if (ChanACL::hasPermission(u, l, (flags & MessageSpeex::AltSpeak) ? ChanACL::AltSpeak : ChanACL::Speak, acCache)) { foreach(p, l->qlPlayers) { if (! p->bDeaf && ! p->bSelfDeaf) - sendMessage(p->uiSession, data, len, qba); + sendMessage(static_cast<User *>(p), data, len, qba); } } } } } +void Server::log(User *u, const char *format, ...) { + char buffer[4096]; + va_list ap; + va_start(ap, format); + vsnprintf(buffer, 4096, format, ap); + va_end(ap); + char fin[4096]; + snprintf(fin, 4096, "<%d:%s(%d)> %s", u->uiSession, qPrintable(u->qsName), u->iId, buffer); -void Server::log(QString s, Connection *c) { - if (c) { - User *u = static_cast<User *>(c); - - int id = 0; - int iid = -1; - QString name; - id = u->uiSession; - iid = u->iId; - name = u->qsName; - qWarning("<%d:%s(%d)> %s", id, qPrintable(name), iid, qPrintable(s)); - } else { - qWarning("%s", qPrintable(s)); - } + dblog(fin); + qWarning("%d => %s", iServerNum, fin); +} + +void Server::log(const char *format, ...) { + char buffer[4096]; + va_list ap; + va_start(ap, format); + vsnprintf(buffer, 4096, format, ap); + va_end(ap); + + dblog(buffer); + qWarning("%d => %s", iServerNum, buffer); } void Server::newClient() { @@ -366,14 +361,22 @@ void Server::newClient() { QHostAddress adr = sock->peerAddress(); quint32 base = adr.toIPv4Address(); + if (meta.banCheck(adr)) { + log("Ignoring connection: %s:%d (Global ban)",qPrintable(sock->peerAddress().toString()),sock->peerPort()); + sock->disconnectFromHost(); + sock->deleteLater(); + return; + } + QPair<quint32,int> ban; foreach(ban, qlBans) { int mask = 32 - ban.second; mask = (1 << mask) - 1; if ((base & ~mask) == (ban.first & ~mask)) { - log(QString("Ignoring connection: %1:%2").arg(sock->peerAddress().toString()).arg(sock->peerPort())); + log("Ignoring connection: %s:%d",qPrintable(sock->peerAddress().toString()),sock->peerPort()); sock->disconnectFromHost(); + sock->deleteLater(); return; } } @@ -398,7 +401,7 @@ void Server::newClient() { connect(u, SIGNAL(message(QByteArray &)), this, SLOT(message(QByteArray &))); connect(u, SIGNAL(handleSslErrors(const QList<QSslError> &)), this, SLOT(sslError(const QList<QSslError> &))); - log(QString("New connection: %1:%2").arg(sock->peerAddress().toString()).arg(sock->peerPort()), u); + log(u, "New connection: %s:%d",qPrintable(sock->peerAddress().toString()),sock->peerPort()); sock->startServerEncryption(); } @@ -424,7 +427,7 @@ void Server::connectionClosed(QString reason) { Connection *c = dynamic_cast<Connection *>(sender()); User *u = static_cast<User *>(c); - log(QString("Connection closed: %1").arg(reason), c); + log(u, "Connection closed: %s", qPrintable(reason)); if (u->sState == Player::Authenticated) { MessageServerLeave mslMsg; @@ -446,7 +449,7 @@ void Server::connectionClosed(QString reason) { qhUserTextureCache.remove(u->iId); if (u->sState == Player::Authenticated) - clearACLCache(); + clearACLCache(u); u->deleteLater(); } @@ -478,7 +481,7 @@ void Server::checkTimeout() { qrwlUsers.lockForRead(); foreach(User *u, qhUsers) { if (u->activityTime() > (iTimeout * 1000)) { - log(QLatin1String("Timeout"), u); + log(u, "Timeout"); qlClose.append(u); } } @@ -556,6 +559,8 @@ void Server::removeChannel(Channel *chan, Player *src, Channel *dest) { } void Server::playerEnterChannel(Player *p, Channel *c, bool quiet) { + clearACLCache(p); + if (quiet && (p->cChannel == c)) return; @@ -592,10 +597,16 @@ bool Server::hasPermission(Player *p, Channel *c, ChanACL::Perm perm) { return ChanACL::hasPermission(p, c, perm, acCache); } -void Server::clearACLCache() { +void Server::clearACLCache(Player *p) { QMutexLocker qml(&qmCache); - foreach(ChanACL::ChanCache *h, acCache) - delete h; - acCache.clear(); + if (p) { + ChanACL::ChanCache *h = acCache.take(p); + if (h) + delete h; + } else { + foreach(ChanACL::ChanCache *h, acCache) + delete h; + acCache.clear(); + } } diff --git a/src/murmur/Server.h b/src/murmur/Server.h index 3111ec423..7bcc5b9ce 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -88,9 +88,8 @@ class User : public Connection, public Player { Server *s; public: BandwidthRecord bwr; - quint32 uiAddress; - quint16 usPort; QHostAddress qha; + quint16 usPort; User(Server *parent, QSslSocket *socket); }; @@ -119,7 +118,6 @@ class Server : public QThread, public MessageHandler { void readParams(); // Registration, implementation in Register.cpp - protected: QTimer qtTick; QHttp *http; void initRegister(); @@ -129,33 +127,30 @@ class Server : public QThread, public MessageHandler { void abort(); // Certificate stuff, implemented partially in Cert.cpp - protected: + public: void initializeCert(); const QString getDigest() const; - protected: - QQueue<int> qqIds; - SslServer *qtsServer; - QTimer *qtTimeout; - protected slots: + + public slots: void newClient(); void connectionClosed(QString); void sslError(const QList<QSslError> &); void message(QByteArray &, Connection *cCon = NULL); void checkTimeout(); void tcpTransmitData(QByteArray, unsigned int); + signals: + void tcpTransmit(QByteArray, unsigned int id); public: int iServerNum; - + QQueue<int> qqIds; + SslServer *qtsServer; + QTimer *qtTimeout; + QUdpSocket *qusUdp; QHash<unsigned int, User *> qhUsers; QHash<unsigned int, Channel *> qhChannels; QReadWriteLock qrwlUsers; - ChanACL::ACLCache acCache; QMutex qmCache; - - bool hasPermission(Player *p, Channel *c, ChanACL::Perm perm); - void clearACLCache(); - MurmurDBus *dbus; QHash<int, QByteArray> qhUserTextureCache; @@ -164,20 +159,26 @@ class Server : public QThread, public MessageHandler { QList<QPair<quint32, int> > qlBans; + void processMsg(PacketDataStream &pds, Connection *cCon); + void sendMessage(User *u, const char *data, int len, QByteArray &cache); + void fakeUdpPacket(Message *msg, Connection *source); + void run(); + + bool hasPermission(Player *p, Channel *c, ChanACL::Perm perm); + void clearACLCache(Player *p = NULL); + void sendAll(Message *); void sendExcept(Message *, Connection *); void sendMessage(unsigned int, Message *); void sendMessage(Connection *, Message *); - void log(QString s, Connection *c = NULL); +// void log(QString s, Connection *c = NULL); + __attribute__((format(printf, 2, 3))) void log(const char *format, ...); + __attribute__((format(printf, 3, 4))) void log(User *u, const char *format, ...); void removeChannel(Channel *c, Player *src, Channel *dest = NULL); void playerEnterChannel(Player *u, Channel *c, bool quiet = false); - void emitPacket(Message *msg); - - User *getUser(unsigned int); - Server(int snum, QObject *parent = NULL); ~Server(); @@ -203,6 +204,7 @@ class Server : public QThread, public MessageHandler { void saveBans(); QVariant getConf(const QString &key, QVariant def); void setConf(const QString &key, const QVariant &value); + void dblog(const char *str); // From msgHandler. Implementation in Messages.cpp virtual void msgSpeex(Connection *, MessageSpeex *); @@ -230,18 +232,6 @@ class Server : public QThread, public MessageHandler { virtual void msgQueryUsers(Connection *, MessageQueryUsers *); virtual void msgTexture(Connection *, MessageTexture *); - // UDP Handling - protected: - QUdpSocket *qusUdp; - QHash<unsigned int, Peer> qhPeers; - QHash<unsigned int, QHostAddress> qhHosts; - void processMsg(PacketDataStream &pds, Connection *cCon); - void sendMessage(unsigned int id, const char *data, int len, QByteArray &cache); - signals: - void tcpTransmit(QByteArray, unsigned int id); - public: - void fakeUdpPacket(Message *msg, Connection *source); - void run(); }; diff --git a/src/murmur/ServerDB.cpp b/src/murmur/ServerDB.cpp index 73c97a6cf..edf46d976 100644 --- a/src/murmur/ServerDB.cpp +++ b/src/murmur/ServerDB.cpp @@ -107,7 +107,7 @@ ServerDB::ServerDB() { } if (found) { QFileInfo fi(db.databaseName()); - qDebug("Openend SQLite database %s", qPrintable(fi.absoluteFilePath())); + qWarning("Openend SQLite database %s", qPrintable(fi.absoluteFilePath())); } } else { db.setDatabaseName(Meta::mp.qsDatabase); @@ -147,8 +147,13 @@ ServerDB::ServerDB() { } SQLDO("CREATE TABLE %1meta (key TEXT PRIMARY KEY, value TEXT)"); SQLDO("CREATE TABLE %1servers (server_id INTEGER PRIMARY KEY AUTOINCREMENT)"); + + SQLDO("CREATE TABLE %1log(server_id INTEGER, msg TEXT, msgtime DATE)"); + SQLDO("CREATE TRIGGER %1log_timestamp AFTER INSERT ON %1log FOR EACH ROW BEGIN UPDATE %1log SET msgtime = datetime('now') WHERE rowid = new.rowid; END;"); + SQLDO("CREATE TABLE %1config (server_id INTEGER, key TEXT, value TEXT)"); SQLDO("CREATE UNIQUE INDEX %1config_key ON %1config(server_id, key)"); + SQLDO("CREATE TRIGGER %1config_server_del AFTER DELETE ON %1servers FOR EACH ROW BEGIN DELETE FROM %1config WHERE server_id = old.server_id; END;"); SQLDO("CREATE TABLE %1players (server_id INTEGER, player_id INTEGER, name TEXT, email TEXT, pw TEXT, lastchannel INTEGER, texture BLOB, last_active DATE)"); SQLDO("CREATE UNIQUE INDEX %1players_name ON %1players (server_id,name)"); @@ -187,7 +192,7 @@ ServerDB::ServerDB() { SQLDO("INSERT INTO %1meta (key, value) VALUES('version','1')"); if (migrate) { - qDebug("Migrating from single-server database to multi-server database"); + qWarning("Migrating from single-server database to multi-server database"); SQLDO("INSERT INTO %1players SELECT 1, player_id, name, email, pw, lastchannel, texture, null FROM playersold"); SQLDO("INSERT INTO %1channels SELECT 1, channel_id, parent_id, name, inheritACL FROM channelsold"); SQLDO("INSERT INTO %1groups SELECT group_id, 1, name, channel_id, inherit, inheritable FROM groupsold"); @@ -570,7 +575,7 @@ void Server::readChannelPrivs(Channel *c) { g->bInheritable = query.value(3).toBool(); QSqlQuery mem; - mem.prepare("SELECT player_id, addit FROM %1group_members WHERE group_id = ?"); + mem.prepare(QString::fromLatin1("SELECT player_id, addit FROM %1group_members WHERE group_id = ?").arg(Meta::mp.qsDBPrefix)); mem.addBindValue(gid); mem.exec(); while (mem.next()) { @@ -754,6 +759,15 @@ void Server::setConf(const QString &key, const QVariant &value) { ServerDB::setConf(iServerNum, key, value); } +void Server::dblog(const char *str) { + TransactionHolder th; + QSqlQuery query; + SQLPREP("INSERT INTO %1log (server_id, msg) VALUES(?,?)"); + query.addBindValue(iServerNum); + query.addBindValue(QLatin1String(str)); + SQLEXEC(); +} + void ServerDB::setConf(int server_id, const QString &key, const QVariant &value) { TransactionHolder th; diff --git a/src/murmur/murmur.cpp b/src/murmur/murmur.cpp index b80cf4924..012d438d2 100644 --- a/src/murmur/murmur.cpp +++ b/src/murmur/murmur.cpp @@ -56,6 +56,8 @@ bool detach = true; bool detach = false; #endif +Meta meta; + LogEmitter le; static void murmurMessageOutput(QtMsgType type, const char *msg) { @@ -219,8 +221,6 @@ int main(int argc, char **argv) { MurmurDBus::registerTypes(); #endif - Meta m; - #ifdef Q_OS_UNIX if (! Meta::mp.qsDBus.isEmpty()) { QDBusConnection qdbc("mainbus"); @@ -244,13 +244,13 @@ int main(int argc, char **argv) { qWarning("Failed to connect to D-Bus %s",qPrintable(Meta::mp.qsDBus)); } MurmurDBus::qdbc = qdbc; - new MetaDBus(&m); - qdbc.registerObject("/", &m); + new MetaDBus(&meta); + qdbc.registerObject("/", &meta); qdbc.registerService("net.sourceforge.mumble.murmur"); } #endif - m.bootAll(); + meta.bootAll(); res=a.exec(); |