标签:
原始套接字
套接字类型指定为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