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.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
#include <net/connectdata.h>
|
||||
|
||||
ConnectData::ConnectData()
|
||||
: m_sockfd(INVALID_SOCKET)
|
||||
: m_sockfd(INVALID_SOCKET), m_peerAddrSize(sizeof(m_peerAddr))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<NetPacket> 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<ConnectData> 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -90,8 +90,9 @@ protected:
|
||||
typedef std::deque<boost::shared_ptr<ConnectData> > ConnectQueue;
|
||||
typedef std::deque<SessionWrapper> SessionQueue;
|
||||
typedef std::list<SessionWrapper> SessionList;
|
||||
typedef std::map<SessionId, boost::timers::portable::microsec_timer> InitTimerSessionMap;
|
||||
typedef std::map<SessionId, boost::timers::portable::microsec_timer> TimerSessionMap;
|
||||
typedef std::map<unsigned, boost::shared_ptr<ServerGameThread> > GameMap;
|
||||
typedef std::map<std::string, boost::timers::portable::microsec_timer> TimerClientAddressMap;
|
||||
typedef std::list<unsigned> RemoveGameList;
|
||||
typedef std::list<boost::shared_ptr<SenderThread> > SenderThreadList;
|
||||
|
||||
@@ -115,6 +116,7 @@ protected:
|
||||
void CloseSessionLoop();
|
||||
void RemoveGameLoop();
|
||||
void KickPlayerLoop();
|
||||
void UpdateAvatarClientTimerLoop();
|
||||
void CleanupAvatarCache();
|
||||
|
||||
void InternalAddGame(boost::shared_ptr<ServerGameThread> 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<ReceiverHelper> m_receiver;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user