tracert.c

来自「一个类似windows」· C语言 代码 · 共 720 行 · 第 1/2 页

C
720
字号
/*
 *  ReactOS Win32 Applications
 *  Copyright (C) 2005 ReactOS Team
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS traceroute utility
 * FILE:        apps/utils/net/tracert/tracert.c
 * PURPOSE:     trace a packets route through a network
 * PROGRAMMERS: Ged Murphy (gedmurphy@gmail.com)
 * REVISIONS:
 *   GM 03/05/05 Created
 *
 */


#include <winsock2.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <ws2tcpip.h>
#include <string.h>
#include <time.h>
#include "tracert.h"

#define WIN32_LEAN_AND_MEAN

#ifdef DBG
#undef DBG
#endif

/*
 * globals
 */
SOCKET icmpSock;                // socket descriptor
SOCKADDR_IN source, dest;       // source and destination address info
ECHO_REPLY_HEADER sendpacket;   // ICMP echo packet
IPv4_HEADER recvpacket;         // return reveive packet

BOOL bUsePerformanceCounter;    // whether to use the high res performance counter
LARGE_INTEGER TicksPerMs;       // number of millisecs in relation to proc freq
LARGE_INTEGER TicksPerUs;       // number of microsecs in relation to proc freq
LONGLONG lTimeStart;            // send packet, timer start
LONGLONG lTimeEnd;              // receive packet, timer end

CHAR cHostname[256];            // target hostname
CHAR cDestIP[18];               // target IP


/*
 * command line options
 */
BOOL bResolveAddresses = TRUE;  // -d  MS ping defaults to true.
INT iMaxHops = 30;              // -h  Max number of hops before trace ends
INT iHostList;                  // -j  @UNIMPLEMENTED@
INT iTimeOut = 2000;            // -w  time before packet times out


/*
 *
 * Parse command line parameters and set any options
 *
 */
static BOOL ParseCmdline(int argc, char* argv[])
{
    int i;

    if (argc < 2)
    {
       Usage();
       return FALSE;
    }

    for (i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            switch (argv[i][1])
            {
               case 'd': bResolveAddresses = FALSE;
                         break;
               case 'h': sscanf(argv[i+1], "%d", &iMaxHops);
                         break;
               case 'j': break; /* @unimplemented@ */
               case 'w': sscanf(argv[i+1], "%d", &iTimeOut);
                         break;
               default:
                  _tprintf(_T("%s is not a valid option.\n"), argv[i]);
                  Usage();
                  return FALSE;
            }
        }
        else
           /* copy target address */
           strncpy(cHostname, argv[i], 255);
    }

    return TRUE;
}



/*
 *
 * Driver function, controls the traceroute program
 *
 */
static INT Driver(VOID)
{

    INT iHopCount = 1;              // hop counter. default max is 30
    USHORT iSeqNum = 0;                // initialise packet sequence number
    INT iTTL = 1;                   // set initial packet TTL to 1
    BOOL bFoundTarget = FALSE;      // Have we reached our destination yet
    BOOL bAwaitPacket;              // indicates whether we have recieved a good packet
    INT iDecRes;                    // DecodeResponse return value
    INT iRecieveReturn;             // RecieveReturn return value
    INT iNameInfoRet;               // getnameinfo return value
    INT iPacketSize = PACKET_SIZE;  // packet size
    WORD wHeaderLen;                // header length
    PECHO_REPLY_HEADER icmphdr;


    //temps for getting host name
    CHAR cHost[256];
    CHAR cServ[256];
    CHAR *ip;

    /* setup winsock */
    WSADATA wsaData;

    /* check for winsock 2 */
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
#ifdef DBG
        _tprintf(_T("WSAStartup failed.\n"));
#endif /* DBG */
        exit(1);
    }

    /* establish what timing method we can use */
    SetupTimingMethod();

    /* setup target info */
    ResolveHostname();

    /* print standard tracing info to screen */
    _tprintf(_T("\nTracing route to %s [%s]\n"), cHostname, cDestIP);
    _tprintf(_T("over a maximum of %d hop"), iMaxHops);
    iMaxHops > 1 ? _tprintf(_T("s:\n\n")) : _tprintf(_T(":\n\n"));

    /* run until we hit either max hops, or we recieve 3 echo replys */
    while ((iHopCount <= iMaxHops) && (bFoundTarget != TRUE))
    {
        INT i;

        _tprintf(_T("%3d   "), iHopCount);
        /* run 3 pings for each hop */
        for (i=0; i<3; i++)
        {
            if (Setup(iTTL) != TRUE)
            {
#ifdef DBG
                _tprintf(_T("error in Setup()\n"));
#endif /* DBG */
                WSACleanup();
                exit(1);
            }
            PreparePacket(iPacketSize, iSeqNum);
            if (SendPacket(iPacketSize) != SOCKET_ERROR)
            {
                /* loop until we get a good packet */
                bAwaitPacket = TRUE;
                while (bAwaitPacket)
                {
                    /* Receive replies until we either get a successful
                     * read, or a fatal error occurs. */
                    if ((iRecieveReturn = ReceivePacket(iPacketSize)) < 0)
                    {
                        /* check the sequence number in the packet
                         * if it's bad, complain and wait for another packet
                         * , otherwise break */
                        wHeaderLen = recvpacket.h_len * 4;
                        icmphdr = (ECHO_REPLY_HEADER *)((char*)&recvpacket + wHeaderLen);
                        if (icmphdr->icmpheader.seq != iSeqNum)
                        {
                            _tprintf(_T("bad sequence number!\n"));
                            continue;
                        }
                        else
                            break;
                    }

                    /* if RecievePacket timed out we don't bother decoding */
                    if (iRecieveReturn != 1)
                    {
                        iDecRes = DecodeResponse(iPacketSize, iSeqNum);

                        switch (iDecRes)
                        {
                           case 0 : bAwaitPacket = FALSE;  /* time exceeded */
                                    break;
                           case 1 : bAwaitPacket = FALSE;  /* echo reply */
                                    break;
                           case 2 : bAwaitPacket = FALSE;  /* destination unreachable */
                                    break;
#ifdef DBG
                           case -1 :
                                     _tprintf(_T("recieved foreign packet\n"));
                                     break;
                           case -2 :
                                     _tprintf(_T("error in DecodeResponse\n"));
                                     break;
                           case -3 :
                                     _tprintf(_T("unknown ICMP packet\n"));
                                     break;
#endif /* DBG */
                           default : break;
                        }
                    }
                    else
                        /* packet timed out. Don't wait for it again */
                        bAwaitPacket = FALSE;
                }
            }

            iSeqNum++;
            _tprintf(_T("   "));
        }

        if(bResolveAddresses)
        {
           /* gethostbyaddr() and getnameinfo() are
            * unimplemented in ROS at present.
            * Alex has advised he will be implementing getnameinfo.
            * I've used that for the time being for testing in Windows*/

              //ip = inet_addr(inet_ntoa(source.sin_addr));
              //host = gethostbyaddr((char *)&ip, 4, 0);

              ip = inet_ntoa(source.sin_addr);

              iNameInfoRet = getnameinfo((SOCKADDR *)&source,
                                 sizeof(SOCKADDR),
                                 cHost,
                                 256,
                                 cServ,
                                 256,
                                 NI_NUMERICSERV);
              if (iNameInfoRet == 0)
              {
                 /* if IP address resolved to a hostname,
                   * print the IP address after it */
                  if (lstrcmpA(cHost, ip) != 0)
                      _tprintf(_T("%s [%s]"), cHost, ip);
                  else
                      _tprintf(_T("%s"), cHost);
              }
              else
              {
                  _tprintf(_T("error: %d"), WSAGetLastError());
#ifdef DBG
                  _tprintf(_T(" getnameinfo failed: %d"), iNameInfoRet);
#endif /* DBG */
              }

        }
        else
           _tprintf(_T("%s"), inet_ntoa(source.sin_addr));

        _tprintf(_T("\n"));

        /* check if we've arrived at the target */
        if (strcmp(cDestIP, inet_ntoa(source.sin_addr)) == 0)
            bFoundTarget = TRUE;
        else
        {
            iTTL++;
            iHopCount++;
            Sleep(500);
        }
    }
    _tprintf(_T("\nTrace complete.\n"));
    WSACleanup();

    return 0;
}


/*
 * Establish if performance counters are available and
 * set up timing figures in relation to processor frequency.
 * If performance counters are not available, we'll be using
 * gettickcount, so set the figures to 1
 *
 */
static VOID SetupTimingMethod(VOID)
{
    LARGE_INTEGER PerformanceCounterFrequency;

    /* check if performance counters are available */
    bUsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency);
    if (bUsePerformanceCounter)
    {
        /* restrict execution to first processor on SMP systems */
        if (SetThreadAffinityMask(GetCurrentThread(), 1) == 0)
            bUsePerformanceCounter = FALSE;

        TicksPerMs.QuadPart  = PerformanceCounterFrequency.QuadPart / 1000;
        TicksPerUs.QuadPart  = PerformanceCounterFrequency.QuadPart / 1000000;
    }

    if (!bUsePerformanceCounter)
    {
        TicksPerMs.QuadPart = 1;
        TicksPerUs.QuadPart = 1;
    }
}


/*
 *
 * Check for a hostname or dotted deciamal for our target.
 * If we have a hostname, resolve to an IP and store it, else
 * just store the target IP address. Also set up other key
 * SOCKADDR_IN members needed for the connection.
 *
 */
static VOID ResolveHostname(VOID)
{
    HOSTENT *hp;
    ULONG addr;

    memset(&dest, 0, sizeof(dest));

    addr = inet_addr(cHostname);
    /* if address is not a dotted decimal */
    if (addr == INADDR_NONE)
    {
       hp = gethostbyname(cHostname);
       if (hp != 0)
       {
          memcpy(&dest.sin_addr, hp->h_addr, hp->h_length);
          //dest.sin_addr = *((struct in_addr *)hp->h_addr);
          dest.sin_family = hp->h_addrtype;
       }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?