From 7283431c8c857bc4a6c859b1520103c7474be4ee Mon Sep 17 00:00:00 2001 From: lotodore Date: Sun, 24 Feb 2008 12:56:39 +0000 Subject: [PATCH] Limit the avatar requests by the server - clients may only upload one avatar within a certain time. This is checked by IP address, so two players within the same NAT system might have to wait for a few seconds. --- src/net/common/connectdata.cpp | 2 +- src/net/common/serveracceptthread.cpp | 32 +++++++++++---- src/net/common/serverlobbythread.cpp | 59 ++++++++++++++++++++++++--- src/net/connectdata.h | 10 ++++- src/net/linux/socket_helper.cpp | 6 +++ src/net/serverlobbythread.h | 10 ++++- src/net/socket_helper.h | 7 ++++ src/net/win32/socket_helper.cpp | 9 ++++ 8 files changed, 117 insertions(+), 18 deletions(-) diff --git a/src/net/common/connectdata.cpp b/src/net/common/connectdata.cpp index 93883004..f6a50240 100644 --- a/src/net/common/connectdata.cpp +++ b/src/net/common/connectdata.cpp @@ -20,7 +20,7 @@ #include ConnectData::ConnectData() -: m_sockfd(INVALID_SOCKET) +: m_sockfd(INVALID_SOCKET), m_peerAddrSize(sizeof(m_peerAddr)) { } diff --git a/src/net/common/serveracceptthread.cpp b/src/net/common/serveracceptthread.cpp index 9f3600e1..b7b6652c 100644 --- a/src/net/common/serveracceptthread.cpp +++ b/src/net/common/serveracceptthread.cpp @@ -182,17 +182,31 @@ ServerAcceptThread::AcceptLoop() { throw ServerException(__FILE__, __LINE__, ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); } - // Optional calls - don't check return value. - // Enable keepalive - won't be of much use but better than nothing. - int keepalive = 1; - setsockopt(tmpData->GetSocket(), SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive)); -#ifdef SO_NOSIGPIPE - int nosigpipe = 1; - setsockopt(tmpData->GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&nosigpipe, sizeof(nosigpipe)); -#endif + // Retrieve peer address. + int addrLen = tmpData->GetPeerAddrSize(); + if (getpeername(tmpData->GetSocket(), tmpData->GetPeerAddr(), &addrLen) != 0) + { + // Something went wrong with the connection, just continue (socket will be closed). + LOG_ERROR("getpeername() failed: " << SOCKET_ERRNO()); + } + else + { + // Set the size of the peer address. + tmpData->SetPeerAddrSize(addrLen); - GetLobbyThread().AddConnection(tmpData); + // Optional calls - don't check return value. + // Enable keepalive - won't be of much use but better than nothing. + int keepalive = 1; + setsockopt(tmpData->GetSocket(), SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive)); + + #ifdef SO_NOSIGPIPE + int nosigpipe = 1; + setsockopt(tmpData->GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&nosigpipe, sizeof(nosigpipe)); + #endif + + GetLobbyThread().AddConnection(tmpData); + } } } diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index 91030bab..42c855d8 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -38,6 +38,7 @@ #define SERVER_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day #define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60 #define SERVER_INIT_SESSION_TIMEOUT_SEC 20 +#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar. #define SERVER_STATISTICS_FILE_NAME "server_statistics.log" #define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn" @@ -313,6 +314,8 @@ ServerLobbyThread::Main() RemoveGameLoop(); // Kick players. KickPlayerLoop(); + // Update avatar limitation lock. + UpdateAvatarClientTimerLoop(); // Cleanup cache. CleanupAvatarCache(); // Save statistics if needed. @@ -442,10 +445,25 @@ ServerLobbyThread::HandleNetPacketInit(SessionWrapper session, const NetPacketIn m_sessionManager.SetSessionPlayerData(session.sessionData->GetId(), tmpPlayerData); session.playerData = tmpPlayerData; - if (initData.showAvatar && !initData.avatar.IsZero() && !GetAvatarManager().HasAvatar(initData.avatar)) - RequestPlayerAvatar(session); + if (initData.showAvatar + && !initData.avatar.IsZero() + && !GetAvatarManager().HasAvatar(initData.avatar)) + { + bool avatarRecentlyRequested = false; + { + boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); + if (m_timerAvatarClientAddressMap.find(session.sessionData->GetClientAddr()) != m_timerAvatarClientAddressMap.end()) + avatarRecentlyRequested = true; + } + if (avatarRecentlyRequested) + SessionError(session, ERR_NET_INVALID_AVATAR_FILE); + else + RequestPlayerAvatar(session); + } else + { EstablishSession(session); + } } void @@ -698,6 +716,11 @@ ServerLobbyThread::RequestPlayerAvatar(SessionWrapper session) { if (!session.playerData.get()) throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0); + // Accept no more new avatars from that client for a certain time. + { + boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); + m_timerAvatarClientAddressMap[session.sessionData->GetClientAddr()] = boost::timers::portable::microsec_timer(); + } // Ask the client to send its avatar. boost::shared_ptr retrieveAvatar(new NetPacketRetrieveAvatar); NetPacketRetrieveAvatar::Data retrieveAvatarData; @@ -745,13 +768,13 @@ void ServerLobbyThread::CloseSessionLoop() { boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex); - InitTimerSessionMap::iterator i = m_initTimerSessionMap.begin(); - InitTimerSessionMap::iterator end = m_initTimerSessionMap.end(); + TimerSessionMap::iterator i = m_initTimerSessionMap.begin(); + TimerSessionMap::iterator end = m_initTimerSessionMap.end(); // Remove sessions if they do not initialize within a certain period. while (i != end) { - InitTimerSessionMap::iterator next = i; + TimerSessionMap::iterator next = i; ++next; if (i->second.elapsed().total_seconds() > SERVER_INIT_SESSION_TIMEOUT_SEC) { @@ -803,6 +826,24 @@ ServerLobbyThread::KickPlayerLoop() m_kickPlayerList.clear(); } +void +ServerLobbyThread::UpdateAvatarClientTimerLoop() +{ + boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); + + TimerClientAddressMap::iterator i = m_timerAvatarClientAddressMap.begin(); + TimerClientAddressMap::iterator end = m_timerAvatarClientAddressMap.end(); + + while (i != end) + { + TimerClientAddressMap::iterator next = i; + ++next; + if (i->second.elapsed().total_seconds() > SERVER_INIT_AVATAR_CLIENT_LOCK_SEC) + m_timerAvatarClientAddressMap.erase(i); + i = next; + } +} + void ServerLobbyThread::CleanupAvatarCache() { @@ -907,6 +948,14 @@ ServerLobbyThread::HandleNewConnection(boost::shared_ptr connData) if (m_sessionManager.GetRawSessionCount() <= SERVER_MAX_NUM_SESSIONS) { + char tmpAddress[MAX_ADDR_STRING_LEN]; + // Only consider address, set port to zero. + if (socket_set_port(0, connData->GetPeerAddr()->sa_family, connData->GetPeerAddr(), connData->GetPeerAddrSize()) + && socket_addr_to_string(connData->GetPeerAddr(), connData->GetPeerAddrSize(), connData->GetPeerAddr()->sa_family, tmpAddress, sizeof(tmpAddress))) + { + tmpAddress[sizeof(tmpAddress) - 1] = 0; // paranoia + sessionData->SetClientAddr(tmpAddress); + } boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex); m_initTimerSessionMap[sessionData->GetId()] = boost::timers::portable::microsec_timer(); } diff --git a/src/net/connectdata.h b/src/net/connectdata.h index 485ae05c..0a42865d 100644 --- a/src/net/connectdata.h +++ b/src/net/connectdata.h @@ -36,8 +36,16 @@ public: SOCKET ReleaseSocket(); + struct sockaddr *GetPeerAddr() {return (sockaddr *)&m_peerAddr;} + int GetPeerAddrSize() const {return m_peerAddrSize;} + + void SetPeerAddrSize(int addrSize) {m_peerAddrSize = addrSize;} + private: - SOCKET m_sockfd; + SOCKET m_sockfd; + + struct sockaddr_storage m_peerAddr; + int m_peerAddrSize; }; #endif diff --git a/src/net/linux/socket_helper.cpp b/src/net/linux/socket_helper.cpp index 83a08e6f..c415063c 100644 --- a/src/net/linux/socket_helper.cpp +++ b/src/net/linux/socket_helper.cpp @@ -34,6 +34,12 @@ socket_string_to_addr(const char *str, int addrFamily, struct sockaddr *addr, in return false; } +bool +socket_addr_to_string(struct sockaddr *addr, int /*addrLen*/, int addrFamily, char *str, int strLen) +{ + return (inet_ntop(addrFamily, addr, str, strLen) != NULL); +} + bool socket_resolve(const char *str, const char *port, int addrFamily, int sockType, int protocol, struct sockaddr *addr, int addrLen) { diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index 3f4318b1..5f289199 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -90,8 +90,9 @@ protected: typedef std::deque > ConnectQueue; typedef std::deque SessionQueue; typedef std::list SessionList; - typedef std::map InitTimerSessionMap; + typedef std::map TimerSessionMap; typedef std::map > GameMap; + typedef std::map TimerClientAddressMap; typedef std::list RemoveGameList; typedef std::list > SenderThreadList; @@ -115,6 +116,7 @@ protected: void CloseSessionLoop(); void RemoveGameLoop(); void KickPlayerLoop(); + void UpdateAvatarClientTimerLoop(); void CleanupAvatarCache(); void InternalAddGame(boost::shared_ptr game); @@ -164,9 +166,12 @@ private: SessionManager m_sessionManager; SessionManager m_gameSessionManager; - InitTimerSessionMap m_initTimerSessionMap; + TimerSessionMap m_initTimerSessionMap; mutable boost::mutex m_initTimerSessionMapMutex; + TimerClientAddressMap m_timerAvatarClientAddressMap; + mutable boost::mutex m_timerAvatarClientAddressMapMutex; + RemoveGameList m_removeGameList; mutable boost::mutex m_removeGameListMutex; @@ -176,6 +181,7 @@ private: PlayerDataMap m_computerPlayers; mutable boost::mutex m_computerPlayersMutex; + GameMap m_gameMap; boost::shared_ptr m_receiver; diff --git a/src/net/socket_helper.h b/src/net/socket_helper.h index 4e159acb..e0f7378d 100644 --- a/src/net/socket_helper.h +++ b/src/net/socket_helper.h @@ -88,6 +88,8 @@ typedef unsigned char u_char; #define SOCKET_IPPROTO_SCTP 0 #endif +#define MAX_ADDR_STRING_LEN 256 + // All char *s are assumed to be UTF-8. /** @@ -96,6 +98,11 @@ typedef unsigned char u_char; */ bool socket_string_to_addr(const char *str, int addrFamily, struct sockaddr *addr, int addrLen); +/** + * Convert a numeric address to an address string. + */ +bool socket_addr_to_string(struct sockaddr *addr, int addrLen, int addrFamily, char *str, int strLen); + /** * Resolve a name to a numeric address. * str is assumed to be UTF-8 encoded. diff --git a/src/net/win32/socket_helper.cpp b/src/net/win32/socket_helper.cpp index 02c217e7..b517c20d 100644 --- a/src/net/win32/socket_helper.cpp +++ b/src/net/win32/socket_helper.cpp @@ -73,6 +73,15 @@ socket_string_to_addr(const char *str, int addrFamily, struct sockaddr * addr, i return retVal; } +bool +socket_addr_to_string(struct sockaddr *addr, int addrLen, int addrFamily, char *str, int strLen) +{ + bool retVal = false; + retVal = (WSAAddressToStringA(addr, addrLen, NULL, str, (DWORD *)&strLen) != SOCKET_ERROR); + + return retVal; +} + bool socket_resolve(const char *str, const char *port, int addrFamily, int sockType, int protocol, struct sockaddr *addr, int addrLen) {