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