Advertisement
Guest User

CN

a guest
Jan 14th, 2020
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.03 KB | None | 0 0
  1. #if HAVE_CONFIG_H
  2. # include <config.h>
  3. #endif
  4.  
  5. #if STDC_HEADERS
  6. # include <stdlib.h>
  7. # include <stdio.h>
  8. # include <string.h>
  9. # include <inttypes.h>
  10. # include <errno.h>
  11. # include <assert.h>
  12. #else
  13. # error "You don't have the standard C99 header files installed"
  14. #endif /* STDC_HEADERS */
  15.  
  16. #ifdef HAVE_STDINT_H
  17. # include <stdint.h>
  18. #endif
  19.  
  20. #if HAVE_UNISTD_H
  21. # include <unistd.h>
  22. #endif
  23.  
  24. #if HAVE_FCNTL_H
  25. # include <fcntl.h>
  26. #endif
  27. #if HAVE_SYS_TYPES_H
  28. # include <sys/types.h>
  29. #endif
  30. #if HAVE_SYS_STAT_H
  31. # include <sys/stat.h>
  32. #endif
  33.  
  34. #if TIME_WITH_SYS_TIME
  35. # include <sys/time.h>
  36. # include <time.h>
  37. #else
  38. # if HAVE_SYS_TIME_H
  39. #  include <sys/time.h>
  40. # else
  41. #  include <time.h>
  42. # endif
  43. #endif
  44.  
  45. #if HAVE_SYS_SOCKET_H
  46. # include <sys/socket.h>
  47. #endif
  48.  
  49. #if HAVE_NETDB_H
  50. # include <netdb.h>
  51. #endif
  52.  
  53. #if HAVE_NETINET_IN_SYSTM_H
  54. # include <netinet/in_systm.h>
  55. #endif
  56. #if HAVE_NETINET_IN_H
  57. # include <netinet/in.h>
  58. #endif
  59. #if HAVE_NETINET_IP_H
  60. # include <netinet/ip.h>
  61. #endif
  62. #if HAVE_NETINET_IP_ICMP_H
  63. # include <netinet/ip_icmp.h>
  64. #endif
  65. #ifdef HAVE_NETINET_IP_VAR_H
  66. # include <netinet/ip_var.h>
  67. #endif
  68. #if HAVE_NETINET_IP6_H
  69. # include <netinet/ip6.h>
  70. #endif
  71. #if HAVE_NETINET_ICMP6_H
  72. # include <netinet/icmp6.h>
  73. #endif
  74.  
  75. #include "oping.h"
  76.  
  77. #if WITH_DEBUG
  78. # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
  79. #else
  80. # define dprintf(...) /**/
  81. #endif
  82.  
  83. #define PING_ERRMSG_LEN 256
  84. #define PING_TABLE_LEN 5381
  85.  
  86. struct pinghost
  87. {
  88.     /* username: name passed in by the user */
  89.     char                    *username;
  90.     /* hostname: name returned by the reverse lookup */
  91.     char                    *hostname;
  92.     struct sockaddr_storage *addr;
  93.     socklen_t                addrlen;
  94.     int                      addrfamily;
  95.     int                      ident;
  96.     int                      sequence;
  97.     struct timeval          *timer;
  98.     double                   latency;
  99.     uint32_t                 dropped;
  100.     int                      recv_ttl;
  101.     uint8_t                  recv_qos;
  102.     char                    *data;
  103.  
  104.     void                    *context;
  105.  
  106.     struct pinghost         *next;
  107.     struct pinghost         *table_next;
  108. };
  109.  
  110. struct pingobj
  111. {
  112.     double                   timeout;
  113.     int                      ttl;
  114.     int                      addrfamily;
  115.     uint8_t                  qos;
  116.     char                    *data;
  117.  
  118.     int                      fd4;
  119.     int                      fd6;
  120.  
  121.     struct sockaddr         *srcaddr;
  122.     socklen_t                srcaddrlen;
  123.  
  124.     char                    *device;
  125.  
  126.     char                    set_mark;
  127.     int                     mark;
  128.  
  129.     char                     errmsg[PING_ERRMSG_LEN];
  130.  
  131.     pinghost_t              *head;
  132.     pinghost_t              *table[PING_TABLE_LEN];
  133. };
  134.  
  135. /*
  136.  * private (static) functions
  137.  */
  138. /* Even though Posix requires "strerror_r" to return an "int",
  139.  * some systems (e.g. the GNU libc) return a "char *" _and_
  140.  * ignore the second argument ... -tokkee */
  141.  
  142. static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
  143.         struct timeval *res)
  144. {
  145.     res->tv_sec  = tv1->tv_sec  + tv2->tv_sec;
  146.     res->tv_usec = tv1->tv_usec + tv2->tv_usec;
  147.  
  148.     while (res->tv_usec > 1000000)
  149.     {
  150.         res->tv_usec -= 1000000;
  151.         res->tv_sec++;
  152.     }
  153.  
  154.     return (0);
  155. }
  156.  
  157. static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
  158.         struct timeval *res)
  159. {
  160.     if ((tv1->tv_sec < tv2->tv_sec)
  161.             || ((tv1->tv_sec == tv2->tv_sec)
  162.                 && (tv1->tv_usec < tv2->tv_usec)))
  163.         return (-1);
  164.  
  165.     res->tv_sec  = tv1->tv_sec  - tv2->tv_sec;
  166.     res->tv_usec = tv1->tv_usec - tv2->tv_usec;
  167.  
  168.     assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec >= 0)));
  169.  
  170.     while (res->tv_usec < 0)
  171.     {
  172.         res->tv_usec += 1000000;
  173.         res->tv_sec--;
  174.     }
  175.  
  176.     return (0);
  177. }
  178.  
  179. static uint16_t ping_icmp4_checksum (char *buf, size_t len)
  180. {
  181.     uint32_t sum = 0;
  182.     uint16_t ret = 0;
  183.  
  184.     uint16_t *ptr;
  185.  
  186.     for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
  187.         sum += *ptr;
  188.  
  189.     if (len == 1)
  190.     {
  191.         *(char *) &ret = *(char *) ptr;
  192.         sum += ret;
  193.     }
  194.  
  195.     /* Do this twice to get all possible carries.. */
  196.     sum = (sum >> 16) + (sum & 0xFFFF);
  197.     sum = (sum >> 16) + (sum & 0xFFFF);
  198.  
  199.     ret = ~sum;
  200.  
  201.     return (ret);
  202. }
  203.  
  204. static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
  205.         size_t buffer_len)
  206. {
  207.     struct ip *ip_hdr;
  208.     struct icmp *icmp_hdr;
  209.  
  210.     size_t ip_hdr_len;
  211.  
  212.     uint16_t recv_checksum;
  213.     uint16_t calc_checksum;
  214.  
  215.     uint16_t ident;
  216.     uint16_t seq;
  217.  
  218.     pinghost_t *ptr;
  219.  
  220.     if (buffer_len < sizeof (struct ip))
  221.         return (NULL);
  222.  
  223.     ip_hdr     = (struct ip *) buffer;
  224.     ip_hdr_len = ip_hdr->ip_hl << 2;
  225.  
  226.     if (buffer_len < ip_hdr_len)
  227.         return (NULL);
  228.  
  229.     buffer     += ip_hdr_len;
  230.     buffer_len -= ip_hdr_len;
  231.  
  232.     if (buffer_len < ICMP_MINLEN)
  233.         return (NULL);
  234.  
  235.     icmp_hdr = (struct icmp *) buffer;
  236.     if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
  237.     {
  238.         dprintf ("Unexpected ICMP type: %"PRIu8"\n", icmp_hdr->icmp_type);
  239.         return (NULL);
  240.     }
  241.  
  242.     recv_checksum = icmp_hdr->icmp_cksum;
  243.     /* This writes to buffer. */
  244.     icmp_hdr->icmp_cksum = 0;
  245.     calc_checksum = ping_icmp4_checksum (buffer, buffer_len);
  246.  
  247.     if (recv_checksum != calc_checksum)
  248.     {
  249.         dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
  250.                 "calculated 0x%04"PRIx16"\n",
  251.                 recv_checksum, calc_checksum);
  252.         return (NULL);
  253.     }
  254.  
  255.     ident = ntohs (icmp_hdr->icmp_id);
  256.     seq   = ntohs (icmp_hdr->icmp_seq);
  257.  
  258.     if (ptr != NULL){
  259.         ptr->recv_ttl = (int)     ip_hdr->ip_ttl;
  260.         ptr->recv_qos = (uint8_t) ip_hdr->ip_tos;
  261.     }
  262.     return (ptr);
  263. }
  264.  
  265. static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam)
  266. {
  267.     int fd = obj->fd4; //Obj is used here
  268.     struct timeval diff, pkt_now = *now;
  269.     pinghost_t *host = NULL;
  270.     int recv_ttl;
  271.  
  272.     /*
  273.      * Set up the receive buffer..
  274.      */
  275.     struct msghdr msghdr;
  276.     struct cmsghdr *cmsg;
  277.     char payload_buffer[4096];
  278.     ssize_t payload_buffer_len;
  279.     char control_buffer[4096];
  280.     struct iovec payload_iovec;
  281.  
  282.     memset (&payload_iovec, 0, sizeof (payload_iovec));
  283.     payload_iovec.iov_base = payload_buffer;
  284.     payload_iovec.iov_len = sizeof (payload_buffer);
  285.  
  286.     memset (&msghdr, 0, sizeof (msghdr));
  287.     /* unspecified source address */
  288.     msghdr.msg_name = NULL;
  289.     msghdr.msg_namelen = 0;
  290.     /* output buffer vector, see readv(2) */
  291.     msghdr.msg_iov = &payload_iovec;
  292.     msghdr.msg_iovlen = 1;
  293.     /* output buffer for control messages */
  294.     msghdr.msg_control = control_buffer;
  295.     msghdr.msg_controllen = sizeof (control_buffer);
  296.     /* flags; this is an output only field.. */
  297.     msghdr.msg_flags = 0;
  298.  
  299.     payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
  300.     if (payload_buffer_len < 0)
  301.     {
  302.         return (-1);
  303.     }
  304.     dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, fd);
  305.  
  306.     /* Iterate over all auxiliary data in msghdr */
  307.     recv_ttl = -1;
  308.     for (cmsg = CMSG_FIRSTHDR (&msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR (&msghdr, cmsg))
  309.     {
  310.         if (cmsg->cmsg_level == SOL_SOCKET)
  311.         {
  312. #ifdef SO_TIMESTAMP
  313.             if (cmsg->cmsg_type == SO_TIMESTAMP)
  314.                 memcpy (&pkt_now, CMSG_DATA (cmsg), sizeof (pkt_now)); //Suspicious; likely can be cut
  315. #endif /* SO_TIMESTAMP */
  316.         }
  317.  
  318.         if (cmsg->cmsg_level != IPPROTO_IP)
  319.                 continue;
  320.  
  321.         if (cmsg->cmsg_type == IP_TTL)
  322.             {
  323.                 memcpy (&recv_ttl, CMSG_DATA (cmsg),
  324.                         sizeof (recv_ttl));
  325.                 dprintf ("TTLv4 = %i;\n", recv_ttl);
  326.             }
  327.  
  328.     } /* }}} for (cmsg) */
  329.  
  330.     host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
  331.    
  332.     if (host == NULL)
  333.         return (-1);
  334.  
  335.     dprintf ("rcvd: %12i.%06i\n",
  336.             (int) pkt_now.tv_sec,
  337.             (int) pkt_now.tv_usec);
  338.     dprintf ("sent: %12i.%06i\n",
  339.             (int) host->timer->tv_sec,
  340.             (int) host->timer->tv_usec);
  341.  
  342.     if (ping_timeval_sub (&pkt_now, host->timer, &diff) < 0)
  343.     {
  344.         timerclear (host->timer);
  345.         return (-1);
  346.     }
  347.  
  348.     dprintf ("diff: %12i.%06i\n",
  349.             (int) diff.tv_sec,
  350.             (int) diff.tv_usec);
  351.  
  352.     if (recv_ttl >= 0)
  353.         host->recv_ttl = recv_ttl;
  354.  
  355.     host->latency  = ((double) diff.tv_usec) / 1000.0;
  356.     host->latency += ((double) diff.tv_sec)  * 1000.0;
  357.  
  358.     timerclear (host->timer);
  359.  
  360.     return (0);
  361. }
  362.  
  363. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  364.  * Sending functions:                                                        *
  365.  *                                                                           *
  366.  * ping_send_all                                                             *
  367.  * +-> ping_send_one_ipv4                                                    *
  368.  * `-> ping_send_one_ipv6                                                    *
  369.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  370. static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
  371.         const void *buf, size_t buflen, int fd)
  372. {
  373.     ssize_t ret;
  374.  
  375.     if (gettimeofday (ph->timer, NULL) == -1)
  376.     {
  377.         timerclear (ph->timer);
  378.         return (-1);
  379.     }
  380.  
  381.     ret = sendto (fd, buf, buflen, 0,
  382.             (struct sockaddr *) ph->addr, ph->addrlen);
  383.  
  384.     if (ret < 0)
  385.     {
  386.         ping_set_errno (obj, errno);
  387.     }
  388.  
  389.     return (ret);
  390. }
  391.  
  392. static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph, int fd)
  393. {
  394.     struct icmp *icmp4;
  395.     int status;
  396.  
  397.     char   buf[4096] = {0};
  398.     size_t buflen;
  399.  
  400.     char *data;
  401.     size_t datalen;
  402.  
  403.     dprintf ("ph->hostname = %s\n", ph->hostname);
  404.  
  405.     icmp4 = (struct icmp *) buf;
  406.     *icmp4 = (struct icmp) {
  407.         .icmp_type = ICMP_ECHO,
  408.         .icmp_id   = htons (ph->ident),
  409.         .icmp_seq  = htons (ph->sequence),
  410.     };
  411.  
  412.     datalen = strlen (ph->data);
  413.     buflen = ICMP_MINLEN + datalen;
  414.     if (sizeof (buf) < buflen)
  415.         return (EINVAL);
  416.  
  417.     data  = buf + ICMP_MINLEN;
  418.     memcpy (data, ph->data, datalen);
  419.  
  420.     icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
  421.  
  422.     dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
  423.  
  424.     status = ping_sendto (obj, ph, buf, buflen, fd);
  425.     if (status < 0)
  426.     {
  427.         perror ("ping_sendto");
  428.         return (-1);
  429.     }
  430.  
  431.     dprintf ("sendto: status = %i\n", status);
  432.  
  433.     return (0);
  434. }
  435.  
  436. static int ping_send_one (pingobj_t *obj, pinghost_t *ptr, int fd)
  437. {
  438.     if (gettimeofday (ptr->timer, NULL) == -1)
  439.     {
  440.         /* start timer.. The GNU `ping6' starts the timer before
  441.          * sending the packet, so I will do that too */
  442.         timerclear (ptr->timer);
  443.         return (-1);
  444.     }
  445.     else
  446.     {
  447.         dprintf ("timer set for hostname = %s\n", ptr->hostname);
  448.     }
  449.  
  450.     dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
  451.     if (ping_send_one_ipv4 (obj, ptr, fd) != 0)
  452.     {
  453.         timerclear (ptr->timer);
  454.         return (-1);
  455.     }
  456.  
  457.     ptr->sequence++;
  458.  
  459.     return (0);
  460. }
  461.  
  462. /*
  463.  * Set the TTL of a socket protocol independently.
  464.  */
  465. static int ping_set_ttl (pingobj_t *obj, int ttl)
  466. {
  467.     int ret = 0;
  468.     char errbuf[PING_ERRMSG_LEN];
  469.  
  470.     if (setsockopt (obj->fd4, IPPROTO_IP, IP_TTL,
  471.             &ttl, sizeof (ttl)))
  472.     {
  473.         ret = errno;
  474.         ping_set_error (obj, "ping_set_ttl",
  475.                 sstrerror (ret, errbuf, sizeof (errbuf)));
  476.         dprintf ("Setting TTLv4 failed: %s\n", errbuf);
  477.     }
  478.  
  479.     return (ret);
  480. }
  481.  
  482. static pinghost_t *ping_alloc (void)
  483. {
  484.     pinghost_t *ph;
  485.     size_t      ph_size;
  486.  
  487.     ph_size = sizeof (pinghost_t)
  488.         + sizeof (struct sockaddr_storage)
  489.         + sizeof (struct timeval);
  490.  
  491.     ph = (pinghost_t *) malloc (ph_size);
  492.     if (ph == NULL)
  493.         return (NULL);
  494.  
  495.     memset (ph, '\0', ph_size);
  496.  
  497.     ph->timer   = (struct timeval *) (ph + 1);
  498.     ph->addr    = (struct sockaddr_storage *) (ph->timer + 1);
  499.  
  500.     ph->addrlen = sizeof (struct sockaddr_storage);
  501.     ph->latency = -1.0;
  502.     ph->dropped = 0;
  503.     ph->ident   = ping_get_ident () & 0xFFFF;
  504.  
  505.     return (ph);
  506. }
  507.  
  508. static void ping_free (pinghost_t *ph)
  509. {
  510.     if (ph == NULL)
  511.         return;
  512.  
  513.     free (ph->username);
  514.     free (ph->hostname);
  515.     free (ph->data);
  516.  
  517.     free (ph);
  518. }
  519.  
  520. /* ping_open_socket opens, initializes and returns a new raw socket to use for
  521.  * ICMPv4 or ICMPv6 packets. addrfam must be either AF_INET or AF_INET6. On
  522.  * error, -1 is returned and obj->errmsg is set appropriately. */
  523. static int ping_open_socket(pingobj_t *obj, int addrfam)
  524. {
  525.     int fd;
  526.  
  527.     fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMP);
  528.  
  529.     if (fd == -1)
  530.     {
  531.         ping_set_errno (obj, errno);
  532.         return -1;
  533.     }
  534.  
  535.     if (obj->srcaddr != NULL)
  536.     {
  537.         assert (obj->srcaddrlen > 0);
  538.         assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
  539.  
  540.         if (bind (fd, obj->srcaddr, obj->srcaddrlen) == -1)
  541.         {
  542.             ping_set_errno (obj, errno);
  543.             close (fd);
  544.             return -1;
  545.         }
  546.     }
  547.  
  548. #ifdef SO_TIMESTAMP
  549.     if (1) /* {{{ */
  550.     {
  551.         int status = setsockopt (fd, SOL_SOCKET, SO_TIMESTAMP,
  552.                                  &(int){1}, sizeof(int));
  553.         if (status != 0)
  554.         {
  555.             ping_set_errno (obj, errno);
  556.             close (fd);
  557.             return -1;
  558.         }
  559.     } /* }}} if (1) */
  560. #endif /* SO_TIMESTAMP */
  561.  
  562.  
  563.     /* Enable receiving the TTL field */
  564.     setsockopt (fd, IPPROTO_IP, IP_RECVTTL, &(int){1}, sizeof(int));
  565.  
  566.  
  567.     return fd;
  568. }
  569.  
  570. /*
  571.  * public methods
  572.  */
  573. const char *ping_get_error (pingobj_t *obj)
  574. {
  575.     if (obj == NULL)
  576.         return (NULL);
  577.     return (obj->errmsg);
  578. }
  579.  
  580. pingobj_t *ping_construct (void)
  581. {
  582.     pingobj_t *obj;
  583.  
  584.     if ((obj = malloc (sizeof (*obj))) == NULL)
  585.         return (NULL);
  586.     memset (obj, 0, sizeof (*obj));
  587.  
  588.     obj->timeout    = PING_DEF_TIMEOUT;
  589.     obj->ttl        = PING_DEF_TTL;
  590.     obj->addrfamily = PING_DEF_AF;
  591.     obj->data       = strdup (PING_DEF_DATA);
  592.     obj->qos        = 0;
  593.     obj->fd4        = -1;
  594.     obj->fd6        = -1;
  595.  
  596.     return (obj);
  597. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement