ping程序剖析
来源:互联网 发布:网络流行文化的论文 编辑:程序博客网 时间:2024/05/18 17:58
在剖析ping之前我们先补充一点知识。。。
(1)结构体addinfo
头文件:#include<netdb.h>
struct addrinfo
{
int ai_flags;
int ai_family; //AF_INET,AF_INET6,UNIX etc
int ai_socktype; //STREAW,DATAGRAM,RAW
int ai_protocol; //IPPROTO_IP,IPPROTO_IPV4,IPPROTO_IPV6 etc
size_t ai_addrlen; //length of ai_addr
char* ai_canonname; //full hostname
struct sockaddr* ai_addr; //addr of host
struct addrinfo* ai_next;
}
(2)getopt函数(分析命令行参数):
头文件:#include<unistd.h>
函数原型: int getopt (int argc,char* const argv[ ],const char*optstring);
extern char* optarg;
extern int optind,opterr,optopt;
getopt()所设置的全局变量包括:
optarg-----指向当前选项参数(如果有)的指针。optind------再次调用getopt()时的下一个指针的索引。
optopt-----最后一个未知选项。
补充说明:
gethostbyname和gethostbyaddr这两个函数仅仅支持IPv4,getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构的链表而不是一个地址清单。
头文件:#include<netdb.h>------>linux下
#include<ws2tcpip.h>------->windows下
函数原型:
int getaddrinfo(const char *hostname,const char* service,const struct addrinfo *hints,
struct addrinfo**result)
参数说明:
(4)setuid函数:
(5)结构体struct msghdr,用于接收数据包,其定义如下:
struct msghdr{
void *msg_name;//保存数据包的目的地址
int msg_namelen;//地址长度
struct iovec *msg_iov;
__kernal_size_t msg_iovlen;
void *msg_control;
__kernal_size_t msg_controllen;
unsigned msg_flags;
};
该结构体可以分为四组:
第一组是msg_name和msg_namelen,记录这个消息的名字,其实就是数据包的目的地址。msg_name是指向一个结构体struct sockaddr的指针。
第二组msg_iov和msg_iovlen,记录这个消息的内容。msg_iov是一个指向结构体struct iovec的指针,实际 上,确切的说,应该是一个结构体struct iovec的数组。下面是该结构体的定义:
struct iovec{
void__user *iov_base;
__kernal_size_t iov_len;
}
msg.msg_iov = { struct iovec = { iov_base = { icmp头+填充字符'E' }, iov_len = 40 } }
msg.msg_len = 1
第三组是msg_control和msg_controllen,它们可被用于发送任何的控制信息,在我们的例子中,没有控制信息要发送。暂时略过。
第四组是msg_flags。其值即为传入的参数flags。raw协议不支持MSG_OOB向标志,即带外数据
(6)下来我们分别来看ip的报文格式和icmp报文格式:在其中我们可以看到icmp报文是作为ip数据报的数据,所以icmp的报头起始位置等于ip报头加上其报文的长度。。。。
下面给出部分的源码(主要是针对IPv4进行注释):
ping.h:
#include"unp.h"#include<netinet/in_systm.h>#include<netinet/ip.h>#include<netinet/ip_icmp.h>#defineBUFSIZE1500/* globals */char sendbuf[BUFSIZE];int datalen;/* # bytes of data following ICMP header */char*host;int nsent;/* add 1 for each sendto() */pid_t pid;/* our PID */int sockfd;int verbose;/* function prototypes */void init_v6(void);void proc_v4(char *, ssize_t, struct msghdr *, struct timeval *);void proc_v6(char *, ssize_t, struct msghdr *, struct timeval *);void send_v4(void);void send_v6(void);void readloop(void);void sig_alrm(int);void tv_sub(struct timeval *, struct timeval *);struct proto { void (*fproc)(char *, ssize_t, struct msghdr *, struct timeval *); void (*fsend)(void); void (*finit)(void); struct sockaddr *sasend;/* sockaddr{} for send, from getaddrinfo */ struct sockaddr *sarecv;/* sockaddr{} for receiving */ socklen_t salen;/* length of sockaddr{}s */ int icmpproto;/* IPPROTO_xxx value for ICMP */} *pr;#ifdefIPV6#include<netinet/ip6.h>#include<netinet/icmp6.h>#endif
main.c
#include"ping.h"struct protoproto_v4 = { proc_v4, send_v4, NULL, NULL, NULL, 0, IPPROTO_ICMP };intdatalen = 56;/* data that goes with ICMP echo request */int main(int argc, char **argv){intc;struct addrinfo*ai;char *h;opterr = 0;/* don't want getopt() writing to stderr */ //处理命令行参数 //例如:./ping 127.0.0.1while ( (c = getopt(argc, argv, "v")) != -1) {switch (c) {case 'v':verbose++;break;case '?':err_quit("unrecognized option: %c", c);}}if (optind != argc-1)err_quit("usage: ping [ -v ] <hostname>"); //host保存ip地址host = argv[optind]; //获取当前的进程号pid = getpid() & 0xffff;/* ICMP ID field is 16 bits */ //发送信号函数Signal(SIGALRM, sig_alrm); //在命令行参数中必须有一个主机名或ip地址,调用Host_serv函数进行处理 //返回addrinfo结构ai = Host_serv(host, NULL, 0, 0);h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);printf("PING %s (%s): %d data bytes\n",ai->ai_canonname ? ai->ai_canonname : h,h, datalen);/* 4initialize according to protocol */ //初始化协议结构体prif (ai->ai_family == AF_INET) {pr = &proto_v4;} elseerr_quit("unknown address family %d", ai->ai_family);pr->sasend = ai->ai_addr;pr->sarecv = Calloc(1, ai->ai_addrlen);pr->salen = ai->ai_addrlen; //调用函数readloop();exit(0);}
Host_serv.c:
/* include host_serv */#include"unp.h"struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype){intn;struct addrinfohints, *res; //清零bzero(&hints, sizeof(struct addrinfo)); //用于返回主机的规范名称hints.ai_flags = AI_CANONNAME;/* always return canonical name */ //其值为0代表:协议无关hints.ai_family = family;/* AF_UNSPEC, AF_INET, AF_INET6, etc. */hints.ai_socktype = socktype;/* 0, SOCK_STREAM, SOCK_DGRAM, etc. */if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)return(NULL);return(res);/* return pointer to first on linked list */}/* end host_serv *//* * There is no easy way to pass back the integer return code from * getaddrinfo() in the function above, short of adding another argument * that is a pointer, so the easiest way to provide the wrapper function * is just to duplicate the simple function as we do here. */struct addrinfo *Host_serv(const char *host, const char *serv, int family, int socktype){intn;struct addrinfohints, *res;bzero(&hints, sizeof(struct addrinfo));hints.ai_flags = AI_CANONNAME;/* always return canonical name */hints.ai_family = family;/* 0, AF_INET, AF_INET6, etc. */hints.ai_socktype = socktype;/* 0, SOCK_STREAM, SOCK_DGRAM, etc. */if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)err_quit("host_serv error for %s, %s: %s", (host == NULL) ? "(no hostname)" : host, (serv == NULL) ? "(no service name)" : serv, gai_strerror(n));return(res);/* return pointer to first on linked list */}
sock_ntop.c:
#include"unp.h"#ifdefHAVE_SOCKADDR_DL_STRUCT#include<net/if_dl.h>#endif/* include sock_ntop */char *sock_ntop(const struct sockaddr *sa, socklen_t salen){ charportstr[8]; static char str[128];/* Unix domain is largest */switch (sa->sa_family) { //当是IPv4协议时case AF_INET: {struct sockaddr_in*sin = (struct sockaddr_in *) sa; //点分十进制与二进制的转化if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)return(NULL); //将端口的网络字节序转换为主机字节序if (ntohs(sin->sin_port) != 0) {snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));strcat(str, portstr);}return(str);}/* end sock_ntop */#ifdefIPV6case AF_INET6: {struct sockaddr_in6*sin6 = (struct sockaddr_in6 *) sa;str[0] = '[';if (inet_ntop(AF_INET6, &sin6->sin6_addr, str + 1, sizeof(str) - 1) == NULL)return(NULL);if (ntohs(sin6->sin6_port) != 0) {snprintf(portstr, sizeof(portstr), "]:%d", ntohs(sin6->sin6_port));strcat(str, portstr);return(str);}return (str + 1);}#endif#ifdefAF_UNIXcase AF_UNIX: {struct sockaddr_un*unp = (struct sockaddr_un *) sa;/* OK to have no pathname bound to the socket: happens on every connect() unless client calls bind() first. */if (unp->sun_path[0] == 0)strcpy(str, "(no pathname bound)");elsesnprintf(str, sizeof(str), "%s", unp->sun_path);return(str);}#endif#ifdefHAVE_SOCKADDR_DL_STRUCTcase AF_LINK: {struct sockaddr_dl*sdl = (struct sockaddr_dl *) sa;if (sdl->sdl_nlen > 0)snprintf(str, sizeof(str), "%*s (index %d)", sdl->sdl_nlen, &sdl->sdl_data[0], sdl->sdl_index);elsesnprintf(str, sizeof(str), "AF_LINK, index=%d", sdl->sdl_index);return(str);}#endifdefault:snprintf(str, sizeof(str), "sock_ntop: unknown AF_xxx: %d, len %d", sa->sa_family, salen);return(str);} return (NULL);}char *Sock_ntop(const struct sockaddr *sa, socklen_t salen){char*ptr;if ( (ptr = sock_ntop(sa, salen)) == NULL)err_sys("sock_ntop error");/* inet_ntop() sets errno */return(ptr);}
proc_v4.c:
#include"ping.h"voidproc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv){inthlen1, icmplen;doublertt;struct ip*ip;struct icmp*icmp;struct timeval*tvsend;ip = (struct ip *) ptr;/* start of IP header */ //获取ip首部长度hlen1 = ip->ip_hl << 2;/* length of IP header */ //如果不是ICMP协议,直接返回if (ip->ip_p != IPPROTO_ICMP)return;/* not ICMP */ //icmp的头部等于ip+ip长度icmp = (struct icmp *) (ptr + hlen1);/* start of ICMP header */if ( (icmplen = len - hlen1) < 8)return;/* malformed packet */ //设置类型为回显if (icmp->icmp_type == ICMP_ECHOREPLY) {if (icmp->icmp_id != pid)return;/* not a response to our ECHO_REQUEST */if (icmplen < 16)return;/* not enough data to use */ //计算往返时间tvsend = (struct timeval *) icmp->icmp_data;tv_sub(tvrecv, tvsend);rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",icmplen, Sock_ntop_host(pr->sarecv, pr->salen),icmp->icmp_seq, ip->ip_ttl, rtt);} else if (verbose) {printf(" %d bytes from %s: type = %d, code = %d\n",icmplen, Sock_ntop_host(pr->sarecv, pr->salen),icmp->icmp_type, icmp->icmp_code);}}
sig_alarm.c
#include"ping.h"voidsig_alrm(int signo){(*pr->fsend)();alarm(1);return;}
send_v4.c:
#include"ping.h"voidsend_v4(void){intlen;struct icmp*icmp;icmp = (struct icmp *) sendbuf;icmp->icmp_type = ICMP_ECHO;icmp->icmp_code = 0;icmp->icmp_id = pid;icmp->icmp_seq = nsent++;memset(icmp->icmp_data, 0xa5, datalen);/* fill with pattern */Gettimeofday((struct timeval *) icmp->icmp_data, NULL);len = 8 + datalen;/* checksum ICMP header and data */icmp->icmp_cksum = 0;icmp->icmp_cksum = in_cksum((u_short *) icmp, len);Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);}
程序的执行结果:
分别使用自己的ping命令和系统的ping命令。。。该ping程序目前没有系统ping的功能那么完整,至少能实现基本的检测。。。。。
上图就是我们整个程序的大致流程。。。。
- ping程序剖析
- 唯快不破:ICMP报文剖析-自己实现ping程序
- ping程序
- ping程序
- ping程序
- ping程序
- Ping程序
- Ping程序
- ping程序
- Ping程序
- ping小程序
- 微软ping程序源代码
- Ping程序的源代码
- Ping程序的源代码
- ping程序实现
- “ping”程序知多少
- 简易ping程序
- Ping程序C++实现
- abstract抽象类
- caffe 实例笔记 2 LeNet详细解读及实现
- VRP系统——U盘开局与Auto-Config
- eclipse配置weblogic时遇到的问题
- [jvm解析系列][六]class里的常量池,访问标志,类的继承关系,如何把一个类在字节码中描述清楚?
- ping程序剖析
- 【MyBatis学习04】mapper代理方法开发dao
- DBMS_STATS.GATHER_TABLE_STATS详解
- 模式汇总
- js data日期初始化的5种方法
- 算法-马氏距离
- 多态--总结及举例
- 超神之路------我独行
- sap 公司代码和工厂代码的关系