分类:
传说中的c
/* 导入库文件 */ #pragma comment( lib,"ws2_32.lib") //加载头文件 #include <WinSock2.h> #include <WS2tcpip.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> //定义常量 //表示要记录的路由 #define IP_RECORD_ROUTE 0x7 //默认数据报的大小 #define DEF_PACKET_SIZE 32 //最大的ICMP数据报大小 #define MAX_PACKET 1024 //最大的ip头长度 #define MAX_IP_HDR_SIZE 60 //icmp报文类型,回显请求 #define ICMP_ECHO 8 //ICMP报文类型,回显应答 #define ICMP_ECHO 0 //最小的ICMP数据报大小 #define ICMP_ECHOREPLY 8 //自定义函数 void InitPing(); typedef struct _iphdr { unsigned int h_len : 4; //IP报头的长度 unsigned int version:4; //IP的版本号 unsigned char tos; //服务类型 unsigned short total_len; //数据报的总长度 unsigned short ident; //唯一的标识符 unsigned short frag_flags; //分段标志 unsigned char ttl; //生存期 unsigned char proto; //协议类型(TCP,UDP) unsigned short checksum; //校验和 unsigned int sourceIP; //源ip地址 unsigned int destIP; //目的ip地址 }IpHeader; //ICMP报头的字段的数据结构 typedef struct _icmphdr { BYTE i_type; //ICMP报文类型 BYTE i_code; //该类型中的代码号 USHORT i_cksum; //校验和 USHORT i_id; //唯一的标识符 USHORT i_seq; //序列号 USHORT timestamp; //时间戳 }IcmpHeader; //IP选项头字段数据结构 typedef struct _ipoptionhdr { unsigned char code; //选项类型 unsigned char len; //选项头长度 unsigned char ptr; //地址偏移长度 unsigned long addr[9]; //记录的ip地址列表 }IpOptionHeader; //定义全局变量 SOCKET m_socket; IpOptionHeader IpOption; SOCKADDR_IN DestAddr; SOCKADDR_IN SourceAddr; char* icmp_data; char* recvbuf; USHORT seq_no; char* lpdest; int datasize; BOOL RecordFlag; double PacketNum; BOOL SucessFlag; //下面开始函数 //初始化函数 void InitPing() { WSADATA wsaData; icmp_data = NULL; seq_no = 0; recvbuf = NULL; RecordFlag = FALSE; lpdest = NULL; datasize = DEF_PACKET_SIZE; PacketNum = 5; SucessFlag = FALSE; //winsock初始化 WSAAccept 对winsocket进行加载 if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { //如果初始化不成功则报错 printf("WSAStartup() failed: %d\n",GetLastError()); return; } m_socket = INVALID_SOCKET; } /*显示信息函数*/ void UserHelp() { printf("UserHelp: ping -r <host> [data size] \n"); printf(" -r record route \n"); printf(" -n record amount \n"); printf(" host remote machine to ping \n"); printf(" datasize can be up to 1KB\n"); } /*获取ping选项函数*/ void GetArgments(int argc,char** argv) { int i; int j; int exp; int len; int m; //如果没有指定目的地址和任何选项 if (argc==1) { printf("\nPlease specify the destination IP address and the ping option as follow!\n"); for (i = 0; i < argc; i++) { len = strlen(argv[i]); if (argv[i][0] == '-') { //选项指令要获取记录条数 if (isdigit(argv[i][1])) { PacketNum = 0; for (j = len-1,exp=0; j >= 1; j--,exp++) { //根据argv[i][j]中的ASCII值计算要获取的记录条数(十进制数) PacketNum += ((double)(argv[i][j] - 48)) * pow(10, exp); } } else { switch (tolower(argv[i][1])) { //选项指令要获取路由信息 case 'r': RecordFlag = TRUE; break; //没有按要求提供选项 default: UserHelp(); break; } } } //参数是数据报大小或者IP地址 else if(isdigit(argv[i][0])) { for (m = 0; m < len; m++) { if (!isdigit(argv[i][m])) { //是IP地址 lpdest = argv[i]; break; } //是数据报大小 else if (m == len - 1) datasize = atoi(argv[i]); } } //参数是主机名 else { lpdest = argv[i]; } } } } //求校验和函数 USHORT CheckSum(USHORT* buffer,int size) { unsigned long cksum = 0; while (size>1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } //每16位取二进制反码求和 cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return (USHORT)(~cksum); } //填充ICMP数据报字段函数 void FillICMPData(char *icmp_data,int datasize) { IcmpHeader* icmp_hdr = NULL; char* datapart = NULL; icmp_hdr = (IcmpHeader*)icmp_data; //ICMP报文类型设置为回显请求 icmp_hdr->i_type = ICMP_ECHO; icmp_hdr->i_cksum = 0; //获取当前经常IP作为标识符 icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); icmp_hdr->i_cksum = 0; icmp_hdr->i_seq = 0; datapart = icmp_data + sizeof(IcmpHeader); //以数字0天吃剩余空间 memset(datapart, 0, datapart - sizeof(IcmpHeader)); } //释放资源函数 void FreeRes() { //关闭创建的套字节 if (m_socket!=INVALID_SOCKET) { closesocket(m_socket); } //释放分配的内存 HeapFree(GetProcessHeap(), 0, recvbuf); HeapFree(GetProcessHeap(), 0, icmp_data); //注销WSAStartup()调用 WSACleanup(); return; } //解读Ip选项头函数 void DecoideIPptions(char *buf,int bytes) { IpOptionHeader* ipopt = NULL; IN_ADDR inaddr; int i; HOSTENT* host = NULL; //获取路由信息的入口地址 ipopt = (IpOptionHeader*)(buf + 20); printf(" RR: "); for (i = 0; i < (ipopt->ptr / 4) - 1; i++) { inaddr.S_un.S_addr = ipopt->addr[i]; if (i!=0) { printf(" "); //根据IP地址获取主机名 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 { //否则输出ip地址 printf("(%-15s)\n", inet_ntoa(inaddr)); } return; } } //解读ICMP报头函数 void DecodeICMPHeader(char* buf,int bytes,SOCKADDR_IN* from) { IpHeader* iphdr = NULL; IcmpHeader* icmphdr = NULL; unsigned short iphdrlen; DWORD tick; static int icmpcount = 0; iphdr = (IpHeader*)buf; //计算ip报头的长度 iphdrlen = iphdr->h_len * 4; tick = GetTickCount(); //如果ip报头的长度为最大长度(基本长度是20字节),则认为有ip选项,因此需要解读ip选项 if ((iphdrlen==MAX_IP_HDR_SIZE)&&(!icmpcount)) { //解读IP选项,即路由信息 DecoideIPptions(buf,bytes); } //如果收到的不是回显应答的报文则报错 if (icmphdr->i_id != ICMP_ECHOREPLY) { printf("nonecho type %d recvd \n",icmphdr->i_type); return; } //核实用收到的ID号和发送的是否一致 if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) { printf("someone else's packet! \n"); return; } //输出信息记录信息 printf("%d bytes from %s:",bytes,inet_ntoa(from->sin_addr)); printf(" icmp_seq =%d. ", icmphdr->i_seq); printf(" icmp_seq =%d. ", tick - icmphdr->timestamp); printf("\n"); icmpcount++; return; } //ping 函数 void PingTest(int timeout) { int ret; int readNum; int fromlen; struct hostent* hp = NULL; //创建原始套接字,该套接字用于ICMP m_socket = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED); //如果套接字创建不成功 if (m_socket==INVALID_SOCKET) { printf("WSASocket() Failed: %d \n",WSAGetLastError()); return; } //要求记录路由选项 if (RecordFlag) { //IP选项每个字段都初始化为零 ZeroMemory(&IpOption, sizeof(IpOption)); //为每一个ICMP包设置路由选项 IpOption.code = IP_RECORD_ROUTE; IpOption.ptr = 4; IpOption.len = 39; ret = setsockopt(m_socket, IPPROTO_IP, IP_OPTIONS, (char*)& IpOption, sizeof(IpOption)); if (ret==SOCKET_ERROR) { printf("setsockopt(IP_OPTIONS) failed:%d \n",WSAGetLastError()); } } //设置接收的超时值 readNum = setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)& timeout, sizeof(timeout)); if (readNum==SOCKET_ERROR) { printf("setsockopt(SO_RCVTIMEO) failed:%d\n ", WSAGetLastError()); return; } //初始化目的的地址为零 memset(&DestAddr, 0, sizeof(DestAddr)); //设置地址族,这里表示使用IP地址族 DestAddr.sin_family = AF_INET; if ((DestAddr.sin_addr.S_un.S_addr= inet_addr(lpdest)) == INADDR_NONE) { //名字解析,根据主机名获取IP地址 if ((hp=gethostbyname(lpdest))!=NULL) { //将获取到的IP地址赋值给目的地址中相对应的字段 memcpy(&(DestAddr.sin_addr), hp->h_addr_list, hp->h_length); //将获取到的地址族群赋值到目的相对应的字段中 DestAddr.sin_family = hp->h_addrtype; printf("DestAddr.sin_addr = %s \n",WSAGetLastError()); } //获取不成功 else { printf("gethostbyname() faild: %d \n ",WSAGetLastError()); return; } } //数据报文需要包含ICMP报头 datasize += sizeof(IcmpHeader); //根据默认堆句柄,从堆中分配MAX_PACKET内存块,新分配内存将初始化为零 icmp_data = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET); recvbuf = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET); //如果奉陪内存不成功 if (!icmp_data) { printf("HeapAlloc() failed: %d \n", GetLastError()); return; } //创建ICMP报文 memset(icmp_data, 0, MAX_PACKET); FillICMPData(icmp_data, datasize); while (1) { static int nCount = 0; int writeNum; //超过指定的记录条数则退出 if (nCount++ == PacketNum) { 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,datasize); //开始发送ICMP请求 writeNum = sendto(m_socket,icmp_data,datasize,0,(struct sockaddr*)&DestAddr,&fromlen); //如果接收不成功 if (readNum==SOCKET_ERROR) { //如果超时则不成功 if (WSAGetLastError()==WSAETIMEDOUT) { printf("timed out \n"); continue; } //其他发送不成功的原因 printf("sendto() failed:%d \n",WSAGetLastError()); return; } //开始接收ICMP应答 fromlen = sizeof(SourceAddr); readNum = recvfrom(m_socket,recvbuf,MAX_PACKET,0,(struct sockaddr*)&SourceAddr,&fromlen); if (readNum == SOCKET_ERROR) { //如果超时则不成功 if (WSAGetLastError() == WSAETIMEDOUT) { printf("timed out \n"); continue; } //其他发送不成功的原因 printf("recvfrom() failed:%d \n", WSAGetLastError()); return; } //解读收到的ICMP数据报 } } int main(int argc,char* argv[]) { InitPing(); GetArgments(argc,argv); PingTest(1000); //延迟1s Sleep(1000); if (SucessFlag) { printf("\n Ping end,you have got %.0f records! \n",PacketNum); } else { printf("Ping end ,no record"); } FreeRes(); getchar(); return 0; /* */ }
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
评价
排名
6
文章
6
粉丝
16
评论
8
{{item.articleTitle}}
{{item.blogName}} : {{item.content}}
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2024TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术