// 9
int __cdecl main(int argc, char **argv)
{
WSADATA wsd;
WSAOVERLAPPED recvol; // 重叠 IO
SOCKET s=INVALID_SOCKET;
char *icmpbuf=NULL;
struct addrinfo *dest=NULL,
*local=NULL;
IPV4_OPTION_HDR ipopt;
SOCKADDR_STORAGE from; // socket地址存储结构(9.1)
DWORD bytes,
flags;
int packetlen=0,
fromlen,
time=0,
rc,
i,
status = 0;
recvol.hEvent = WSA_INVALID_EVENT;
// 分析输入参数
if (ValidateArgs(argc, argv) == FALSE)
{
status = -1;
goto EXIT;
}
// socket模块启动初始化
if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)
{
printf("WSAStartup() failed: %d\n", rc);
status = -1;
goto EXIT;
}
// 解析目的地址
dest = ResolveAddress(
gDestination,
"0",
gAddressFamily,
0,
0
);
if (dest == NULL)
{
printf("bad name %s\n", gDestination);
status = -1;
goto CLEANUP;
}
gAddressFamily = dest->ai_family;
if (gAddressFamily == AF_INET)
gProtocol = IPPROTO_ICMP;
else if (gAddressFamily == AF_INET6)
gProtocol = IPPROTO_ICMP6;
// 获得本地地址,绑定使用
local = ResolveAddress(
NULL,
"0",
gAddressFamily,
0,
0
);
if (local == NULL)
{
printf("Unable to obtain the bind address!\n");
status = -1;
goto CLEANUP;
}
//创建Raw套接字,protocol = IPPROTO_ICMP
s = socket(gAddressFamily, SOCK_RAW, gProtocol);
if (s == INVALID_SOCKET)
{
printf("socket failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
SetTtl(s, gTtl); //设置最大跳站数为128
if (gAddressFamily == AF_INET)
packetlen += sizeof(ICMP_HDR);
else if (gAddressFamily == AF_INET6)
packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);
// packetlen 为 数据长度+ICMP头部长度
packetlen += gDataSize;
// 分配空间存储ICMP请求(9.2)
icmpbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, packetlen);
if (icmpbuf == NULL)
{
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
// 初始化 ICMP 头部
if (gAddressFamily == AF_INET)
{
if (bRecordRoute) // 如有路由记录功能,初始化选项数据段
{
ZeroMemory(&ipopt, sizeof(ipopt));
ipopt.opt_code = IP_RECORD_ROUTE; // 路由记录选项
ipopt.opt_ptr = 4; // 指向可用的地址,每个地址占用4字节,此指针指向第一个可用的存储偏移地址
ipopt.opt_len = 39; // 选项数据段长度
rc = setsockopt(s, IPPROTO_IP, IP_OPTIONS,
(char *)&ipopt, sizeof(ipopt));
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "setsockopt(IP_OPTIONS) failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
}
InitIcmpHeader(icmpbuf, gDataSize);
}
else if (gAddressFamily == AF_INET6)
{
InitIcmp6Header(icmpbuf, gDataSize);
}
// 绑定地址,此套接字可获得外部单元向这个地址发送的数据
rc = bind(s, local->ai_addr, (int)local->ai_addrlen);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// 建立接收操作
memset(&recvol, 0, sizeof(recvol));
recvol.hEvent = WSACreateEvent(); // 事件初始化
if (recvol.hEvent == WSA_INVALID_EVENT)
{
fprintf(stderr, "WSACreateEvent failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// 异步接收
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
printf("\nPinging ");
PrintAddress(dest->ai_addr, (int)dest->ai_addrlen);
printf(" with %d bytes of data\n\n", gDataSize);
// ping 4 次
for(i=0; i < DEFAULT_SEND_COUNT ;i++)
{
// 设置序列号并计算校验和
SetIcmpSequence(icmpbuf);
ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);
time = GetTickCount();
rc = sendto( // 发送icmp请求
s,
icmpbuf,
packetlen,
0,
dest->ai_addr,
(int)dest->ai_addrlen
);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "sendto failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// 等待ICMP回复(9.3)
rc = WaitForSingleObject((HANDLE)recvol.hEvent, DEFAULT_RECV_TIMEOUT);
if (rc == WAIT_FAILED)
{
fprintf(stderr, "WaitForSingleObject failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
else if (rc == WAIT_TIMEOUT)
{
printf("Request timed out.\n");
}
else
{
// 收到ICMP回复
rc = WSAGetOverlappedResult(
s,
&recvol,
&bytes,
FALSE,
&flags
);
if (rc == FALSE)
{
fprintf(stderr, "WSAGetOverlappedResult failed: %d\n", WSAGetLastError());
}
time = GetTickCount() - time;
WSAResetEvent(recvol.hEvent);
printf("Reply from ");
PrintAddress((SOCKADDR *)&from, fromlen);
if (time == 0)
printf(": bytes=%d time<1ms TTL=%d\n", gDataSize, gTtl);
else
printf(": bytes=%d time=%dms TTL=%d\n", gDataSize, time, gTtl);
PrintPayload(recvbuf, bytes);
if (i < DEFAULT_SEND_COUNT - 1)
{
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
}
}
Sleep(1000);
}
CLEANUP:
if (dest)
freeaddrinfo(dest);
if (local)
freeaddrinfo(local);
if (s != INVALID_SOCKET)
closesocket(s);
if (recvol.hEvent != WSA_INVALID_EVENT)
WSACloseEvent(recvol.hEvent);
if (icmpbuf)
HeapFree(GetProcessHeap(), 0, icmpbuf);
WSACleanup();
EXIT:
("pause");
return status;
}