diff --git a/engine/server/application/LoginServer/src/shared/ClientConnection.cpp b/engine/server/application/LoginServer/src/shared/ClientConnection.cpp index 65c94369..1d108247 100755 --- a/engine/server/application/LoginServer/src/shared/ClientConnection.cpp +++ b/engine/server/application/LoginServer/src/shared/ClientConnection.cpp @@ -57,13 +57,16 @@ ClientConnection::~ClientConnection() void ClientConnection::onConnectionClosed() { // client has disconnected - DEBUG_REPORT_LOG(true, ("Client %lu disconnected\n", getStationId())); - LOG("LoginClientConnection", ("onConnectionClosed() for stationId (%lu) at IP (%s)", m_stationId, getRemoteAddress().c_str())); - LoginServer::getInstance().removeClient(m_clientId); - - if (!m_isValidated) + if (m_clientId) { - SessionApiClient * session = LoginServer::getInstance().getSessionApiClient(); + // if it is a 0 they are a dos faggot, most likely + DEBUG_REPORT_LOG(true, ("Client %lu disconnected\n", m_stationId)); + LOG("LoginClientConnection", ("onConnectionClosed() for stationId (%lu) at IP (%s)", m_stationId, getRemoteAddress().c_str())); + LoginServer::getInstance().removeClient(m_clientId); + } + + if (!m_isValidated) { + SessionApiClient *session = LoginServer::getInstance().getSessionApiClient(); if (session) session->dropClient(this); } diff --git a/engine/server/application/LoginServer/src/shared/ConfigLoginServer.cpp b/engine/server/application/LoginServer/src/shared/ConfigLoginServer.cpp index 1e11c8df..06218435 100755 --- a/engine/server/application/LoginServer/src/shared/ConfigLoginServer.cpp +++ b/engine/server/application/LoginServer/src/shared/ConfigLoginServer.cpp @@ -69,7 +69,7 @@ void ConfigLoginServer::install(void) KEY_INT (centralServicePort, 44452); KEY_INT (clientServicePort, 44453); KEY_INT (clientOverflowLimit, 1024 * 8); - KEY_INT (maxClients, 3000); + KEY_INT (maxClients, 250); KEY_INT (taskServicePort, 44459); KEY_INT (pingServicePort, 44460); KEY_INT (httpServicePort, 44490); @@ -116,6 +116,7 @@ void ConfigLoginServer::install(void) KEY_BOOL(requireSecureLoginForCsTool, true); KEY_BOOL(useExternalAuth, false); KEY_STRING(externalAuthURL, ""); + KEY_INT(maxConnectionsPerIP, 2); // assuming 2 people playing in the same household int index = 0; char const * result = 0; diff --git a/engine/server/application/LoginServer/src/shared/ConfigLoginServer.h b/engine/server/application/LoginServer/src/shared/ConfigLoginServer.h index 3a84098a..330eb9f3 100755 --- a/engine/server/application/LoginServer/src/shared/ConfigLoginServer.h +++ b/engine/server/application/LoginServer/src/shared/ConfigLoginServer.h @@ -17,14 +17,14 @@ class ConfigLoginServer int maxClients; int taskServicePort; int pingServicePort; - int httpServicePort; + int httpServicePort; bool validateClientVersion; bool validateStationKey; bool doSessionLogin; - bool doConsumption; + bool doConsumption; const char * sessionServers; int sessionType; - + const char * DSN; const char * databaseUID; const char * schemaOwner; @@ -54,7 +54,7 @@ class ConfigLoginServer const char * purgeAccountSourceTable; const char * adminAccountDataTable; bool allowSkipTutorialToAll; - + bool internalBypassOnlineLimit; int populationExtremelyHeavyThresholdPercent; @@ -63,11 +63,13 @@ class ConfigLoginServer int populationMediumThresholdPercent; int populationLightThresholdPercent; int csToolPort; - - bool requireSecureLoginForCsTool; - bool useExternalAuth; + + bool requireSecureLoginForCsTool; + bool useExternalAuth; const char * externalAuthURL; + + int maxConnectionsPerIP; }; static const uint16 getCentralServicePort(); @@ -82,7 +84,7 @@ class ConfigLoginServer static const bool getDoConsumption(); static const char * getSessionServers(); static const int getSessionType(); - + static const int getMaxClients (); static const char * getPrivateIpMask(); static const char * getDSN(); @@ -105,7 +107,7 @@ class ConfigLoginServer static void install (); static void remove (); - static int getNumberOfSessionServers(); + static int getNumberOfSessionServers(); static char const * getSessionServer(int index); static const int getDisconnectSleepTime (void); static const int getClusterGroup(); @@ -118,24 +120,26 @@ class ConfigLoginServer static int getPurgePhaseAdvanceDays(int purgePhase); static const char * getAdminAccountDataTable(); static bool getAllowSkipTutorialToAll(); - + static bool getInternalBypassOnlineLimit(); static const int getCSToolPort(); - static bool getRequireSecureLoginForCsTool(); + static bool getRequireSecureLoginForCsTool(); static int getPopulationExtremelyHeavyThresholdPercent(); static int getPopulationVeryHeavyThresholdPercent(); static int getPopulationHeavyThresholdPercent(); static int getPopulationMediumThresholdPercent(); static int getPopulationLightThresholdPercent(); - - static bool getUseExternalAuth(); + + static bool getUseExternalAuth(); static const char * getExternalAuthUrl(); // has character creation for this cluster been disabled through config option static bool isCharacterCreationDisabled(std::string const & cluster); - static stdset::fwd const & getCharacterCreationDisabledClusterList(); + static stdset::fwd const & getCharacterCreationDisabledClusterList(); + + static const int getMaxConnectionsPerIP(); private: static Data * data; @@ -483,6 +487,11 @@ inline const char * ConfigLoginServer::getExternalAuthUrl() { return data->externalAuthURL; } + +inline const int ConfigLoginServer::getMaxConnectionsPerIP() +{ + return data->maxConnectionsPerIP; +} // ====================================================================== #endif // _ConfigLoginServer_H diff --git a/engine/server/application/LoginServer/src/shared/LoginServer.cpp b/engine/server/application/LoginServer/src/shared/LoginServer.cpp index 2216d3cb..177bfec5 100755 --- a/engine/server/application/LoginServer/src/shared/LoginServer.cpp +++ b/engine/server/application/LoginServer/src/shared/LoginServer.cpp @@ -146,11 +146,11 @@ LoginServer::LoginServer() : setup.compress = ConfigLoginServer::getCompressClientNetworkTraffic(); setup.useTcp = false; - clientService = new Service(ConnectionAllocator(), setup); + clientService = new Service(ConnectionAllocator(), setup, ConfigLoginServer::getMaxConnectionsPerIP()); setup.compress = false; setup.port = ConfigLoginServer::getPingServicePort(); - pingService = new Service(ConnectionAllocator(), setup); + pingService = new Service(ConnectionAllocator(), setup, ConfigLoginServer::getMaxConnectionsPerIP()); setup.useTcp = true; if (ConfigLoginServer::getDevelopmentMode()) @@ -246,16 +246,22 @@ ClientConnection* LoginServer::getUnvalidatedClient(int clientId) void LoginServer::removeClient(int clientId) { - WARNING_STRICT_FATAL(clientId == 0, ("Tried to remove a client with client id == 0")); - std::map::iterator i = m_clientMap.find(clientId); - if (i != m_clientMap.end()) - { - if (i->second->getIsValidated()) - { - IGNORE_RETURN(m_validatedClientMap.erase(i->second->getStationId())); - } - IGNORE_RETURN(m_clientMap.erase(clientId)); - } + if (clientId) // yeah why bother if it's 0 or null? i realize 0 is a valid int but since previously the warning below fired if it was 0....yeah, no + { + std::map::iterator i = m_clientMap.find(clientId); + if (i != m_clientMap.end()) + { + if (i->second->getIsValidated()) + { + IGNORE_RETURN(m_validatedClientMap.erase(i->second->getStationId())); + } + IGNORE_RETURN(m_clientMap.erase(clientId)); + } + } + else + { + WARNING_STRICT_FATAL(true, ("Tried to remove a client with client id == 0")); + } } //----------------------------------------------------------------------- diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp index 7c40e0ae..ed5d8ddf 100755 --- a/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp @@ -69,25 +69,21 @@ bool DB::OCIServer::checkerr(OCISession const & session, int status) WARNING(true,("Database error: %.*s",512,errbuf)); LOG("DatabaseError",("Database error: %.*s",512,errbuf)); FATAL(DB::Server::getFatalOnError() || session.getFatalOnError(),("Database error: %.*s",512,errbuf)); - - return false; - - /* - switch (errcode) + + switch ((int) errcode) { case 1013: FATAL(true,("Cancelled by user request (ctrl-c or kill signal).\n")); break; - case 12541: REPORT_LOG(true,("Database Error - %.*s\n", 512, errbuf)); return false; - default: FATAL(true,("Unhandled Database Error - %.*s\n", 512, errbuf)); break; - } - */ + } + + return false; } case OCI_INVALID_HANDLE: REPORT_LOG(true,("Error - OCI_INVALID_HANDLE\n")); diff --git a/engine/shared/library/sharedNetwork/src/shared/Service.cpp b/engine/shared/library/sharedNetwork/src/shared/Service.cpp index 9e0f0c8f..7558aa29 100755 --- a/engine/shared/library/sharedNetwork/src/shared/Service.cpp +++ b/engine/shared/library/sharedNetwork/src/shared/Service.cpp @@ -24,10 +24,11 @@ ConnectionAllocatorBase::~ConnectionAllocatorBase() //----------------------------------------------------------------------- //Service::Service(const ConnectionAllocatorBase & c, const unsigned short port, const int m, const int keepAliveDelay, const std::string & interfaceAddress, const bool compress) : -Service::Service(const ConnectionAllocatorBase & c, const NetworkSetupData & setup) : +Service::Service(const ConnectionAllocatorBase & c, const NetworkSetupData & setup, const int maxConnectionsPerIP) : connectionAllocator(c.clone()), m_callback(new MessageDispatch::Callback), -m_tcpServer(0) +m_tcpServer(0), +m_maxConnectionsPerIP(maxConnectionsPerIP) { std::string realAddress; if(setup.bindInterface.length() > 0) @@ -165,7 +166,7 @@ m_tcpServer(0) m_maxConnections = setup.maxConnections; newService(this); - m_callback->connect(*this, &Service::onConnectionClosed); + `->connect(*this, &Service::onConnectionClosed); } //----------------------------------------------------------------------- @@ -214,10 +215,38 @@ void Service::onConnectionOpened(Connection * c) { if (c) { - c->setService(this); - c->onConnectionOpened(); - connections.insert(c); - WARNING((connections.size() >= static_cast(m_maxConnections)), ("Service has reached it's maximum connection count %d", m_maxConnections)); + if (connections.size() < static_cast(m_maxConnections) || m_maxConnections == 0) + { + std::string remoteIP = c->getRemoteAddress(); + int numConnections = 0; + + // if we're trying to throttle dos attempts + if (m_maxConnectionsPerIP > 0) + { + // we have to recount each time so that we don't cut off legit users + for (auto i = connections.begin(); i != connections.end(); ++i) { + if (remoteIP == (*i)->getRemoteAddress()) { + numConnections++; + } + } + } + + if (m_maxConnectionsPerIP == 0 || (numConnections < m_maxConnectionsPerIP)) + { + c->setService(this); + c->onConnectionOpened(); + connections.insert(c); + } + else + { + WARNING(true, ("Client at IP %s has attempted more connections than allowed (%i). Potential DoS Attack?", remoteIP.c_str(), m_maxConnectionsPerIP)); + removeConnection(c); // asshole (probably) + } + } + else + { + WARNING(true, ("Service has reached it's maximum connection count (%d).", m_maxConnections)); + } } } @@ -260,6 +289,7 @@ int Service::flushAndConfirmAllData() unsigned long startTime = Clock::timeMs(); bool needReport = false; unsigned long stallReportDelay = ConfigSharedNetwork::getStallReportDelay(); + do { if(stallReportDelay > 0) diff --git a/engine/shared/library/sharedNetwork/src/shared/Service.h b/engine/shared/library/sharedNetwork/src/shared/Service.h index c300c0bb..f32d4d06 100755 --- a/engine/shared/library/sharedNetwork/src/shared/Service.h +++ b/engine/shared/library/sharedNetwork/src/shared/Service.h @@ -59,7 +59,7 @@ class Service : public NetworkHandler { public: //Service(const ConnectionAllocatorBase & connectionAllocator, const unsigned short listenPort, const int maxConnections, const int keepAliveDelay = 1000, const std::string & interfaceAddress = std::string(""), const bool compress=false); - Service(const ConnectionAllocatorBase & connectionAllocator, const NetworkSetupData & setupData); + Service(const ConnectionAllocatorBase & connectionAllocator, const NetworkSetupData & setupData, const int maxConnectionsPerIP = 0); virtual ~Service(); void onConnectionOpened(TcpClient * t); @@ -86,6 +86,7 @@ private: const ConnectionAllocatorBase * connectionAllocator; std::set connections; int m_maxConnections; + int m_maxConnectionsPerIP; // dos protection for ping and login servers MessageDispatch::Callback * m_callback; TcpServer * m_tcpServer; }; diff --git a/external/3rd/library/udplibrary/UdpLibrary.cpp b/external/3rd/library/udplibrary/UdpLibrary.cpp index c8436157..66440c3d 100755 --- a/external/3rd/library/udplibrary/UdpLibrary.cpp +++ b/external/3rd/library/udplibrary/UdpLibrary.cpp @@ -992,9 +992,9 @@ void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) if (con == nullptr) { - // packet coming from an unknown ip/port - // if it is a connection request packet, then establish a new connection object to reply to it - // connection establish packet must always be at least 6 bytes long as we must have a version number, no matter how it changes + // packet coming from an unknown ip/port + // if it is a connection request packet, then establish a new connection object to reply to it + // connection establish packet must always be at least 6 bytes long as we must have a version number, no matter how it changes if (e->mBuffer[0] == 0 && e->mBuffer[1] == UdpConnection::cUdpPacketConnect && e->mLen == UdpConnection::cUdpPacketConnectSize) { if (mConnectionListCount >= mParams.maxConnections) @@ -1009,8 +1009,8 @@ void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) mParams.handler->OnConnectRequest(newcon); if (newcon->GetRefCount() == 1) { - // we are going to end up destroying this connection when we release it on this next line - // so disconnect it first giving it a reason + // we are going to end up destroying this connection when we release it on this next line + // so disconnect it first giving it a reason newcon->InternalDisconnect(0, UdpConnection::cDisconnectReasonConnectionRefused); } newcon->Release(); @@ -1023,9 +1023,9 @@ void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) { if (e->mBuffer[0] == 0 && e->mBuffer[1] == UdpConnection::cUdpPacketRequestRemap) { - // ok, we got a packet from somebody, that we don't know who they are, but, it appears they are asking - // for their address/port to be remapped. If we allow port (and/or address) remapping, then go ahead - // an honor their request if possible + // ok, we got a packet from somebody, that we don't know who they are, but, it appears they are asking + // for their address/port to be remapped. If we allow port (and/or address) remapping, then go ahead + // an honor their request if possible uchar *ptr = e->mBuffer + 2; int connectCode = UdpMisc::GetValue32(ptr); ptr += 4; @@ -1036,10 +1036,10 @@ void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) { if (mParams.allowAddressRemapping || con->mIp == e->mIp) { - // one final security check to ensure these are really the same connection, compare encryption codes + // one final security check to ensure these are really the same connection, compare encryption codes if (con->mConnectionConfig.encryptCode == encryptCode) { - // remapping is allowed, remap ourselves to the address of the incoming request + // remapping is allowed, remap ourselves to the address of the incoming request mAddressHashTable->Remove(con, AddressHashValue(con->mIp, con->mPort)); con->mIp = e->mIp; con->mPort = e->mPort; @@ -1052,18 +1052,18 @@ void UdpManager::ProcessRawPacket(const PacketHistoryEntry *e) } - // got a packet from somebody and we don't know who they are and the packet we got was not a connection request - // just in case they are a previous client who thinks they are still connected, we will send them an internal - // packet telling them that we don't know who they are + // got a packet from somebody and we don't know who they are and the packet we got was not a connection request + // just in case they are a previous client who thinks they are still connected, we will send them an internal + // packet telling them that we don't know who they are if (mParams.replyUnreachableConnection) { - // do not reply back with unreachable if the packet coming in is a terminate or unreachable packet itself + // do not reply back with unreachable if the packet coming in is a terminate or unreachable packet itself if (e->mBuffer[0] != 0 || (e->mBuffer[0] == 0 && e->mBuffer[1] != UdpConnection::cUdpPacketUnreachableConnection && e->mBuffer[1] != UdpConnection::cUdpPacketTerminate)) { - // since we do not have a connection-object associated with this incoming packet, there is no way we could - // encrypt it or add CRC bytes to it, since we have no idea what the other end of the connection is expecting - // in this regard. As such, the UnreachableConnection packet (like the connect and confirm packets) is one - // of those internal packet types that is designated as not being encrypted or CRC'ed. + // since we do not have a connection-object associated with this incoming packet, there is no way we could + // encrypt it or add CRC bytes to it, since we have no idea what the other end of the connection is expecting + // in this regard. As such, the UnreachableConnection packet (like the connect and confirm packets) is one + // of those internal packet types that is designated as not being encrypted or CRC'ed. unsigned char buf[8]; buf[0] = 0; buf[1] = UdpConnection::cUdpPacketUnreachableConnection; @@ -1389,10 +1389,10 @@ void UdpConnection::InternalDisconnect(int flushTimeout, DisconnectReason reason return; } - // send a termination packet to the other side - // do not send a termination packet if we are still negotiating (we are not allowed to send any packets while negotiating) - // if you attempt to send a packet while negotiating, then it will potentially attempt to encrypt it before an encryption - // method is determined, resulting in a function call through an invalid pointer + // send a termination packet to the other side + // do not send a termination packet if we are still negotiating (we are not allowed to send any packets while negotiating) + // if you attempt to send a packet while negotiating, then it will potentially attempt to encrypt it before an encryption + // method is determined, resulting in a function call through an invalid pointer if (!mSilentDisconnect) { if (mStatus == cStatusConnected || mStatus == cStatusDisconnectPending)