用C++自己實現Ping程序

wdey 10年前發布 | 2K 次閱讀 C/C++

//
// Ping.h
//

pragma pack(1)

define ICMP_ECHOREPLY 0

define ICMP_ECHOREQ 8

// IP Header -- RFC 791 typedef struct tagIPHDR { u_char VIHL; // Version and IHL u_char TOS; // Type Of Service short TotLen; // Total Length short ID; // Identification short FlagOff; // Flags and Fragment Offset u_char TTL; // Time To Live u_char Protocol; // Protocol u_short Checksum; // Checksum struct in_addr iaSrc; // Internet Address - Source struct in_addr iaDst; // Internet Address - Destination }IPHDR, *PIPHDR;

// ICMP Header - RFC 792 typedef struct tagICMPHDR { u_char Type; // Type u_char Code; // Code u_short Checksum; // Checksum u_short ID; // Identification u_short Seq; // Sequence //char Data; // Data }ICMPHDR, *PICMPHDR;

define REQ_DATASIZE 36

define REQ_datasize2 30

define Renum 66

// ICMP Echo Request typedef struct tagECHOREQUEST { ICMPHDR icmpHdr; DWORD dwTime; char cData[REQ_DATASIZE]; }ECHOREQUEST, PECHOREQUEST; typedef struct tempECHREQUEST{ ICMPHDR icmpHdr; DWORD dwTime; char cData[Renum]; }tempECHREQUEST,PtempECHOREQUEST; typedef struct tempECHREQUEST2{ ICMPHDR icmpHdr; DWORD dwTime; char cData[Renum+8]; }tempecho,Ptempecho; // ICMP Echo Reply typedef struct tagECHOREPLY { IPHDR ipHdr; ECHOREQUEST echoRequest; char cFiller[256]; }ECHOREPLY, PECHOREPLY; //IP echo request typedef struct tagIPEchorequest{ IPHDR ipHeader; ECHOREQUEST icmp_data; }IPECHOREQUET,PIPECHOREQUEST; typedef struct tagIPEchoTwo{ IPHDR ipHeader; char cData[REQ_datasize2]; }IPEchoTwo,PIPEchoTwo; typedef struct tagIPechoTwo2{ IPHDR ipHeader; char cData[REQ_datasize2+8]; }IPechotwo2,PIPechotwo2; //ip echo reply typedef struct tagIPREPLY{ IPHDR ipHdr; ECHOREQUEST echoRequest; char cFiller[256]; }IPECHOREPLY,PIPECHOREPLY;

pragma pack()

// PING.C -- Ping program using ICMP and RAW Sockets //

include <stdio.h>

include <stdlib.h>

include <winsock2.h>

include "ws2tcpip.h"

include <iostream>

pragma comment(lib,"WS2_32.lib")

include "ping.h"

// Internal Functions void Ping(LPCSTR pstrHost); void ReportError(LPCSTR pstrFrom); int WaitForEchoReply(SOCKET s); u_short in_cksum(u_short *addr, int len);

// ICMP Echo Request/Reply functions int SendEchoRequest(SOCKET, LPSOCKADDR_IN); DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);

//IP internal function void sendIP(LPCSTR pstrHost);

int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr); int SendIPEchoRequest(SOCKET, LPSOCKADDR_IN);

static short nId = 1; static short nSeq = 1; static short ip_id = rand()%256; static char DataToSend[Renum]; static short flag1 = 0; //標記,0標識不重復,1標識有重復 static short flag2 = 0; //標記,0標識順序,1標識逆序 char sourseIP[16]; // main() void main(int argc, char **argv) { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2,1); int nRet;

// Check arguments
if (argc != 2)
{
    fprintf(stderr,"nUsage: ping hostnamen");
    return;
}

// Init WinSock如果成功,則返回0
nRet = WSAStartup(wVersionRequested, &wsaData);
if (nRet)
{
    fprintf(stderr,"nError initializing WinSockn");
    return;
}

// Check version
if (wsaData.wVersion != wVersionRequested)
{
    fprintf(stderr,"nWinSock version not supportedn");
    return;
}

printf("        n輸入目的主機IP地址:n");
gets(argv[1]);
sendIP(argv[1]);

system("pause");
// Free WinSock
WSACleanup();

}

// Ping() // Calls SendEchoRequest() and // RecvEchoReply() and prints results void Ping(LPCSTR pstrHost) { SOCKET rawSocket; //原始套接字 LPHOSTENT lpHost; //保存目的主機信息 struct sockaddr_in saDest; //目的主機 struct sockaddr_in saSrc; //源主機 DWORD dwTimeSent; //發送時間 DWORD dwElapsed; //時間間隔 u_char cTTL; //ttl int nLoop; //發送循環的ping的次數 int nRet;

// Create a Raw socket
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (rawSocket == SOCKET_ERROR)
{
    ReportError("socket()");
    return;
}

// Lookup host
lpHost = gethostbyname(pstrHost);      
if (lpHost == NULL)
{
    fprintf(stderr,"nHost not found: %sn", pstrHost);
    return;
}

// Setup destination socket address
saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
saDest.sin_family = AF_INET;
saDest.sin_port = 0;

// Tell the user what we're doing
printf("nPinging %s [%s] with %d bytes of data:n",
            pstrHost,
            inet_ntoa(saDest.sin_addr),
            REQ_DATASIZE);

// Ping multiple times
for (nLoop = 0; nLoop < 4; nLoop++)
{
    // Send ICMP echo request
    SendEchoRequest(rawSocket, &saDest);

    // Use select() to wait for data to be received
    nRet = WaitForEchoReply(rawSocket);
    if (nRet == SOCKET_ERROR)
    {
        ReportError("select()");
        break;
    }
    if (!nRet)
    {
        printf("nTimeOut");
        break;
    }

    // Receive reply
    dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);

    // Calculate elapsed time
    dwElapsed = GetTickCount() - dwTimeSent;
    printf("nReply from: %s: bytes=%d time=%ldms TTL=%d",
           inet_ntoa(saSrc.sin_addr),
           REQ_DATASIZE,
           dwElapsed,
           cTTL);
}
printf("n");
nRet = closesocket(rawSocket);
if (nRet == SOCKET_ERROR)
    ReportError("closesocket()");

} / 使用原始套接字編程,實現分片IP數據包的構造,能夠用兩個IP分片 構成ICMP ECHO請求,并接收另一方協議棧返回的ICMP應答。/ void sendIP(LPCSTR pstrHost){

SOCKET    rawSocket;            //原始套接字
LPHOSTENT lpHost;               //保存目的主機信息
struct    sockaddr_in saDest;   //目的主機
struct    sockaddr_in saSrc;    //源主機
DWORD     dwTimeSent;           //發送時間
DWORD     dwElapsed;            //時間間隔
u_char    cTTL;                 //ttl
//int       nLoop;              //發送循環的ping的次數,這里是需要分片的片數目
int       nRet;

// Create a Raw socket創建IP數據包的套接字
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (rawSocket == SOCKET_ERROR)
{
    ReportError("socket()");
    return;
}
//設置套接字選項
BOOL blflag = true;
int set_return = setsockopt(rawSocket,
    IPPROTO_IP,
    IP_HDRINCL,
    (char*)&blflag,
    sizeof(blflag));
if (set_return)
{
    printf("設置選項失敗!%d",set_return);
    return;
}
if (set_return == SOCKET_ERROR)
{
    printf("設置選項錯誤!");
    return;
}

// Lookup host
lpHost = gethostbyname(pstrHost);      
if (lpHost == NULL)
{
    fprintf(stderr,"nHost not found: %sn", pstrHost);
    return;
}
printf("n輸入本機IP地址:n");
gets(sourseIP);
// Setup destination socket address
saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
saDest.sin_family = AF_INET;
saDest.sin_port = 0;

// Tell the user what we're doing
printf("n發送數據包(2個分片第一片)到 %s --[%s] 數據大小是%d :n",
                    pstrHost,
                    inet_ntoa(saDest.sin_addr),
                    REQ_DATASIZE);
// Ping multiple times
printf("n分片發送,不重疊輸入0,重疊輸入1:n");
std::cin>>flag1;
printf("n分片發送,兩片順序發送輸入0,逆序發送輸入1:n");
std::cin>>flag2;

if (flag2==0)
{
    SendIPEchoRequest(rawSocket,&saDest);
    SendIPEchoRequestFragmentTwo(rawSocket,&saDest);
}
if (flag2==1)
{
    SendIPEchoRequestFragmentTwo(rawSocket,&saDest);
    SendIPEchoRequest(rawSocket,&saDest);
}
// Use select() to wait for data to be received
nRet = WaitForEchoReply(rawSocket);
if (nRet == SOCKET_ERROR)
    ReportError("select()");

if (!nRet)
    printf("nTimeOut");
// Receive reply//////////////////////////////////////////////
dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
printf("n檢查收到的時間:%dn",dwTimeSent);
// Calculate elapsed time

dwElapsed = GetTickCount() - dwTimeSent;
printf("n當前的時間 :%dn",GetTickCount());
printf("nReply from: %s: bytes=%d time=%ldms TTL=%d",
inet_ntoa(saSrc.sin_addr),
        REQ_DATASIZE,
        dwElapsed,
        cTTL);

printf("n");
nRet = closesocket(rawSocket);
if (nRet == SOCKET_ERROR)
    ReportError("closesocket()");

}

// SendEchoRequest() // Fill in echo request header // and send to destination int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr) { static ECHOREQUEST echoReq;
static short nId = 1; static short nSeq = 1; int nRet;

// Fill in echo request
echoReq.icmpHdr.Type        = ICMP_ECHOREQ;
echoReq.icmpHdr.Code        = 0;
echoReq.icmpHdr.Checksum    = 0;
echoReq.icmpHdr.ID          = nId++;
echoReq.icmpHdr.Seq         = nSeq++;

// Fill in some data to send
for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
    echoReq.cData[nRet] = 'a'+nRet;

//  printf("nn%dnn",sizeof(echoReq));
// Save tick count when sent
echoReq.dwTime              = GetTickCount();

// Put data in packet and compute checksum
echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));

// Send the echo request                                 
nRet = sendto(s,                        /* socket */
    (LPSTR)&echoReq,            /* buffer */
    sizeof(ECHOREQUEST),
    0,                          /* flags */
    (LPSOCKADDR)lpstToAddr, /* destination */
    sizeof(SOCKADDR_IN));   /* address length */

if (nRet == SOCKET_ERROR)
    ReportError("sendto()");
return (nRet);

}

/發送第一片數據/ int SendIPEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr){

static IPECHOREQUET ipechoReq;

//第一片的IP頭部處理過程
ipechoReq.ipHeader.VIHL = 0x45;                                 //版本是4,首部長度是5
ipechoReq.ipHeader.TOS = 0x0;                                   //由標準組織分配
ipechoReq.ipHeader.ID = ip_id;                                  //ip的標識(一唯),這里一定要注意,ip標識不變,同一個數據包的兩片
ipechoReq.ipHeader.TTL = 64;                                    //網絡中的最大壽命

ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP);          //源主機地址
ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr;             //
ipechoReq.ipHeader.Protocol = IPPROTO_ICMP;                     //協議的類型包含ICMP
ipechoReq.ipHeader.FlagOff = htons(0x2000);                     //第一片標志位是001000000000000
ipechoReq.ipHeader.TotLen = sizeof(IPECHOREQUET);               //總產度設置成64,數據部分應該是52

ipechoReq.ipHeader.Checksum = 0;                                //

//IP包頭校驗和計算
ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR));

// Fill in echo request
ipechoReq.icmp_data.icmpHdr.Type        = ICMP_ECHOREQ;         //請求數據包類型
ipechoReq.icmp_data.icmpHdr.Code        = 0;                    //
ipechoReq.icmp_data.icmpHdr.Checksum    = 0;                    //檢驗和字段
ipechoReq.icmp_data.icmpHdr.ID          = nId++;                //成對出現的
ipechoReq.icmp_data.icmpHdr.Seq         = nSeq++;               //成對出現

// Save tick count when sent
ipechoReq.icmp_data.dwTime              = GetTickCount();

printf("n發送的時間:%dn",ipechoReq.icmp_data.dwTime);
memset(DataToSend,0,Renum);
//ICMP頭部填充////////////////////////////////////////////////////////////////////////////
int nRet;
for (nRet = 0;nRet < REQ_DATASIZE;nRet++)
    DataToSend[nRet] = 'a';
for(nRet = REQ_DATASIZE;nRet<Renum;nRet++)
    DataToSend[nRet] = 'b';

//第一片需要發送的數據
for (nRet = 0;nRet < REQ_DATASIZE;nRet++)
    ipechoReq.icmp_data.cData[nRet] = DataToSend[nRet];

//計算校驗和的部分構成
if (flag1 == 0)
{
    tempECHREQUEST tempecho;
    memset(tempecho.cData,0,Renum);
    tempecho.icmpHdr = ipechoReq.icmp_data.icmpHdr;
    tempecho.dwTime = ipechoReq.icmp_data.dwTime;
    memcpy(tempecho.cData,DataToSend,Renum);

    // Put data in packet and compute checksum
    ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&tempecho,
        sizeof(tempECHREQUEST));
}
if (flag1==1)
{
    tempecho    temp;
    memset(temp.cData,0,Renum+8);
    temp.dwTime = ipechoReq.icmp_data.dwTime;
    temp.icmpHdr = ipechoReq.icmp_data.icmpHdr;
    memcpy(temp.cData,DataToSend,REQ_DATASIZE);                 //第一片復制
    memcpy(temp.cData+REQ_DATASIZE,DataToSend+REQ_DATASIZE-8,REQ_datasize2);
    ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&temp,
                                                    sizeof(tempecho));
}
//發送echo 請求
nRet = sendto(s,
                    (LPSTR)&ipechoReq,
                    sizeof(IPECHOREQUET),
                    0,
                    (LPSOCKADDR)lpstToAddr,
                    sizeof(SOCKADDR_IN));
if (nRet == SOCKET_ERROR)
    ReportError("sendto()");
return nRet;

} / 發送第二片數據/ int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr){ IPEchoTwo ipechoReq; //第二片的IP頭部處理過程

ipechoReq.ipHeader.VIHL = 0x45;                                 //版本是4,首部長度是5
ipechoReq.ipHeader.TOS = 0x0;                                   //由標準組織分配
ipechoReq.ipHeader.ID = ip_id;                                  //ip的標識(一唯)
ipechoReq.ipHeader.TTL = 64;                                    //網絡中的最大壽命
ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP);  //源主機地址
ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr;             //
ipechoReq.ipHeader.Protocol = IPPROTO_ICMP;                     //協議的類型包含ICMP
//////////////////////////////////////////////////////////////////////////////////////
if (flag1 == 0)
    ipechoReq.ipHeader.FlagOff = htons(0x6);                        //第二片標志位是
if (flag1 == 1)
    ipechoReq.ipHeader.FlagOff = htons(0x5);                        //第二片標志位是
ipechoReq.ipHeader.TotLen = sizeof(IPEchoTwo);                  //總產度設置成64,數據部分應該是44

ipechoReq.ipHeader.Checksum = 0;                                //

//IP包頭校驗和計算
ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR));

//ICMP頭部填充
int nRet;

//填充其他的數據部分/////////////////////////////////////////////////////////////////////////////
if (flag1 == 0){
    memcpy(ipechoReq.cData,DataToSend+REQ_DATASIZE,REQ_datasize2);
    nRet = sendto(s,
                    (LPSTR)&ipechoReq,
                    sizeof(IPEchoTwo),
                    0,
                    (LPSOCKADDR)lpstToAddr,
                    sizeof(SOCKADDR_IN));
}
if (flag1 == 1){
    IPechotwo2 ipecho;
    ipecho.ipHeader = ipechoReq.ipHeader;
    memset(ipecho.cData,0,REQ_datasize2+8);
    memcpy(ipecho.cData,DataToSend+REQ_DATASIZE-8,REQ_datasize2+8);
    nRet = sendto(s,
                    (LPSTR)&ipechoReq,
                    sizeof(IPechotwo2),
                    0,
                    (LPSOCKADDR)lpstToAddr,
                    sizeof(SOCKADDR_IN));

}
//發送echo 請求

if (nRet == SOCKET_ERROR)
    ReportError("sendto()");
return nRet;

} // RecvEchoReply() // Receive incoming data // and parse out fields DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL) { IPECHOREPLY ipreply; int nRet; int nAddrLen = sizeof(struct sockaddr_in);

// Receive the echo reply  
nRet = recvfrom(s,                          // socket
                (LPSTR)&ipreply,            // buffer
                sizeof(IPECHOREPLY),        // size of buffer
                0,                          // flags
                (LPSOCKADDR)lpsaFrom,       // From address
                &nAddrLen);                 // pointer to address len

// Check return value
if (nRet == SOCKET_ERROR)
    ReportError("recvfrom()");

// return time sent and IP TTL
*pTTL = ipreply.ipHdr.TTL;
return(ipreply.echoRequest.dwTime);        

}

// What happened? void ReportError(LPCSTR pWhere) { fprintf(stderr,"n%s error: %dn", WSAGetLastError()); }

// WaitForEchoReply() // Use select() to determine when // data is waiting to be read int WaitForEchoReply(SOCKET s) { struct timeval Timeout; fd_set readfds; /fd_set結構體的定義 typedef struct fd_set{ u_int fd_count; //集合中的socket數量 SOCKET fd_array[FD_SETSIZE]; //集合中包含的Socket數組 }fd_set; / readfds.fd_count = 1; readfds.fd_array[0] = s; Timeout.tv_sec = 5; Timeout.tv_usec = 0;

return(select(1, &readfds, NULL, NULL, &Timeout));

}

// // Mike Muuss' in_cksum() function // and his comments from the original // ping program // // Author - // Mike Muuss // U. S. Army Ballistic Research Laboratory // December, 1983

/*

  • I N _ C K S U M *
  • Checksum routine for Internet Protocol family headers (C Version) / u_short in_cksum(u_short addr, int len) { register int nleft = len; register u_short w = addr; register u_short answer; register int sum = 0;

    /*

    • Our algorithm is simple, using a 32 bit accumulator (sum),
    • we add sequential 16 bit words to it, and at the end, fold
    • back all the carry bits from the top 16 bits into the lower
    • 16 bits. / while( nleft > 1 ) { sum += w++; nleft -= 2; }

      / mop up an odd byte, if necessary / if( nleft == 1 ) { u_short u = 0;

      (u_char )(&u) = (u_char )w ; sum += u; }

      /*

    • add back carry outs from top 16 bits to low 16 bits / sum = (sum >> 16) + (sum & 0xffff); / add hi 16 to low 16 / sum += (sum >> 16); / add carry / answer = ~sum; / truncate to 16 bits */ return (answer); }</pre>
 本文由用戶 wdey 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!