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>
|
#include <net/connectdata.h>
|
||||||
|
|
||||||
ConnectData::ConnectData()
|
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());
|
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
|
// Retrieve peer address.
|
||||||
int nosigpipe = 1;
|
int addrLen = tmpData->GetPeerAddrSize();
|
||||||
setsockopt(tmpData->GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&nosigpipe, sizeof(nosigpipe));
|
if (getpeername(tmpData->GetSocket(), tmpData->GetPeerAddr(), &addrLen) != 0)
|
||||||
#endif
|
{
|
||||||
|
// 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_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day
|
||||||
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
|
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
|
||||||
#define SERVER_INIT_SESSION_TIMEOUT_SEC 20
|
#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_FILE_NAME "server_statistics.log"
|
||||||
#define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn"
|
#define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn"
|
||||||
@@ -313,6 +314,8 @@ ServerLobbyThread::Main()
|
|||||||
RemoveGameLoop();
|
RemoveGameLoop();
|
||||||
// Kick players.
|
// Kick players.
|
||||||
KickPlayerLoop();
|
KickPlayerLoop();
|
||||||
|
// Update avatar limitation lock.
|
||||||
|
UpdateAvatarClientTimerLoop();
|
||||||
// Cleanup cache.
|
// Cleanup cache.
|
||||||
CleanupAvatarCache();
|
CleanupAvatarCache();
|
||||||
// Save statistics if needed.
|
// Save statistics if needed.
|
||||||
@@ -442,10 +445,25 @@ ServerLobbyThread::HandleNetPacketInit(SessionWrapper session, const NetPacketIn
|
|||||||
m_sessionManager.SetSessionPlayerData(session.sessionData->GetId(), tmpPlayerData);
|
m_sessionManager.SetSessionPlayerData(session.sessionData->GetId(), tmpPlayerData);
|
||||||
session.playerData = tmpPlayerData;
|
session.playerData = tmpPlayerData;
|
||||||
|
|
||||||
if (initData.showAvatar && !initData.avatar.IsZero() && !GetAvatarManager().HasAvatar(initData.avatar))
|
if (initData.showAvatar
|
||||||
RequestPlayerAvatar(session);
|
&& !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
|
else
|
||||||
|
{
|
||||||
EstablishSession(session);
|
EstablishSession(session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -698,6 +716,11 @@ ServerLobbyThread::RequestPlayerAvatar(SessionWrapper session)
|
|||||||
{
|
{
|
||||||
if (!session.playerData.get())
|
if (!session.playerData.get())
|
||||||
throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0);
|
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.
|
// Ask the client to send its avatar.
|
||||||
boost::shared_ptr<NetPacket> retrieveAvatar(new NetPacketRetrieveAvatar);
|
boost::shared_ptr<NetPacket> retrieveAvatar(new NetPacketRetrieveAvatar);
|
||||||
NetPacketRetrieveAvatar::Data retrieveAvatarData;
|
NetPacketRetrieveAvatar::Data retrieveAvatarData;
|
||||||
@@ -745,13 +768,13 @@ void
|
|||||||
ServerLobbyThread::CloseSessionLoop()
|
ServerLobbyThread::CloseSessionLoop()
|
||||||
{
|
{
|
||||||
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||||
InitTimerSessionMap::iterator i = m_initTimerSessionMap.begin();
|
TimerSessionMap::iterator i = m_initTimerSessionMap.begin();
|
||||||
InitTimerSessionMap::iterator end = m_initTimerSessionMap.end();
|
TimerSessionMap::iterator end = m_initTimerSessionMap.end();
|
||||||
|
|
||||||
// Remove sessions if they do not initialize within a certain period.
|
// Remove sessions if they do not initialize within a certain period.
|
||||||
while (i != end)
|
while (i != end)
|
||||||
{
|
{
|
||||||
InitTimerSessionMap::iterator next = i;
|
TimerSessionMap::iterator next = i;
|
||||||
++next;
|
++next;
|
||||||
if (i->second.elapsed().total_seconds() > SERVER_INIT_SESSION_TIMEOUT_SEC)
|
if (i->second.elapsed().total_seconds() > SERVER_INIT_SESSION_TIMEOUT_SEC)
|
||||||
{
|
{
|
||||||
@@ -803,6 +826,24 @@ ServerLobbyThread::KickPlayerLoop()
|
|||||||
m_kickPlayerList.clear();
|
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
|
void
|
||||||
ServerLobbyThread::CleanupAvatarCache()
|
ServerLobbyThread::CleanupAvatarCache()
|
||||||
{
|
{
|
||||||
@@ -907,6 +948,14 @@ ServerLobbyThread::HandleNewConnection(boost::shared_ptr<ConnectData> connData)
|
|||||||
|
|
||||||
if (m_sessionManager.GetRawSessionCount() <= SERVER_MAX_NUM_SESSIONS)
|
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);
|
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||||
m_initTimerSessionMap[sessionData->GetId()] = boost::timers::portable::microsec_timer();
|
m_initTimerSessionMap[sessionData->GetId()] = boost::timers::portable::microsec_timer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,16 @@ public:
|
|||||||
|
|
||||||
SOCKET ReleaseSocket();
|
SOCKET ReleaseSocket();
|
||||||
|
|
||||||
|
struct sockaddr *GetPeerAddr() {return (sockaddr *)&m_peerAddr;}
|
||||||
|
int GetPeerAddrSize() const {return m_peerAddrSize;}
|
||||||
|
|
||||||
|
void SetPeerAddrSize(int addrSize) {m_peerAddrSize = addrSize;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SOCKET m_sockfd;
|
SOCKET m_sockfd;
|
||||||
|
|
||||||
|
struct sockaddr_storage m_peerAddr;
|
||||||
|
int m_peerAddrSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ socket_string_to_addr(const char *str, int addrFamily, struct sockaddr *addr, in
|
|||||||
return false;
|
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
|
bool
|
||||||
socket_resolve(const char *str, const char *port, int addrFamily, int sockType, int protocol, struct sockaddr *addr, int addrLen)
|
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<boost::shared_ptr<ConnectData> > ConnectQueue;
|
||||||
typedef std::deque<SessionWrapper> SessionQueue;
|
typedef std::deque<SessionWrapper> SessionQueue;
|
||||||
typedef std::list<SessionWrapper> SessionList;
|
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<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<unsigned> RemoveGameList;
|
||||||
typedef std::list<boost::shared_ptr<SenderThread> > SenderThreadList;
|
typedef std::list<boost::shared_ptr<SenderThread> > SenderThreadList;
|
||||||
|
|
||||||
@@ -115,6 +116,7 @@ protected:
|
|||||||
void CloseSessionLoop();
|
void CloseSessionLoop();
|
||||||
void RemoveGameLoop();
|
void RemoveGameLoop();
|
||||||
void KickPlayerLoop();
|
void KickPlayerLoop();
|
||||||
|
void UpdateAvatarClientTimerLoop();
|
||||||
void CleanupAvatarCache();
|
void CleanupAvatarCache();
|
||||||
|
|
||||||
void InternalAddGame(boost::shared_ptr<ServerGameThread> game);
|
void InternalAddGame(boost::shared_ptr<ServerGameThread> game);
|
||||||
@@ -164,9 +166,12 @@ private:
|
|||||||
SessionManager m_sessionManager;
|
SessionManager m_sessionManager;
|
||||||
SessionManager m_gameSessionManager;
|
SessionManager m_gameSessionManager;
|
||||||
|
|
||||||
InitTimerSessionMap m_initTimerSessionMap;
|
TimerSessionMap m_initTimerSessionMap;
|
||||||
mutable boost::mutex m_initTimerSessionMapMutex;
|
mutable boost::mutex m_initTimerSessionMapMutex;
|
||||||
|
|
||||||
|
TimerClientAddressMap m_timerAvatarClientAddressMap;
|
||||||
|
mutable boost::mutex m_timerAvatarClientAddressMapMutex;
|
||||||
|
|
||||||
RemoveGameList m_removeGameList;
|
RemoveGameList m_removeGameList;
|
||||||
mutable boost::mutex m_removeGameListMutex;
|
mutable boost::mutex m_removeGameListMutex;
|
||||||
|
|
||||||
@@ -176,6 +181,7 @@ private:
|
|||||||
PlayerDataMap m_computerPlayers;
|
PlayerDataMap m_computerPlayers;
|
||||||
mutable boost::mutex m_computerPlayersMutex;
|
mutable boost::mutex m_computerPlayersMutex;
|
||||||
|
|
||||||
|
|
||||||
GameMap m_gameMap;
|
GameMap m_gameMap;
|
||||||
|
|
||||||
boost::shared_ptr<ReceiverHelper> m_receiver;
|
boost::shared_ptr<ReceiverHelper> m_receiver;
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ typedef unsigned char u_char;
|
|||||||
#define SOCKET_IPPROTO_SCTP 0
|
#define SOCKET_IPPROTO_SCTP 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_ADDR_STRING_LEN 256
|
||||||
|
|
||||||
// All char *s are assumed to be UTF-8.
|
// 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);
|
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.
|
* Resolve a name to a numeric address.
|
||||||
* str is assumed to be UTF-8 encoded.
|
* 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;
|
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
|
bool
|
||||||
socket_resolve(const char *str, const char *port, int addrFamily, int sockType, int protocol, struct sockaddr *addr, int addrLen)
|
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