#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 *sendUdpCallback, std::map *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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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; }