在《基于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 < 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 ;
}
#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 ;
}
#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)算法
IP,TCP,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 ;
}
#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 ;
}