标签:
原始套接字
套接字类型指定为SOCK_RAW
socket(AF_INET, SOCK_STREAM, 0);
创建原始套接字时 第三个参数将成为IP头中协议域的值
发送ICMP报文时 必须由程序自己计算校验和
FUNC:将数据以字为单位 加到一个双字中
如果数据长度是奇数 最后一个字节将被扩展到字
最后将这个双子的高16位和低16位相加后取反
// 原始套接字.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #pragma comment(lib,"ws2_32.lib") class CInitSock { public: CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer, majorVer); if(::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } } ~CInitSock() { ::WSACleanup(); } }; CInitSock theSock; typedef struct icmp_hdr { unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 // 下面是回显头 unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳 } ICMP_HDR, *PICMP_HDR; typedef struct _IPHeader // 20字节的IP头 { UCHAR iphVerLen; // 版本号和头长度(各占4位) UCHAR ipTOS; // 服务类型 USHORT ipLength; // 封包总长度,即整个IP报的长度 USHORT ipID; // 封包标识,惟一标识发送的每一个数据报 USHORT ipFlags; // 标志 UCHAR ipTTL; // 生存时间,就是TTL UCHAR ipProtocol; // 协议,可能是TCP、UDP、ICMP等 USHORT ipChecksum; // 校验和 ULONG ipSource; // 源IP地址 ULONG ipDestination; // 目标IP地址 } IPHeader, *PIPHeader; #pragma region 检查校验和 USHORT CheckSum(USHORT* szBuffer,int size) { unsigned long uCkSum = 0; //将数据以字为单位累加到uCkSum中 while (size >1) { uCkSum += *szBuffer++; size -= sizeof(USHORT); } if (size) //奇数 { uCkSum += *(UCHAR*)szBuffer; } uCkSum = (uCkSum >> 16) + (uCkSum & 0xffff); uCkSum += (uCkSum >> 16); return (USHORT)(~uCkSum); } #pragma endregion 检查校验和 BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv) { int ret = ::setsockopt(s, SOL_SOCKET, bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime)); return ret != SOCKET_ERROR; } int _tmain(int argc, _TCHAR* argv[]) { // 目的IP地址,即要Ping的IP地址 char szDestIp[] = "127.0.0.1"; // 127.0.0.1 // 创建原始套节字 SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); // 设置接收超时 SetTimeout(sRaw, 1000, TRUE); // 设置目的地址 SOCKADDR_IN dest; dest.sin_family = AF_INET; dest.sin_port = htons(0); dest.sin_addr.S_un.S_addr = inet_addr(szDestIp); // 创建ICMP封包 char szBuffer[sizeof(ICMP_HDR) + 32]; ICMP_HDR* pIcmp = (ICMP_HDR*)szBuffer; // 填写ICMP封包数据 pIcmp->icmp_type = 8; // 请求一个ICMP回显 pIcmp->icmp_code = 0; pIcmp->icmp_id = (USHORT)::GetCurrentProcessId(); pIcmp->icmp_checksum = 0; pIcmp->icmp_sequence = 0; // 填充数据部分,可以为任意 memset(&szBuffer[sizeof(ICMP_HDR)], ‘E‘, 32); // 开始发送和接收ICMP封包 USHORT nSeq = 0; char recvBuf[1024]; SOCKADDR_IN from; int nLen = sizeof(from); while(TRUE) { static int nCount = 0; int nRet; if(nCount++ == 4) break; pIcmp->icmp_checksum = 0; pIcmp->icmp_timestamp = ::GetTickCount(); pIcmp->icmp_sequence = nSeq++; pIcmp->icmp_checksum = CheckSum((USHORT*)szBuffer, sizeof(ICMP_HDR) + 32); nRet = ::sendto(sRaw, szBuffer, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest)); if(nRet == SOCKET_ERROR) { printf(" sendto() failed: %d \n", ::WSAGetLastError()); //以管理员权限开启 return -1; } nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen); if(nRet == SOCKET_ERROR) { if(::WSAGetLastError() == WSAETIMEDOUT) { printf(" timed out\n"); continue; } printf(" recvfrom() failed: %d\n", ::WSAGetLastError()); return -1; } // 下面开始解析接收到的ICMP封包 int nTick = ::GetTickCount(); if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR)) { printf(" Too few bytes from %s \n", ::inet_ntoa(from.sin_addr)); } // 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头 ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader)); if(pRecvIcmp->icmp_type != 0) // 回显 { printf(" nonecho type %d recvd \n", pRecvIcmp->icmp_type); return -1; } if(pRecvIcmp->icmp_id != ::GetCurrentProcessId()) { printf(" someone else‘s packet! \n"); return -1; } printf(" %d bytes from %s:", nRet, inet_ntoa(from.sin_addr)); printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence); printf(" time: %d ms", nTick - pRecvIcmp->icmp_timestamp); printf(" \n"); ::Sleep(1000); } return 0; }
// 路由跟踪.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #include "Ws2tcpip.h" #pragma comment (lib,"ws2_32.lib") class CInitSock { public: CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer, majorVer); if(::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } } ~CInitSock() { ::WSACleanup(); } }; CInitSock theSock; typedef struct icmp_hdr { unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 // 下面是回显头 unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳 } ICMP_HDR, *PICMP_HDR; #pragma region 检查校验和 USHORT CheckSum(USHORT* szBuffer,int size) { unsigned long uCkSum = 0; //将数据以字为单位累加到uCkSum中 while (size >1) { uCkSum += *szBuffer++; size -= sizeof(USHORT); } if (size) //奇数 { uCkSum += *(UCHAR*)szBuffer; } uCkSum = (uCkSum >> 16) + (uCkSum & 0xffff); uCkSum += (uCkSum >> 16); return (USHORT)(~uCkSum); } #pragma endregion 检查校验和 BOOL SetTTL(SOCKET s, int nValue) { int ret = ::setsockopt(s, IPPROTO_IP, IP_TTL, (char*)&nValue, sizeof(nValue)); return ret != SOCKET_ERROR; } BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv) { int ret = ::setsockopt(s, SOL_SOCKET, bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime)); return ret != SOCKET_ERROR; } int _tmain(int argc, _TCHAR* argv[]) { char *szDestIp = "192.168.0.103"; // 210.181.18.12910.16.115.25 61.55.66.30 char recvBuf[1024] = { 0 }; // 创建用于接收ICMP封包的原始套节字,绑定到本地端口 SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); sockaddr_in in; in.sin_family = AF_INET; in.sin_port = 0; in.sin_addr.S_un.S_addr = INADDR_ANY; if(::bind(sRaw, (sockaddr*)&in, sizeof(in)) == SOCKET_ERROR) { printf(" bind() failed 错误码: %d\n",WSAGetLastError()); return 0; } SetTimeout(sRaw, 5*1000,TRUE); // 创建用于发送UDP封包的套节字 SOCKET sSend = ::socket(AF_INET, SOCK_DGRAM, 0); SOCKADDR_IN destAddr; destAddr.sin_family = AF_INET; destAddr.sin_port = ::htons(22); destAddr.sin_addr.S_un.S_addr = ::inet_addr(szDestIp); int nTTL = 1; int nRet; ICMP_HDR *pICMPHdr; int nTick; SOCKADDR_IN recvAddr; do { // 设置UDP封包的TTL值 SetTTL(sSend, nTTL); nTick = ::GetTickCount(); // 发送这个UDP封包 nRet = ::sendto(sSend, "hello", 5, 0, (sockaddr*)&destAddr, sizeof(destAddr)); if(nRet == SOCKET_ERROR) { printf(" sendto() failed \n"); break; } // 等待接收路由器返回的ICMP报文 int nLen = sizeof(recvAddr); nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&recvAddr, &nLen); if(nRet == SOCKET_ERROR) { if(::WSAGetLastError() == WSAETIMEDOUT) { printf(" time out \n"); break; } else { printf(" recvfrom() failed \n"); break; } } // 解析接收到的ICMP数据 pICMPHdr = (ICMP_HDR*)&recvBuf[20]; // sizeof(IPHeader) if(pICMPHdr->icmp_type != 11 && pICMPHdr->icmp_type != 3 && pICMPHdr->icmp_code != 3) { printf(" Unexpected Type: %d , code: %d \n", pICMPHdr->icmp_type, pICMPHdr->icmp_code); } else { char *szIP = ::inet_ntoa(recvAddr.sin_addr); printf(" 第%d个路由器,IP地址:%s \n", nTTL, szIP); printf(" 用时:%d毫秒 \n", ::GetTickCount() - nTick); } if(destAddr.sin_addr.S_un.S_addr == recvAddr.sin_addr.S_un.S_addr) { printf("目标可达 \n"); break; } printf("//------------------------------------// \n"); }while(nTTL++ < 20); ::closesocket(sRaw); ::closesocket(sSend); return 0; }
标签:
原文地址:http://www.cnblogs.com/yifi/p/5757043.html