📄 ping.c
字号:
#include <stdio.h>#include <signal.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <netinet/in.h>#include <netinet/ip.h>#include <netinet/ip_icmp.h>#include <netdb.h>#include <setjmp.h>#include <errno.h>#define PACKET_SIZE 4096#define MAX_WAIT_TIME 5#define MAX_NO_PACKETS 2 //发包的个数#define ICMP_ECHO 8#define ICMP_ECHOREPLY 0char sendpacket[PACKET_SIZE];char recvpacket[PACKET_SIZE];int sockfd,datalen = 56;int nsend = 0,nreceived = 0;struct sockaddr_in dest_addr;pid_t pid;struct sockaddr_in from;struct timeval tvrecv;void statistics(int signo);unsigned short cal_chksum(unsigned short *addr,int len);int pack(int pack_no);void send_packet(void);void recv_packet(void);int unpack(char *buf,int len);void statistics(int signo){ if (MAX_NO_PACKETS != nreceived) printf ("not found\n"); else printf ("found\n"); close(sockfd); exit(1);}unsigned short cal_chksum(unsigned short *addr,int len) //校验和算法{ int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while(nleft>1) //把ICMP报头二进制数据以2字节为单位累加起来 { sum += *w++; nleft-=2; } //若ICMP报头为奇数个字节,会剩下最后一字节。 if( nleft == 1) //把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加 { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum>>16)+(sum&0xffff); sum += (sum>>16); answer = ~sum; return answer;}int pack(int pack_no) //设置ICMP报头{ int i,packsize; struct icmp *icmp; struct timeval *tval; icmp=(struct icmp*)sendpacket; icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_cksum = 0; icmp->icmp_seq=pack_no; icmp->icmp_id = pid; packsize=8+datalen; tval= (struct timeval *)icmp->icmp_data; gettimeofday(tval,NULL); //记录发送时间 icmp->icmp_cksum=cal_chksum( (unsigned short *)icmp,packsize); //校验算法 return packsize;}void send_packet() //发送三个ICMP报文{ int packetsize; while( nsend < MAX_NO_PACKETS) { nsend++; packetsize = pack(nsend); //设置ICMP报头 if( sendto(sockfd,sendpacket,packetsize,0, (struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0 ) { perror("sendto error"); continue; } sleep(1); //每隔一秒发送一个ICMP报文 }}void recv_packet() //接收所有ICMP报文{ int n,fromlen; extern int errno; signal(SIGALRM,statistics); fromlen = sizeof(from); while( nreceived < nsend) { alarm(MAX_WAIT_TIME); if( (n = recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen)) < 0 ) { if(errno == EINTR) continue; } gettimeofday(&tvrecv,NULL); //记录接收时间 if(unpack(recvpacket,n) == -1) { continue; } nreceived++; }}int unpack(char *buf,int len) //剥去ICMP报头{ int i,iphdrlen; struct ip *ip; struct icmp *icmp; struct timeval *tvsend; ip = (struct ip *)buf; iphdrlen = ip->ip_hl<<2; //求ip报头长度,即ip报头的长度标志乘4 icmp = (struct icmp *)(buf + iphdrlen); //越过ip报头,指向ICMP报头 len -= iphdrlen; //ICMP报头及ICMP数据报的总长度 if(len < 8) //小于ICMP报头长度则不合理 return -1; //确保所接收的是我所发的的ICMP的回应 if( (icmp->icmp_type != ICMP_ECHOREPLY) || (icmp->icmp_id != pid) ) return -1;}int main(int argc,char *argv[]){ struct hostent *host; struct protoent *protocol; unsigned long inaddr = 0l; int waittime = MAX_WAIT_TIME; int size = 50*1024; if(argc < 2) { printf("usage:%s hostname/IP address\n",argv[0]); exit(1); } if( (protocol = getprotobyname("icmp") ) == NULL) { perror("getprotobyname"); exit(1); } if( (sockfd = socket(AF_INET,SOCK_RAW,protocol->p_proto)) < 0 ) //生成使用ICMP的原始套接字,这种套接字只有root才能生成 { perror("socket error"); exit(1); } setuid(getuid()); //回收root权限,设置当前用户权限 setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size) ); bzero(&dest_addr,sizeof(dest_addr)); dest_addr.sin_family = AF_INET; if((inaddr = inet_addr(argv[1])) == INADDR_NONE) //判断是主机名还是ip地址 { if((host = gethostbyname(argv[1]) ) == NULL) //是主机名 { printf("gethostbyname error\n"); exit(1); } memcpy( (char *)&dest_addr.sin_addr,host->h_addr,host->h_length); } else //是ip地址 dest_addr.sin_addr.s_addr = inaddr; pid = getpid(); //获取main的进程id,用于设置ICMP的标志符 send_packet(); //发送所有ICMP报文 recv_packet(); //接收所有ICMP报文 statistics(SIGALRM); //进行统计 return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -