You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
7.4 KiB

/*==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 <http://www.gnu.org/licenses/>.
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);
}