使用Raw Socket实现Ping
仅仅采用ICMP.DLL并不能完全实现ICMP灵活多变的各类报文,只有使用Raw Socket才是ICMP的终极解决之道。
使用Raw Socket发送ICMP报文前,我们要完全依靠自己的代码组装报文:
//功能:初始化ICMP的报头, 给data部分填充数据, 计算校验和 void init_ping_packet(ICMPHeader *icmp_hdr, int packet_size, int seq_no) { //设置ICMP报头字段 icmp_hdr->type = ICMP_ECHO_REQUEST; icmp_hdr->code = 0; icmp_hdr->checksum = 0; icmp_hdr->id = (unsigned short)GetCurrentProcessId(); icmp_hdr->seq = seq_no; icmp_hdr->timestamp = GetTickCount(); // 填充data域 // 计算校验和 |
计算校验和(Checksum)的函数为:
//功能:计算ICMP包的校验和 unsigned short ip_checksum(unsigned short *buffer, int size) { unsigned long cksum = 0; // 将所有的16数相加 //和的前16位和后16位相加 return (unsigned short)(~cksum); |
在真正发送Ping报文前,需要先初始化Raw Socket:
// 功能:初始化RAW Socket, 设置ttl, 初始化目标地址 // 返回值:<0 失败 int setup_for_ping(char *host, int ttl, SOCKET &sd, sockaddr_in &dest) { // 创建原始套接字 sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0); if (sd == INVALID_SOCKET) { cerr << "Failed to create raw socket: " << WSAGetLastError() << endl; return - 1; } if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*) &ttl, sizeof(ttl)) ==SOCKET_ERROR) // 初始化目标主机信息块 // 将第1个参数转换为目标IP地址 |
下面可以利用Raw Socket发送生成的ICMP报文:
//功能:发送生成的ICMP包 //返回值:<0 发送失败 int send_ping(SOCKET sd, const sockaddr_in &dest, ICMPHeader *send_buf, int packet_size) { // 发送send_buf缓冲区中的报文 cout << "Sending " << packet_size << " bytes to " << inet_ntoa(dest.sin_addr) << "..." << flush; int bwrote = sendto(sd, (char*)send_buf, packet_size, 0, (sockaddr*) &dest,sizeof(dest)); if (bwrote == SOCKET_ERROR) { cerr << "send failed: " << WSAGetLastError() << endl; return - 1; } else if (bwrote < packet_size) { cout << "sent " << bwrote << " bytes..." << flush; } return 0; } |
发送Ping报文后,我们需要接收Ping回复ICMP报文:
//功能:接收Ping回复 //返回值: <0 接收失败 int recv_ping(SOCKET sd, sockaddr_in &source, IPHeader *recv_buf, int packet_size) { // 等待Ping回复 int fromlen = sizeof(source); int bread = recvfrom(sd, (char*)recv_buf, packet_size + sizeof(IPHeader), 0,(sockaddr*) &source, &fromlen); if (bread == SOCKET_ERROR) { cerr << "read failed: "; if (WSAGetLastError() == WSAEMSGSIZE) { cerr << "buffer too small" << endl; } else { cerr << "error #" << WSAGetLastError() << endl; } return - 1; } return 0; } |
并使用如下函数对接收到的报文进行解析:
// 功能:解析接收到的ICMP报文 // 返回值: -2忽略, -1失败, 0 成功 int decode_reply(IPHeader *reply, int bytes, sockaddr_in *from) { // 偏移到ICMP报头 unsigned short header_len = reply->h_len *4; ICMPHeader *icmphdr = (ICMPHeader*)((char*)reply + header_len); // 报文太短 // 指出往返时间TTL // 输出信息 |
ping的实现(原始套接字系列三),布布扣,bubuko.com