C++ Exercises(十七)---网际校验和算法

     在《基于ARP协议获取局域网内主机MAC地址》中使用了WinpCap来发送ARP请求,查询局域网内主机MAC地址,这篇来试试直接用Windows API函数来实现,最后再来探索用于IP,TCP,UDP等众多协议的网际校验和算法。

1,查询局域网主机MAC地址

#include  < WinSock2.h >
#include 
< IPHlpApi.h >
#include 
< iostream >
using   namespace  std;

#pragma  comment(lib,"Iphlpapi")
#pragma  comment(lib,"Ws2_32")

int  _tmain( int  argc, _TCHAR *  argv[])
{
    MIB_IPADDRTABLE
*  pIPAddrTable  =  (MIB_IPADDRTABLE * )malloc( sizeof (MIB_IPADDRTABLE));
    ULONG dwSize
= 0 ,dwRetVal = 0 ;
    
if  (GetIpAddrTable(pIPAddrTable, & dwSize, 0 ) == ERROR_INSUFFICIENT_BUFFER)
    {
        free(pIPAddrTable);
        pIPAddrTable 
=  (MIB_IPADDRTABLE * )malloc(dwSize);
    }
    
if  ((dwRetVal = GetIpAddrTable(pIPAddrTable, & dwSize, 0 )) == NO_ERROR)
    {
        ULONG ulHostIp 
=  ntohl(pIPAddrTable -> table[ 0 ].dwAddr);  // 本机IP
        ULONG ulHostMask  =  ntohl(pIPAddrTable -> table[ 0 ].dwMask);  // 子网掩码
         for  (ULONG i =   1 ;i < ( ~ ulHostMask); ++ i)
        {
            
static  ULONG ulNo  =   0 ;
            HRESULT hr;
            IPAddr ipAddr;
            ULONG pulMac[
2 ];
            ULONG ulLen;
            ipAddr 
=  htonl(i + (ulHostIp & ulHostMask));
            memset(pulMac,
0xff , sizeof (pulMac));
            ulLen 
=   6 ;
            hr 
=  SendARP(ipAddr, 0 ,pulMac, & ulLen);  // 发送ARP请求
             if  (ulLen == 6 )
            {
                ulNo
++ ;
                PBYTE pbHexMax 
=  (PBYTE)pulMac;
                unsigned 
char   *  strIpAddr  =  (unsigned  char   * )( & ipAddr);
                printf(
" %d:MAC地址% 02X:% 02X:% 02X:% 02X:% 02X:% 02X IP地址% d. % d. % d. % d\n " ,ulNo,pbHexMax[ 0 ],pbHexMax[ 1 ],pbHexMax[ 2 ],pbHexMax[ 3 ],pbHexMax[ 4 ],pbHexMax[ 5 ],strIpAddr[ 0 ],strIpAddr[ 1 ],strIpAddr[ 2 ],strIpAddr[ 3 ]);
            }
        }
    }
    
else
    {
        printf(
" 失败 " );
    }
    printf(
" 结束!\n " );
    free(pIPAddrTable);
    
return   0 ;
}
2 ,获取本机网卡信息
#include  < WinSock2.h >
#include 
< IPHlpApi.h >
#include 
< iostream >
using   namespace  std;

#pragma  comment(lib,"Iphlpapi")
#pragma  comment(lib,"Ws2_32")

int  _tmain( int  argc, _TCHAR *  argv[])
{
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter 
=  NULL;
    DWORD dwRetVal 
=   0 ;
    pAdapterInfo 
=  (IP_ADAPTER_INFO * )malloc( sizeof (IP_ADAPTER_INFO));
    ULONG ulOutBufLen 
=   sizeof (IP_ADAPTER_INFO);
    dwRetVal 
=  GetAdaptersInfo(pAdapterInfo, & ulOutBufLen);
    
if  (dwRetVal == ERROR_BUFFER_OVERFLOW)
    {
        free(pAdapterInfo);
        pAdapterInfo 
=  (IP_ADAPTER_INFO * )malloc(ulOutBufLen);
        dwRetVal 
=  GetAdaptersInfo(pAdapterInfo, & ulOutBufLen);
    }
    
if  (dwRetVal == NO_ERROR)
    {
        pAdapter 
=  pAdapterInfo;
        
while  (pAdapter)
        {
            printf(
" 适配器名称:\t% s\n " ,pAdapter -> AdapterName);
            printf(
" 适配器描述信息:\t% s\n " ,pAdapter -> Description);
            printf(
" MAC地址:\t% 02X:% 02X:% 02X:% 02X:% 02X:% 02X\n " ,pAdapter -> Address[ 0 ],pAdapter -> Address[ 1 ],
                pAdapter
-> Address[ 2 ],pAdapter -> Address[ 3 ],pAdapter -> Address[ 4 ],pAdapter -> Address[ 5 ]);
            printf(
" IP地址:\t% s\n " ,pAdapter -> IpAddressList.IpAddress.String);
            printf(
" 子网掩码:\t% s\n " ,pAdapter -> IpAddressList.IpMask.String);
            printf(
" 网关地址:\t% s\n " ,pAdapter -> GatewayList.IpAddress.String);
            
if  (pAdapter -> DhcpEnabled)
            {
                printf(
" DHCP enabled: yes\n " );
                printf(
" DHCP服务器:\t% s\n " ,pAdapter -> DhcpServer.IpAddress.String);
                printf(
" 租约:% ld\n " ,pAdapter -> LeaseObtained);
            }
            
else
            {
                printf(
" DHCP enabled:no\n " );
            }
            
if  (pAdapter -> HaveWins)
            {
                printf(
" Have Wins:Yes\n " );
                printf(
" Primary Wins Server:\t% s\n " ,pAdapter -> PrimaryWinsServer.IpAddress.String);
                printf(
" Secondary Server:\t% s\n " ,pAdapter -> SecondaryWinsServer.IpAddress.String);
            }
            
else
            {
                printf(
" Have Wins:No\n " );
            }
            pAdapter 
=  pAdapter -> Next;
        }
    }
    
else
    {
        printf(
" 失败\n " );
    }
    
return   0 ;
}

3,网际校验和(internet checksum)算法

      IPTCP,UDP等许多协议的头部都设置了校验和项,计算校验和的算法一般采用网际校验和算法,它将被校验的数据按16位进行划分(若数据字节长度为奇数,则在数据尾部补一个字节0),对每16位求反码和,然后再对和取反码。

#include < iostream >
#include
< fstream >
using   namespace  std;
#include
< winsock.h >              //  本机字节序转换为网络字节序:htons
#pragma  comment(lib, "WS2_32.LIB")

/* *************************************************************************
 * 计算给定数据的校验和
 *
 *        输入参数:
 *            pBuffer        指向需要校验的数据缓冲区
 *            nSize        需要校验的数据的大小,以字节为单位
 *
 *        返回值:
 *            16位的校验结果
 *
 *************************************************************************
*/
unsigned 
short  checksum_calculating(unsigned  short   * pBuffer,  int  nSize)
{
    unsigned 
long  dwCksum  =   0 ;         //  32位累加和
    
//  以两字节为单位反复累加
     while (nSize  >   1 )
    {
        dwCksum 
+=   * pBuffer ++ ;
        nSize 
-=   sizeof (unsigned  short );
    }
    
//  如果总字节数为奇数则加上最后一个字节
     if  (nSize)
    {
        dwCksum 
+=   * (unsigned  char * ) pBuffer;
    }
    
//  将位累加和的高位与低位第一次相加
    dwCksum  =  (dwCksum  >>   16 +  (dwCksum  &   0xffff );
    
//  将上一步可能产生的高位进位再次与低位累加
    dwCksum  +=  (dwCksum  >>   16 );
    
//  返回位校验和
     return  (unsigned  short ) ( ~ dwCksum);
}


int  main( int  argc,  char   *  argv[])
{
        
    
//  创建输入文件流
    ifstream fInfile;
    fstream fOutfile; 
//  创建输出文件流
    fInfile.open(argv[ 1 ], ios:: in | ios::binary);  //  以二进制方式打开指定的输入文件
    fInfile.seekg( 0 , ios::end);  //  把文件指针移到文件末尾
    unsigned  short  wLen  =  (unsigned  short )fInfile.tellg(); //  取得输入文件的长度
    fInfile.seekg( 0 , ios::beg);  //  文件指针位置初始化
    
//  定义数据报缓冲区,缓冲区大小为+wLen ,其中为数据报类型字段、长度字段
    
//  以及校验和字段的长度和,wLen为数据字段长度,即输入文件长度(以字节为单位)
     char   *  pBuf  =   new   char [ 4   +  wLen];
    pBuf[
0 =  unsigned  char ( 0xab );         //  给数据报类型字段赋值,这里随便弄了个0Xab
    pBuf[ 1 =  unsigned  char (wLen);         //  给数据报长度字段赋值
     * (unsigned  short   * )(pBuf  +   2 =   0 ;     //  计算校验和之前,校验和字段先置为0
    fInfile.read(pBuf + 4 , wLen);             //  根据输入文件填充数据报的数据字段
    
//  计算校验和并把结果填入到数据报的校验和字段
     * (unsigned  short   * )(pBuf + 2 =  checksum_calculating((unsigned  short   * )pBuf, 4 + wLen);
    
//  输出校验和计算结果
    cout.width( 4 );                    
    cout 
<<   " 校验和为:x "   <<  hex  <<  htons(  * (unsigned  short   * )(pBuf + 2 ) )
         
<<   "     (以网络顺序显示) " <<  endl;
    
//  以二进制方式打开输出文件
    fOutfile.open(argv[ 2 ],ios:: in | ios:: out | ios::binary | ios::trunc);
    
//  将pBuf中的数据报写入输出文件
    fOutfile.write(( char   * )pBuf, wLen + 4 );
    cout
<<   " 数据报已成功保存在 "   <<  argv[ 2 <<   " 文件中! "   <<  endl;
    delete [] pBuf;        
//  释放数据报缓冲区
    fInfile.close();     //  关闭输入文件流
    fOutfile.close();     //  关闭输出文件流
     return   0 ;
}

 

你可能感兴趣的:(C++)