| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: PingThread.cpp //////////////////////////////////////////////////////
- // Ping thread
- // Author: Matthew D. Campbell, August 2002
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include <winsock.h> // This one has to be here. Prevents collisions with windsock2.h
- #include "GameNetwork/GameSpy/PingThread.h"
- #include "mutex.h"
- #include "thread.h"
- #include "Common/StackDump.h"
- #include "Common/SubsystemInterface.h"
- //-------------------------------------------------------------------------
- static const Int NumWorkerThreads = 10;
- typedef std::queue<PingRequest> RequestQueue;
- typedef std::queue<PingResponse> ResponseQueue;
- class PingThreadClass;
- class Pinger : public PingerInterface
- {
- public:
- virtual ~Pinger();
- Pinger();
- virtual void startThreads( void );
- virtual void endThreads( void );
- virtual Bool areThreadsRunning( void );
- virtual void addRequest( const PingRequest& req );
- virtual Bool getRequest( PingRequest& resp );
- virtual void addResponse( const PingResponse& resp );
- virtual Bool getResponse( PingResponse& resp );
- virtual Bool arePingsInProgress( void );
- virtual Int getPing( AsciiString hostname );
- virtual void clearPingMap( void );
- virtual AsciiString getPingString( Int timeout );
- private:
- MutexClass m_requestMutex;
- MutexClass m_responseMutex;
- MutexClass m_pingMapMutex;
- RequestQueue m_requests;
- ResponseQueue m_responses;
- Int m_requestCount;
- Int m_responseCount;
- std::map<std::string, Int> m_pingMap;
- PingThreadClass *m_workerThreads[NumWorkerThreads];
- };
- PingerInterface* PingerInterface::createNewPingerInterface( void )
- {
- return NEW Pinger;
- }
- PingerInterface *ThePinger;
- //-------------------------------------------------------------------------
- class PingThreadClass : public ThreadClass
- {
- public:
- PingThreadClass() : ThreadClass() {}
- void Thread_Function();
- private:
- Int doPing( UnsignedInt IP, Int timeout );
- };
- //-------------------------------------------------------------------------
- Pinger::Pinger() : m_requestCount(0), m_responseCount(0)
- {
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- m_workerThreads[i] = NULL;
- }
- }
- Pinger::~Pinger()
- {
- endThreads();
- }
- void Pinger::startThreads( void )
- {
- endThreads();
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- m_workerThreads[i] = NEW PingThreadClass;
- m_workerThreads[i]->Execute();
- }
- }
- void Pinger::endThreads( void )
- {
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- if (m_workerThreads[i])
- {
- delete m_workerThreads[i];
- m_workerThreads[i] = NULL;
- }
- }
- }
- Bool Pinger::areThreadsRunning( void )
- {
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- if (m_workerThreads[i])
- {
- if (m_workerThreads[i]->Is_Running())
- return true;
- }
- }
- return false;
- }
- void Pinger::addRequest( const PingRequest& req )
- {
- MutexClass::LockClass m(m_requestMutex);
- ++m_requestCount;
- m_requests.push(req);
- }
- Bool Pinger::getRequest( PingRequest& req )
- {
- MutexClass::LockClass m(m_requestMutex, 0);
- if (m.Failed())
- return false;
- if (m_requests.empty())
- return false;
- req = m_requests.front();
- m_requests.pop();
- return true;
- }
- void Pinger::addResponse( const PingResponse& resp )
- {
- {
- MutexClass::LockClass m(m_pingMapMutex);
- m_pingMap[resp.hostname] = resp.avgPing;
- }
- {
- MutexClass::LockClass m(m_responseMutex);
- ++m_responseCount;
- m_responses.push(resp);
- }
- }
- Bool Pinger::getResponse( PingResponse& resp )
- {
- MutexClass::LockClass m(m_responseMutex, 0);
- if (m.Failed())
- return false;
- if (m_responses.empty())
- return false;
- resp = m_responses.front();
- m_responses.pop();
- return true;
- }
- Bool Pinger::arePingsInProgress( void )
- {
- return (m_requestCount != m_responseCount);
- }
- Int Pinger::getPing( AsciiString hostname )
- {
- MutexClass::LockClass m(m_pingMapMutex, 0);
- if (m.Failed())
- return false;
- std::map<std::string, Int>::const_iterator it = m_pingMap.find(hostname.str());
- if (it != m_pingMap.end())
- return it->second;
- return -1;
- }
- void Pinger::clearPingMap( void )
- {
- MutexClass::LockClass m(m_pingMapMutex);
- m_pingMap.clear();
- }
- AsciiString Pinger::getPingString( Int timeout )
- {
- MutexClass::LockClass m(m_pingMapMutex);
- AsciiString pingString;
- AsciiString tmp;
- for (std::map<std::string, Int>::const_iterator it = m_pingMap.begin(); it != m_pingMap.end(); ++it)
- {
- Int ping = it->second;
- if (ping < 0 || ping > timeout)
- ping = timeout;
- ping = ping * 255 / timeout;
- tmp.format("%2.2X", ping);
- pingString.concat(tmp);
- }
- return pingString;
- }
- //-------------------------------------------------------------------------
- void PingThreadClass::Thread_Function()
- {
- try {
- _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
- PingRequest req;
- WSADATA wsaData;
- // Fire up winsock (prob already done, but doesn't matter)
- WORD wVersionRequested = MAKEWORD(1, 1);
- WSAStartup( wVersionRequested, &wsaData );
- while ( running )
- {
- // deal with requests
- if (ThePinger->getRequest(req))
- {
- // resolve the hostname
- const char *hostnameBuffer = req.hostname.c_str();
- UnsignedInt IP = 0xFFFFFFFF;
- if (isdigit(hostnameBuffer[0]))
- {
- IP = inet_addr(hostnameBuffer);
- in_addr hostNode;
- hostNode.s_addr = IP;
- DEBUG_LOG(("pinging %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
- }
- else
- {
- HOSTENT *hostStruct;
- in_addr *hostNode;
- hostStruct = gethostbyname(hostnameBuffer);
- if (hostStruct == NULL)
- {
- DEBUG_LOG(("pinging %s - host lookup failed\n", hostnameBuffer));
-
- // Even though this failed to resolve IP, still need to send a
- // callback.
- IP = 0xFFFFFFFF; // flag for IP resolve failed
- }
- hostNode = (in_addr *) hostStruct->h_addr;
- IP = hostNode->s_addr;
- DEBUG_LOG(("pinging %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
- }
- // do ping
- Int totalPing = 0;
- Int goodReps = 0;
- Int reps = req.repetitions;
- while (reps-- && running && IP != 0xFFFFFFFF)
- {
- Int ping = doPing(IP, req.timeout);
- if (ping >= 0)
- {
- totalPing += ping;
- ++goodReps;
- }
- // end our timeslice
- Switch_Thread();
- }
- if (!goodReps)
- totalPing = -1;
- else
- totalPing = totalPing / goodReps;
- PingResponse resp;
- resp.hostname = req.hostname;
- resp.avgPing = totalPing;
- resp.repetitions = goodReps;
- ThePinger->addResponse(resp);
- }
- // end our timeslice
- Switch_Thread();
- }
- WSACleanup();
- } catch ( ... ) {
- DEBUG_CRASH(("Exception in ping thread!"));
- }
- }
- //-------------------------------------------------------------------------
- //-------------------------------------------------------------------------
- //-------------------------------------------------------------------------
- //-------------------------------------------------------------------------
- //-------------------------------------------------------------------------
- //-------------------------------------------------------------------------
- HANDLE WINAPI IcmpCreateFile(VOID); /* INVALID_HANDLE_VALUE on error */
- BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle); /* FALSE on error */
- /* Note 2: For the most part, you can refer to RFC 791 for detials
- * on how to fill in values for the IP option information structure.
- */
- typedef struct ip_option_information
- {
- UnsignedByte Ttl; /* Time To Live (used for traceroute) */
- UnsignedByte Tos; /* Type Of Service (usually 0) */
- UnsignedByte Flags; /* IP header flags (usually 0) */
- UnsignedByte OptionsSize; /* Size of options data (usually 0, max 40) */
- UnsignedByte FAR *OptionsData; /* Options data buffer */
- }
- IPINFO, *PIPINFO, FAR *LPIPINFO;
- /* Note 1: The Reply Buffer will have an array of ICMP_ECHO_REPLY
- * structures, followed by options and the data in ICMP echo reply
- * datagram received. You must have room for at least one ICMP
- * echo reply structure, plus 8 bytes for an ICMP header.
- */
- typedef struct icmp_echo_reply
- {
- UnsignedInt Address; /* source address */
- ////////UnsignedInt Status; /* IP status value (see below) */
- UnsignedInt RTTime; /* Round Trip Time in milliseconds */
- UnsignedShort DataSize; /* reply data size */
- UnsignedShort Reserved; /* */
- void FAR *Data; /* reply data buffer */
- struct ip_option_information Options; /* reply options */
- }
- ICMPECHO, *PICMPECHO, FAR *LPICMPECHO;
- DWORD WINAPI IcmpSendEcho(
- HANDLE IcmpHandle, /* handle returned from IcmpCreateFile() */
- UnsignedInt DestAddress, /* destination IP address (in network order) */
- LPVOID RequestData, /* pointer to buffer to send */
- WORD RequestSize, /* length of data in buffer */
- LPIPINFO RequestOptns, /* see Note 2 */
- LPVOID ReplyBuffer, /* see Note 1 */
- DWORD ReplySize, /* length of reply (must allow at least 1 reply) */
- DWORD Timeout /* time in milliseconds to wait for reply */
- );
- #define IP_STATUS_BASE 11000
- #define IP_SUCCESS 0
- #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
- #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
- #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
- #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
- #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
- #define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
- #define IP_BAD_OPTION (IP_STATUS_BASE + 7)
- #define IP_HW_ERROR (IP_STATUS_BASE + 8)
- #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
- #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
- #define IP_BAD_REQ (IP_STATUS_BASE + 11)
- #define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
- #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
- #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
- #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
- #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
- #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
- #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
- #define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
- #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
- #define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
- #define IP_UNLOAD (IP_STATUS_BASE + 22)
- #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
- #define MAX_IP_STATUS IP_GENERAL_FAILURE
- #define IP_PENDING (IP_STATUS_BASE + 255)
- #define BUFSIZE 8192
- #define DEFAULT_LEN 32
- #define LOOPLIMIT 4
- #define DEFAULT_TTL 64
- Int PingThreadClass::doPing(UnsignedInt IP, Int timeout)
- {
- /*
- * Initialize default settings
- */
- IPINFO stIPInfo, *lpstIPInfo;
- HANDLE hICMP, hICMP_DLL;
- int i, j, nDataLen, nLoopLimit, nTimeOut, nTTL, nTOS;
- DWORD dwReplyCount;
- ///////IN_ADDR stDestAddr;
- BOOL fRet, fDontStop;
- ///BOOL fTraceRoute;
- nDataLen = DEFAULT_LEN;
- nLoopLimit = LOOPLIMIT;
- nTimeOut = timeout;
- fDontStop = FALSE;
- lpstIPInfo = NULL;
- nTTL = DEFAULT_TTL;
- nTOS = 0;
- Int pingTime = -1; // in case of error
- char achReqData[BUFSIZE];
- char achRepData[sizeof(ICMPECHO) + BUFSIZE];
- HANDLE ( WINAPI *lpfnIcmpCreateFile )( VOID ) = NULL;
- BOOL ( WINAPI *lpfnIcmpCloseHandle )( HANDLE ) = NULL;
- DWORD (WINAPI *lpfnIcmpSendEcho)(HANDLE, DWORD, LPVOID, WORD, LPVOID,
- LPVOID, DWORD, DWORD) = NULL;
- /*
- * Load the ICMP.DLL
- */
- hICMP_DLL = LoadLibrary("ICMP.DLL");
- if (hICMP_DLL == 0)
- {
- DEBUG_LOG(("LoadLibrary() failed: Unable to locate ICMP.DLL!\n"));
- goto cleanup;
- }
- /*
- * Get pointers to ICMP.DLL functions
- */
- lpfnIcmpCreateFile = (void * (__stdcall *)(void))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCreateFile");
- lpfnIcmpCloseHandle = (int (__stdcall *)(void *))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCloseHandle");
- lpfnIcmpSendEcho = (unsigned long (__stdcall *)(void *, unsigned long, void *, unsigned short,
- void *, void *, unsigned long, unsigned long))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpSendEcho" );
- if ((!lpfnIcmpCreateFile) ||
- (!lpfnIcmpCloseHandle) ||
- (!lpfnIcmpSendEcho))
- {
- DEBUG_LOG(("GetProcAddr() failed for at least one function.\n"));
- goto cleanup;
- }
- /*
- * IcmpCreateFile() - Open the ping service
- */
- hICMP = (HANDLE) lpfnIcmpCreateFile();
- if (hICMP == INVALID_HANDLE_VALUE)
- {
- DEBUG_LOG(("IcmpCreateFile() failed"));
- goto cleanup;
- }
- /*
- * Init data buffer printable ASCII
- * 32 (space) through 126 (tilde)
- */
- for (j = 0, i = 32; j < nDataLen; j++, i++)
- {
- if (i >= 126)
- i = 32;
- achReqData[j] = i;
- }
- /*
- * Init IPInfo structure
- */
- lpstIPInfo = &stIPInfo;
- stIPInfo.Ttl = nTTL;
- stIPInfo.Tos = nTOS;
- stIPInfo.Flags = 0;
- stIPInfo.OptionsSize = 0;
- stIPInfo.OptionsData = NULL;
- /*
- * IcmpSendEcho() - Send the ICMP Echo Request
- * and read the Reply
- */
- dwReplyCount = lpfnIcmpSendEcho(
- hICMP,
- IP,
- achReqData,
- nDataLen,
- lpstIPInfo,
- achRepData,
- sizeof(achRepData),
- nTimeOut);
- if (dwReplyCount != 0)
- {
- //////////IN_ADDR stDestAddr;
- DWORD dwStatus;
- pingTime = (*(UnsignedInt *) & (achRepData[8]));
- // I've seen the ping time bigger than the timeout by a little
- // bit. How lame.
- if (pingTime > timeout)
- pingTime = timeout;
- dwStatus = *(DWORD *) & (achRepData[4]);
- if (dwStatus != IP_SUCCESS)
- {
- DEBUG_LOG(("ICMPERR: %d\n", dwStatus));
- }
- }
- else
- {
- DEBUG_LOG(("IcmpSendEcho() failed: %d\n", dwReplyCount));
- // Ok we didn't get a packet, just say everything's OK
- // and the time was -1
- pingTime = -1;
- goto cleanup;
- }
- /*
- * IcmpCloseHandle - Close the ICMP handle
- */
- fRet = lpfnIcmpCloseHandle(hICMP);
- if (fRet == FALSE)
- {
- DEBUG_LOG(("Error closing ICMP handle\n"));
- }
- // Say what you will about goto's but it's handy for stuff like this
- cleanup:
- // Shut down...
- if (hICMP_DLL)
- FreeLibrary((HINSTANCE)hICMP_DLL);
- return pingTime;
- }
- //-------------------------------------------------------------------------
|