From 7ec7480d5c6288ac22830bbd57e965fd0e8038c5 Mon Sep 17 00:00:00 2001 From: Casper Hornstrup Date: Sun, 17 Sep 2000 09:40:33 +0000 Subject: [PATCH] Added ping application (won't work on ReactOS yet) svn path=/trunk/; revision=1365 --- reactos/apps/utils/net/ping/makefile | 46 +++ reactos/apps/utils/net/ping/ping.c | 572 +++++++++++++++++++++++++++ 2 files changed, 618 insertions(+) create mode 100644 reactos/apps/utils/net/ping/makefile create mode 100644 reactos/apps/utils/net/ping/ping.c diff --git a/reactos/apps/utils/net/ping/makefile b/reactos/apps/utils/net/ping/makefile new file mode 100644 index 00000000000..f6078560334 --- /dev/null +++ b/reactos/apps/utils/net/ping/makefile @@ -0,0 +1,46 @@ +# +# ReactOS Ping Utility +# +PATH_TO_TOP = ../../.. + +TARGETNAME=ping +BASE_CFLAGS = -I../../../include + +OBJECTS = $(TARGETNAME).o +PROGS = $(TARGETNAME).exe +LIBS = ../../../lib/ntdll/ntdll.a \ + ../../../lib/ws2_32/ws2_32.a + +CLEAN_FILES = $(TARGETNAME).o $(TARGETNAME).exe $(TARGETNAME).sym + +all: $(TARGETNAME).exe + +clean: $(CLEAN_FILES:%=%_clean) + +$(CLEAN_FILES:%=%_clean): %_clean: + - $(RM) $* + +.phony: clean $(CLEAN_FILES:%=%_clean) + +install: $(PROGS:%=$(FLOPPY_DIR)/apps/%) + +$(PROGS:%=$(FLOPPY_DIR)/apps/%): $(FLOPPY_DIR)/apps/%: % +ifeq ($(DOSCLI),yes) + $(CP) $* $(FLOPPY_DIR)\apps\$* +else + $(CP) $* $(FLOPPY_DIR)/apps/$* +endif + +dist: $(PROGS:%=../../$(DIST_DIR)/apps/%) + +$(PROGS:%=../../$(DIST_DIR)/apps/%): ../../$(DIST_DIR)/apps/%: % +ifeq ($(DOSCLI),yes) + $(CP) $* ..\..\$(DIST_DIR)\apps\$* +else + $(CP) $* ../../$(DIST_DIR)/apps/$* +endif + +$(TARGETNAME).exe: $(OBJECTS) $(LIBS) + $(CC) $(OBJECTS) $(LIBS) -o $(TARGETNAME).exe + +include ../../../rules.mak diff --git a/reactos/apps/utils/net/ping/ping.c b/reactos/apps/utils/net/ping/ping.c new file mode 100644 index 00000000000..07914db3171 --- /dev/null +++ b/reactos/apps/utils/net/ping/ping.c @@ -0,0 +1,572 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS ping utility + * FILE: apps/net/ping/ping.c + * PURPOSE: Network test utility + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/09/2000 Created + */ +#include +#include +#include +#include +#include +#ifndef _MSC_VER + +/* FIXME: Where should this be? */ +#define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length); + +/* Should be in the header files somewhere (exported by ntdll.dll) */ +long atol(const char *str); + +char * _i64toa(__int64 value, char *string, int radix); + +#endif + + +/* General ICMP constants */ +#define ICMP_MINSIZE 8 /* Minimum ICMP packet size */ +#define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */ + +/* ICMP message types */ +#define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */ +#define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */ + +#pragma pack(4) + +/* IPv4 header structure */ +typedef struct _IPv4_HEADER { + unsigned char IHL:4; + unsigned char Version:4; + unsigned char TOS; + unsigned short Length; + unsigned short Id; + unsigned short FragFlags; + unsigned char TTL; + unsigned char Protocol; + unsigned short Checksum; + unsigned int SrcAddress; + unsigned int DstAddress; +} IPv4_HEADER, *PIPv4_HEADER; + +/* ICMP echo request/reply header structure */ +typedef struct _ICMP_HEADER { + unsigned char Type; + unsigned char Code; + unsigned short Checksum; + unsigned short Id; + unsigned short SeqNum; +} ICMP_HEADER, *PICMP_HEADER; + +typedef struct _ICMP_ECHO_PACKET { + ICMP_HEADER Icmp; + LARGE_INTEGER Timestamp; +} ICMP_ECHO_PACKET, *PICMP_ECHO_PACKET; + +#pragma pack(1) + +BOOL InvalidOption; +BOOL NeverStop; +BOOL ResolveAddresses; +UINT PingCount; +UINT DataSize; /* ICMP echo request data size */ +BOOL DontFragment; +ULONG TTLValue; +ULONG TOSValue; +ULONG Timeout; +CHAR TargetName[256]; +SOCKET IcmpSock; +SOCKADDR_IN Target; +LPSTR TargetIP; +FD_SET Fds; +TIMEVAL Timeval; +UINT CurrentSeqNum; +UINT SentCount; +UINT LostCount; +BOOL MinRTTSet; +LARGE_INTEGER MinRTT; /* Minimum round trip time in microseconds */ +LARGE_INTEGER MaxRTT; +LARGE_INTEGER SumRTT; +LARGE_INTEGER AvgRTT; +LARGE_INTEGER TicksPerMs; /* Ticks per millisecond */ +LARGE_INTEGER TicksPerUs; /* Ticks per microsecond */ +BOOL UsePerformanceCounter; + + +/* Display usage information on screen */ +VOID Usage(VOID) +{ + printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n"); + printf("Options:\n"); + printf(" -t Ping the specified host until stopped.\n"); + printf(" To stop - type Control-C.\n"); + printf(" -n count Number of echo requests to send.\n"); + printf(" -l size Send buffer size.\n"); + printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n"); +} + +/* Reset configuration to default values */ +VOID Reset(VOID) +{ + LARGE_INTEGER PerformanceCounterFrequency; + + NeverStop = FALSE; + ResolveAddresses = FALSE; + PingCount = 4; + DataSize = 32; + DontFragment = FALSE; + TTLValue = 128; + TOSValue = 0; + Timeout = 1000; + UsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency); + + if (UsePerformanceCounter) { + /* Performance counters may return incorrect results on some multiprocessor + platforms so we restrict execution on the first processor. This may fail on + Windows NT so we fall back to GetCurrentTick() for timing */ + if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) { + UsePerformanceCounter = FALSE; + } + + /* Convert frequency to ticks per millisecond */ + TicksPerMs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000; + /* And to ticks per microsecond */ + TicksPerUs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000000; + } + if (!UsePerformanceCounter) { + /* 1 tick per millisecond for GetCurrentTick() */ + TicksPerMs.QuadPart = 1; + /* GetCurrentTick() cannot handle microseconds */ + TicksPerUs.QuadPart = 1; + } +} + +/* Return ULONG in a string */ +ULONG GetULONG(LPSTR String) +{ + UINT i, Length; + ULONG Value; + + i = 0; + Length = strlen(String); + while ((i < Length) && ((String[i] < '0') || (String[i] > '9'))) i++; + if ((i >= Length) || ((String[i] < '0') || (String[i] > '9'))) { + InvalidOption = TRUE; + return 0; + } + Value = (ULONG)atol(&String[i]); + + return Value; +} + +/* Return ULONG in a string. Try next paramter if not successful */ +ULONG GetULONG2(LPSTR String1, LPSTR String2, PINT i) +{ + ULONG Value; + + Value = GetULONG(String1); + if (InvalidOption) { + InvalidOption = FALSE; + if (String2[0] != '-') { + Value = GetULONG(String2); + if (!InvalidOption) + *i += 1; + } + } + + return Value; +} + +/* Parse command line parameters */ +BOOL ParseCmdline(int argc, char* argv[]) +{ + INT i; + BOOL ShowUsage; + BOOL FoundTarget; + + if (argc < 2) { + ShowUsage = TRUE; + } else { + ShowUsage = FALSE; + } + FoundTarget = FALSE; + InvalidOption = FALSE; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 't': NeverStop = TRUE; break; + case 'a': ResolveAddresses = TRUE; break; + case 'n': PingCount = GetULONG2(&argv[i][2], argv[i + 1], &i); break; + case 'l': + DataSize = GetULONG2(&argv[i][2], argv[i + 1], &i); + if ((DataSize < 0) || (DataSize > ICMP_MAXSIZE - sizeof(ICMP_ECHO_PACKET))) { + printf("Bad value for option -l, valid range is from 0 to %d.\n", + ICMP_MAXSIZE - sizeof(ICMP_ECHO_PACKET)); + return FALSE; + } + break; + case 'f': DontFragment = TRUE; break; + case 'i': TTLValue = GetULONG2(&argv[i][2], argv[i + 1], &i); break; + case 'v': TOSValue = GetULONG2(&argv[i][2], argv[i + 1], &i); break; + case 'w': Timeout = GetULONG2(&argv[i][2], argv[i + 1], &i); break; + default: + printf("Bad option %s.\n", argv[i]); + Usage(); + return FALSE; + } + if (InvalidOption) { + printf("Bad option format %s.\n", argv[i]); + return FALSE; + } + } else { + if (FoundTarget) { + printf("Bad parameter %s.\n", argv[i]); + return FALSE; + } else { + strcpy(TargetName, argv[i]); + FoundTarget = TRUE; + } + } + } + + if ((!ShowUsage) && (!FoundTarget)) { + printf("Name or IP address of destination host must be specified.\n"); + return FALSE; + } + + if (ShowUsage) { + Usage(); + return FALSE; + } + return TRUE; +} + +/* Calculate checksum of data */ +WORD Checksum(PUSHORT data, UINT size) +{ + ULONG sum = 0; + + while (size > 1) { + sum += *data++; + size -= sizeof(USHORT); + } + + if (size) + sum += *(UCHAR*)data; + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + + return (USHORT)(~sum); +} + +/* Prepare to ping target */ +BOOL Setup(VOID) +{ + WORD wVersionRequested; + WSADATA WsaData; + INT Status; + ULONG Addr; + PHOSTENT phe; + + wVersionRequested = MAKEWORD(2, 2); + + Status = WSAStartup(wVersionRequested, &WsaData); + if (Status != 0) { + printf("Could not initialize winsock dll.\n"); + return FALSE; + } + + IcmpSock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0); + if (IcmpSock == INVALID_SOCKET) { + printf("Could not create socket (#%d).\n", WSAGetLastError()); + return FALSE; + } + + ZeroMemory(&Target, sizeof(Target)); + phe = NULL; + Addr = inet_addr(TargetName); + if (Addr == INADDR_NONE) { + phe = gethostbyname(TargetName); + if (phe == NULL) { + printf("Unknown host %s.\n", TargetName); + return FALSE; + } + } + + if (phe != NULL) { + CopyMemory(&Target.sin_addr, phe->h_addr_list, phe->h_length); + } else { + Target.sin_addr.s_addr = Addr; + } + + if (phe != NULL) { + Target.sin_family = phe->h_addrtype; + } else { + Target.sin_family = AF_INET; + } + + TargetIP = inet_ntoa(Target.sin_addr); + CurrentSeqNum = 0; + SentCount = 0; + LostCount = 0; + MinRTT.QuadPart = 0; + MaxRTT.QuadPart = 0; + SumRTT.QuadPart = 0; + MinRTTSet = FALSE; + return TRUE; +} + +/* Close socket */ +VOID Cleanup(VOID) +{ + if (IcmpSock != INVALID_SOCKET) + closesocket(IcmpSock); + + WSACleanup(); +} + +VOID QueryTime(PLARGE_INTEGER Time) +{ + if (UsePerformanceCounter) { + if (QueryPerformanceCounter(Time) == 0) { + /* This should not happen, but we fall + back to GetCurrentTick() if it does */ + Time->u.LowPart = (ULONG)GetTickCount(); + Time->u.HighPart = 0; + + /* 1 tick per millisecond for GetCurrentTick() */ + TicksPerMs.QuadPart = 1; + /* GetCurrentTick() cannot handle microseconds */ + TicksPerUs.QuadPart = 1; + + UsePerformanceCounter = FALSE; + } + } else { + Time->u.LowPart = (ULONG)GetTickCount(); + Time->u.HighPart = 0; + } +} + +VOID TimeToMsString(LPSTR String, LARGE_INTEGER Time) +{ + UINT i, Length; + CHAR Convstr[40]; + LARGE_INTEGER LargeTime; + + LargeTime.QuadPart = Time.QuadPart / TicksPerMs.QuadPart; + _i64toa(LargeTime.QuadPart, Convstr, 10); + strcpy(String, Convstr); + strcat(String, ","); + + LargeTime.QuadPart = (Time.QuadPart % TicksPerMs.QuadPart) / TicksPerUs.QuadPart; + _i64toa(LargeTime.QuadPart, Convstr, 10); + Length = strlen(Convstr); + if (Length < 4) { + for (i = 0; i < 4 - Length; i++) + strcat(String, "0"); + } + + strcat(String, Convstr); + strcat(String, "ms"); +} + +/* Locate the ICMP data and print it. Returns TRUE if the packet was good, + FALSE if not */ +BOOL DecodeResponse(PCHAR buffer, UINT size, PSOCKADDR_IN from) +{ + PIPv4_HEADER IpHeader; + PICMP_ECHO_PACKET Icmp; + UINT IphLength; + CHAR Time[100]; + LARGE_INTEGER RelativeTime; + LARGE_INTEGER LargeTime; + + IpHeader = (PIPv4_HEADER)buffer; + + IphLength = IpHeader->IHL * 4; + + if (size < IphLength + ICMP_MINSIZE) + return FALSE; + + Icmp = (PICMP_ECHO_PACKET)(buffer + IphLength); + + if (Icmp->Icmp.Type != ICMPMSG_ECHOREPLY) + return FALSE; + + if (Icmp->Icmp.Id != (USHORT)GetCurrentProcessId()) + return FALSE; + + QueryTime(&LargeTime); + + RelativeTime.QuadPart = (LargeTime.QuadPart - Icmp->Timestamp.QuadPart); + + TimeToMsString(Time, RelativeTime); + + printf("Reply from %s: bytes=%d time=%s TTL=%d\n", inet_ntoa(from->sin_addr), + size - IphLength - sizeof(ICMP_ECHO_PACKET) , Time, IpHeader->TTL); + if (RelativeTime.QuadPart < MinRTT.QuadPart) { + MinRTT.QuadPart = RelativeTime.QuadPart; + MinRTTSet = TRUE; + } + if (RelativeTime.QuadPart > MaxRTT.QuadPart) + MaxRTT.QuadPart = RelativeTime.QuadPart; + SumRTT.QuadPart += RelativeTime.QuadPart; + + return TRUE; +} + +/* Send and receive one ping */ +BOOL Ping(VOID) +{ + INT Status; + SOCKADDR From; + UINT Length; + PVOID Buffer; + UINT Size; + PICMP_ECHO_PACKET Packet; + + /* Account for extra space for IP header when packet is received */ + Size = DataSize + 128; + Buffer = GlobalAlloc(0, Size); + if (!Buffer) { + printf("Not enough free resources available.\n"); + return FALSE; + } + + ZeroMemory(Buffer, Size); + Packet = (PICMP_ECHO_PACKET)Buffer; + + /* Assemble ICMP echo request packet */ + Packet->Icmp.Type = ICMPMSG_ECHOREQUEST; + Packet->Icmp.Code = 0; + Packet->Icmp.Id = (USHORT)GetCurrentProcessId(); + Packet->Icmp.SeqNum = (USHORT)CurrentSeqNum; + Packet->Icmp.Checksum = 0; + + /* Timestamp is part of data area */ + QueryTime(&Packet->Timestamp); + + CopyMemory(Buffer, &Packet->Icmp, sizeof(ICMP_ECHO_PACKET) + DataSize); + /* Calculate checksum for ICMP header and data area */ + Packet->Icmp.Checksum = Checksum((PUSHORT)&Packet->Icmp, sizeof(ICMP_ECHO_PACKET) + DataSize); + + CurrentSeqNum++; + + /* Send ICMP echo request */ + + FD_ZERO(&Fds); + FD_SET(IcmpSock, &Fds); + Timeval.tv_sec = Timeout / 1000; + Timeval.tv_usec = Timeout % 1000; + Status = select(0, NULL, &Fds, NULL, &Timeval); + if ((Status != SOCKET_ERROR) && (Status != 0)) { + Status = sendto(IcmpSock, Buffer, sizeof(ICMP_ECHO_PACKET) + DataSize, + 0, (SOCKADDR*)&Target, sizeof(Target)); + } + SentCount++; + if (Status == SOCKET_ERROR) { + if (WSAGetLastError() == WSAEHOSTUNREACH) { + printf("Destination host unreachable.\n"); + } else { + printf("Could not transmit data (%d).\n", WSAGetLastError()); + } + GlobalFree(Buffer); + return FALSE; + } + + /* Expect to receive ICMP echo reply */ + FD_ZERO(&Fds); + FD_SET(IcmpSock, &Fds); + Timeval.tv_sec = Timeout / 1000; + Timeval.tv_usec = Timeout % 1000; + + Status = select(0, &Fds, NULL, NULL, &Timeval); + if ((Status != SOCKET_ERROR) && (Status != 0)) { + Length = sizeof(From); + Status = recvfrom(IcmpSock, Buffer, Size, 0, &From, &Length); + } + if (Status == SOCKET_ERROR) { + if (WSAGetLastError() != WSAETIMEDOUT) { + printf("Could not receive data (%d).\n", WSAGetLastError()); + GlobalFree(Buffer); + return FALSE; + } + Status = 0; + } + + if (Status == 0) { + printf("Request timed out.\n"); + GlobalFree(Buffer); + return TRUE; + } + + if (!DecodeResponse(Buffer, Status, (PSOCKADDR_IN)&From)) { + printf("Request timed out.\n"); + LostCount++; + } + + GlobalFree(Buffer); + return TRUE; +} + + +/* Program entry point */ +int main(int argc, char* argv[]) +{ + UINT Count; + CHAR MinTime[20]; + CHAR MaxTime[20]; + CHAR AvgTime[20]; + + Reset(); + + if ((ParseCmdline(argc, argv)) && (Setup())) { + + printf("\nPinging %s [%s] with %d bytes of data:\n\n", + TargetName, TargetIP, DataSize); + + Count = 0; + while ((NeverStop) || (Count < PingCount)) { + Ping(); + Sleep(Timeout); + Count++; + }; + + Cleanup(); + + /* Calculate avarage round trip time */ + if ((SentCount - LostCount) > 0) { + AvgRTT.QuadPart = SumRTT.QuadPart / (SentCount - LostCount); + } else { + AvgRTT.QuadPart = 0; + } + + /* Calculate loss percent */ + if (LostCount > 0) { + Count = (SentCount * 100) / LostCount; + } else { + Count = 0; + } + + if (!MinRTTSet) + MinRTT.QuadPart = 0; + + TimeToMsString(MinTime, MinRTT); + TimeToMsString(MaxTime, MaxRTT); + TimeToMsString(AvgTime, AvgRTT); + + /* Print statistics */ + printf("\nPing statistics for %s:\n", TargetIP); + printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n", + SentCount, SentCount - LostCount, LostCount, Count); + printf("Approximate round trip times in milli-seconds:\n"); + printf(" Minimum = %s, Maximum = %s, Average = %s\n", + MinTime, MaxTime, AvgTime); + } + return 0; +} + +/* EOF */