#ifndef IPHEADER_H<br />#define IPHEADER_H<br />// 定義TCP頭結構<br />struct TcpHeader<br />{<br /> unsigned short int th_sport; // 16位源連接埠<br /> unsigned short int th_dport; // 16位目的連接埠<br /> unsigned int th_seq; // 32位序號<br /> unsigned int th_ack; // 32位確認號<br /> unsigned char th_lenres; // 4位首部長度/6位保留字<br /> unsigned char th_flag; // 6位標誌位<br /> unsigned short int th_win; // 16位視窗大小<br /> unsigned short int th_sum; // 16位校正和<br /> unsigned short int th_urp; // 16位緊急資料位移量<br />};<br />struct PseudoHeader // 定義偽首部<br />{<br /> unsigned long saddr; // 源地址<br /> unsigned long daddr; // 目的地址<br /> char mbz; // 固定為0<br /> char ptcl; // 協議類型<br /> unsigned short tcpl; // TCP或者UDP包的長度<br />};<br />// 定義IP頭結構<br />struct IpHeader<br />{<br /> unsigned char ip_hl : 4, ip_v : 4; // 4位首部長度 + 4位IP版本號碼<br /> unsigned char ip_tos; // 8位服務類型<br /> unsigned short int ip_len; // 16位總長度(位元組)<br /> unsigned short int ip_id; // 16位標識<br /> unsigned short int ip_off; // 3位標誌位<br /> unsigned char ip_ttl; // 8位存留時間TTL<br /> unsigned char ip_p; // 8位協議(TCP, UDP, ...)<br /> unsigned short int ip_sum; // 16位IP首部校正和<br /> unsigned int ip_src; // 32位源IP地址<br /> unsigned int ip_dst; // 32位目的IP地址<br />};<br />// 定義UDP頭結構<br />struct UdpHeader<br />{<br /> unsigned short uh_sport;<br /> unsigned short uh_dport;<br /> unsigned short uh_len;<br /> unsigned short uh_cksum;<br />};<br />// 定義ICMP echo request包的結構<br />struct IcmpHeader<br />{<br /> byte type;<br /> byte code;<br /> unsigned short checksum;<br /> unsigned short id;<br /> unsigned short seq;<br /> unsigned long timestamp;<br />};<br />#endif // IPHEADER_H<br />
#include <iostream><br />#include <cstdlib><br />#include <cstdio><br />#include <winsock2.h><br />#include <ws2tcpip.h> // 注意標頭檔包含的順序<br />#include <winbase.h><br />#include <windows.h><br />#include <windef.h><br />#include "ipheader.h"<br />using namespace std;<br />#define ICMP_ECHO_REPLY 0<br />#define ICMP_DEST_UNREACH 3<br />#define ICMP_TTL_EXPIRE 11<br />#define ICMP_ECHO_REQUEST 8<br />#define ICMP_MIN 8 // 最小的ICMP包大小<br />// 功能:計算校正和<br />unsigned short ip_checksum(unsigned short *buffer, int size)<br />{<br /> unsigned long cksum = 0;<br /> // 將所有16位無符號數相加<br /> while (size > 1) {<br /> cksum += *buffer++;<br /> size -= sizeof(unsigned short);<br /> }<br /> if (size) // 加上最後一個BYTE<br /> cksum += *(unsigned char *)buffer;<br /> // 和的前16位和後16位相加<br /> cksum = (cksum >> 16) + (cksum & 0xffff);<br /> cksum += (cksum >> 16);<br /> return (unsigned short)(~cksum);<br />}<br />// 初始化icmp資料包的各個欄位<br />// 給data部分填充資料, 計算校正和<br />void init_ping_packet(IcmpHeader *icmp_hdr, int packet_size, int seq_no)<br />{<br /> // 設定ICMP前序欄位<br /> icmp_hdr->type = 8; // 常量ICMP_ECHO_REQUEST = 8<br /> icmp_hdr->code = 0;<br /> icmp_hdr->checksum = 0;<br /> icmp_hdr->id = (unsigned short)GetCurrentProcessId();<br /> // id號隨機,這裡以進程號作為id號<br /> icmp_hdr->seq = seq_no;<br /> icmp_hdr->timestamp = GetTickCount();<br /> // 填充data域<br /> const unsigned long int deadmeat = 0xDEADBEEF; // 用於填充的資料部分<br /> char *datapart = (char *)icmp_hdr + sizeof(IcmpHeader); // 指向資料部分<br /> int bytes_left = packet_size - sizeof(IcmpHeader); // 包大小減去首部長度就是資料部分長度<br /> while (bytes_left > 0) {<br /> memcpy(datapart, &deadmeat, min(int(sizeof(deadmeat)), bytes_left));<br /> bytes_left -= sizeof(deadmeat);<br /> datapart += sizeof(deadmeat);<br /> }<br /> // 計算校正和<br /> icmp_hdr->checksum = ip_checksum((unsigned short *)icmp_hdr, packet_size);<br />}<br />// 功能:初始化RAW Socket, 初始化目標地址<br />// 傳回值:< 0 失敗<br />int setup_for_ping(const char *host, SOCKET &sd, sockaddr_in &dest)<br />{<br /> WORD wVersionRequested;<br /> WSADATA wsaData;<br /> int err;<br /> wVersionRequested = MAKEWORD(2, 2);<br /> err = WSAStartup(wVersionRequested, &wsaData); // 初始化<br /> if ( err != 0 ) {<br /> cerr << "WSAStartup Error" << endl;<br /> return -1;<br /> }<br /> // 檢查winsock DLL的版本<br /> if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) {<br /> cerr << "wsaData.wVersion != 2 || wsaData.wVersion != 2" << endl;<br /> WSACleanup();<br /> return -1;<br /> }<br /> // 建立原始通訊端,協議類型為IPPROTO_ICMP<br /> sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0);<br /> if (sd == INVALID_SOCKET) {<br /> cerr << "Failed to create raw socket: " << WSAGetLastError() << endl;<br /> return -1;<br /> }<br /> unsigned int timeout = 1000;<br /> if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {<br /> cerr << "SO_RCVTIMEo setsockopt failed: " << WSAGetLastError() << endl;<br /> return -1;<br /> }<br /> if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {<br /> cerr << "SO_SNDTIMEo setsockopt failed: " << WSAGetLastError() << endl;<br /> return -1;<br /> }<br /> // 初始化目標主機的資訊<br /> memset(&dest, 0, sizeof(dest));<br /> // 將命令列的第1個參數轉換為目標IP地址<br /> dest.sin_addr.S_un.S_addr = inet_addr(host);<br /> if (dest.sin_addr.S_un.S_addr != INADDR_NONE) {<br /> dest.sin_family = AF_INET;<br /> } else {<br /> cerr << "dest ip not right!";<br /> return -1;<br /> }<br /> return 0;<br />}<br />// 功能:發送產生的ICMP包<br />// 傳回值:< 0 發送失敗<br />int send_ping(SOCKET sd, const sockaddr_in &dest, IcmpHeader *send_buf, int packet_size)<br />{<br /> // 發送send_buf緩衝區中的報文<br /> cout << "Sending " << packet_size << " bytes to " << inet_ntoa(dest.sin_addr) << "..." << flush;<br /> int bwrote = sendto(sd, (char *)send_buf, packet_size, 0, (sockaddr *)&dest, sizeof(dest));<br /> if (bwrote == SOCKET_ERROR) {<br /> cerr << "send failed: " << WSAGetLastError() << endl;<br /> return -1;<br /> } else if (bwrote < packet_size) {<br /> cout << "sent " << bwrote << " bytes..." << flush;<br /> }<br /> return 0;<br />}<br />// 發送Ping報文後,接收回複ICMP報文:<br />// 傳回值: < 0 接收失敗<br />int recv_ping(SOCKET sd, sockaddr_in &source, IpHeader *recv_buf, int &packet_size)<br />{<br /> // 等待Ping回複<br /> int fromlen = sizeof(source);<br /> int bread = recvfrom(sd, (char *)recv_buf, 200, 0, (sockaddr *)&source, &fromlen);<br /> if (bread == SOCKET_ERROR) {<br /> cerr << "read failed: ";<br /> if (WSAGetLastError() == WSAEMSGSIZE)<br /> cerr << "buffer too small" << endl;<br /> else<br /> cerr << "error #" << WSAGetLastError() << endl;<br /> return -1;<br /> }<br /> packet_size = bread;<br /> return 0;<br />}<br />// 對接收到的報文進行解析:<br />// 功能:解析接收到的ICMP報文<br />// 傳回值:-2: 忽略, -1: 失敗, 0: 成功<br />int decode_reply(IpHeader *reply, int bytes, sockaddr_in *from)<br />{<br /> // 將指標位移到ICMP前序<br /> unsigned short header_len = reply->ip_hl * 4; // ip包首部長度<br /> IcmpHeader *icmphdr = (IcmpHeader *)((char *)reply + header_len);<br /> // 報文太短<br /> if (bytes < header_len + ICMP_MIN) {<br /> cerr << "too few bytes from " << inet_ntoa(from->sin_addr) << endl;<br /> return -1;<br /> } else if (icmphdr->type != ICMP_ECHO_REPLY) { // 解析回複報文類型<br /> //非正常回複<br /> if (icmphdr->type != ICMP_TTL_EXPIRE) { // ttl減為零<br /> if (icmphdr->type == ICMP_DEST_UNREACH) //主機不可達<br /> cerr << "Destination unreachable" << endl;<br /> else //非法的ICMP包類型<br /> cerr << "Unknown ICMP packet type " << int(icmphdr->type) << " received" << endl;<br /> return -1;<br /> }<br /> } else if (icmphdr->id != (unsigned short)GetCurrentProcessId()) { // id號不匹配<br /> //不是本進程發的包, 可能是同機的其它ping進程發的<br /> return -2;<br /> }<br /> // 輸出資訊<br /> cout << endl << bytes << " bytes from " << inet_ntoa(from->sin_addr) << ", icmp_seq " << icmphdr->seq << ", ";<br /> if (icmphdr->type == ICMP_TTL_EXPIRE) {<br /> cerr << "TTL expired." << endl;<br /> } else {<br /> cout << "TTL:" << (int)reply->ip_ttl << " ";<br /> cout << "time: " << (GetTickCount() - icmphdr->timestamp) << " ms." << endl;<br /> }<br /> return 0;<br />}<br />int main()<br />{<br /> int i = 0;<br /> SOCKET s;<br /> sockaddr_in dest;<br /> // IcmpHeader icmp_hdr; // sizeof(IcmpHeader = 12)<br /> char sendbuf[200]; // sendbuf包括ICMP頭,還有留出寫資料的部分<br /> char recv_buf[200]; // sendbuf要接收IP頭以及ICMP包,所以要留出足夠的空間<br /> int recvlen = 20;<br /> // IpHeader recv_buf;<br /> while (i < 4) {<br /> if (setup_for_ping("127.0.0.1", s, dest)) {<br /> cerr <<"destip not in proper form, exit";<br /> return -1;<br /> }<br /> init_ping_packet((IcmpHeader *)sendbuf, 20, 1000);<br /> // ICMP頭長度以及資料加起來為20<br /> send_ping(s, dest, (IcmpHeader *)sendbuf, 20);<br /> recv_ping(s, dest, (IpHeader *)recv_buf, recvlen);<br /> decode_reply((IpHeader *)recv_buf, recvlen, &dest);<br /> i++;<br /> }<br /> getchar();<br />}<br />