• 原始套接字


    原始套接字
    套接字类型指定为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 
    ", ::WSAGetLastError());	//以管理员权限开启
    			return -1;
    		}
    		nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
    		if(nRet == SOCKET_ERROR)
    		{
    			if(::WSAGetLastError() == WSAETIMEDOUT)
    			{
    				printf(" timed out
    ");
    				continue;
    			}
    			printf(" recvfrom() failed: %d
    ", ::WSAGetLastError());
    			return -1;
    		}
    
    		// 下面开始解析接收到的ICMP封包
    		int nTick = ::GetTickCount();
    		if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))
    		{
    			printf(" Too few bytes from %s 
    ", ::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 
    ", pRecvIcmp->icmp_type);
    			return -1;
    		}
    
    		if(pRecvIcmp->icmp_id != ::GetCurrentProcessId())
    		{
    			printf(" someone else's packet! 
    ");
    			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(" 
    ");
    
    		::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
    ",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 
    ");
    			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 
    ");
    				break;
    			}
    			else
    			{
    				printf(" recvfrom() failed 
    ");
    				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 
    ",
    				pICMPHdr->icmp_type, pICMPHdr->icmp_code);
    		}
    		else
    		{
    			char *szIP = ::inet_ntoa(recvAddr.sin_addr);
    
    			printf(" 第%d个路由器,IP地址:%s 
    ", nTTL, szIP);
    			printf("      用时:%d毫秒 
    ", ::GetTickCount() - nTick);
    		}
    
    		if(destAddr.sin_addr.S_un.S_addr == recvAddr.sin_addr.S_un.S_addr)
    		{	
    			printf("目标可达 
    ");
    			break;
    		}
    
    		printf("//------------------------------------// 
    ");
    
    	}while(nTTL++ < 20);
    
    	::closesocket(sRaw);
    	::closesocket(sSend);
    	return 0;
    }
    

      

    爱程序 不爱bug 爱生活 不爱黑眼圈 我和你们一样 我和你们不一样 我不是凡客 我要做geek
  • 相关阅读:
    爬虫笔记:PyQuery模块(七)
    25丨固若金汤的根本(下):数字签名与证书
    爬虫笔记:Beautiful Soup 使用(六)
    24丨固若金汤的根本(上):对称加密与非对称加密
    爬虫笔记:requests模块使用(四)
    五安全篇(7讲)23-TLS又是什么?
    爬虫笔记:http请求详解(三)
    22丨冷链周转:HTTP的缓存代理
    python中expandtabs()函数的用法
    Python全栈工程师(while、占位符)
  • 原文地址:https://www.cnblogs.com/yifi/p/5757043.html
Copyright © 2020-2023  润新知