基于linux下实现的ping程序(C语言)

linux下实现的ping程序

一、设计目的

PING程序是我们使用的比较多的用于测试网络连通性的程序。PING程序基于ICMP,使用ICMP的回送请求和回送应答来工作。由计算机网络课程知道,ICMP是基于IP的一个协议,ICMP包通过IP的封装之后传递。

课程设计中选取PING程序的设计,其目的是通过PING程序的设计,能初步掌握TCP/IP网络协议的基本实现方法,对网络的实现机制有进一步的认识。

熟悉SOCKET的编程,包括基本的系统调用如SOCKET、BIND等。

二、设计内容

2.1 RAW模式的SOCKET编程

PING程序是面向用户的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。为了实现直接对IP和ICMP包进行操作,实验中使用RAW模式的SOCKET编程。

2.2 具体内容

2.2.1 定义数据结构

定义IP数据报、ICMP包等相关的数据结构。

ICMP数据头结构

typedef struct Icmp
{unsigned char type;       		//类型unsigned char code;				//代码unsigned short check_sum;		//检验和unsigned short id;				//标识符unsigned short seq;				//序列号
}IcmpHeader;

IP数据包头结构

typedef struct iphdr   
{  unsigned int headLen:4;   		//首部长度unsigned int version:4;  		//版本unsigned char tos;  			//区分服务unsigned short totalLen;  		//总长度unsigned short ident;  			//标识unsigned short fragAndFlags;  	//标志与片偏移unsigned char ttl;  			//生存时间unsigned char proto;  			//协议unsigned short checkSum;  		//检验和unsigned int sourceIP;  		//源地址unsigned int destIP;  			//目的地址
}IpHeader;

2.2.2 程序实现

在LINUX环境下实现PING程序

2.2.3 程序功能

  • ping ip 地址
    如ping 192.168.1.1
  • ping 域名(进行DNS解析)
    如ping www.baidu.com
  • 参数“ -n 数字”进行设置ping 的次数
    如ping www.baidu.com –n 10
  • 参数 -t 无限循环
    如ping 192.168.1.140 -t
  • 分析ping到的数据报
    如最短时间,最长时间,平均时间和丢包率
  • ping ?
    提供帮助提示

三、实验平台与语言

  • 平台:linux
  • 语言:C语言

四、功能模块实现

4.1 总体设计方案

流程图

主要代码

// ICMP数据头结构  
typedef struct Icmp
{unsigned char type;       	//类型unsigned char code;			//代码unsigned short check_sum;	//检验和unsigned short id;			//标识符unsigned short seq;			//序列号
}IcmpHeader;
//执行ping功能
int ping(const char *ip,  int send_count)
{int rawfd;struct sockaddr_in dest_adr;char icmp_data[1024];int size = sizeof(IcmpHeader)+32;int r, i = 0, send, recv=0, lost=0;char recv_buf[1024];int all_time[1024] = {0};//创建原始套接字rawfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);if(rawfd == -1)	{perror("create socket failed!");return -1;}//设置目的地址与端口dest_adr.sin_family = AF_INET;dest_adr.sin_port = htons(80);inet_aton(ip, &dest_adr.sin_addr);//封装icmp数据包pack_icmp(icmp_data, size);printf("\n正 Ping %s 具有%d个字节的数据:\n", ip, size-sizeof(IcmpHeader));if(send_count == LOOP){//无限循环while(1){if(ping_one(rawfd,dest_adr, ip, icmp_data , all_time) != -1){recv++;}send++;}}else{for(i = 0; i<send_count; i++) {if(ping_one(rawfd,dest_adr, ip, icmp_data , all_time) != -1){recv++;	}send++;}}printf("\n%s 的 Ping 统计信息:\n   数据包:已发送 = %d,已接收 = %d, 丢失 
= %d<%.1f%% 丢失>, \n",ip, send, recv, lost, ((float)lost/(float)send)*100);printf("往返行程的估计时间:\n 最短=%dms 最长=%dms 平均 = %dms \n\n",min(all_time, send), max(all_time, send), average(all_time, send));close(rawfd);return 0;
}

4.2 DNS域名解析功能实现

4.2.1 DNS 服务器

  • 地址:202.96.134.133
  • 端口:53
# define DNS_PORT 53
# define DNS_IP	"202.96.134.133"
# define DNS_IP2	"8.8.8.8"

4.2.2 DNS 的实现基础:

通过UDP发送查询报文给DNS 服务器,然后从服务通过UDP 返回的回应报文中解析得到对应域名的IP。

4.2.3 DNS 报文格式

4.2.4 首部格式

其中标志字段:16位

定义首部结构体

typedef struct DNSheader
{unsigned short id;unsigned char  qr_opcode_aa_tc_rd;unsigned char  ra_zero_rcode;unsigned short qdcount;unsigned short ancount;unsigned short nscount;unsigned short arcount;
}DnsHeader;

4.2.5 问题记录格式

  • 查询名字:域名的可变长字段;其中计数字段指明每一节中的字符数
  • 查询类型:16位;值的意义如下表,查询ip 时为1
  • 查询类别:16位;定义使用DNS的特定协议, 一般为1

查询名字打包代码:

//把域名打包成dns数据报的数据部分 如(3www5baidu3com)
//计算每段的数量
for(i = 0; i < name_len; i++)
{if(netname[i] == '.'){len[flg++] = i - s;s += len[flg-1]+1;}
}
len[flg] = i - s;
i = 0;
flg = 0;
data[i++] = len[flg++];
//加入每段字节的数量
for(; i < name_len+1; i++)
{if(netname[i-1] == '.'){data[i] = len[flg++];}else{data[i] = netname[i-1];}
}

4.2.6 资源记录格式

资源数据:可变长;值内容取决于类型字段的值,可以是数值、域名、偏移指针、字符串。

4.2.7 在资源数据中提取ip和原域名

失败的解决方法

由于没有查到详细的解析资料,因此通过对整个报文每个字节进行分析,发现其格式的规律:ip地址放在报文的末尾,可以通过指针快速定位。

采样分析的域名有(www.baidu.com 和 www.sina.com),下图是对 www.baidu.com 的分析。

代码如下:

//直接定位ip地址
Ipadr *ip =(Ipadr *) (sen_buf+(r-8-4-8-2));
//如果ip地址长度不为4, 则返回
if(ip->len != 0x0400)
{return -1;
}
//次ip地址转为字符串
sprintf(get_ip, "%u.%u.%u.%u", ip->a, ip->b, ip->c, ip->d);

失败原因:过于投机取巧,取得两个分析对象不够特殊,对DNS回答格式完全不解

新的解决方法

通过用wireshark抓取DNS包进行分析,由于分析次数过多,此处以 www.baidu.com 为例。

由抓取的dns包分析可得到回答部分的格式如下:

answer1:
name : 不定长,c0  0c指段偏移地址
type:  16 位 0005 是别名answer
class:  16位, 0001
ttl:32位
data length: 16位
cname: 别名,长度data lengthanswer 2
name:c0 2b 指向别名
type:  16位 0001 是ip answer
class : 16位 0001
ttl: 32位
data length:16位
address: 4个字节
answer 3
与回应2相似 ip 不一样

进一步分析

可以发现回答部分有两个类型(只是本设计的情况,DNS有很多种类型),type 字段为5时为别名回应,为1 时为ip回应,因此通过type和上面得到的格式来进行ip和别名的获取。

其中还发现,DNS为了减小数据报文的长度,回应部份重复的字段会省略,并通过偏移指针指向重复部分,由上面的分析可知是用c0(代替字段长度)来转义下一字节为偏移指针。

获取代码设计

int parse_dns_respone(unsigned char *recv_buf,unsigned char **answer_o, int data_len ,char 
*get_ip, const char *netname)
{int asw_type;int i,j, k;static int fn = 0, fi = 0;int r;unsigned char cname[40];unsigned char *answer = *answer_o;answer +=2;//查询域名asw_type = ntohs(*((unsigned short*)answer));answer +=2;//typeanswer +=2;//classanswer +=4;//ttlif(asw_type == 5) 			//域名包{bzero(cname, sizeof(cname));//解析域名parse_dns_name(recv_buf, &answer, cname);fn = 1; 							//标记已取得别名}else if(asw_type == 1)		//ip 回应包{//解析IPparse_dns_ip(&answer ,get_ip);if(fn == 1)printf("\n----  %s(%s)",cname , get_ip);elseprintf("\n----  %s(%s)",netname , get_ip);fi = 1;								//标记已取得IP}*answer_o = answer;if(fn != 0 && fi != 0) return 2;			//已取得别名和域名。则结果else if(fi != 0) return 1;return 0;
}//解析IP
void parse_dns_ip(unsigned char **answer_o ,char *get_ip)
{ unsigned char *answer = *answer_o;Ipadr *ip =(Ipadr *)answer;			//取提ip	sprintf(get_ip, "%u.%u.%u.%u", ip->a, ip->b, ip->c, ip->d);*answer_o = answer;}
//解析域名
void parse_dns_name(unsigned char *recv_buf, unsigned char **answer_o, unsigned char *cname)
{ int tmp_len;int d_length;int i, k;unsigned char *answer = *answer_o;d_length = ntohs(*((unsigned short*)answer));//总长度answer +=2;							//data lengthi=0;for(k=0; k<d_length; k++){tmp_len = *answer++;			//名字段长度if(i != 0)cname[i++]='.';if(tmp_len == 0xc0)				//CO转义为复字段{int tmp  =  *answer++;		//获得偏移指针k ++;i--;while(1){tmp_len = recv_buf[tmp++];//跳转到偏移位置if(tmp_len == 0) break;	  //偏移位置结果cname[i++]='.';//填充域名get_seg_name(&recv_buf[tmp], cname+i, tmp_len);tmp += tmp_len;i += tmp_len;}continue;}	if(tmp_len == 0) break;//填充域名get_seg_name(answer, cname+i, tmp_len);k += tmp_len;i += tmp_len;//移动指针answer += tmp_len;}cname[i] = '\0';*answer_o = answer;
}

4.2.8 遇到的问题

有多段连续的域名字段重复

原来只考虑到有一个域名字段是重复的,但是有些是有多段连续的域名字段重复的,解决的方法是(直到len字节为0 才认为没有重复字段了)。

while(1)
{tmp_len = recv_buf[tmp++];//跳转到偏移位置if(tmp_len == 0) break;	  //偏移位置结果cname[i++]='.';//填充域名get_seg_name(&recv_buf[tmp], cname+i, tmp_len);tmp += tmp_len;i += tmp_len;
}

有多个域名回答

原来只考虑到只有一个域名名回答,但有些(如 www.sina.com )是有多个域名的,因此解决方法是域名回答可以多次解析,只有取得IP地址才结束,而不是原来只解析两个回答就认为拿到了IP地址。

4.2.9 本实现的不足

  • 没有完全掌握DNS 回应报文的格文
  • 服务器单一,没有备用服务器
  • 还存在未知域名不能解析的,未能确保能解析所有正确域名

五、结果分析

Ping  ip 地址 如 ping 192.168.1.1

Ping 域名 如:ping www.sougou.com

Ping –n 如:ping 14.215.177.37 –n 2

Ping –t 如: ping www.baidu.com -t

Ping ?

六、心得体会

做的永远比想象中的难,修改了多次代码,刚开始想只要IP 地址,要愿意深入分析回应数据报,只是进过一些特例来定位ip 地址就好,想不到两个特例效果是一样的(www.baidu.com 和 www.sina.com ), ip 地址都是在末尾前几个字节,但是,由于DNS 的去掉重复的功能,造成只有是只有2个IP 地址的域名才能有效,多于或少于两个的都不行,因此又得花时间重新进行分析,然后才想到利用抓包软件来协助分析,又花了不少心血修正这个BUG,程序员真不好做,坐到腰酸背痛。

但是,还情事还没那么顺利,拿多几个域名来试之后又发现了问题,具有多个别名的域名没办法正确解析,在原来的基础上又很难修改,因此决定把代码封装起来,封装多几个函数,然后在封装好的基础上解决多个别名的问题,但是在调试中又出现多个连绵的域名字段重复省略导致解析也来的域名完全的问题,又进行了一番修改。真不容易。

总结

  • DNS的格式还需要找相关资料来学习
  • 以后写代码如果不是用于学习的,则应该找完成的框架进行修改,这样可以省时间,且写出来的程序也会比较稳定
  • 写程序前最好先做出详细方案,避免一些BUG
  • 网络知识的学习还有很多要学,网络编程要学的知识更加多

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年12月16日Github流行趋势

项目名称&#xff1a;PDFMathTranslate 项目维护者&#xff1a;Byaidu reycn hellofinch Wybxc YadominJinta项目介绍&#xff1a;基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&#xff0c;提供 CLI/GUI/Docker。项目star数…

国标GB28181协议平台Liveweb:搭建建筑工地无线视频联网监控系统方案

随着科技高速发展&#xff0c;视频信号经过数字压缩&#xff0c;通过互联网宽带或者移动4G网络传递&#xff0c;可实现远程视频监控功能。将这一功能运用于施工现场安全管理&#xff0c;势必会大大提高管理效率&#xff0c;提升监管层次。而这些&#xff0c;通过Liveweb监控系统…

Ansible playbook 详解与实战操作

一、概述 playbook 与 ad-hoc 相比,是一种完全不同的运用 ansible 的方式&#xff0c;类似与 saltstack 的 state 状态文件。ad-hoc 无法持久使用&#xff0c;playbook 可以持久使用。 playbook 是由一个或多个 play 组成的列表&#xff0c;play 的主要功能在于将事先归并为一…

电脑玩《刺客信条》时中,遇到找不到d3dx9_42.dll的问题是什么原因?缺失d3dx9_42.dll应该怎么解决呢?下面一起来看看吧!

电脑玩《刺客信条》时&#xff0c;找不到d3dx9_42.dll的原因及解决办法 对于许多热爱《刺客信条》这款游戏的玩家来说&#xff0c;在游戏中遇到找不到d3dx9_42.dll的问题无疑是非常令人头疼的。这一错误不仅会导致游戏无法启动&#xff0c;还可能引发运行过程中的图形错误、卡…

Apache Solr RCE(CVE-2017-12629)--vulhub

Apache Solr 远程命令执行漏洞&#xff08;CVE-2017-12629&#xff09; Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发&#xff0c;主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个…

Mysql索引类型总结

按照数据结构维度划分&#xff1a; BTree 索引&#xff1a;MySQL 里默认和最常用的索引类型。只有叶子节点存储 value&#xff0c;非叶子节点只有指针和 key。存储引擎 MyISAM 和 InnoDB 实现 BTree 索引都是使用 BTree&#xff0c;但二者实现方式不一样&#xff08;前面已经介…

中间件 redis安装

redis官网地址&#xff1a;Redis - The Real-time Data Platform 环境 CentOS Linux release 7.9.2009 (Core) java version "17.0.12" 2024-07-16 LTS 1、通过压缩包安装redis 1&#xff0c;远程下载redis压缩包&#xff0c;或去官网下载&#xff1a;Downloads …

台球助教平台系统开发APP和小程序信息收藏功能需求解析(第十二章)

以下是开发台球助教系统客户端&#xff08;APP&#xff0c;小程序&#xff0c;H5&#xff09;几端的信息收藏功能的详细需求和功能说明&#xff0c;内容比较详细&#xff0c;可以说是一个教科书式的详细说明了&#xff0c;这套需求说明不仅仅用在我们的台球助教系统程序上&…

freertos入门---堆的概念

freertos入门—堆的概念 堆就是一块空闲的内存。下面举个例子更好的理解堆的概念&#xff1a;   堆是一块空闲的内存&#xff0c;我们可以定义一个数组char heap_buf[1024]&#xff0c;可以看到该数组就是一个空闲的内存&#xff0c;我们只需要在它上面实现内存的分配和释放那…

操作系统(17)文件和文件系统

一、文件 定义&#xff1a;文件是数据的有序集合&#xff0c;是用户存储信息于辅存的基本逻辑单位。文件可以是字符流构成的无结构文件&#xff0c;也可以是包含相似记录的结构化文件。 类型&#xff1a; 按性质和用途&#xff1a;系统文件&#xff08;由系统软件构成的文件&a…

ASP.NET|日常开发中读写TXT文本详解

ASP.NET&#xff5c;日常开发中读写TXT文本详解 前言一、读取 TXT 文本1.1 使用StreamReader类 二、写入 TXT 文本2.1 使用StreamWriter类 三、文件编码问题3.1 常见编码格式 四、错误处理和性能考虑4.1 错误处理4.2 性能考虑 结束语优质源码分享 ASP.NET&#xff5c;日常开发中…

notepad++快捷键-多行编辑中如何使所有行的光标都向后移动一个单词的长度(每行单词长度不一定一致)

问题&#xff1a;在使用notepad进行多行编辑&#xff08;多行光标移动一个单词长度&#xff09;时&#xff08;将下图由左边变为右边&#xff09;&#xff0c;在使用Ctrl左键拖拽选中多行后&#xff0c;想要将每行的光标向后移动一个单词的长度&#xff08;每行的单词长度不一样…

【IC】Hybrid Bonding技术

从纳米到埃米&#xff0c;芯片制造商正在竭尽全力缩小电路的尺寸。但面对算力需求的激增&#xff0c;一项涉及更大尺寸&#xff08;数百或数千纳米&#xff09;的技术——混合键合&#xff08;Hybrid Bonding&#xff09;——将在未来五年内扮演重要角色。近日&#xff0c;IEEE…

洛谷 B3643 图的存储 C语言

题目&#xff1a;https://www.luogu.com.cn/problem/B3643 题目描述 给定一个 n 个顶点 m 条边的无向图。请以邻接矩阵和邻接表的形式输出这一张图。 输入格式 第一行输入两个正整数 n 和 m&#xff0c;表示图的顶点数和边数。 第二行开始&#xff0c;往后 m 行&#xff0…

MATLAB里面,try-catch-end系列语言的含义与用法(含例程)

在 MATLAB 中&#xff0c;try-catch-end 语句用于处理可能会引发错误的代码。它允许你在“尝试”部分执行代码&#xff0c;如果代码执行过程中发生错误&#xff0c;将转到“捕获”部分执行相应的处理。这种错误处理机制可以提高程序的健壮性&#xff0c;避免因小错误导致整个程…

Python练习之列表的使用

&#xff08;搭配主页知识点&#xff09; 【练习要求】 针对知识点列表定义、追加、列表元素读取、查找安排的本实例。要求实现&#xff1a;有一个列表&#xff0c;内容是:[21,25,21,23,22,20]&#xff0c;记录的是一批学生的年龄请通过列表的功能(方法)&#xff0c;对其进行…

安装虚拟机(VMware)教程+win7

VMware 一.下载VMware Wworkstation Pro二、安装VMware三、安装虚拟机 一.下载VMware Wworkstation Pro 1.去vmware官网下载 官网 2.网盘下载 通过网盘分享的文件&#xff1a;vmware 链接: https://pan.baidu.com/s/1bOff79NFAmDlISQo6LK6PQ?pwdhunr 提取码: hunr --来自百…

C语言总共n位数,将后面的K个数与前面的数对调位置,前后二部分的数字顺序不变

例如&#xff1a;n5&#xff0c;k2&#xff0c;要处理的数字是12345&#xff0c;则处理后变成45123 这个问题可以通过以下步骤解决&#xff1a; 确定前后两部分的分界点。 对前后两部分分别进行反转。 以下是一个简单的C语言示例代码&#xff1a; #include<stdio.h>…

C# Winform双色纸牌接龙小游戏源码

文章目录 一、设计来源双色纸牌接龙小游戏讲解1.1 主界面1.2 游戏界面1.3 游戏界面快成功了 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/144419994 …

无人机航测系统技术特点!

一、无人机航测系统的设计逻辑 无人机航测系统的设计逻辑主要围绕实现高效、准确、安全的航空摄影测量展开。其设计目标是通过无人机搭载相机和传感器&#xff0c;利用先进的飞行控制系统和数据处理技术&#xff0c;实现对地表信息的全方位、高精度获取。 需求分析&#xff1…