diff --git a/Changelog b/Changelog index 57c77e118..eb607e93a 100644 --- a/Changelog +++ b/Changelog @@ -17,6 +17,7 @@ - BUGFIX: Optimized RSS module memory usage - BUGFIX: Consider HTTP downloads >1MB as invalid .torrent files and abort - BUGFIX: Fix Web UI authentication with some browsers + - BUGFIX: Set Web UI ban period to 1 hour - COSMETIC: Improved style management * Mon Jan 18 2010 - Christophe Dumez - v2.1.0 diff --git a/src/httpconnection.cpp b/src/httpconnection.cpp index 8d37d419e..af544119a 100644 --- a/src/httpconnection.cpp +++ b/src/httpconnection.cpp @@ -47,7 +47,7 @@ #include HttpConnection::HttpConnection(QTcpSocket *socket, Bittorrent *BTSession, HttpServer *parent) - : QObject(parent), socket(socket), parent(parent), BTSession(BTSession) + : QObject(parent), socket(socket), parent(parent), BTSession(BTSession) { socket->setParent(this); connect(socket, SIGNAL(readyRead()), this, SLOT(read())); @@ -131,8 +131,9 @@ QString HttpConnection::translateDocument(QString data) { void HttpConnection::respond() { //qDebug("Respond called"); - int nb_fail = parent->client_failed_attempts.value(socket->peerAddress().toString(), 0); - if(nb_fail > 4) { + const QString &peer_ip = socket->peerAddress().toString(); + const int nb_fail = parent->NbFailedAttemptsForIp(peer_ip); + if(nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) { generator.setStatusLine(403, "Forbidden"); generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts.")); write(); @@ -142,8 +143,8 @@ void HttpConnection::respond() { qDebug("Auth: %s", qPrintable(auth.split(" ").first())); if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) { // Update failed attempt counter - parent->client_failed_attempts.insert(socket->peerAddress().toString(), nb_fail+1); - qDebug("client IP: %s (%d failed attempts)", qPrintable(socket->peerAddress().toString()), nb_fail); + parent->increaseNbFailedAttemptsForIp(peer_ip); + qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail+1); // Return unauthorized header generator.setStatusLine(401, "Unauthorized"); generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", algorithm=\"MD5\", qop=\"auth\""); @@ -151,7 +152,7 @@ void HttpConnection::respond() { return; } // Client sucessfuly authenticated, reset number of failed attempts - parent->client_failed_attempts.remove(socket->peerAddress().toString()); + parent->resetNbFailedAttemptsForIp(peer_ip); QString url = parser.url(); // Favicon if(url.endsWith("favicon.ico")) { diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0edaafcec..292827194 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -37,6 +37,48 @@ #include #include #include +#include + +const int BAN_TIME = 3600000; // 1 hour + +class UnbanTimer: public QTimer { + public: + UnbanTimer(QObject *parent, QString peer_ip): QTimer(parent), peer_ip(peer_ip){ + setSingleShot(true); + setInterval(BAN_TIME); + } + ~UnbanTimer() { + qDebug("||||||||||||Deleting ban timer|||||||||||||||"); + } + QString peer_ip; +}; + +void HttpServer::UnbanTimerEvent() { + UnbanTimer* ubantimer = static_cast(sender()); + qDebug("Ban period has expired for %s", qPrintable(ubantimer->peer_ip)); + client_failed_attempts.remove(ubantimer->peer_ip); + ubantimer->deleteLater(); +} + +int HttpServer::NbFailedAttemptsForIp(QString ip) const { + return client_failed_attempts.value(ip, 0); +} + +void HttpServer::increaseNbFailedAttemptsForIp(QString ip) { + const int nb_fail = client_failed_attempts.value(ip, 0); + client_failed_attempts.insert(ip, nb_fail+1); + if(nb_fail == MAX_AUTH_FAILED_ATTEMPTS-1) { + // Max number of failed attempts reached + // Start ban period + UnbanTimer* ubantimer = new UnbanTimer(this, ip); + connect(ubantimer, SIGNAL(timeout()), this, SLOT(UnbanTimerEvent())); + ubantimer->start(); + } +} + +void HttpServer::resetNbFailedAttemptsForIp(QString ip) { + client_failed_attempts.remove(ip); +} HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) { username = Preferences::getWebUiUsername().toLocal8Bit(); diff --git a/src/httpserver.h b/src/httpserver.h index 373708efe..940ef5e4b 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -42,28 +42,34 @@ class Bittorrent; class QTimer; class EventManager; +const int MAX_AUTH_FAILED_ATTEMPTS = 5; + class HttpServer : public QTcpServer { - Q_OBJECT + Q_OBJECT - private: - QByteArray username; - QByteArray password_ha1; - Bittorrent *BTSession; - EventManager *manager; - QTimer *timer; +public: + HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0); + ~HttpServer(); + void setAuthorization(QString username, QString password_ha1); + bool isAuthorized(QByteArray auth, QString method) const; + EventManager *eventManager() const; + QString generateNonce() const; + int NbFailedAttemptsForIp(QString ip) const; + void increaseNbFailedAttemptsForIp(QString ip); + void resetNbFailedAttemptsForIp(QString ip); - public: - HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0); - ~HttpServer(); - void setAuthorization(QString username, QString password_ha1); - bool isAuthorized(QByteArray auth, QString method) const; - EventManager *eventManager() const; - QString generateNonce() const; - QHash client_failed_attempts; +private slots: + void newHttpConnection(); + void onTimer(); + void UnbanTimerEvent(); - private slots: - void newHttpConnection(); - void onTimer(); +private: + QByteArray username; + QByteArray password_ha1; + Bittorrent *BTSession; + EventManager *manager; + QTimer *timer; + QHash client_failed_attempts; }; #endif