tracert.c

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

C
720
字号
       else
       {
          _tprintf(_T("Unable to resolve target system name %s.\n"), cHostname);
          WSACleanup();
          exit(1);
       }
    }
    else
    {
        dest.sin_addr.s_addr = addr;
        dest.sin_family = AF_INET;
    }
    /* copy destination IP address into a string */
    strcpy(cDestIP, inet_ntoa(dest.sin_addr));
}



/*
 *
 * Create our socket which will be used for sending and recieving,
 * Socket Type is raw, Protocol is ICMP. Also set the TTL value which will be
 * set in the outgoing IP packet.
 *
 */
static INT Setup(INT iTTL)
{
    INT iSockRet;

    /* create raw socket */
    icmpSock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0);
    if (icmpSock == INVALID_SOCKET)
    {
       _tprintf(_T("Could not create socket : %d.\n"), WSAGetLastError());
       if (WSAGetLastError() == WSAEACCES)
       {
            _tprintf(_T("\n\nYou must be an administrator to run this program!\n\n"));
            WSACleanup();
            exit(1);
       }
       return FALSE;
    }

    /* setup for TTL */
    iSockRet = setsockopt(icmpSock, IPPROTO_IP, IP_TTL, (const char *)&iTTL, sizeof(iTTL));
    if (iSockRet == SOCKET_ERROR)
    {
       _tprintf(_T("TTL setsockopt failed : %d. \n"), WSAGetLastError());
       return FALSE;
    }

    return TRUE;
}



/*
 * Prepare the ICMP echo request packet for sending.
 * Calculate the packet checksum
 *
 */
static VOID PreparePacket(INT iPacketSize, USHORT iSeqNum)
{
    /* assemble ICMP echo request packet */
    sendpacket.icmpheader.type      = ECHO_REQUEST;
    sendpacket.icmpheader.code      = 0;
    sendpacket.icmpheader.checksum  = 0;
    sendpacket.icmpheader.id        = (USHORT)GetCurrentProcessId();
    sendpacket.icmpheader.seq       = iSeqNum;

    /* calculate checksum of packet */
    sendpacket.icmpheader.checksum  = CheckSum((PUSHORT)&sendpacket, sizeof(ICMP_HEADER) + iPacketSize);
}



/*
 *
 * Get the system time and send the ICMP packet to the destination
 * address.
 *
 */
static INT SendPacket(INT datasize)
{
    INT iSockRet;
    INT iPacketSize;

    iPacketSize = sizeof(ECHO_REPLY_HEADER) + datasize;

#ifdef DBG
    _tprintf(_T("\nsending packet of %d bytes\n"), iPacketSize);
#endif /* DBG */

    /* get time packet was sent */
    lTimeStart = GetTime();

    iSockRet = sendto(icmpSock,              //socket
                      (char *)&sendpacket,   //buffer
                      iPacketSize,           //size of buffer
                      0,                     //flags
                      (SOCKADDR *)&dest,     //destination
                      sizeof(dest));         //address length

    if (iSockRet == SOCKET_ERROR)
    {
        if (WSAGetLastError() == WSAEACCES)
        {
            _tprintf(_T("\n\nYou must be an administrator to run this program!\n\n"));
            WSACleanup();
            exit(1);
        }
        else
        {
#ifdef DBG
            _tprintf(_T("sendto failed %d\n"), WSAGetLastError());
#endif /* DBG */
            return FALSE;
        }
    }
#ifdef DBG
    _tprintf(_T("sent %d bytes\n"), iSockRet);
#endif /* DBG */

    /* return number of bytes sent */
    return iSockRet;
}



/*
 *
 * Set up a timeout value and put the socket in a select poll.
 * Wait until we recieve an IPv4 reply packet in reply to the ICMP
 * echo request packet and get the time the packet was recieved.
 * If we don't recieve a packet, do some checking to establish why.
 *
 */
static INT ReceivePacket(INT datasize)
{
    TIMEVAL timeVal;
    FD_SET readFDS;
    int iSockRet = 0, iSelRet;
    int iFromLen;
    int iPacketSize;

    /* allow for a larger recv buffer to store ICMP TTL
     * exceed, IP header and orginal ICMP request */
    iPacketSize = MAX_REC_SIZE + datasize;

    iFromLen = sizeof(source);

#ifdef DBG
    _tprintf(_T("receiving packet. Available buffer, %d bytes\n"), iPacketSize);
#endif /* DBG */

    /* monitor icmpSock for incomming connections */
    FD_ZERO(&readFDS);
    FD_SET(icmpSock, &readFDS);

    /* set timeout values */
    timeVal.tv_sec  = iTimeOut / 1000;
    timeVal.tv_usec = iTimeOut % 1000;

    iSelRet = select(0, &readFDS, NULL, NULL, &timeVal);

    if ((iSelRet != SOCKET_ERROR) && (iSelRet != 0))
    {
        iSockRet = recvfrom(icmpSock,              //socket
                           (char *)&recvpacket,    //buffer
                           iPacketSize,            //size of buffer
                           0,                      //flags
                           (SOCKADDR *)&source,    //source address
                           &iFromLen);             //pointer to address length
        /* get time packet was recieved */
        lTimeEnd = GetTime();
    /* if socket timed out */
    }
    else if (iSelRet == 0)
    {
        _tprintf(_T("   *  "));
        return 1;
    }
    else if (iSelRet == SOCKET_ERROR)
    {
        _tprintf(_T("select() failed in sendPacket() %d\n"), WSAGetLastError());
        return -1;
    }


    if (iSockRet == SOCKET_ERROR)
    {
        _tprintf(_T("recvfrom failed: %d\n"), WSAGetLastError());
        return -2;
    }
#ifdef DBG
    else
    _tprintf(_T("reveived %d bytes\n"), iSockRet);
#endif /* DBG */

    return 0;
}



/*
 *
 * Cast the IPv4 packet to an echo reply and to a TTL exceed.
 * Check the 'type' field to establish what was recieved, and
 * ensure the packet is related to the originating process.
 * It all is well, print the time taken for the round trip.
 *
 */
static INT DecodeResponse(INT iPacketSize, USHORT iSeqNum)
{
    unsigned short header_len = recvpacket.h_len * 4;
    /* cast the recieved packet into an ECHO reply and a TTL Exceed so we can check the ID*/
    ECHO_REPLY_HEADER *IcmpHdr = (ECHO_REPLY_HEADER *)((char*)&recvpacket + header_len);
    TTL_EXCEED_HEADER *TTLExceedHdr = (TTL_EXCEED_HEADER *)((char *)&recvpacket + header_len);

    /* Make sure the reply is ok */
    if (iPacketSize < header_len + ICMP_MIN_SIZE)
    {
        _tprintf(_T("too few bytes from %s\n"), inet_ntoa(dest.sin_addr));
        return -2;
    }

    switch (IcmpHdr->icmpheader.type)
    {
           case TTL_EXCEEDED :
                if (TTLExceedHdr->OrigIcmpHeader.id != (USHORT)GetCurrentProcessId())
                {
                /* FIXME */
                /* we've picked up a packet not related to this process
                 * probably from another local program. We ignore it */
#ifdef DGB
                    _tprintf(_T("header id,  process id  %d"), TTLExceedHdr->OrigIcmpHeader.id, GetCurrentProcessId());
#endif /* DBG */
                    //_tprintf(_T("oops ");
                    return -1;
                }
                _tprintf(_T("%3Ld ms"), (lTimeEnd - lTimeStart) / TicksPerMs.QuadPart);
                return 0;
           case ECHO_REPLY :
                if (IcmpHdr->icmpheader.id != (USHORT)GetCurrentProcessId())
                {
                /* FIXME */
                /* we've picked up a packet not related to this process
                 * probably from another local program. We ignore it */
#ifdef DGB
                    _tprintf(_T("\nPicked up wrong packet. icmpheader.id = %d and process id = %d"), IcmpHdr->icmpheader.id, GetCurrentProcessId());
#endif /* DBG */
                    //_tprintf(_T("oops ");
                    return -1;
                }
                _tprintf(_T("%3Ld ms"), (lTimeEnd - lTimeStart) / TicksPerMs.QuadPart);
                return 1;
           case DEST_UNREACHABLE :
                _tprintf(_T("  *  "));
                return 2;
           default :
                /* unknown ICMP packet */
                return -3;
    }
}


/*
 *
 * Get the system time using preformance counters if available,
 * otherwise fall back to GetTickCount()
 *
 */

static LONGLONG GetTime(VOID)
{
    LARGE_INTEGER Time;

    if (bUsePerformanceCounter)
    {
        if (QueryPerformanceCounter(&Time) == 0)
        {
            Time.u.LowPart = (DWORD)GetTickCount();
            Time.u.HighPart = 0;
            return (LONGLONG)Time.u.LowPart;
        }
    }
    else
    {
            Time.u.LowPart = (DWORD)GetTickCount();
            Time.u.HighPart = 0;
            return (LONGLONG)Time.u.LowPart;
    }
    return Time.QuadPart;
}


/*
 *
 * Calculate packet checksum.
 *
 */
static WORD CheckSum(PUSHORT data, UINT size)
{
    DWORD dwSum = 0;

    while (size > 1)
    {
        dwSum += *data++;
        size -= sizeof(USHORT);
    }

    if (size)
        dwSum += *(UCHAR*)data;

    dwSum = (dwSum >> 16) + (dwSum & 0xFFFF);
    dwSum += (dwSum >> 16);

    return (USHORT)(~dwSum);
}


/*
 *
 * print program usage to screen
 *
 */
static VOID Usage(VOID)
{
    _tprintf(_T("\nUsage: tracert [-d] [-h maximum_hops] [-j host-list] [-w timeout] target_name\n\n"
                "Options:\n"
                "    -d                 Do not resolve addresses to hostnames.\n"
                "    -h maximum_hops    Maximum number of hops to search for target.\n"
                "    -j host-list       Loose source route along host-list.\n"
                "    -w timeout         Wait timeout milliseconds for each reply.\n\n"));

    /* temp notes to stop user questions until getnameinfo/gethostbyaddr and getsockopt are implemented */
    _tprintf(_T("NOTES\n-----\n"
           "- Setting TTL values is not currently supported in ReactOS, so the trace will\n"
           "  jump straight to the destination. This feature will be implemented soon.\n"
           "- Host info is not currently available in ReactOS and will fail with strange\n"
           "  results. Use -d to force it not to resolve IP's.\n"
           "- For testing purposes, all should work as normal in a Windows environment\n\n"));
}



/*
 *
 * Program entry point
 *
 */
int main(int argc, char* argv[])
{
    if (!ParseCmdline(argc, argv)) return -1;

    Driver();

    return 0;
}

⌨️ 快捷键说明

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