rosaria/Legacy/Aria/ArNetworking/src/ArServerClient.cpp

1564 lines
46 KiB
C++
Raw Normal View History

2021-12-16 15:07:59 +01:00
#include "Aria.h"
#include "ArExport.h"
#include "ArServerClient.h"
#include "ArServerCommands.h"
#include "ArClientCommands.h"
#include "ArServerData.h"
#include "ArServerUserInfo.h"
/**
In addition to the normal contructorness this contructor starts the
connection between the server and the client going
@param tcpSocket the socket that has been established between the
client and the server
@param udpPort the udp port the server is using
@param authKey this authKey must be presented by the client to the
server on the server's UDP port for the client to be recognized by
the server
@param introKey this key must be presented by the server to the
client so that the client will recognize the server
@param sendUdpCallback the callback used to send Udp packets (since
the udp packets need to be sent from the server's udp port and the
serverclient here doesn't have one)
@param dataMap the map between command numbers and the command data
@param passwordKey the key (just plain text thats used in an md5
along with the serverKey and the password over on the client side)
that was generated for the passwords on this exact connection
@param serverKey the key (just plain text thats used in an md5
along with the passwordKey and the password over on the client
side) that should be used for any connections to this server
@param debugLogging If true, log some debug messages
@param logPasswordFailureVerbosely If true, log password errors at
ArLog::Verbose
level, if false, log them at ArLog::Normal level.
@param userInfo the pointer to the item that holds our usernames
and passwords
@param rejecting
@param rejectingString string to reject with
**/
AREXPORT ArServerClient::ArServerClient(
ArSocket *tcpSocket, unsigned int udpPort, long authKey,
long introKey, ArRetFunctor2<bool, ArNetPacket *,
struct sockaddr_in *> *sendUdpCallback,
std::map<unsigned int, ArServerData *> *dataMap,
const char *passwordKey, const char *serverKey,
const ArServerUserInfo *userInfo, int rejecting,
const char *rejectingString, bool debugLogging,
const char *serverClientName, bool logPasswordFailureVerbosely,
bool allowSlowPackets, bool allowIdlePackets,
const char *enforceProtocolVersion,
ArServerCommands::Type enforceType) :
myProcessPacketCB(this, &ArServerClient::processPacket, NULL, true),
myRequestTransactionCount(0),
myRequestTransactionMutex()
{
ArNetPacket packet;
// set our default to no command
pushCommand(0);
myAuthKey = authKey;
myIntroKey = introKey;
myTcpSocket.transfer(tcpSocket);
myTcpSocket.setCloseCallback(tcpSocket->getCloseCallback());
myTcpSocket.setNonBlock();
myTcpReceiver.setSocket(&myTcpSocket);
myTcpReceiver.setProcessPacketCB(&myProcessPacketCB);
myTcpSender.setSocket(&myTcpSocket);
mySendUdpCB = sendUdpCallback;
myDataMap = dataMap;
if (udpPort == 0)
myTcpOnly = true;
else
myTcpOnly = false;
mySentTcpOnly = myTcpOnly;
myUserInfo = userInfo;
myPasswordKey = passwordKey;
myServerKey = serverKey;
myRejecting = rejecting;
if (rejectingString != NULL)
myRejectingString = rejectingString;
myDebugLogging = debugLogging;
if (myDebugLogging)
myVerboseLogLevel = ArLog::Normal;
else
myVerboseLogLevel = ArLog::Verbose;
myTcpSender.setDebugLogging(myDebugLogging);
myLogPrefix = serverClientName;
myLogPrefix += ": ";
myTcpSender.setLoggingPrefix(myLogPrefix.c_str());
myTcpReceiver.setLoggingPrefix(myLogPrefix.c_str());
myLogPasswordFailureVerbosely = logPasswordFailureVerbosely;
if (enforceProtocolVersion != NULL)
myEnforceProtocolVersion = enforceProtocolVersion;
else
myEnforceProtocolVersion = "";
myEnforceType = enforceType;
mySlowPacketsMutex.setLogName("ArServerClient::mySlowPacketsMutex");
myIdlePacketsMutex.setLogName("ArServerClient::myIdlePacketsMutex");
myAllowSlowPackets = allowSlowPackets;
myAllowIdlePackets = allowIdlePackets;
myRequestTransactionMutex.setLogName("ArServerClient::myRequestTransactionMutex");
setIdentifier(ArServerClientIdentifier());
internalSwitchState(STATE_SENT_INTRO);
packet.empty();
packet.setCommand(ArServerCommands::INTRODUCTION);
packet.strToBuf("alpha");
packet.uByte2ToBuf(udpPort);
packet.uByte4ToBuf(myAuthKey);
packet.uByte4ToBuf(myIntroKey);
packet.strToBuf(myPasswordKey.c_str());
packet.strToBuf(myEnforceProtocolVersion.c_str());
sendPacketTcp(&packet);
mySlowIdleThread = NULL;
myHaveSlowPackets = false;
myHaveIdlePackets = false;
myCreationTime.setToNow();
resetTracking();
}
AREXPORT ArServerClient::~ArServerClient()
{
myTcpReceiver.setSocket(NULL);
myTcpReceiver.setProcessPacketCB(NULL);
myTcpSocket.close();
/// MPL fixing a memory leak
// new way to get the request change functor called
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
while ((it = myRequested.begin()) != myRequested.end())
{
data = (*it);
serverData = (*it)->getServerData();
myRequested.pop_front();
delete data;
serverData->callRequestChangedFunctor();
}
/* old way
ArUtil::deleteSet(myRequested.begin(), myRequested.end());
myRequested.clear();
*/
ArUtil::deleteSetPairs(myTrackingSentMap.begin(),
myTrackingSentMap.end());
myTrackingSentMap.clear();
ArUtil::deleteSetPairs(myTrackingReceivedMap.begin(),
myTrackingReceivedMap.end());
myTrackingReceivedMap.clear();
ArUtil::deleteSet(mySlowPackets.begin(), mySlowPackets.end());
mySlowPackets.clear();
ArUtil::deleteSet(myIdlePackets.begin(), myIdlePackets.end());
myIdlePackets.clear();
}
void ArServerClient::internalSwitchState(ServerState state)
{
myState = state;
myStateStart.setToNow();
if (myDebugLogging)
ArLog::log(ArLog::Normal, "%sSwitching to state %d",
myLogPrefix.c_str(), (int)state);
}
AREXPORT void ArServerClient::setBackupTimeout(double timeoutInMins)
{
myBackupTimeout = timeoutInMins;
myTcpSender.setBackupTimeout(myBackupTimeout);
}
AREXPORT bool ArServerClient::tcpCallback(void)
{
if (myState == STATE_REJECTED)
{
myTcpSender.sendData();
ArUtil::sleep(10);
return false;
}
if (myState == STATE_DISCONNECTED && (myHaveSlowPackets ||
myHaveIdlePackets))
return true;
if (myState == STATE_DISCONNECTED)
return false;
if (myState == STATE_SENT_INTRO && myStateStart.secSince() >= 60)
{
ArLog::log(myVerboseLogLevel, "%sTook too long for %s to connect",
myLogPrefix.c_str(), getIPString());
return false;
}
if (myTcpOnly && !mySentTcpOnly)
{
mySentTcpOnly = true;
ArNetPacket sending;
sending.setCommand(ArServerCommands::TCP_ONLY);
sendPacketTcp(&sending);
}
if (!myTcpReceiver.readData())
{
ArLog::log(myVerboseLogLevel, "%sTrouble receiving tcp data from %s",
myLogPrefix.c_str(), getIPString());
internalSwitchState(STATE_DISCONNECTED);
return tcpCallback();
//return false;
}
if (!myTcpSender.sendData())
{
ArLog::log(myVerboseLogLevel, "%sTrouble sending tcp data to %s",
myLogPrefix.c_str(), getIPString());
internalSwitchState(STATE_DISCONNECTED);
return tcpCallback();
//return false;
}
return true;
}
AREXPORT void ArServerClient::handleRequests(void)
{
if (myState != STATE_CONNECTED)
return;
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
ArTime lastSent;
// walk through our list
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
lastSent = data->getLastSent();
// see if this needs to be called
if (data->getMSec() != -1 &&
(data->getMSec() == 0 || lastSent.mSecSince() > data->getMSec()))
{
serverData = data->getServerData();
// call it, then set it so we know we did
pushCommand(serverData->getCommand());
pushForceTcpFlag(false);
if (serverData->getFunctor() != NULL)
serverData->getFunctor()->invoke(this, data->getPacket());
popCommand();
popForceTcpFlag();
data->setLastSentToNow();
}
}
}
void ArServerClient::sendListPacket(void)
{
ArNetPacket packet;
std::map<unsigned int, ArServerData *>::iterator it;
unsigned int count;
unsigned int shortLen;
unsigned int longLen;
ArServerData *serverData;
// First we send a list of numbers, names and descriptions
packet.setCommand(ArServerCommands::LIST);
// number of entries (we'll overwrite it later with the right number)
shortLen = packet.getLength();
packet.uByte2ToBuf(0);
// loop through the data to build the packet
for (it = myDataMap->begin(), count = 0; it != myDataMap->end(); it++)
{
serverData = (*it).second;
if (myUserInfo == NULL ||
serverData->getCommandGroup() == NULL ||
serverData->getCommandGroup()[0] == '\0' ||
myGroups.count(serverData->getCommandGroup()) != 0 ||
myGroups.count("all") != 0)
{
count++;
packet.uByte2ToBuf(serverData->getCommand());
packet.strToBuf(serverData->getName());
packet.strToBuf(serverData->getDescription());
}
}
// put the real number of entries in the right spot
longLen = packet.getLength();
packet.setLength(shortLen);
packet.uByte2ToBuf(count);
packet.setLength(longLen);
sendPacketTcp(&packet);
// then we send a list of the arguments and returns... they aren't
// combined so that the packet structure doesn't need to change
packet.empty();
packet.setCommand(ArServerCommands::LISTARGRET);
// number of entries (we'll overwrite it later with the right number)
shortLen = packet.getLength();
packet.uByte2ToBuf(0);
// loop through the data to build the packet
for (it = myDataMap->begin(), count = 0; it != myDataMap->end(); it++)
{
serverData = (*it).second;
if (myUserInfo == NULL ||
serverData->getCommandGroup() == NULL ||
serverData->getCommandGroup()[0] == '\0' ||
myGroups.count(serverData->getCommandGroup()) != 0 ||
myGroups.count("all") != 0)
{
count++;
packet.uByte2ToBuf(serverData->getCommand());
packet.strToBuf(serverData->getArgumentDescription());
packet.strToBuf(serverData->getReturnDescription());
}
}
// put the real number of entries in the right spot
longLen = packet.getLength();
packet.setLength(shortLen);
packet.uByte2ToBuf(count);
packet.setLength(longLen);
sendPacketTcp(&packet);
// then we send a list of command groups... they aren't
// combined so that the packet structure doesn't need to change
packet.empty();
packet.setCommand(ArServerCommands::LISTGROUPANDFLAGS);
// number of entries (we'll overwrite it later with the right number)
shortLen = packet.getLength();
packet.uByte2ToBuf(0);
// loop through the data to build the packet
for (it = myDataMap->begin(), count = 0; it != myDataMap->end(); it++)
{
serverData = (*it).second;
if (myUserInfo == NULL ||
serverData->getCommandGroup() == NULL ||
serverData->getCommandGroup()[0] == '\0' ||
myGroups.count(serverData->getCommandGroup()) != 0 ||
myGroups.count("all") != 0)
{
count++;
packet.uByte2ToBuf(serverData->getCommand());
packet.strToBuf(serverData->getCommandGroup());
packet.strToBuf(serverData->getDataFlagsString());
}
}
// put the real number of entries in the right spot
longLen = packet.getLength();
packet.setLength(shortLen);
packet.uByte2ToBuf(count);
packet.setLength(longLen);
sendPacketTcp(&packet);
}
AREXPORT void ArServerClient::processPacket(ArNetPacket *packet, bool tcp)
{
std::string str;
struct sockaddr_in sin;
unsigned int clientUdpPort;
ArNetPacket retPacket;
//printf("Command number %d\n", packet->getCommand());
// if we're in intro mode and received back the intro
if (myState == STATE_SENT_INTRO &&
packet->getCommand() == ArClientCommands::INTRODUCTION)
{
char user[512];
unsigned char password[16];
char enforceProtocolVersion[256];
clientUdpPort = packet->bufToUByte2();
packet->bufToStr(user, sizeof(user));
packet->bufToData((char *)password, 16);
packet->bufToStr(enforceProtocolVersion, sizeof(enforceProtocolVersion));
ArServerCommands::Type clientType = (ArServerCommands::Type)packet->bufToByte();
if (myRejecting != 0)
{
retPacket.empty();
retPacket.setCommand(ArServerCommands::REJECTED);
retPacket.byte2ToBuf(myRejecting);
retPacket.strToBuf(myRejectingString.c_str());
sendPacketTcp(&retPacket);
ArLog::log(ArLog::Normal,
"%sRejected connection from %s (%d, %s)",
myLogPrefix.c_str(), getIPString(), myRejecting,
myRejectingString.c_str());
internalSwitchState(STATE_REJECTED);
return;
}
if (!myEnforceProtocolVersion.empty() &&
strcmp(enforceProtocolVersion, myEnforceProtocolVersion.c_str()) != 0)
{
retPacket.empty();
retPacket.setCommand(ArServerCommands::REJECTED);
retPacket.byte2ToBuf(4);
retPacket.strToBuf("Server rejecting client connection since protocol version does not match");
sendPacketTcp(&retPacket);
ArLog::log(ArLog::Normal, "%sServer rejecting client connection since protocol version does not match", myLogPrefix.c_str());
internalSwitchState(STATE_REJECTED);
}
if (myEnforceType != ArServerCommands::TYPE_UNSPECIFIED &&
(myEnforceType != clientType ||
myEnforceType == ArServerCommands::TYPE_NONE))
{
retPacket.empty();
retPacket.setCommand(ArServerCommands::REJECTED);
retPacket.byte2ToBuf(6);
retPacket.strToBuf("Server rejecting client connection since type (real or sim) does not match (but is specified)");
sendPacketTcp(&retPacket);
ArLog::log(ArLog::Normal, "%sServer rejecting client connection since type (real or sim) does not match (but is specified)", myLogPrefix.c_str());
internalSwitchState(STATE_REJECTED);
}
// if user info is NULL we're not checking passwords
if (myUserInfo != NULL &&
!myUserInfo->matchUserPassword(user, password, myPasswordKey.c_str(),
myServerKey.c_str(),
myLogPasswordFailureVerbosely))
{
retPacket.empty();
retPacket.setCommand(ArServerCommands::REJECTED);
retPacket.byte2ToBuf(1);
retPacket.strToBuf("");
sendPacketTcp(&retPacket);
ArLog::log(ArLog::Normal, "%sRejected user '%s' or password from %s",
myLogPrefix.c_str(), user, getIPString());
internalSwitchState(STATE_REJECTED);
return;
}
if (myUserInfo != NULL)
myGroups = myUserInfo->getUsersGroups(user);
else
myGroups.clear();
sin.sin_family = AF_INET;
sin.sin_addr = *myTcpSocket.inAddr();
sin.sin_port = ArSocket::hostToNetOrder(clientUdpPort);
if (myUserInfo != NULL)
ArLog::log(ArLog::Normal,
"%sClient connected from %s with user %s",
myLogPrefix.c_str(), getIPString(), user);
else
ArLog::log(ArLog::Normal,
"%sClient connected from %s", myLogPrefix.c_str(),
getIPString());
setUdpAddress(&sin);
// send that we've connected
retPacket.empty();
retPacket.setCommand(ArServerCommands::CONNECTED);
sendPacketTcp(&retPacket);
// note that we're connected
internalSwitchState(STATE_CONNECTED);
// send them the list
sendListPacket();
// send the udp introduction if we're using udp
if (!myTcpOnly)
{
retPacket.empty();
retPacket.setCommand(ArServerCommands::UDP_INTRODUCTION);
retPacket.byte4ToBuf(myIntroKey);
sendPacketUdp(&retPacket);
}
}
// if we aren't in intro mode and got an intro somethings wrong
else if (packet->getCommand() == ArClientCommands::INTRODUCTION)
{
ArLog::log(ArLog::Terse,
"%sReceived introduction when not in intro mode",
myLogPrefix.c_str());
return;
}
// if we got this over tcp then they only want tcp
else if (packet->getCommand() == ArClientCommands::UDP_INTRODUCTION)
{
if (!myTcpOnly)
{
ArLog::log(ArLog::Normal, "%sGot UDP introduction over tcp, assuming client only wants tcp data.", myLogPrefix.c_str());
myTcpOnly = true;
}
return;
}
// if we're connected and got a udp confirmation
else if ((myState == STATE_CONNECTED ||
myState == STATE_SENT_INTRO) &&
packet->getCommand() == ArClientCommands::UDP_CONFIRMATION)
{
myUdpConfirmedTo = true;
ArLog::log(myVerboseLogLevel,
"%s: udp connection to client confirmed.", myLogPrefix.c_str());
return;
}
// if we're not connected (or close) and got a confirmation
else if (packet->getCommand() == ArClientCommands::UDP_CONFIRMATION)
{
ArLog::log(ArLog::Normal,
"%sReceived udp confirmation when not connected.",
myLogPrefix.c_str());
return;
}
else if (packet->getCommand() == ArClientCommands::TCP_ONLY)
{
ArLog::log(myVerboseLogLevel, "%sClient only wants tcp data.", myLogPrefix.c_str());
myTcpOnly = true;
return;
}
else if (packet->getCommand() == ArClientCommands::SHUTDOWN)
{
ArLog::log(ArLog::Normal, "%sClient from %s has disconnected.",
myLogPrefix.c_str(), getIPString());
internalSwitchState(STATE_DISCONNECTED);
return;
}
// if we're connected its a request, then set all that up
else if (myState == STATE_CONNECTED &&
packet->getCommand() == ArClientCommands::REQUEST)
{
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
unsigned int command;
long mSec;
// see which one they requested
command = packet->bufToUByte2();
mSec = packet->bufToByte4();
// first we see if we already have this one
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
serverData = data->getServerData();
if (serverData->getCommand() == command)
{
trackPacketReceived(packet, command);
data->setMSec(mSec);
data->setPacket(packet);
data->getPacket()->setCommand(command);
serverData->callRequestChangedFunctor();
ArLog::log(myVerboseLogLevel,
"%sRevised request for command %s to %d mSec with new argument",
myLogPrefix.c_str(),
findCommandName(serverData->getCommand()), mSec);
return;
}
}
// we didn't have it, so make a new one
std::map<unsigned int, ArServerData *>::iterator sdit;
if ((sdit = myDataMap->find(command)) == myDataMap->end())
{
ArLog::log(ArLog::Terse,
"%sGot request for command %d which doesn't exist",
myLogPrefix.c_str(), command);
return;
}
serverData = (*sdit).second;
if (serverData == NULL)
{
ArLog::log(ArLog::Terse,
"%sprocessPackets request handler has NULL serverData",
myLogPrefix.c_str());
}
if (myUserInfo != NULL &&
serverData->getCommandGroup() != NULL &&
serverData->getCommandGroup()[0] != '\0' &&
myGroups.count(serverData->getCommandGroup()) == 0 &&
myGroups.count("all") == 0)
{
ArLog::log(ArLog::Normal,
"%s%s tried to request command '%s' but it doesn't have access to that command",
myLogPrefix.c_str(), getIPString(),
serverData->getName());
return;
}
trackPacketReceived(packet, command);
data = new ArServerClientData(serverData, mSec, packet);
data->getPacket()->setCommand(command);
ArLog::log(myVerboseLogLevel,
"%sadded request for command %s every %d mSec",
myLogPrefix.c_str(), serverData->getName(), mSec);
if (mSec == 0)
ArLog::log(ArLog::Normal, "%sClient from %s requested command %s every at 0 msec", myLogPrefix.c_str(),
getIPString(), serverData->getName());
myRequested.push_front(data);
serverData->callRequestChangedFunctor();
pushCommand(command);
pushForceTcpFlag(false);
if (serverData->getFunctor() != NULL)
serverData->getFunctor()->invoke(this, data->getPacket());
popCommand();
popForceTcpFlag();
return;
}
// if we got a request when we're not connected
else if (packet->getCommand() == ArClientCommands::REQUEST)
{
ArLog::log(ArLog::Normal,
"Got a request while not connected.",
myLogPrefix.c_str());
return;
}
// if we're connected its a requestStop, then set all that up
else if (myState == STATE_CONNECTED &&
packet->getCommand() == ArClientCommands::REQUESTSTOP)
{
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
unsigned int command;
// see which one they requested
command = packet->bufToUByte2();
// first we see if we have this one
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
serverData = data->getServerData();
// we have a match, so set the new params then return
if (data->getServerData()->getCommand() == command)
{
trackPacketReceived(packet, command);
myRequested.erase(it);
ArLog::log(myVerboseLogLevel, "%sStopped request for command %s",
myLogPrefix.c_str(),
findCommandName(serverData->getCommand()));
delete data;
serverData->callRequestChangedFunctor();
return;
}
}
// if we don't have it... that means that it wasn't here
// find out what to call it
std::map<unsigned int, ArServerData *>::iterator sdit;
if ((sdit = myDataMap->find(command)) == myDataMap->end())
{
ArLog::log(ArLog::Terse,
"%sGot a requeststop for command %d which doesn't exist",
myLogPrefix.c_str(), command);
return;
}
trackPacketReceived(packet, command);
serverData = (*sdit).second;
if (serverData == NULL)
ArLog::log(ArLog::Terse,
"%srequeststop handler has NULL serverData on back command %d",
myLogPrefix.c_str(), command);
else
ArLog::log(ArLog::Normal, "%s: Got a stop request for command %s that isn't requested", myLogPrefix.c_str(), serverData->getName());
return;
}
// if we got a requestStop when we're not connected
else if (packet->getCommand() == ArClientCommands::REQUESTSTOP)
{
ArLog::log(ArLog::Normal,
"%sGot a requeststop while not connected.",
myLogPrefix.c_str());
return;
}
// if we're connected and its a command to execute just once
else if (myState == STATE_CONNECTED)
{
unsigned int command;
std::map<unsigned int, ArServerData *>::iterator it;
ArServerData *serverData;
command = packet->getCommand();
if ((it = myDataMap->find(command)) == myDataMap->end())
{
ArLog::log(ArLog::Terse,
"%sArServerClient got request for command %d which doesn't exist",
myLogPrefix.c_str(), command);
return;
}
serverData = (*it).second;
if (myUserInfo != NULL &&
serverData->getCommandGroup() != NULL &&
serverData->getCommandGroup()[0] != '\0' &&
myGroups.count(serverData->getCommandGroup()) == 0 &&
myGroups.count("all") == 0)
{
ArLog::log(ArLog::Normal,
"%s%s tried to request command '%s' once but it doesn't have access to that command", myLogPrefix.c_str(),
getIPString(),
serverData->getName());
return;
}
trackPacketReceived(packet, command);
// copy it out and return if its an idle packet
if (myAllowIdlePackets && serverData->isIdlePacket())
{
myHaveIdlePackets = true;
if (command <= 255)
ArLog::log(myVerboseLogLevel, "%sStoring idle command %d",
myLogPrefix.c_str(), command);
else
ArLog::log(myVerboseLogLevel, "%sStoring idle command %s",
myLogPrefix.c_str(), serverData->getName());
myIdlePacketsMutex.lock();
ArNetPacket *idlePacket = new ArNetPacket(packet->getLength() + 5);
idlePacket->duplicatePacket(packet);
myIdlePackets.push_back(idlePacket);
myIdlePacketsMutex.unlock();
return;
}
// If its a slow or an idle packet (and we're not allowing the
// idle behavior) and we allow slow packets then copy it
else if (myAllowSlowPackets && (serverData->isSlowPacket() ||
serverData->isIdlePacket()))
{
myHaveSlowPackets = true;
if (command <= 255)
ArLog::log(myVerboseLogLevel, "%sStoring slow command %d",
myLogPrefix.c_str(), command);
else
ArLog::log(myVerboseLogLevel, "%sStoring slow command %s",
myLogPrefix.c_str(), serverData->getName());
mySlowPacketsMutex.lock();
ArNetPacket *slowPacket = new ArNetPacket(packet->getLength() + 5);
slowPacket->duplicatePacket(packet);
mySlowPackets.push_back(slowPacket);
mySlowPacketsMutex.unlock();
return;
}
if (command <= 255)
ArLog::log(myVerboseLogLevel, "%sGot command %s",
myLogPrefix.c_str(), serverData->getName());
else
ArLog::log(ArLog::Verbose, "%sGot command %s",
myLogPrefix.c_str(), serverData->getName());
pushCommand(command);
pushForceTcpFlag(tcp);
if (serverData->getFunctor() != NULL)
serverData->getFunctor()->invoke(this, packet);
if (serverData->getRequestOnceFunctor() != NULL)
serverData->getRequestOnceFunctor()->invokeR(this, packet);
popCommand();
popForceTcpFlag();
return;
}
else
{
ArLog::log(ArLog::Terse,
"%sRogue packet command %s in state %d", myLogPrefix.c_str(),
findCommandName(packet->getCommand()), myState);
}
}
AREXPORT void ArServerClient::forceDisconnect(bool quiet)
{
if (!quiet)
ArLog::log(ArLog::Normal, "Client from %s has been forcibly disconnected.",
getIPString());
internalSwitchState(STATE_DISCONNECTED);
}
AREXPORT void ArServerClient::processAuthPacket(ArNetPacket *packet,
struct sockaddr_in *sin)
{
ArNetPacket retPacket;
long authKey;
// check the auth key again, just in case
authKey = packet->bufToUByte4();
if (authKey != myAuthKey)
{
ArLog::log(ArLog::Terse, "ArServerClient: authKey given does not match actual authKey, horrible error.");
return;
}
if (mySin.sin_port != sin->sin_port)
{
ArLog::log(myVerboseLogLevel,
"Client says it is using port %u but is using port %u\n",
ArSocket::netToHostOrder(mySin.sin_port),
ArSocket::netToHostOrder(sin->sin_port));
}
myUdpConfirmedFrom = true;
mySin.sin_port = sin->sin_port;
mySin.sin_addr = sin->sin_addr;
ArLog::log(myVerboseLogLevel, "Client connected to server on udp port %u",
ArSocket::netToHostOrder(mySin.sin_port));
// TODO put some state info here to note that its fully udp connected
retPacket.empty();
retPacket.setCommand(ArServerCommands::UDP_INTRODUCTION);
retPacket.byte4ToBuf(myIntroKey);
sendPacketUdp(&retPacket);
retPacket.empty();
retPacket.setCommand(ArServerCommands::UDP_CONFIRMATION);
sendPacketTcp(&retPacket);
}
AREXPORT void ArServerClient::broadcastPacketTcp(ArNetPacket *packet)
{
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
ArTime lastSent;
// walk through our list
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
serverData = data->getServerData();
// see if this is our data, if it is send the packet
if (serverData->getCommand() == packet->getCommand())
{
sendPacketTcp(packet);
return;
}
}
// we didn't have the data to send
}
AREXPORT void ArServerClient::broadcastPacketUdp(ArNetPacket *packet)
{
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
ArTime lastSent;
// walk through our list
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
serverData = data->getServerData();
// see if this is our data, if it is send the packet
if (serverData->getCommand() == packet->getCommand())
{
sendPacketUdp(packet);
return;
}
}
// we didn't have the data to send
}
AREXPORT bool ArServerClient::sendPacketTcp(ArNetPacket *packet)
{
if (!setupPacket(packet))
{
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(ArLog::Normal,
"%s sendPacket: could not set up tcp command %d",
myLogPrefix.c_str(), packet->getCommand());
return false;
}
else
{
trackPacketSent(packet, true);
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(ArLog::Normal, "%sSending tcp command %d",
myLogPrefix.c_str(), packet->getCommand());
myTcpSender.sendPacket(packet, myLogPrefix.c_str());
return true;
}
}
AREXPORT bool ArServerClient::sendPacketUdp(ArNetPacket *packet)
{
if (myTcpOnly || getForceTcpFlag())
return sendPacketTcp(packet);
if (!setupPacket(packet))
{
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(ArLog::Normal,
"%s sendPacket: could not set up udp command %d",
myLogPrefix.c_str(), packet->getCommand());
return false;
}
else if (mySendUdpCB != NULL)
{
trackPacketSent(packet, false);
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(ArLog::Normal, "%sSending udp command %d",
myLogPrefix.c_str(), packet->getCommand());
return mySendUdpCB->invokeR(packet, &mySin);
}
else
{
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(ArLog::Normal, "%sCould not send udp command %d",
myLogPrefix.c_str(), packet->getCommand());
return false;
}
}
AREXPORT bool ArServerClient::setupPacket(ArNetPacket *packet)
{
if (packet->getCommand() == 0)
{
if (getCommand() != 0)
{
packet->setCommand(getCommand());
}
else
{
ArLog::log(ArLog::Terse, "ArServerClient::sendPacket: packet to send has no command and we have no command in the stack");
return false;
}
}
if (myState == STATE_DISCONNECTED)
{
if (myDebugLogging && packet->getCommand() <= 255)
ArLog::log(myVerboseLogLevel, "%s sendPacket: command %s trying to be sent while disconnected", myLogPrefix.c_str(), findCommandName(packet->getCommand()));
return false;
}
if (packet->getLength() > ArNetPacket::MAX_LENGTH)
{
ArLog::log(ArLog::Terse,
"% send packet: Packet for command %s packet is bad at %d",
myLogPrefix.c_str(), findCommandName(packet->getCommand()),
packet->getLength());
return false;
}
packet->finalizePacket();
return true;
}
AREXPORT void ArServerClient::setUdpAddress(struct sockaddr_in *sin)
{
mySin = *sin;
}
AREXPORT struct sockaddr_in *ArServerClient::getUdpAddress(void)
{
return &mySin;
}
AREXPORT long ArServerClient::getAuthKey(void)
{
return myAuthKey;
}
unsigned int ArServerClient::getCommand(void)
{
if (mySlowIdleThread == NULL || ArThread::self() != mySlowIdleThread)
{
if (!myCommandStack.empty())
return myCommandStack.front();
else
return 0;
}
else
{
if (!mySlowIdleCommandStack.empty())
return mySlowIdleCommandStack.front();
else
return 0;
}
}
void ArServerClient::pushCommand(unsigned int command)
{
myCommandStack.push_front(command);
}
void ArServerClient::popCommand(void)
{
if (myCommandStack.size() == 0)
{
ArLog::log(ArLog::Terse,
"ArServerClient::popCommand: popCommand tried to be called when stack empty.");
}
myCommandStack.pop_front();
}
void ArServerClient::pushSlowIdleCommand(unsigned int command)
{
mySlowIdleCommandStack.push_front(command);
}
void ArServerClient::popSlowIdleCommand(void)
{
if (mySlowIdleCommandStack.size() == 0)
{
ArLog::log(ArLog::Terse,
"ArServerClient::popCommand: popSlowIdleCommand tried to be called when stack empty.");
}
mySlowIdleCommandStack.pop_front();
}
bool ArServerClient::getForceTcpFlag(void)
{
if (mySlowIdleThread == NULL || ArThread::self() != mySlowIdleThread)
{
if (!myForceTcpStack.empty())
return myForceTcpStack.front();
else
return false;
}
else
{
if (!mySlowIdleForceTcpStack.empty())
return mySlowIdleForceTcpStack.front();
else
return false;
}
}
void ArServerClient::pushForceTcpFlag(bool forceTcp)
{
myForceTcpStack.push_front(forceTcp);
}
void ArServerClient::popForceTcpFlag(void)
{
if (myForceTcpStack.size() == 0)
{
ArLog::log(ArLog::Terse,
"ArServerClient::popForceTcp: popForceTcpFlag tried to be called when stack empty.");
}
myForceTcpStack.pop_front();
}
void ArServerClient::pushSlowIdleForceTcpFlag(bool forceTcp)
{
mySlowIdleForceTcpStack.push_front(forceTcp);
}
void ArServerClient::popSlowIdleForceTcpFlag(void)
{
if (mySlowIdleForceTcpStack.size() == 0)
{
ArLog::log(ArLog::Terse,
"ArServerClient::popForceTcp: popSlowIdleForceTcpFlag tried to be called when stack empty.");
}
mySlowIdleForceTcpStack.pop_front();
}
AREXPORT void ArServerClient::shutdown(void)
{
ArNetPacket packet;
packet.setCommand(ArServerCommands::SHUTDOWN);
sendPacketTcp(&packet);
myTcpSender.sendData();
}
AREXPORT const char *ArServerClient::getIPString(void) const
{
return myIPString.c_str();
}
AREXPORT ArServerClientIdentifier ArServerClient::getIdentifier(void) const
{
return myIdentifier;
}
AREXPORT void ArServerClient::setIdentifier(
ArServerClientIdentifier identifier)
{
std::string oldIPString = myIPString;
myIdentifier = identifier;
myIPString = myTcpSocket.getRawIPString();
if (myIdentifier.getIDString() != 0 &&
myIdentifier.getIDString()[0] != '\0')
{
myIPString += " ";
myIPString += myIdentifier.getIDString();
}
if (!oldIPString.empty() && oldIPString != myIPString)
ArLog::log(ArLog::Normal, "%s%s changed to %s", myLogPrefix.c_str(),
oldIPString.c_str(), getIPString());
myTcpSocket.setIPString(getIPString());
}
void ArServerClient::trackPacketSent(ArNetPacket *packet, bool tcp)
{
if (myTrackingSentMap.find(packet->getCommand()) == myTrackingSentMap.end())
myTrackingSentMap[packet->getCommand()] = new Tracker;
if (tcp)
{
myTrackingSentMap[packet->getCommand()]->myPacketsTcp++;
myTrackingSentMap[packet->getCommand()]->myBytesTcp += packet->getLength();
}
else
{
myTrackingSentMap[packet->getCommand()]->myPacketsUdp++;
myTrackingSentMap[packet->getCommand()]->myBytesUdp += packet->getLength();
}
}
void ArServerClient::trackPacketReceived(ArNetPacket *packet,
ArTypes::UByte2 command)
{
if (myTrackingReceivedMap.find(command) == myTrackingReceivedMap.end())
myTrackingReceivedMap[command] = new Tracker;
if (packet->getPacketSource() == ArNetPacket::TCP)
{
myTrackingReceivedMap[command]->myPacketsTcp++;
myTrackingReceivedMap[command]->myBytesTcp += packet->getLength();
}
else
{
myTrackingReceivedMap[command]->myPacketsUdp++;
myTrackingReceivedMap[command]->myBytesUdp += packet->getLength();
}
}
AREXPORT void ArServerClient::logTracking(bool terse)
{
std::map<ArTypes::UByte2, Tracker *>::iterator it;
ArTypes::UByte2 command;
Tracker *tracker = NULL;
long seconds;
seconds = myTrackingStarted.secSince();
if (seconds == 0)
seconds = 1;
char name[512];
long packetsReceivedTcp = 0;
long bytesReceivedTcp = 0;
long packetsReceivedUdp = 0;
long bytesReceivedUdp = 0;
ArLog::log(ArLog::Terse, "");
ArLog::log(ArLog::Terse, "Received tracking for %s (active %d seconds):",
getIPString(), seconds);
for (it = myTrackingReceivedMap.begin(); it != myTrackingReceivedMap.end(); it++)
{
command = (*it).first;
tracker = (*it).second;
packetsReceivedTcp += tracker->myPacketsTcp;
bytesReceivedTcp += tracker->myBytesTcp;
packetsReceivedUdp += tracker->myPacketsUdp;
bytesReceivedUdp += tracker->myBytesUdp;
std::map<unsigned int, ArServerData *>::iterator nameIt;
if ((nameIt = myDataMap->find(command)) != myDataMap->end())
snprintf(name, sizeof(name), "%s", (*nameIt).second->getName());
// if we're command 255 or less and there's no name its probably
// one of the server commands we don't really need to track
else if (command <= 255)
continue;
// we should know what the name of everything other then the
// server command is, but print if we don't, just in case
else
snprintf(name, sizeof(name), "#%d", command);
if (terse)
{
ArLog::log(ArLog::Terse,
"%35s %7ld pkts %10ld B %7ld B/sec",
name, tracker->myPacketsTcp + tracker->myPacketsUdp,
tracker->myBytesTcp + tracker->myBytesUdp,
((tracker->myBytesTcp + tracker->myBytesUdp)/
seconds));
}
else
{
ArLog::log(ArLog::Terse,
"%35s %7ld tcp pkts %10ld tcp B %7ld tcp B/S %7ld udp pkts %10ld udp B %7ld udp B/s ",
name, tracker->myPacketsTcp, tracker->myBytesTcp,
tracker->myBytesTcp/seconds,
tracker->myPacketsUdp, tracker->myBytesUdp,
tracker->myBytesUdp/seconds);
}
}
ArLog::log(ArLog::Terse, "");
if (terse)
{
ArLog::log(ArLog::Terse, "%-35s %7ld pkts %10ld B %7ld B/sec",
"Total Received", packetsReceivedTcp + packetsReceivedUdp,
bytesReceivedTcp + bytesReceivedUdp,
(bytesReceivedTcp + bytesReceivedUdp) / seconds);
}
else
{
ArLog::log(ArLog::Terse, "%-35s %7ld tcp pkts %10ld tcp B %7ld tcp B/S %7ld udp pkts %10ld udp B %7ld udp B/sec",
"Total Received", packetsReceivedTcp, bytesReceivedTcp,
bytesReceivedTcp/seconds, packetsReceivedUdp, bytesReceivedUdp,
bytesReceivedUdp/seconds);
ArLog::log(ArLog::Terse, "%-35s %7ld tcp rcvs %10ld tcp B %7ld tcp B/S",
"Low level TCP Received", myTcpSocket.getRecvs(),
myTcpSocket.getBytesRecvd(),
myTcpSocket.getBytesRecvd()/seconds);
}
long packetsSentTcp = 0;
long bytesSentTcp = 0;
long packetsSentUdp = 0;
long bytesSentUdp = 0;
ArLog::log(ArLog::Terse, "");
ArLog::log(ArLog::Terse, "Sent tracking for %s (active %d seconds):",
getIPString(), seconds);
for (it = myTrackingSentMap.begin(); it != myTrackingSentMap.end(); it++)
{
command = (*it).first;
tracker = (*it).second;
packetsSentTcp += tracker->myPacketsTcp;
bytesSentTcp += tracker->myBytesTcp;
packetsSentUdp += tracker->myPacketsUdp;
bytesSentUdp += tracker->myBytesUdp;
std::map<unsigned int, ArServerData *>::iterator nameIt;
if ((nameIt = myDataMap->find(command)) != myDataMap->end())
snprintf(name, sizeof(name), "%s", (*nameIt).second->getName());
// if we're command 255 or less and there's no name its probably
// one of the server commands we don't really need to track
else if (command <= 255)
continue;
// we should know what the name of everything other then the
// server command is, but print if we don't, just in case
else
snprintf(name, sizeof(name), "#%d", command);
if (terse)
{
ArLog::log(ArLog::Terse,
"%35s %7ld pkts %10ld B %7ld B/sec",
name, tracker->myPacketsTcp + tracker->myPacketsUdp,
tracker->myBytesTcp + tracker->myBytesUdp,
((tracker->myBytesTcp + tracker->myBytesUdp)/
seconds));
}
else
{
ArLog::log(ArLog::Terse,
"%35s %7ld tcp pkts %10ld tcp B %7ld tcp B/S %7ld udp pkts %10ld udp B %7ld udp B/s ",
name, tracker->myPacketsTcp, tracker->myBytesTcp,
tracker->myBytesTcp/seconds,
tracker->myPacketsUdp, tracker->myBytesUdp,
tracker->myBytesUdp/seconds);
}
}
ArLog::log(ArLog::Terse, "");
if (terse)
{
ArLog::log(ArLog::Terse, "%-35s %7ld pkts %10ld B %7ld B/sec",
"Total Sent", packetsSentTcp + packetsSentUdp,
bytesSentTcp + bytesSentUdp,
(bytesSentTcp + bytesSentUdp) / seconds);
ArLog::log(ArLog::Terse, "");
ArLog::log(ArLog::Terse, "%-35s %7ld pkts %10ld B %7ld B/sec",
"Total Sent and Received",
(packetsSentTcp + packetsSentUdp +
packetsReceivedTcp + packetsReceivedUdp),
(bytesSentTcp + bytesSentUdp +
bytesReceivedTcp + bytesReceivedUdp),
(bytesSentTcp + bytesSentUdp +
bytesReceivedTcp + bytesReceivedUdp) / seconds);
}
else
{
ArLog::log(ArLog::Terse, "%-35s %7ld tcp pkts %10ld tcp B %7ld tcp B/S %7ld udp pkts %10ld udp B %7ld udp B/sec",
"Total Sent", packetsSentTcp, bytesSentTcp,
bytesSentTcp / seconds,
packetsSentUdp, bytesSentUdp, bytesSentUdp / seconds);
ArLog::log(ArLog::Terse, "%-35s %7ld tcp snds %10ld tcp B %7ld tcp B/S",
"Low level TCP Sent", myTcpSocket.getSends(),
myTcpSocket.getBytesSent(),
myTcpSocket.getBytesSent() / seconds);
ArLog::log(ArLog::Terse, "");
ArLog::log(ArLog::Terse, "%-35s %7ld tcp pkts %10ld tcp B %7ld tcp B/S %7ld udp pkts %10ld udp B %7ld udp B/sec",
"Total Sent and Received", packetsSentTcp = packetsReceivedTcp,
bytesSentTcp + bytesReceivedTcp,
(bytesSentTcp + bytesReceivedTcp) / seconds,
packetsSentUdp + packetsReceivedUdp,
bytesSentUdp + bytesReceivedUdp,
(bytesSentUdp + bytesReceivedUdp) / seconds);
}
ArLog::log(ArLog::Terse, "");
}
AREXPORT void ArServerClient::resetTracking(void)
{
std::map<ArTypes::UByte2, Tracker *>::iterator it;
myTrackingStarted.setToNow();
for (it = myTrackingSentMap.begin(); it != myTrackingSentMap.end(); it++)
(*it).second->reset();
for (it = myTrackingReceivedMap.begin();
it != myTrackingReceivedMap.end();
it++)
(*it).second->reset();
myTcpSocket.resetTracking();
}
AREXPORT bool ArServerClient::hasGroupAccess(const char *group)
{
if (myUserInfo == NULL || group == NULL || group[0] == '\0' ||
myGroups.count(group) > 0 || myGroups.count("all") > 0)
return true;
else
return false;
}
/**
@param command the command number, you can use findCommandFromName
@return returns lowest amount of time requested for this packet,
note that 0 and higher means thats how often it was asked for, -1
means nothing requested the data at an interval but wants it when
its been pushed, and -2 means that nothing wants the data
**/
AREXPORT long ArServerClient::getFrequency(ArTypes::UByte2 command)
{
std::list<ArServerClientData *>::iterator it;
ArServerClientData *data;
ArServerData *serverData;
// walk through our list
for (it = myRequested.begin(); it != myRequested.end(); ++it)
{
data = (*it);
serverData = data->getServerData();
// see if this is our data, if it is send the packet
if (serverData->getCommand() == command)
{
if (data->getMSec() >= 0)
return data->getMSec();
else
return -1;
}
}
return -2;
}
AREXPORT void ArServerClient::startRequestTransaction()
{
myRequestTransactionMutex.lock();
myRequestTransactionCount++;
myRequestTransactionMutex.unlock();
} // end method startRequestTransaction
AREXPORT bool ArServerClient::endRequestTransaction()
{
bool isSuccess = false;
myRequestTransactionMutex.lock();
if (myRequestTransactionCount > 0) {
myRequestTransactionCount--;
isSuccess = true;
}
else {
ArLog::log(ArLog::Normal,
"ArServerClient::endRequestTransaction() transaction not in progress");
}
myRequestTransactionMutex.unlock();
return isSuccess;
} // end method endRequestTransaction
AREXPORT int ArServerClient::getRequestTransactionCount()
{
myRequestTransactionMutex.lock();
int c = myRequestTransactionCount;
myRequestTransactionMutex.unlock();
return c;
} // end method getRequestTransactionCount
/**
* Note that this method is not very efficient; it performs a linear search
* of all commands.
**/
AREXPORT unsigned int ArServerClient::findCommandFromName(const char *commandName) const
{
if (ArUtil::isStrEmpty(commandName)) {
return 0;
}
for (std::map<unsigned int, ArServerData *>::const_iterator iter =
myDataMap->begin();
iter != myDataMap->end();
iter++) {
if (strcmp(commandName, (*iter).second->getName()) == 0) {
return iter->first;
}
}
return 0;
} // end method findCommandFromName
const char *ArServerClient::findCommandName(unsigned int command) const
{
std::map<unsigned int, ArServerData *>::const_iterator nameIt;
if ((nameIt = myDataMap->find(command)) != myDataMap->end())
return (*nameIt).second->getName();
else
return NULL;
}
AREXPORT bool ArServerClient::slowPacketCallback(void)
{
ArNetPacket *slowPacket;
unsigned int command;
std::map<unsigned int, ArServerData *>::iterator it;
ArServerData *serverData;
if (mySlowIdleThread == NULL)
mySlowIdleThread = ArThread::self();
mySlowPacketsMutex.lock();
while (!mySlowPackets.empty())
{
slowPacket = mySlowPackets.front();
mySlowPackets.pop_front();
mySlowPacketsMutex.unlock();
command = slowPacket->getCommand();
if ((it = myDataMap->find(command)) == myDataMap->end())
{
ArLog::log(ArLog::Terse,
"%sArServerClient got request for command %d which doesn't exist during slow... very odd",
myLogPrefix.c_str(), command);
delete slowPacket;
return false;
}
serverData = (*it).second;
ArLog::log(myVerboseLogLevel, "Processing slow command %s",
serverData->getName());
pushSlowIdleCommand(command);
pushSlowIdleForceTcpFlag(true);
if (serverData->getFunctor() != NULL)
serverData->getFunctor()->invoke(this, slowPacket);
if (serverData->getRequestOnceFunctor() != NULL)
serverData->getRequestOnceFunctor()->invokeR(this, slowPacket);
popSlowIdleCommand();
popSlowIdleForceTcpFlag();
delete slowPacket;
mySlowPacketsMutex.lock();
}
mySlowPacketsMutex.unlock();
myHaveSlowPackets = false;
return true;
}
AREXPORT bool ArServerClient::idlePacketCallback(void)
{
ArNetPacket *idlePacket;
unsigned int command;
std::map<unsigned int, ArServerData *>::iterator it;
ArServerData *serverData;
if (mySlowIdleThread == NULL)
mySlowIdleThread = ArThread::self();
myIdlePacketsMutex.lock();
while (!myIdlePackets.empty())
{
idlePacket = myIdlePackets.front();
myIdlePackets.pop_front();
myIdlePacketsMutex.unlock();
command = idlePacket->getCommand();
if ((it = myDataMap->find(command)) == myDataMap->end())
{
ArLog::log(ArLog::Terse,
"%sArServerClient got request for command %d which doesn't exist during idle... very odd",
myLogPrefix.c_str(), command);
delete idlePacket;
return false;
}
serverData = (*it).second;
ArLog::log(myVerboseLogLevel, "Processing idle command %s",
serverData->getName());
pushSlowIdleCommand(command);
pushSlowIdleForceTcpFlag(true);
if (serverData->getFunctor() != NULL)
serverData->getFunctor()->invoke(this, idlePacket);
if (serverData->getRequestOnceFunctor() != NULL)
serverData->getRequestOnceFunctor()->invokeR(this, idlePacket);
popSlowIdleCommand();
popSlowIdleForceTcpFlag();
delete idlePacket;
myIdlePacketsMutex.lock();
}
myIdlePacketsMutex.unlock();
myHaveIdlePackets = false;
return true;
}