PingThread.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PingThread.cpp //////////////////////////////////////////////////////
  24. // Ping thread
  25. // Author: Matthew D. Campbell, August 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include <winsock.h> // This one has to be here. Prevents collisions with windsock2.h
  28. #include "GameNetwork/GameSpy/PingThread.h"
  29. #include "mutex.h"
  30. #include "thread.h"
  31. #include "Common/StackDump.h"
  32. #include "Common/SubsystemInterface.h"
  33. //-------------------------------------------------------------------------
  34. static const Int NumWorkerThreads = 10;
  35. typedef std::queue<PingRequest> RequestQueue;
  36. typedef std::queue<PingResponse> ResponseQueue;
  37. class PingThreadClass;
  38. class Pinger : public PingerInterface
  39. {
  40. public:
  41. virtual ~Pinger();
  42. Pinger();
  43. virtual void startThreads( void );
  44. virtual void endThreads( void );
  45. virtual Bool areThreadsRunning( void );
  46. virtual void addRequest( const PingRequest& req );
  47. virtual Bool getRequest( PingRequest& resp );
  48. virtual void addResponse( const PingResponse& resp );
  49. virtual Bool getResponse( PingResponse& resp );
  50. virtual Bool arePingsInProgress( void );
  51. virtual Int getPing( AsciiString hostname );
  52. virtual void clearPingMap( void );
  53. virtual AsciiString getPingString( Int timeout );
  54. private:
  55. MutexClass m_requestMutex;
  56. MutexClass m_responseMutex;
  57. MutexClass m_pingMapMutex;
  58. RequestQueue m_requests;
  59. ResponseQueue m_responses;
  60. Int m_requestCount;
  61. Int m_responseCount;
  62. std::map<std::string, Int> m_pingMap;
  63. PingThreadClass *m_workerThreads[NumWorkerThreads];
  64. };
  65. PingerInterface* PingerInterface::createNewPingerInterface( void )
  66. {
  67. return NEW Pinger;
  68. }
  69. PingerInterface *ThePinger;
  70. //-------------------------------------------------------------------------
  71. class PingThreadClass : public ThreadClass
  72. {
  73. public:
  74. PingThreadClass() : ThreadClass() {}
  75. void Thread_Function();
  76. private:
  77. Int doPing( UnsignedInt IP, Int timeout );
  78. };
  79. //-------------------------------------------------------------------------
  80. Pinger::Pinger() : m_requestCount(0), m_responseCount(0)
  81. {
  82. for (Int i=0; i<NumWorkerThreads; ++i)
  83. {
  84. m_workerThreads[i] = NULL;
  85. }
  86. }
  87. Pinger::~Pinger()
  88. {
  89. endThreads();
  90. }
  91. void Pinger::startThreads( void )
  92. {
  93. endThreads();
  94. for (Int i=0; i<NumWorkerThreads; ++i)
  95. {
  96. m_workerThreads[i] = NEW PingThreadClass;
  97. m_workerThreads[i]->Execute();
  98. }
  99. }
  100. void Pinger::endThreads( void )
  101. {
  102. for (Int i=0; i<NumWorkerThreads; ++i)
  103. {
  104. if (m_workerThreads[i])
  105. {
  106. delete m_workerThreads[i];
  107. m_workerThreads[i] = NULL;
  108. }
  109. }
  110. }
  111. Bool Pinger::areThreadsRunning( void )
  112. {
  113. for (Int i=0; i<NumWorkerThreads; ++i)
  114. {
  115. if (m_workerThreads[i])
  116. {
  117. if (m_workerThreads[i]->Is_Running())
  118. return true;
  119. }
  120. }
  121. return false;
  122. }
  123. void Pinger::addRequest( const PingRequest& req )
  124. {
  125. MutexClass::LockClass m(m_requestMutex);
  126. ++m_requestCount;
  127. m_requests.push(req);
  128. }
  129. Bool Pinger::getRequest( PingRequest& req )
  130. {
  131. MutexClass::LockClass m(m_requestMutex, 0);
  132. if (m.Failed())
  133. return false;
  134. if (m_requests.empty())
  135. return false;
  136. req = m_requests.front();
  137. m_requests.pop();
  138. return true;
  139. }
  140. void Pinger::addResponse( const PingResponse& resp )
  141. {
  142. {
  143. MutexClass::LockClass m(m_pingMapMutex);
  144. m_pingMap[resp.hostname] = resp.avgPing;
  145. }
  146. {
  147. MutexClass::LockClass m(m_responseMutex);
  148. ++m_responseCount;
  149. m_responses.push(resp);
  150. }
  151. }
  152. Bool Pinger::getResponse( PingResponse& resp )
  153. {
  154. MutexClass::LockClass m(m_responseMutex, 0);
  155. if (m.Failed())
  156. return false;
  157. if (m_responses.empty())
  158. return false;
  159. resp = m_responses.front();
  160. m_responses.pop();
  161. return true;
  162. }
  163. Bool Pinger::arePingsInProgress( void )
  164. {
  165. return (m_requestCount != m_responseCount);
  166. }
  167. Int Pinger::getPing( AsciiString hostname )
  168. {
  169. MutexClass::LockClass m(m_pingMapMutex, 0);
  170. if (m.Failed())
  171. return false;
  172. std::map<std::string, Int>::const_iterator it = m_pingMap.find(hostname.str());
  173. if (it != m_pingMap.end())
  174. return it->second;
  175. return -1;
  176. }
  177. void Pinger::clearPingMap( void )
  178. {
  179. MutexClass::LockClass m(m_pingMapMutex);
  180. m_pingMap.clear();
  181. }
  182. AsciiString Pinger::getPingString( Int timeout )
  183. {
  184. MutexClass::LockClass m(m_pingMapMutex);
  185. AsciiString pingString;
  186. AsciiString tmp;
  187. for (std::map<std::string, Int>::const_iterator it = m_pingMap.begin(); it != m_pingMap.end(); ++it)
  188. {
  189. Int ping = it->second;
  190. if (ping < 0 || ping > timeout)
  191. ping = timeout;
  192. ping = ping * 255 / timeout;
  193. tmp.format("%2.2X", ping);
  194. pingString.concat(tmp);
  195. }
  196. return pingString;
  197. }
  198. //-------------------------------------------------------------------------
  199. void PingThreadClass::Thread_Function()
  200. {
  201. try {
  202. _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
  203. PingRequest req;
  204. WSADATA wsaData;
  205. // Fire up winsock (prob already done, but doesn't matter)
  206. WORD wVersionRequested = MAKEWORD(1, 1);
  207. WSAStartup( wVersionRequested, &wsaData );
  208. while ( running )
  209. {
  210. // deal with requests
  211. if (ThePinger->getRequest(req))
  212. {
  213. // resolve the hostname
  214. const char *hostnameBuffer = req.hostname.c_str();
  215. UnsignedInt IP = 0xFFFFFFFF;
  216. if (isdigit(hostnameBuffer[0]))
  217. {
  218. IP = inet_addr(hostnameBuffer);
  219. in_addr hostNode;
  220. hostNode.s_addr = IP;
  221. DEBUG_LOG(("pinging %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
  222. }
  223. else
  224. {
  225. HOSTENT *hostStruct;
  226. in_addr *hostNode;
  227. hostStruct = gethostbyname(hostnameBuffer);
  228. if (hostStruct == NULL)
  229. {
  230. DEBUG_LOG(("pinging %s - host lookup failed\n", hostnameBuffer));
  231. // Even though this failed to resolve IP, still need to send a
  232. // callback.
  233. IP = 0xFFFFFFFF; // flag for IP resolve failed
  234. }
  235. hostNode = (in_addr *) hostStruct->h_addr;
  236. IP = hostNode->s_addr;
  237. DEBUG_LOG(("pinging %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
  238. }
  239. // do ping
  240. Int totalPing = 0;
  241. Int goodReps = 0;
  242. Int reps = req.repetitions;
  243. while (reps-- && running && IP != 0xFFFFFFFF)
  244. {
  245. Int ping = doPing(IP, req.timeout);
  246. if (ping >= 0)
  247. {
  248. totalPing += ping;
  249. ++goodReps;
  250. }
  251. // end our timeslice
  252. Switch_Thread();
  253. }
  254. if (!goodReps)
  255. totalPing = -1;
  256. else
  257. totalPing = totalPing / goodReps;
  258. PingResponse resp;
  259. resp.hostname = req.hostname;
  260. resp.avgPing = totalPing;
  261. resp.repetitions = goodReps;
  262. ThePinger->addResponse(resp);
  263. }
  264. // end our timeslice
  265. Switch_Thread();
  266. }
  267. WSACleanup();
  268. } catch ( ... ) {
  269. DEBUG_CRASH(("Exception in ping thread!"));
  270. }
  271. }
  272. //-------------------------------------------------------------------------
  273. //-------------------------------------------------------------------------
  274. //-------------------------------------------------------------------------
  275. //-------------------------------------------------------------------------
  276. //-------------------------------------------------------------------------
  277. //-------------------------------------------------------------------------
  278. HANDLE WINAPI IcmpCreateFile(VOID); /* INVALID_HANDLE_VALUE on error */
  279. BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle); /* FALSE on error */
  280. /* Note 2: For the most part, you can refer to RFC 791 for detials
  281. * on how to fill in values for the IP option information structure.
  282. */
  283. typedef struct ip_option_information
  284. {
  285. UnsignedByte Ttl; /* Time To Live (used for traceroute) */
  286. UnsignedByte Tos; /* Type Of Service (usually 0) */
  287. UnsignedByte Flags; /* IP header flags (usually 0) */
  288. UnsignedByte OptionsSize; /* Size of options data (usually 0, max 40) */
  289. UnsignedByte FAR *OptionsData; /* Options data buffer */
  290. }
  291. IPINFO, *PIPINFO, FAR *LPIPINFO;
  292. /* Note 1: The Reply Buffer will have an array of ICMP_ECHO_REPLY
  293. * structures, followed by options and the data in ICMP echo reply
  294. * datagram received. You must have room for at least one ICMP
  295. * echo reply structure, plus 8 bytes for an ICMP header.
  296. */
  297. typedef struct icmp_echo_reply
  298. {
  299. UnsignedInt Address; /* source address */
  300. ////////UnsignedInt Status; /* IP status value (see below) */
  301. UnsignedInt RTTime; /* Round Trip Time in milliseconds */
  302. UnsignedShort DataSize; /* reply data size */
  303. UnsignedShort Reserved; /* */
  304. void FAR *Data; /* reply data buffer */
  305. struct ip_option_information Options; /* reply options */
  306. }
  307. ICMPECHO, *PICMPECHO, FAR *LPICMPECHO;
  308. DWORD WINAPI IcmpSendEcho(
  309. HANDLE IcmpHandle, /* handle returned from IcmpCreateFile() */
  310. UnsignedInt DestAddress, /* destination IP address (in network order) */
  311. LPVOID RequestData, /* pointer to buffer to send */
  312. WORD RequestSize, /* length of data in buffer */
  313. LPIPINFO RequestOptns, /* see Note 2 */
  314. LPVOID ReplyBuffer, /* see Note 1 */
  315. DWORD ReplySize, /* length of reply (must allow at least 1 reply) */
  316. DWORD Timeout /* time in milliseconds to wait for reply */
  317. );
  318. #define IP_STATUS_BASE 11000
  319. #define IP_SUCCESS 0
  320. #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
  321. #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
  322. #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
  323. #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
  324. #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
  325. #define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
  326. #define IP_BAD_OPTION (IP_STATUS_BASE + 7)
  327. #define IP_HW_ERROR (IP_STATUS_BASE + 8)
  328. #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
  329. #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
  330. #define IP_BAD_REQ (IP_STATUS_BASE + 11)
  331. #define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
  332. #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
  333. #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
  334. #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
  335. #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
  336. #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
  337. #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
  338. #define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
  339. #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
  340. #define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
  341. #define IP_UNLOAD (IP_STATUS_BASE + 22)
  342. #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
  343. #define MAX_IP_STATUS IP_GENERAL_FAILURE
  344. #define IP_PENDING (IP_STATUS_BASE + 255)
  345. #define BUFSIZE 8192
  346. #define DEFAULT_LEN 32
  347. #define LOOPLIMIT 4
  348. #define DEFAULT_TTL 64
  349. Int PingThreadClass::doPing(UnsignedInt IP, Int timeout)
  350. {
  351. /*
  352. * Initialize default settings
  353. */
  354. IPINFO stIPInfo, *lpstIPInfo;
  355. HANDLE hICMP, hICMP_DLL;
  356. int i, j, nDataLen, nLoopLimit, nTimeOut, nTTL, nTOS;
  357. DWORD dwReplyCount;
  358. ///////IN_ADDR stDestAddr;
  359. BOOL fRet, fDontStop;
  360. ///BOOL fTraceRoute;
  361. nDataLen = DEFAULT_LEN;
  362. nLoopLimit = LOOPLIMIT;
  363. nTimeOut = timeout;
  364. fDontStop = FALSE;
  365. lpstIPInfo = NULL;
  366. nTTL = DEFAULT_TTL;
  367. nTOS = 0;
  368. Int pingTime = -1; // in case of error
  369. char achReqData[BUFSIZE];
  370. char achRepData[sizeof(ICMPECHO) + BUFSIZE];
  371. HANDLE ( WINAPI *lpfnIcmpCreateFile )( VOID ) = NULL;
  372. BOOL ( WINAPI *lpfnIcmpCloseHandle )( HANDLE ) = NULL;
  373. DWORD (WINAPI *lpfnIcmpSendEcho)(HANDLE, DWORD, LPVOID, WORD, LPVOID,
  374. LPVOID, DWORD, DWORD) = NULL;
  375. /*
  376. * Load the ICMP.DLL
  377. */
  378. hICMP_DLL = LoadLibrary("ICMP.DLL");
  379. if (hICMP_DLL == 0)
  380. {
  381. DEBUG_LOG(("LoadLibrary() failed: Unable to locate ICMP.DLL!\n"));
  382. goto cleanup;
  383. }
  384. /*
  385. * Get pointers to ICMP.DLL functions
  386. */
  387. lpfnIcmpCreateFile = (void * (__stdcall *)(void))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCreateFile");
  388. lpfnIcmpCloseHandle = (int (__stdcall *)(void *))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpCloseHandle");
  389. lpfnIcmpSendEcho = (unsigned long (__stdcall *)(void *, unsigned long, void *, unsigned short,
  390. void *, void *, unsigned long, unsigned long))GetProcAddress( (HINSTANCE)hICMP_DLL, "IcmpSendEcho" );
  391. if ((!lpfnIcmpCreateFile) ||
  392. (!lpfnIcmpCloseHandle) ||
  393. (!lpfnIcmpSendEcho))
  394. {
  395. DEBUG_LOG(("GetProcAddr() failed for at least one function.\n"));
  396. goto cleanup;
  397. }
  398. /*
  399. * IcmpCreateFile() - Open the ping service
  400. */
  401. hICMP = (HANDLE) lpfnIcmpCreateFile();
  402. if (hICMP == INVALID_HANDLE_VALUE)
  403. {
  404. DEBUG_LOG(("IcmpCreateFile() failed"));
  405. goto cleanup;
  406. }
  407. /*
  408. * Init data buffer printable ASCII
  409. * 32 (space) through 126 (tilde)
  410. */
  411. for (j = 0, i = 32; j < nDataLen; j++, i++)
  412. {
  413. if (i >= 126)
  414. i = 32;
  415. achReqData[j] = i;
  416. }
  417. /*
  418. * Init IPInfo structure
  419. */
  420. lpstIPInfo = &stIPInfo;
  421. stIPInfo.Ttl = nTTL;
  422. stIPInfo.Tos = nTOS;
  423. stIPInfo.Flags = 0;
  424. stIPInfo.OptionsSize = 0;
  425. stIPInfo.OptionsData = NULL;
  426. /*
  427. * IcmpSendEcho() - Send the ICMP Echo Request
  428. * and read the Reply
  429. */
  430. dwReplyCount = lpfnIcmpSendEcho(
  431. hICMP,
  432. IP,
  433. achReqData,
  434. nDataLen,
  435. lpstIPInfo,
  436. achRepData,
  437. sizeof(achRepData),
  438. nTimeOut);
  439. if (dwReplyCount != 0)
  440. {
  441. //////////IN_ADDR stDestAddr;
  442. DWORD dwStatus;
  443. pingTime = (*(UnsignedInt *) & (achRepData[8]));
  444. // I've seen the ping time bigger than the timeout by a little
  445. // bit. How lame.
  446. if (pingTime > timeout)
  447. pingTime = timeout;
  448. dwStatus = *(DWORD *) & (achRepData[4]);
  449. if (dwStatus != IP_SUCCESS)
  450. {
  451. DEBUG_LOG(("ICMPERR: %d\n", dwStatus));
  452. }
  453. }
  454. else
  455. {
  456. DEBUG_LOG(("IcmpSendEcho() failed: %d\n", dwReplyCount));
  457. // Ok we didn't get a packet, just say everything's OK
  458. // and the time was -1
  459. pingTime = -1;
  460. goto cleanup;
  461. }
  462. /*
  463. * IcmpCloseHandle - Close the ICMP handle
  464. */
  465. fRet = lpfnIcmpCloseHandle(hICMP);
  466. if (fRet == FALSE)
  467. {
  468. DEBUG_LOG(("Error closing ICMP handle\n"));
  469. }
  470. // Say what you will about goto's but it's handy for stuff like this
  471. cleanup:
  472. // Shut down...
  473. if (hICMP_DLL)
  474. FreeLibrary((HINSTANCE)hICMP_DLL);
  475. return pingTime;
  476. }
  477. //-------------------------------------------------------------------------