返回顶部

收藏

用C++自己实现Ping程序

更多
//
// 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);
}

标签:ping,socket,C++

收藏

0人收藏

支持

0

反对

0

发表评论