Windows ping源码

需要测试外网的联通性,想到了用ping。网上下载了ping的源代码,调试下整理如下:

  1 /******************************************************************************\
  2 * ping.c - Simple ping utility using SOCK_RAW
  3 *
  4 * This is a part of the Microsoft Source Code Samples.
  5 * Copyright 1996-1997 Microsoft Corporation.
  6 * All rights reserved.
  7 * This source code is only intended as a supplement to
  8 * Microsoft Development Tools and/or WinHelp documentation.
  9 * See these sources for detailed information regarding the
 10 * Microsoft samples programs.
 11 \******************************************************************************/
 12
 13 #pragma pack(4)
 14
 15 #define WIN32_LEAN_AND_MEAN
 16 #include <winsock2.h>
 17 #include <stdio.h>
 18 #include <stdlib.h>
 19 #pragma comment(lib,"ws2_32.lib")
 20
 21 #define ICMP_ECHO 8
 22 #define ICMP_ECHOREPLY 0
 23
 24 #define ICMP_MIN 8                                           // minimum 8 byte icmp packet (just header)
 25
 26 /* The IP header */
 27 typedef struct iphdr
 28 {
 29     unsigned int h_len:4; // length of the header
 30     unsigned int version:4; // Version of IP
 31     unsigned char tos; // Type of service
 32     unsigned short total_len; // total length of the packet
 33     unsigned short ident; // unique identifier
 34     unsigned short frag_and_flags; // flags
 35     unsigned char ttl;
 36     unsigned char proto; // protocol (TCP, UDP etc)
 37     unsigned short checksum; // IP checksum
 38     unsigned int sourceIP;
 39     unsigned int destIP;
 40 }IpHeader;
 41
 42 //
 43 // ICMP header
 44 //
 45 typedef struct _ihdr {
 46     BYTE i_type;     //消息类型
 47     BYTE i_code;     //代码  /* type sub code */
 48     USHORT i_cksum;  //校验和
 49     USHORT i_id;     //ID号
 50     USHORT i_seq;    //序列号
 51     ULONG timestamp; //时间戳  /* This is not the std header, but we reserve space for time */
 52 }IcmpHeader;         //ICMP报文  包括报头和数据
 53
 54 #define STATUS_FAILED 0xFFFF
 55 #define DEF_PACKET_SIZE 32
 56 #define MAX_PACKET 1024
 57
 58 #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
 59 #define xfree(p) HeapFree (GetProcessHeap(),0,(p))
 60
 61 void fill_icmp_data(char *, int);
 62 USHORT checksum(USHORT *, int);
 63 void decode_resp(char *,int ,struct sockaddr_in *);
 64
 65 int main(int argc, char **argv)
 66 {
 67     WSADATA wsaData;
 68     SOCKET sockRaw;
 69     struct sockaddr_in dest;
 70     struct hostent * hp;
 71     int bread,datasize;
 72     int timeout = 1000;
 73     char *dest_ip;
 74     char *icmp_data;
 75     char *recvbuf;
 76     unsigned int addr=0;
 77     USHORT seq_no = 0;
 78     struct sockaddr_in from;
 79     int fromlen = sizeof(from);
 80
 81     if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0)
 82     {
 83         fprintf(stderr,"WSAStartup failed: %d\n",GetLastError());
 84         ExitProcess(STATUS_FAILED);
 85     }
 86
 87     /*
 88     为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO),
 89 //    必须将标志位设为WSA_FLAG_OVERLAPPED !
 90     */
 91     sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,WSA_FLAG_OVERLAPPED);                    //建立一个原始套接字
 92     //sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,0);
 93
 94     if (sockRaw == INVALID_SOCKET)
 95     {
 96         fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
 97         ExitProcess(STATUS_FAILED);
 98     }
 99
100     timeout = 1000;   //设置接收超时时间
101     bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout, sizeof(timeout)); //RECVTIMEO是接收超时时间
102     if(bread == SOCKET_ERROR)
103     {
104         fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
105         ExitProcess(STATUS_FAILED);
106     }
107
108     timeout = 1000;   //设置发送超时时间
109     bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout)); //SNDTIMEO是发送超时时间
110     if(bread == SOCKET_ERROR)
111     {
112         fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
113         ExitProcess(STATUS_FAILED);
114     }
115     memset(&dest,0,sizeof(dest));            //目标地址清零
116
117     hp = gethostbyname("www.baidu.com");      //通过域名或者主机名获取IP地址
118     if (!hp)  //失败返回NULL
119     {
120          ExitProcess(STATUS_FAILED);
121     }
122     else
123     {
124          addr = inet_addr("14.215.177.37");    //www.baidu.com的ip地址
125     }
126
127     if ((!hp) && (addr == INADDR_NONE))        //既不是域名也不是点分十进制的IP地址
128     {
129         ExitProcess(STATUS_FAILED);
130     }
131
132     if (hp != NULL)                           //获取的是域名
133         memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);   //从hostent得到的对方ip地址
134     else
135         dest.sin_addr.s_addr = addr;
136
137     if (hp)
138         dest.sin_family = hp->h_addrtype;    //sin_family不是一定只能填AF_INET吗?
139     else
140         dest.sin_family = AF_INET;
141
142     dest_ip = inet_ntoa(dest.sin_addr);        //目标IP地址
143
144     datasize = DEF_PACKET_SIZE;             //ICMP包数据大小设定为32
145
146     datasize += sizeof(IcmpHeader);         //另外加上ICMP包的包头 其实包头占12个字节
147
148     icmp_data = (char *)xmalloc(MAX_PACKET);//发送icmp_data数据包内存
149     recvbuf = (char *)xmalloc(MAX_PACKET);  //存放接收到的数据
150
151     if (!icmp_data)                         //分配内存
152     {
153         ExitProcess(STATUS_FAILED);
154     }
155
156     memset(icmp_data,0,MAX_PACKET);
157     fill_icmp_data(icmp_data,datasize);         //只填充了ICMP包
158
159     fprintf(stdout,"\nPinging %s ....\n\n",dest_ip);
160
161     while(1)
162     {
163         int bwrote;
164
165         ((IcmpHeader*)icmp_data)->i_cksum = 0;
166         ((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); //时间戳
167
168         ((IcmpHeader*)icmp_data)->i_seq = seq_no++;           //ICMP的序列号
169         ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);   //icmp校验位
170
171         //下面这个函数的问题是 发送数据只是ICMP数据包,而接收到的数据时包含ip头的 也就是发送和接收不对等
172         //问题是sockRaw 设定了协议为 IPPROTO_ICMP
173         bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest, sizeof(dest));
174         if (bwrote == SOCKET_ERROR)
175         {
176             if (WSAGetLastError() == WSAETIMEDOUT)     //发送时间超时
177             {
178                 printf("timed out\n");
179                 continue;
180             }
181
182             fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());
183             ExitProcess(STATUS_FAILED);
184         }
185
186         if (bwrote < datasize )
187         {
188             fprintf(stdout,"Wrote %d bytes\n",bwrote);
189         }
190
191         bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from, &fromlen);
192         if (bread == SOCKET_ERROR)
193         {
194             if (WSAGetLastError() == WSAETIMEDOUT)
195             {
196                 printf("timed out\n");
197                 continue;
198             }
199             fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());
200             ExitProcess(STATUS_FAILED);
201         }
202         decode_resp(recvbuf,bread,&from);
203
204         Sleep(1000);
205     }
206
207     WSACleanup();
208     system("pause");
209
210     return 0;
211 }
212
213 /*
214 The response is an IP packet. We must decode the IP header to locate
215 the ICMP data
216 */
217 void decode_resp(char *buf, int bytes,struct sockaddr_in *from)
218 {
219     IpHeader *iphdr;
220     IcmpHeader *icmphdr;
221     unsigned short iphdrlen;
222
223     iphdr = (IpHeader *)buf;      //接收到的数据就是原始的IP数据报
224
225     iphdrlen = iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes
226
227     if (bytes < iphdrlen + ICMP_MIN)
228     {
229         printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
230     }
231
232     icmphdr = (IcmpHeader*)(buf + iphdrlen);
233
234     if(icmphdr->i_type == 3)
235     {
236         printf("network unreachable -- Response from %s.\n",inet_ntoa(from->sin_addr));
237         return ;
238     }
239
240     if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
241     {
242         fprintf(stderr,"someone else‘s packet!\n");
243         return ;
244     }
245     printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
246     printf(" icmp_seq = %d ",icmphdr->i_seq);
247     printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);
248     printf(" ttl: %d",iphdr->ttl);
249     printf("\n");
250 }
251
252 //完成ICMP的校验
253 USHORT checksum(USHORT *buffer, int size)
254 {
255     unsigned long cksum=0;
256
257     while(size >1)
258     {
259         cksum+=*buffer++;
260         size -=sizeof(USHORT);
261     }
262
263     if(size )
264     {
265         cksum += *(UCHAR*)buffer;
266     }
267
268     cksum = (cksum >> 16) + (cksum & 0xffff);
269     cksum += (cksum >>16);
270     return (USHORT)(~cksum);
271 }
272
273 /*
274 Helper function to fill in various stuff in our ICMP request.
275 */
276 void fill_icmp_data(char * icmp_data, int datasize){
277
278     IcmpHeader *icmp_hdr;
279     char *datapart;
280
281     icmp_hdr = (IcmpHeader*)icmp_data;
282
283     icmp_hdr->i_type = ICMP_ECHO;                   //ICMP_ECHO要求收到包的主机回复此ICMP包
284     icmp_hdr->i_code = 0;
285     icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //id填当前进程的id
286     icmp_hdr->i_cksum = 0;
287     icmp_hdr->i_seq = 0;
288
289     datapart = icmp_data + sizeof(IcmpHeader);
290     //
291     // Place some junk in the buffer.
292     //
293     memset(datapart,‘E‘, datasize - sizeof(IcmpHeader));  //填充了一些废物
294 } 

我下到代码的时候,第91行创建原始套接字的地方原本是被屏蔽的第92行,区别在与创建套接字时赋予的标志位不一样。

WSASocket函数的定义如下:

SOCKET WSASocket (
  int af,
  int type,
  int protocol,
  LPWSAPROTOCOL_INFO lpProtocolInfo,
  GROUP g,
  DWORD dwFlags
  );

af:[in]一个地址族规范。目前仅支持AF_INET格式,亦即ARPA Internet地址格式。

type:新套接口的类型描述。

protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。

lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。

g:保留给未来使用的套接口组。套接口组的标识符。

iFlags:套接口属性描述。

具体详细介绍看微软官方介绍文档:https://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx

时间: 2024-11-07 04:26:10

Windows ping源码的相关文章

MFC Windows程序设计源码免费下载

本人最近在网上找到了<MFC Windows程序设计>第二版的书内程序的源码,特意上传CSDN上面,供学习MFC的程序员们免费下载. 源码下载: http://download.csdn.net/detail/bboot/7486011 还有该书的电子版下载地址: 现在不方面上传,晚上回去上传. MFC Windows程序设计源码免费下载

yate: windows下源码下载,配置,编译

源码下载:使用svn下载checkout:http://voip.null.ro/svn/yate/trunk 配置:(本人使用的是vs2008,故下载的qt工具都是对应2008) 1. 下载并安装qt-opensource-windows-x86-vs2008-4.8.7.exe ,下载地址:http://download.qt.io/official_releases/qt,可按照自己的vs版本下载相应的版本 qt-vs-addin-1.1.11-opensource.exe  http:/

windows下源码安装调试postgresql

环境:windows 10 postgresql版本:postgresql-9.6.5 使用工具:vs2017社区版 辅助工具:perl.diff.flex.bison 相关工具下载地址: perl下载链接:http://pan.baidu.com/s/1i5aPilB 密码:k6f0 diff.flex.bison下载链接:http://pan.baidu.com/s/1hrHotes 密码:4ku6 以上工具均为绿色版,解压后,设置环境变量即可 此处使用的编译调试工具为vs2017社区版,该

windows平台源码编译最新版openssl

1.从openssl官网下载最新版openssl      https://www.openssl.org/source/ The latest stable version is the 1.1.0 series of releases 最新版为1.1.0  2016.8.25更新版 2.本机安装VS Studio,我的是vs2008 3.安装ActivePerl(http://www.activestate.com/activeperl/downloads 官网地址),最好安装5.20版,因

教你如何剖析源码

一.源码阅读需求 在学习中,我们会需要了解,学习,使用一个框架,一个新的函数库.在工作中,因为业务需求,因为性能问题,可能通过一个更高性能的工具,架构去优化我们的程序. 那么,问题就来了.网站下载了源码,目录一层一层,头文件无数,打开之后又是一个函数上百行代码,一个类可能有近千行代码.看着都崩溃了,只想关机去静静. 那么,现在.就谈谈如何阅读源码的问题了. 二.源码阅读方法 认知过程: ①了解库/框架的功能-->具体做法:找到相关文档或者书籍,获取库/框架的功能简介.知道他是干什么的.(相关知识

32位win7+vs2008编译mysql 5.6.22源码并安装

以下这部分安装说明是来自http://www.2cto.com/database/201407/316681.html的win7+vs2010源码编译mysql,文章最后会说明用vs2008编译遇见的一些问题以及源码安装mysql5.6.22 最近由于在实习,工作重点在于一些数据库的开发,为了更好的理解数据库的实现机制,目前萌生了要研究一下mysql数据库源码的想法.那么好吧,说干就干,首先我们需要实现对源码的编译,这里我们选择win7+VS2010来实现,但是试了很多次都失败了.这是我多次配置

[安卓]windows下如何安装Android源码

本文改写于:http://www.cnblogs.com/skyme/archive/2011/05/14/2046040.html 1.下载并安装git: 在git-scm.com上下载并安装git,安装后它会自动加载在windows右键菜单中. 2.生成下载命令列表: git下载的格式形如: git clone https://android.googlesource.com/name 把name换成具体的目录即可,android源码的目录极多,手动手写太过繁杂,改写文章提供了一个java程

配置Windows 2008 R2 64位 Odoo 8.0/9.0 源码开发调试环境

安装过程中,需要互联网连接下载python依赖库: 1.安装: Windows Server 2008 R2 x64标准版 2.安装: Python 2.7.10 amd64 到C:\Python27 并将下列路径加到PATH环境变量: C:\Python27\;C:\Python27\Scripts; 3.安装: Oracle jdk 1.7 到C:\Java 并配置 JAVA_HOME 环境变量,如 C:\Java\jdk1.7.0_71 4.安装: PostgreSQL 9.4.4-3 x

C#版Windows服务安装卸载小工具-附源码

前言 在我们的工作中,经常遇到Windows服务的安装和卸载,在之前公司也普写过一个WinForm程序选择安装路径,这次再来个小巧灵活的控制台程序,不用再选择,只需放到需要安装服务的目录中运行就可以实现安装或卸载. 开发思路 1.由于系统的权限限制,在运行程序时需要以管理员身份运行 2.因为需要实现安装和卸载两个功能,在程序运行时提示本次操作是安装还是卸载  需要输入 1 或 2 3.接下来程序会查找当前目录中的可执行文件并过滤程序本身和有时我们复制进来的带有vhost的文件,并列出列表让操作者