SOCK_RAW, icmp, ping实现

// ICMP_study.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"
#include <winsock2.h>  
#include <WS2tcpip.h>  
#include <stdio.h>  
#include <stdlib.h>  




#define ICMP_ECHO 8  
#define ICMP_ECHOREPLY 0  
#define ICMP_MIN 8 //Minimum 8-byte ICMP packet (header)  


#define DEF_PACKET_SIZE 32  
#define MAX_PACKET 1024  
#define MAX_IP_HDR_SIZE 60  


#pragma comment(lib, "ws2_32.lib")


//IP header structure  
typedef struct _iphdr  
{  
unsigned int h_len:4;//Length of the header  
unsigned int version:4;//Version of IP  
unsigned char tos;//Type of service  
unsigned short total_len;//Total length of the packet  
unsigned short ident;//Unique identifier  
unsigned short frag_and_flags;//Flags  
unsigned char ttl;//Time to live  
unsigned char proto;//Protocol (TCP,UDP,etc.)  
unsigned short checksum;//IP checksum  


unsigned int sourceIP;  
unsigned int destIP;  
} IpHeader;  


//ICMP header structure  
typedef struct _icmphdr  
{  
BYTE i_type;  
BYTE i_code;//Type sub code  
USHORT i_cksum;  
USHORT i_id;  
USHORT i_seq;  


//This is not the standard header, but we reserve space for time  
ULONG timestamp;  
} IcmpHeader;  


//IP option header--use with socket option IP_OPTIONS  
typedef struct _ipoptionhdr  
{  
unsigned char code;//Option type  
unsigned char len;//Length of option hdr  
unsigned char ptr;//Offset into optons  
unsigned long addr[9];//List of IP addrs  
} IpOptionHeader;  




//Helper function to fill in various fields for our ICMP request  
void FillICMPData(char* icmp_data, int datasize)  
{  
//此结构体的填充是根据协议来的
IcmpHeader* icmp_hdr = (IcmpHeader*)icmp_data;  
icmp_hdr->i_type = ICMP_ECHO;//Request an ICMP echo  
icmp_hdr->i_code = 0;  
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();  
icmp_hdr->i_cksum = 0;  
icmp_hdr->i_seq = 0;  
}  


//This function calculates the 16-bit one's complement sum  
//of the supplied buffer (ICMP) header  
USHORT checksum(USHORT* buffer, int size)  
{  
unsigned long cksum = 0;  


while (size > 1)  
{  
cksum += *buffer++;  
size -= sizeof(USHORT);  
}  


if (size)  
{  
cksum += *(UCHAR*)buffer;  
}  


cksum = (cksum>>16) + (cksum & 0xffff);  
cksum += (cksum>>16);  


return (USHORT)(~cksum);  
}  


//If the IP option header is present, find the IP options  
//within the IP header and print the record route option values  
void DecodeIPOptions(char* buf, int bytes)  
{  
IpOptionHeader* ipopt = (IpOptionHeader*)(buf + 20);  


printf("RR:    ");  
for (int i = 0; i < (ipopt->ptr / 4) - 1; i++)  
{  
IN_ADDR inaddr;  
inaddr.S_un.S_addr = ipopt->addr[i];  


if (i != 0)  
{  
printf("        ");  
}  


HOSTENT* host = gethostbyaddr((char*)&inaddr.S_un.S_addr,  
sizeof(inaddr.S_un.S_addr), AF_INET);  
if (host)  
{  
printf("(%-15s) %s\n", inet_ntoa(inaddr), host->h_name);  
}  
else  
{  
printf("(%-15s)\n", inet_ntoa(inaddr));  
}  
}  


return;  
}  


//The response is an IP packet. We must decode the IP header to  
//locate the ICMP data.  
void DecodeICMPHeader(char* buf, int bytes, struct sockaddr_in* from)  
{  
static int icmpcount = 0;  
IpHeader* iphdr = (IpHeader*)buf;  


//Number of 32-bit words * 4 = bytes  
unsigned short iphdrlen = iphdr->h_len * 4; //这是在分析ip包
DWORD tick = GetTickCount();  


if ((iphdrlen == MAX_IP_HDR_SIZE) && (!icmpcount))  
{  
DecodeIPOptions(buf, bytes);  
}  


if (bytes < iphdrlen + ICMP_MIN)  
{  
printf("Too few bytes from %s\n", inet_ntoa(from->sin_addr));  
}  


IcmpHeader* icmphdr = (IcmpHeader*)(buf + iphdrlen);  
if (icmphdr->i_type != ICMP_ECHOREPLY)  
{  
printf("nonecho type %d recvd\n", icmphdr->i_type);  
return;  
}  


//Make sure this is an ICMP reply to something we sent!  
if (icmphdr->i_id != (USHORT)GetCurrentProcessId())  
{  
printf("someone else's packet!\n");  
return;  
}  


//此处算出来的timestamp,是发送与接收之间的间隔
printf("%d bytes from %s:", bytes, inet_ntoa(from->sin_addr));  
printf("  icmp_seq = %d. ", icmphdr->i_seq);  
printf("  time:%d ms", tick - icmphdr->timestamp);  
printf("\n");  


icmpcount++;  
return;  
}  


BOOL fgInitSocket()
{
WSADATA wsaData;  
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)  
{  
printf("WSAStartup() failed:%d\n", GetLastError());  
return FALSE;  
}  
return TRUE;
}


SOCKET sCreateRawSocket()
{
SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);  
if (sockRaw == INVALID_SOCKET)  
{  
printf("WSASocket() failed:%d\n", WSAGetLastError());  
return INVALID_SOCKET;  
}  


//Set the send/recv timeout values  
int timeout = 1000;  


int bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));  
if (bread == SOCKET_ERROR)  
{  
printf("setsockopt(SO_RCVTIMEO) failed:%d\n", WSAGetLastError());  
return INVALID_SOCKET;  
}  


timeout = 1000;  
bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));  
if (bread == SOCKET_ERROR)  
{  
printf("setsockopt(SO_SNDTIMEO) failed:%d\n", WSAGetLastError());  
return INVALID_SOCKET;  

return sockRaw;
}


int _tmain(int argc, _TCHAR* argv[])
{
if (!fgInitSocket())
{
return -1;
}


SOCKET sockRaw = sCreateRawSocket();
if (sockRaw == INVALID_SOCKET)
{
return -1;
}


struct sockaddr_in from;  
int fromlen = sizeof(from);  


struct sockaddr_in dest;  
memset(&dest, 0, sizeof(dest));  
dest.sin_family = AF_INET;  
dest.sin_addr.s_addr = inet_addr("101.226.103.106");  


//Create the ICMP packet  
char icmp_data[MAX_PACKET] = {0};
memset(icmp_data, 0, MAX_PACKET);  
FillICMPData(icmp_data, sizeof(IcmpHeader));  


char recvbuf[MAX_PACKET] = {0};


//Start sending/receiving ICMP packets  
USHORT seq_no = 0;  
while (true)  
{  
static int nCount = 0;  
int bwrote;  


if (nCount++ == 4)  
{  
break;  
}  


((IcmpHeader*)icmp_data)->i_cksum = 0;  
((IcmpHeader*)icmp_data)->timestamp = GetTickCount();  
((IcmpHeader*)icmp_data)->i_seq = seq_no++;  
((IcmpHeader*)icmp_data)->i_cksum =  checksum((USHORT*)icmp_data, sizeof(IcmpHeader));  


bwrote = sendto(sockRaw, icmp_data, sizeof(IcmpHeader), 0, (struct sockaddr*)&dest, sizeof(dest));  
if (bwrote == SOCKET_ERROR)  
{  
if (WSAGetLastError() == WSAETIMEDOUT)  
{  
printf("timed out\n");  
continue;  
}  


printf("sendto() failed:%d\n", WSAGetLastError());  
return -1;  
}  


if (bwrote < sizeof(IcmpHeader))  
{  
printf("Wrote %d bytes\n", bwrote);  
}  


int bread = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0,  (struct sockaddr*)&from, &fromlen);  
if (bread == SOCKET_ERROR)  
{  
int iErr = GetLastError();
if (iErr == WSAETIMEDOUT)  
{  
printf("timed out\n");  
continue;  
}  


printf("revefrom() failed:%d\n", iErr);  
return -1;  
}  


DecodeICMPHeader(recvbuf, bread, &from);  


Sleep(1000);  
}  


//Cleanup  
if (sockRaw != INVALID_SOCKET)  
{  
closesocket(sockRaw);  
}  


WSACleanup(); 
return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值