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:
lotodore
2008-02-24 12:56:39 +00:00
parent 75ef6616e8
commit 7283431c8c
8 changed files with 117 additions and 18 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
#include <net/connectdata.h>
ConnectData::ConnectData()
: m_sockfd(INVALID_SOCKET)
: m_sockfd(INVALID_SOCKET), m_peerAddrSize(sizeof(m_peerAddr))
{
}
+23 -9
View File
@@ -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);
}
}
}
+54 -5
View File
@@ -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();
}
+9 -1
View File
@@ -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
+6
View File
@@ -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)
{
+8 -2
View File
@@ -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;
+7
View File
@@ -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.
+9
View File
@@ -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)
{