/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, 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 . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ /***************************************************************************** * * $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp * ***/ #include "Pch.h" #pragma hdrstop /***************************************************************************** * * Local types * ***/ typedef HANDLE (PASCAL FAR * FIcmpCreateFile) (); typedef DWORD (PASCAL FAR * FIcmpSendEcho) ( HANDLE icmpHandle, DWORD destinationAddress, LPVOID requestData, WORD requestSize, PIP_OPTION_INFORMATION options, LPVOID replyBuffer, DWORD replySize, DWORD timeoutMs ); struct PingParam { NetDiag * diag; FNetDiagDumpProc dump; FNetDiagTestCallback callback; void * param; ENetProtocol protocol; unsigned srv; }; /***************************************************************************** * * Local data * ***/ static const unsigned kPingCount = 5; static const unsigned kPayloadBytes = 32; static FIcmpCreateFile IcmpCreateFile; static FIcmpSendEcho IcmpSendEcho; static byte s_payload[kPayloadBytes]; /***************************************************************************** * * Local functions * ***/ //============================================================================ static const wchar * IpStatusToString (ULONG status) { switch (status) { case IP_SUCCESS: return L"IP_SUCCESS"; case IP_BUF_TOO_SMALL: return L"IP_BUF_TOO_SMALL"; case IP_DEST_NET_UNREACHABLE: return L"IP_DEST_NET_UNREACHABLE"; case IP_DEST_HOST_UNREACHABLE: return L"IP_DEST_HOST_UNREACHABLE"; case IP_DEST_PROT_UNREACHABLE: return L"IP_DEST_PROT_UNREACHABLE"; case IP_DEST_PORT_UNREACHABLE: return L"IP_DEST_PORT_UNREACHABLE"; case IP_NO_RESOURCES: return L"IP_NO_RESOURCES"; case IP_BAD_OPTION: return L"IP_BAD_OPTION"; case IP_HW_ERROR: return L"IP_HW_ERROR"; case IP_PACKET_TOO_BIG: return L"IP_PACKET_TOO_BIG"; case IP_REQ_TIMED_OUT: return L"IP_REQ_TIMED_OUT"; case IP_BAD_REQ: return L"IP_BAD_REQ"; case IP_BAD_ROUTE: return L"IP_BAD_ROUTE"; case IP_TTL_EXPIRED_TRANSIT: return L"IP_TTL_EXPIRED_TRANSIT"; case IP_TTL_EXPIRED_REASSEM: return L"IP_TTL_EXPIRED_REASSEM"; case IP_PARAM_PROBLEM: return L"IP_PARAM_PROBLEM"; case IP_SOURCE_QUENCH: return L"IP_SOURCE_QUENCH"; case IP_OPTION_TOO_BIG: return L"IP_OPTION_TOO_BIG"; case IP_BAD_DESTINATION: return L"IP_BAD_DESTINATION"; default: return L"Unknown error"; } } //============================================================================ static void __cdecl PingThreadProc (void * param) { PingParam * p = (PingParam *)param; HANDLE icmp = IcmpCreateFile(); if (!icmp) { p->dump(L"[ICMP] Failed to create ICMP handle"); p->callback(p->diag, p->protocol, kNetErrFileNotFound, p->param); return; } char addr[64]; wchar waddr[64]; NetAddressNodeToString(p->diag->nodes[p->srv], waddr, arrsize(waddr)); StrToAnsi(addr, waddr, arrsize(addr)); ENetError result = kNetSuccess; byte reply[kPayloadBytes + sizeof(ICMP_ECHO_REPLY)]; for (unsigned i = 0; i < kPingCount; ++i) { DWORD retval = IcmpSendEcho( icmp, inet_addr(addr), s_payload, sizeof(s_payload), NULL, reply, sizeof(reply), 4000 ); PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)reply; if (retval) { p->dump(L"[ICMP] Reply from %s. ms=%u", waddr, pEchoReply->RoundTripTime); } else { result = kNetErrConnectFailed; p->dump(L"[ICMP] No reply from %s. %s", waddr, IpStatusToString(pEchoReply->Status)); } } p->callback(p->diag, p->protocol, result, p->param); p->diag->DecRef("ICMP"); DEL(p); } /***************************************************************************** * * Module functions * ***/ //============================================================================ void IcmpStartup () { if (g_lib) { IcmpCreateFile = (FIcmpCreateFile)GetProcAddress(g_lib, "IcmpCreateFile"); IcmpSendEcho = (FIcmpSendEcho)GetProcAddress(g_lib, "IcmpSendEcho"); } MemSet(s_payload, (byte)((unsigned_ptr)&s_payload >> 4), arrsize(s_payload)); } //============================================================================ void IcmpShutdown () { IcmpCreateFile = nil; IcmpSendEcho = nil; } /***************************************************************************** * * Exports * ***/ //============================================================================ void NetDiagIcmp ( NetDiag * diag, ENetProtocol protocol, FNetDiagDumpProc dump, FNetDiagTestCallback callback, void * param ) { ASSERT(diag); ASSERT(dump); ASSERT(callback); if (!IcmpCreateFile || !IcmpSendEcho) { dump(L"[ICMP] Failed to load IP helper API"); callback(diag, protocol, kNetErrNotSupported, param); return; } unsigned srv = NetProtocolToSrv(protocol); if (srv == kNumDiagSrvs) { dump(L"[ICMP] Unsupported protocol: %s", NetProtocolToString(protocol)); callback(diag, protocol, kNetErrNotSupported, param); return; } unsigned node = 0; diag->critsect.Enter(); { node = diag->nodes[srv]; } diag->critsect.Leave(); if (!node) { dump(L"[ICMP] No address set for protocol: %s", NetProtocolToString(protocol)); callback(diag, protocol, kNetSuccess, param); return; } wchar nodeStr[64]; NetAddressNodeToString(node, nodeStr, arrsize(nodeStr)); dump(L"[ICMP] Pinging %s with %u bytes of data...", nodeStr, kPayloadBytes); PingParam * pingParam = NEWZERO(PingParam); pingParam->diag = diag; pingParam->srv = srv; pingParam->protocol = protocol; pingParam->dump = dump; pingParam->callback = callback; pingParam->param = param; diag->IncRef("ICMP"); _beginthread(PingThreadProc, 0, pingParam); }