免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1772 | 回复: 0
打印 上一主题 下一主题

[C] 重写了unp 28章的ping, 通俗易懂, 新手,勿喷 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-03-29 15:52 |只看该作者 |倒序浏览
本帖最后由 madfrogme 于 2013-03-29 16:05 编辑

一直在网上找ping的源代码,很不通俗易懂, 后来发现unp上28章就是,可是调试那个程序时候也是各种苦逼


于是就把书里的代码重新通俗的写了一遍,错误处理全部是用perror,和fprintf


所有代码集中到了一个文件里,也只有200行,初学,还望包涵,自己也学到了很多东西,


代码在github里, 可以加强的地方应该有很多,它只能ping一个主机,接下来的目标是fping,可以


同时ping多个主机, 不过fping里对时间的处理那一块好复杂,看晕了

编译之后要
$ sudo chown root a.out
$ sudo chmod u+s a.out
就可以创建raw socket了,而不要root权限了



git@github.com:ColdFreak/Ping.git
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <error.h>
  4. #include <errno.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netdb.h>
  8. #include <signal.h>
  9. #include <strings.h> /* bzero() */
  10. #include <string.h>
  11. #include <arpa/inet.h> /* inet_ntop()*/
  12. #include <netinet/ip_icmp.h> /* struct icmp */
  13. #include <sys/time.h>
  14. #include <netinet/in.h> /* struct sockaddr_in */

  15. #define BUFSIZE        1500

  16. char *host;
  17. int sockfd;
  18. pid_t pid;
  19. int datalen = 56;
  20. char sendbuf[BUFSIZE];
  21. char h[128];
  22. int nsent = 0;

  23. struct addrinfo hints;
  24. struct addrinfo *res;
  25. struct sockaddr *sasend;
  26. struct sockaddr *sarecv;

  27. uint16_t in_cksum(uint16_t *addr, int len);
  28. void sig_alrm(int signo);
  29. void send_v4(void);
  30. void readloop(void);
  31. void tv_sub(struct timeval *out, struct timeval *in);
  32. void proc_v4 (char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv);

  33. int main(int argc, char **argv) {
  34.         struct icmp *icmp;
  35.         struct sockaddr_in *sin;
  36.         socklen_t salen;
  37.         int n; /* getaddrinfo return value */
  38.         char *buffer;

  39.         if(argc != 2) {
  40.                 perror("usage: ./ping <hostname>");
  41.                 exit (1);
  42.         }

  43.         host = argv[1];
  44.         pid = getpid() & 0xffff;
  45.         signal(SIGALRM, sig_alrm);

  46.         bzero(&hints, sizeof(struct addrinfo));
  47.         hints.ai_flags = AI_CANONNAME;
  48.         if( ( n = getaddrinfo(host, NULL, &hints, &res)) != 0) {
  49.                 fprintf(stderr, "getaddrinfo: %s\n",gai_strerror(n));
  50.                 exit (1);
  51.         }
  52.        
  53.         sin = (struct sockaddr_in *)(res->ai_addr);       
  54.         switch (sin->sin_family) {
  55.                 case AF_INET:
  56.                         if(inet_ntop(AF_INET,&(sin->sin_addr), h, 128) == NULL) {
  57.                                 perror("inet_ntop");
  58.                                 exit (1);
  59.                         }
  60.                         break;
  61.                
  62.                 default:
  63.                         perror("Not IPv4 address");
  64.                         exit (1);
  65.         }
  66.         printf("PING %s (%s): %d data bytes\n",res->ai_canonname ? res->ai_canonname:h, h, datalen);
  67.         readloop();
  68.         return 0;
  69. }

  70. uint16_t in_cksum(uint16_t *addr, int len) {
  71.         int nleft = len;
  72.         uint32_t sum = 0;
  73.         uint16_t *w = addr;
  74.         uint16_t answer = 0;

  75.         while(nleft > 1) {
  76.                 sum += *w++;
  77.                 nleft -= 2;
  78.         }
  79.         if (nleft == 1) {
  80.                 *(unsigned char *)(&answer) = *(unsigned char *)w;
  81.                 sum += answer;
  82.         }
  83.         sum = (sum >> 16) + (sum & 0xffff);
  84.         sum += (sum >> 16);
  85.         answer = ~sum;
  86.         return (answer);
  87. }
  88. void send_v4(void) {
  89.         struct icmp *icmp;
  90.         int len;
  91.         socklen_t salen;
  92.         icmp = (struct icmp *)sendbuf;
  93.         icmp->icmp_type = ICMP_ECHO;
  94.         icmp->icmp_code = 0;
  95.         icmp->icmp_seq = nsent++;
  96.         icmp->icmp_id = pid;
  97.         memset(icmp->icmp_data,0, datalen);
  98.         if(gettimeofday((struct timeval *)icmp->icmp_data, NULL) < 0) {
  99.                 perror("gettimeofday");
  100.                 exit (1);
  101.         }
  102.         len = 8 + datalen;
  103.         icmp->icmp_cksum = 0;
  104.         icmp->icmp_cksum = in_cksum((u_short *)icmp,len);

  105.         sasend = res->ai_addr;
  106.         salen = res->ai_addrlen;
  107.         if(sendto(sockfd, sendbuf, len, 0, sasend, salen) != len) {
  108.                 perror("sendto");
  109.                 exit(1);
  110.         }
  111. }
  112. void readloop(void) {
  113.         ssize_t n;
  114.         int size;
  115.         char recvbuf[BUFSIZE];
  116.         char controlbuf[BUFSIZE];
  117.         struct msghdr msg;
  118.         struct iovec iov;
  119.         struct timeval tval;
  120.        

  121.         // create socket from here
  122.         if(res->ai_family == AF_INET) {
  123.                 if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
  124.                         perror("socket");
  125.                         exit(1);
  126.                 }
  127.         }
  128.         else {
  129.                 fprintf(stderr,"unknown address family %d", res->ai_family);
  130.                 exit (1);
  131.         }
  132.         setuid(getuid());
  133.        
  134.         size = 60 * 1024;
  135.         setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
  136.         sig_alrm(SIGALRM); /* send the first packet */

  137.         iov.iov_base = recvbuf;
  138.         iov.iov_len = sizeof(recvbuf);
  139.         msg.msg_name = sarecv;
  140.         msg.msg_iov = &iov;
  141.         msg.msg_iovlen = 1;
  142.         msg.msg_control = controlbuf;

  143.         for( ; ; ) {/* now receive message after sending packet */
  144.                 n = recvmsg(sockfd, &msg, 0);
  145.                 if( n < 0) {
  146.                         if(errno == EINTR )
  147.                                 continue;
  148.                         else {
  149.                                 perror("recvmsg");
  150.                                 exit(1);
  151.                         }
  152.                 }
  153.                 if(gettimeofday(&tval, NULL) < 0) {
  154.                         perror("gettimeofday");
  155.                         exit (1);
  156.                 }
  157.                 proc_v4(recvbuf, n, &msg, &tval);
  158.         }
  159. }

  160. void sig_alrm(int signo) {
  161.         send_v4();
  162.         alarm(1);
  163.         return ;
  164. }


  165. void proc_v4 (char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv) {
  166.         int hlenl, icmplen;
  167.         struct ip *ip;
  168.         struct icmp *icmp;
  169.         struct timeval *tvsend;
  170.         double rtt;

  171.         ip = (struct ip*)ptr;
  172.         /* only check two fields of ip header, length and protocol */
  173.         hlenl = ip->ip_hl << 2; /* length of ip header */
  174.         if(ip->ip_p != IPPROTO_ICMP )
  175.                 return;                /*second, check protocol */
  176.         icmp = (struct icmp *)(ptr + hlenl); /* start of icmp header */
  177.         if((icmplen = len - hlenl) < 8)
  178.                 return ;
  179.         if(icmp->icmp_type == ICMP_ECHOREPLY) {
  180.                 if(icmp->icmp_id != pid)
  181.                         return;
  182.                 if(icmplen < 16)
  183.                         return;
  184.                 tvsend = (struct timeval *)icmp->icmp_data;
  185.                 tv_sub(tvrecv,tvsend);
  186.                 rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec/1000.0;
  187.                 printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",icmplen, h, icmp->icmp_seq, ip->ip_ttl, rtt);
  188.                
  189.         }
  190. }

  191. void tv_sub(struct timeval *out, struct timeval *in) {
  192.         if((out->tv_usec -= in->tv_usec) < 0) {
  193.                 --out->tv_sec;
  194.                 out->tv_usec += 100000;
  195.         }
  196.         out->tv_sec -= in->tv_sec;
  197. }       
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP