"Fossies" - the Fresh Open Source Software Archive

Member "liboping-1.10.0/src/liboping.c" (11 May 2017, 42200 Bytes) of package /linux/privat/liboping-1.10.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "liboping.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.0_vs_1.10.0.

    1 /**
    2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
    3  * Copyright (C) 2006-2017  Florian octo Forster <ff at octo.it>
    4  *
    5  * This library is free software; you can redistribute it and/or modify it
    6  * under the terms of the GNU Lesser General Public License as published by the
    7  * Free Software Foundation; either version 2.1 of the License, or (at your
    8  * option) any later version.
    9  * 
   10  * This library is distributed in the hope that it will be useful, but WITHOUT
   11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   13  * for more details.
   14  * 
   15  * You should have received a copy of the GNU Lesser General Public License
   16  * along with this library; if not, write to the Free Software Foundation,
   17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   18  */
   19 
   20 #ifdef __APPLE__
   21 #define __APPLE_USE_RFC_3542
   22 #endif
   23 
   24 #if HAVE_CONFIG_H
   25 # include <config.h>
   26 #endif
   27 
   28 #if STDC_HEADERS
   29 # include <stdlib.h>
   30 # include <stdio.h>
   31 # include <string.h>
   32 # include <inttypes.h>
   33 # include <errno.h>
   34 # include <assert.h>
   35 #else
   36 # error "You don't have the standard C99 header files installed"
   37 #endif /* STDC_HEADERS */
   38 
   39 #ifdef HAVE_STDINT_H
   40 # include <stdint.h>
   41 #endif
   42 
   43 #if HAVE_UNISTD_H
   44 # include <unistd.h>
   45 #endif
   46 
   47 #if HAVE_FCNTL_H
   48 # include <fcntl.h>
   49 #endif
   50 #if HAVE_SYS_TYPES_H
   51 # include <sys/types.h>
   52 #endif
   53 #if HAVE_SYS_STAT_H
   54 # include <sys/stat.h>
   55 #endif
   56 
   57 #if TIME_WITH_SYS_TIME
   58 # include <sys/time.h>
   59 # include <time.h>
   60 #else
   61 # if HAVE_SYS_TIME_H
   62 #  include <sys/time.h>
   63 # else
   64 #  include <time.h>
   65 # endif
   66 #endif
   67 
   68 #if HAVE_SYS_SOCKET_H
   69 # include <sys/socket.h>
   70 #endif
   71 
   72 #if HAVE_NETDB_H
   73 # include <netdb.h>
   74 #endif
   75 
   76 #if HAVE_NETINET_IN_SYSTM_H
   77 # include <netinet/in_systm.h>
   78 #endif
   79 #if HAVE_NETINET_IN_H
   80 # include <netinet/in.h>
   81 #endif
   82 #if HAVE_NETINET_IP_H
   83 # include <netinet/ip.h>
   84 #endif
   85 #if HAVE_NETINET_IP_ICMP_H
   86 # include <netinet/ip_icmp.h>
   87 #endif
   88 #ifdef HAVE_NETINET_IP_VAR_H
   89 # include <netinet/ip_var.h>
   90 #endif
   91 #if HAVE_NETINET_IP6_H
   92 # include <netinet/ip6.h>
   93 #endif
   94 #if HAVE_NETINET_ICMP6_H
   95 # include <netinet/icmp6.h>
   96 #endif
   97 
   98 #include "oping.h"
   99 
  100 #if WITH_DEBUG
  101 # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
  102 #else
  103 # define dprintf(...) /**/
  104 #endif
  105 
  106 #define PING_ERRMSG_LEN 256
  107 #define PING_TABLE_LEN 5381
  108 
  109 struct pinghost
  110 {
  111     /* username: name passed in by the user */
  112     char                    *username;
  113     /* hostname: name returned by the reverse lookup */
  114     char                    *hostname;
  115     struct sockaddr_storage *addr;
  116     socklen_t                addrlen;
  117     int                      addrfamily;
  118     int                      ident;
  119     int                      sequence;
  120     struct timeval          *timer;
  121     double                   latency;
  122     uint32_t                 dropped;
  123     int                      recv_ttl;
  124     uint8_t                  recv_qos;
  125     char                    *data;
  126 
  127     void                    *context;
  128 
  129     struct pinghost         *next;
  130     struct pinghost         *table_next;
  131 };
  132 
  133 struct pingobj
  134 {
  135     double                   timeout;
  136     int                      ttl;
  137     int                      addrfamily;
  138     uint8_t                  qos;
  139     char                    *data;
  140 
  141     int                      fd4;
  142     int                      fd6;
  143 
  144     struct sockaddr         *srcaddr;
  145     socklen_t                srcaddrlen;
  146 
  147     char                    *device;
  148 
  149     char                    set_mark;
  150     int                     mark;
  151 
  152     char                     errmsg[PING_ERRMSG_LEN];
  153 
  154     pinghost_t              *head;
  155     pinghost_t              *table[PING_TABLE_LEN];
  156 };
  157 
  158 /*
  159  * private (static) functions
  160  */
  161 /* Even though Posix requires "strerror_r" to return an "int",
  162  * some systems (e.g. the GNU libc) return a "char *" _and_
  163  * ignore the second argument ... -tokkee */
  164 static char *sstrerror (int errnum, char *buf, size_t buflen)
  165 {
  166     buf[0] = 0;
  167 
  168 #if !HAVE_STRERROR_R
  169     {
  170         snprintf (buf, buflen, "Error %i (%#x)", errnum, errnum);
  171     }
  172 /* #endif !HAVE_STRERROR_R */
  173 
  174 #elif STRERROR_R_CHAR_P
  175     {
  176         char *temp;
  177         temp = strerror_r (errnum, buf, buflen);
  178         if (buf[0] == 0)
  179         {
  180             if ((temp != NULL) && (temp != buf) && (temp[0] != 0))
  181                 strncpy (buf, temp, buflen);
  182             else
  183                 strncpy (buf, "strerror_r did not return "
  184                         "an error message", buflen);
  185         }
  186     }
  187 /* #endif STRERROR_R_CHAR_P */
  188 
  189 #else
  190     if (strerror_r (errnum, buf, buflen) != 0)
  191     {
  192         snprintf (buf, buflen, "Error %i (%#x); "
  193                 "Additionally, strerror_r failed.",
  194                 errnum, errnum);
  195     }
  196 #endif /* STRERROR_R_CHAR_P */
  197 
  198     buf[buflen - 1] = 0;
  199 
  200     return (buf);
  201 } /* char *sstrerror */
  202 
  203 static void ping_set_error (pingobj_t *obj, const char *function,
  204             const char *message)
  205 {
  206     snprintf (obj->errmsg, sizeof (obj->errmsg),
  207             "%s: %s", function, message);
  208     obj->errmsg[sizeof (obj->errmsg) - 1] = 0;
  209 }
  210 
  211 static void ping_set_errno (pingobj_t *obj, int error_number)
  212 {
  213     sstrerror (error_number, obj->errmsg, sizeof (obj->errmsg));
  214 }
  215 
  216 static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
  217         struct timeval *res)
  218 {
  219     res->tv_sec  = tv1->tv_sec  + tv2->tv_sec;
  220     res->tv_usec = tv1->tv_usec + tv2->tv_usec;
  221 
  222     while (res->tv_usec > 1000000)
  223     {
  224         res->tv_usec -= 1000000;
  225         res->tv_sec++;
  226     }
  227 
  228     return (0);
  229 }
  230 
  231 static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
  232         struct timeval *res)
  233 {
  234     if ((tv1->tv_sec < tv2->tv_sec)
  235             || ((tv1->tv_sec == tv2->tv_sec)
  236                 && (tv1->tv_usec < tv2->tv_usec)))
  237         return (-1);
  238 
  239     res->tv_sec  = tv1->tv_sec  - tv2->tv_sec;
  240     res->tv_usec = tv1->tv_usec - tv2->tv_usec;
  241 
  242     assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec >= 0)));
  243 
  244     while (res->tv_usec < 0)
  245     {
  246         res->tv_usec += 1000000;
  247         res->tv_sec--;
  248     }
  249 
  250     return (0);
  251 }
  252 
  253 static uint16_t ping_icmp4_checksum (char *buf, size_t len)
  254 {
  255     uint32_t sum = 0;
  256     uint16_t ret = 0;
  257 
  258     uint16_t *ptr;
  259 
  260     for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
  261         sum += *ptr;
  262 
  263     if (len == 1)
  264     {
  265         *(char *) &ret = *(char *) ptr;
  266         sum += ret;
  267     }
  268 
  269     /* Do this twice to get all possible carries.. */
  270     sum = (sum >> 16) + (sum & 0xFFFF);
  271     sum = (sum >> 16) + (sum & 0xFFFF);
  272 
  273     ret = ~sum;
  274 
  275     return (ret);
  276 }
  277 
  278 static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
  279         size_t buffer_len)
  280 {
  281     struct ip *ip_hdr;
  282     struct icmp *icmp_hdr;
  283 
  284     size_t ip_hdr_len;
  285 
  286     uint16_t recv_checksum;
  287     uint16_t calc_checksum;
  288 
  289     uint16_t ident;
  290     uint16_t seq;
  291 
  292     pinghost_t *ptr;
  293 
  294     if (buffer_len < sizeof (struct ip))
  295         return (NULL);
  296 
  297     ip_hdr     = (struct ip *) buffer;
  298     ip_hdr_len = ip_hdr->ip_hl << 2;
  299 
  300     if (buffer_len < ip_hdr_len)
  301         return (NULL);
  302 
  303     buffer     += ip_hdr_len;
  304     buffer_len -= ip_hdr_len;
  305 
  306     if (buffer_len < ICMP_MINLEN)
  307         return (NULL);
  308 
  309     icmp_hdr = (struct icmp *) buffer;
  310     if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
  311     {
  312         dprintf ("Unexpected ICMP type: %"PRIu8"\n", icmp_hdr->icmp_type);
  313         return (NULL);
  314     }
  315 
  316     recv_checksum = icmp_hdr->icmp_cksum;
  317     /* This writes to buffer. */
  318     icmp_hdr->icmp_cksum = 0;
  319     calc_checksum = ping_icmp4_checksum (buffer, buffer_len);
  320 
  321     if (recv_checksum != calc_checksum)
  322     {
  323         dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
  324                 "calculated 0x%04"PRIx16"\n",
  325                 recv_checksum, calc_checksum);
  326         return (NULL);
  327     }
  328 
  329     ident = ntohs (icmp_hdr->icmp_id);
  330     seq   = ntohs (icmp_hdr->icmp_seq);
  331 
  332     for (ptr = obj->table[ident % PING_TABLE_LEN];
  333             ptr != NULL; ptr = ptr->table_next)
  334     {
  335         dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
  336                 ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
  337 
  338         if (ptr->addrfamily != AF_INET)
  339             continue;
  340 
  341         if (!timerisset (ptr->timer))
  342             continue;
  343 
  344         if (ptr->ident != ident)
  345             continue;
  346 
  347         if (((ptr->sequence - 1) & 0xFFFF) != seq)
  348             continue;
  349 
  350         dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
  351                 "seq = %"PRIu16"\n",
  352                 ptr->hostname, ident, seq);
  353 
  354         break;
  355     }
  356 
  357     if (ptr == NULL)
  358     {
  359         dprintf ("No match found for ident = 0x%04"PRIx16", seq = %"PRIu16"\n",
  360                 ident, seq);
  361     }
  362 
  363     if (ptr != NULL){
  364         ptr->recv_ttl = (int)     ip_hdr->ip_ttl;
  365         ptr->recv_qos = (uint8_t) ip_hdr->ip_tos;
  366     }
  367     return (ptr);
  368 }
  369 
  370 #ifndef ICMP6_ECHO_REQUEST
  371 # ifdef ICMP6_ECHO /* AIX netinet/ip6_icmp.h */
  372 #  define ICMP6_ECHO_REQUEST ICMP6_ECHO
  373 # else
  374 #  define ICMP6_ECHO_REQUEST 128
  375 # endif
  376 #endif
  377 
  378 #ifndef ICMP6_ECHO_REPLY
  379 # ifdef ICMP6_ECHOREPLY /* AIX netinet/ip6_icmp.h */
  380 #  define ICMP6_ECHO_REPLY ICMP6_ECHOREPLY
  381 # else
  382 #  define ICMP6_ECHO_REPLY 129
  383 # endif
  384 #endif
  385 
  386 static pinghost_t *ping_receive_ipv6 (pingobj_t *obj, char *buffer,
  387         size_t buffer_len)
  388 {
  389     struct icmp6_hdr *icmp_hdr;
  390 
  391     uint16_t ident;
  392     uint16_t seq;
  393 
  394     pinghost_t *ptr;
  395 
  396     if (buffer_len < ICMP_MINLEN)
  397         return (NULL);
  398 
  399     icmp_hdr = (struct icmp6_hdr *) buffer;
  400     buffer     += ICMP_MINLEN;
  401     buffer_len -= ICMP_MINLEN;
  402 
  403     if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
  404     {
  405         dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type);
  406         return (NULL);
  407     }
  408 
  409     if (icmp_hdr->icmp6_code != 0)
  410     {
  411         dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code);
  412         return (NULL);
  413     }
  414 
  415     ident = ntohs (icmp_hdr->icmp6_id);
  416     seq   = ntohs (icmp_hdr->icmp6_seq);
  417 
  418     /* We have to iterate over all hosts, since ICMPv6 packets may
  419      * be received on any raw v6 socket. */
  420     for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
  421     {
  422         dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
  423                 ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
  424 
  425         if (ptr->addrfamily != AF_INET6)
  426             continue;
  427 
  428         if (!timerisset (ptr->timer))
  429             continue;
  430 
  431         if (ptr->ident != ident)
  432             continue;
  433 
  434         if (((ptr->sequence - 1) & 0xFFFF) != seq)
  435             continue;
  436 
  437         dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
  438                 "seq = %"PRIu16"\n",
  439                 ptr->hostname, ident, seq);
  440 
  441         break;
  442     }
  443 
  444     if (ptr == NULL)
  445     {
  446         dprintf ("No match found for ident = 0x%04"PRIx16", "
  447                 "seq = %"PRIu16"\n",
  448                 ident, seq);
  449     }
  450 
  451     return (ptr);
  452 }
  453 
  454 static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam)
  455 {
  456     int fd = addrfam == AF_INET6 ? obj->fd6 : obj->fd4;
  457     struct timeval diff, pkt_now = *now;
  458     pinghost_t *host = NULL;
  459     int recv_ttl;
  460     uint8_t recv_qos;
  461 
  462     /*
  463      * Set up the receive buffer..
  464      */
  465     struct msghdr msghdr;
  466     struct cmsghdr *cmsg;
  467     char payload_buffer[4096];
  468     ssize_t payload_buffer_len;
  469     char control_buffer[4096];
  470     struct iovec payload_iovec;
  471 
  472     memset (&payload_iovec, 0, sizeof (payload_iovec));
  473     payload_iovec.iov_base = payload_buffer;
  474     payload_iovec.iov_len = sizeof (payload_buffer);
  475 
  476     memset (&msghdr, 0, sizeof (msghdr));
  477     /* unspecified source address */
  478     msghdr.msg_name = NULL;
  479     msghdr.msg_namelen = 0;
  480     /* output buffer vector, see readv(2) */
  481     msghdr.msg_iov = &payload_iovec;
  482     msghdr.msg_iovlen = 1;
  483     /* output buffer for control messages */
  484     msghdr.msg_control = control_buffer;
  485     msghdr.msg_controllen = sizeof (control_buffer);
  486     /* flags; this is an output only field.. */
  487     msghdr.msg_flags = 0;
  488 #ifdef MSG_XPG4_2
  489     msghdr.msg_flags |= MSG_XPG4_2;
  490 #endif
  491 
  492     payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
  493     if (payload_buffer_len < 0)
  494     {
  495 #if WITH_DEBUG
  496         char errbuf[PING_ERRMSG_LEN];
  497         dprintf ("recvfrom: %s\n",
  498                 sstrerror (errno, errbuf, sizeof (errbuf)));
  499 #endif
  500         return (-1);
  501     }
  502     dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, fd);
  503 
  504     /* Iterate over all auxiliary data in msghdr */
  505     recv_ttl = -1;
  506     recv_qos = 0;
  507     for (cmsg = CMSG_FIRSTHDR (&msghdr); /* {{{ */
  508             cmsg != NULL;
  509             cmsg = CMSG_NXTHDR (&msghdr, cmsg))
  510     {
  511         if (cmsg->cmsg_level == SOL_SOCKET)
  512         {
  513 #ifdef SO_TIMESTAMP
  514             if (cmsg->cmsg_type == SO_TIMESTAMP)
  515                 memcpy (&pkt_now, CMSG_DATA (cmsg), sizeof (pkt_now));
  516 #endif /* SO_TIMESTAMP */
  517         }
  518         else if (addrfam == AF_INET) /* {{{ */
  519         {
  520             if (cmsg->cmsg_level != IPPROTO_IP)
  521                 continue;
  522 
  523             if (cmsg->cmsg_type == IP_TOS)
  524             {
  525                 memcpy (&recv_qos, CMSG_DATA (cmsg),
  526                         sizeof (recv_qos));
  527                 dprintf ("TOSv4 = 0x%02"PRIx8";\n", recv_qos);
  528             } else
  529             if (cmsg->cmsg_type == IP_TTL)
  530             {
  531                 memcpy (&recv_ttl, CMSG_DATA (cmsg),
  532                         sizeof (recv_ttl));
  533                 dprintf ("TTLv4 = %i;\n", recv_ttl);
  534             }
  535             else
  536             {
  537                 dprintf ("Not handling option %i.\n",
  538                         cmsg->cmsg_type);
  539             }
  540         } /* }}} */
  541         else if (addrfam == AF_INET6) /* {{{ */
  542         {
  543             if (cmsg->cmsg_level != IPPROTO_IPV6)
  544                 continue;
  545 
  546             if (cmsg->cmsg_type == IPV6_TCLASS)
  547             {
  548                 memcpy (&recv_qos, CMSG_DATA (cmsg),
  549                         sizeof (recv_qos));
  550                 dprintf ("TOSv6 = 0x%02"PRIx8";\n", recv_qos);
  551             } else
  552 #ifdef IPV6_HOPLIMIT
  553             if (cmsg->cmsg_type == IPV6_HOPLIMIT)
  554             {
  555                 memcpy (&recv_ttl, CMSG_DATA (cmsg),
  556                         sizeof (recv_ttl));
  557                 dprintf ("TTLv6 = %i;\n", recv_ttl);
  558             }
  559             else
  560 #endif
  561 #ifdef IPV6_UNICAST_HOPS
  562             if (cmsg->cmsg_type == IPV6_UNICAST_HOPS)
  563             {
  564                 memcpy (&recv_ttl, CMSG_DATA (cmsg),
  565                         sizeof (recv_ttl));
  566                 dprintf ("TTLv6 = %i;\n", recv_ttl);
  567             }
  568             else
  569 #endif
  570 #ifdef IPV6_MULTICAST_HOPS
  571             if (cmsg->cmsg_type == IPV6_MULTICAST_HOPS)
  572             {
  573                 memcpy (&recv_ttl, CMSG_DATA (cmsg),
  574                         sizeof (recv_ttl));
  575                 dprintf ("TTLv6 = %i;\n", recv_ttl);
  576             }
  577             else
  578 #endif
  579             {
  580                 dprintf ("Not handling option %i.\n",
  581                         cmsg->cmsg_type);
  582             }
  583         } /* }}} */
  584         else
  585         {
  586             dprintf ("Don't know how to handle "
  587                     "unknown protocol %i.\n",
  588                     cmsg->cmsg_level);
  589         }
  590     } /* }}} for (cmsg) */
  591 
  592     if (addrfam == AF_INET)
  593     {
  594         host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
  595         if (host == NULL)
  596             return (-1);
  597     }
  598     else if (addrfam == AF_INET6)
  599     {
  600         host = ping_receive_ipv6 (obj, payload_buffer, payload_buffer_len);
  601         if (host == NULL)
  602             return (-1);
  603     }
  604     else
  605     {
  606         dprintf ("ping_receive_one: Unknown address family %i.\n",
  607                 addrfam);
  608         return (-1);
  609     }
  610 
  611     dprintf ("rcvd: %12i.%06i\n",
  612             (int) pkt_now.tv_sec,
  613             (int) pkt_now.tv_usec);
  614     dprintf ("sent: %12i.%06i\n",
  615             (int) host->timer->tv_sec,
  616             (int) host->timer->tv_usec);
  617 
  618     if (ping_timeval_sub (&pkt_now, host->timer, &diff) < 0)
  619     {
  620         timerclear (host->timer);
  621         return (-1);
  622     }
  623 
  624     dprintf ("diff: %12i.%06i\n",
  625             (int) diff.tv_sec,
  626             (int) diff.tv_usec);
  627 
  628     if (recv_ttl >= 0)
  629         host->recv_ttl = recv_ttl;
  630     host->recv_qos = recv_qos;
  631 
  632     host->latency  = ((double) diff.tv_usec) / 1000.0;
  633     host->latency += ((double) diff.tv_sec)  * 1000.0;
  634 
  635     timerclear (host->timer);
  636 
  637     return (0);
  638 }
  639 
  640 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  641  * Sending functions:                                                        *
  642  *                                                                           *
  643  * ping_send_all                                                             *
  644  * +-> ping_send_one_ipv4                                                    *
  645  * `-> ping_send_one_ipv6                                                    *
  646  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  647 static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
  648         const void *buf, size_t buflen, int fd)
  649 {
  650     ssize_t ret;
  651 
  652     if (gettimeofday (ph->timer, NULL) == -1)
  653     {
  654         timerclear (ph->timer);
  655         return (-1);
  656     }
  657 
  658     ret = sendto (fd, buf, buflen, 0,
  659             (struct sockaddr *) ph->addr, ph->addrlen);
  660 
  661     if (ret < 0)
  662     {
  663 #if defined(EHOSTUNREACH)
  664         if (errno == EHOSTUNREACH)
  665             return (0);
  666 #endif
  667 #if defined(ENETUNREACH)
  668         if (errno == ENETUNREACH)
  669             return (0);
  670 #endif
  671         ping_set_errno (obj, errno);
  672     }
  673 
  674     return (ret);
  675 }
  676 
  677 static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph, int fd)
  678 {
  679     struct icmp *icmp4;
  680     int status;
  681 
  682     char   buf[4096] = {0};
  683     size_t buflen;
  684 
  685     char *data;
  686     size_t datalen;
  687 
  688     dprintf ("ph->hostname = %s\n", ph->hostname);
  689 
  690     icmp4 = (struct icmp *) buf;
  691     *icmp4 = (struct icmp) {
  692         .icmp_type = ICMP_ECHO,
  693         .icmp_id   = htons (ph->ident),
  694         .icmp_seq  = htons (ph->sequence),
  695     };
  696 
  697     datalen = strlen (ph->data);
  698     buflen = ICMP_MINLEN + datalen;
  699     if (sizeof (buf) < buflen)
  700         return (EINVAL);
  701 
  702     data  = buf + ICMP_MINLEN;
  703     memcpy (data, ph->data, datalen);
  704 
  705     icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
  706 
  707     dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
  708 
  709     status = ping_sendto (obj, ph, buf, buflen, fd);
  710     if (status < 0)
  711     {
  712         perror ("ping_sendto");
  713         return (-1);
  714     }
  715 
  716     dprintf ("sendto: status = %i\n", status);
  717 
  718     return (0);
  719 }
  720 
  721 static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph, int fd)
  722 {
  723     struct icmp6_hdr *icmp6;
  724     int status;
  725 
  726     char buf[4096] = {0};
  727     int  buflen;
  728 
  729     char *data;
  730     int   datalen;
  731 
  732     dprintf ("ph->hostname = %s\n", ph->hostname);
  733 
  734     icmp6 = (struct icmp6_hdr *) buf;
  735     *icmp6 = (struct icmp6_hdr) {
  736         .icmp6_type  = ICMP6_ECHO_REQUEST,
  737         .icmp6_id    = htons (ph->ident),
  738         .icmp6_seq   = htons (ph->sequence),
  739     };
  740 
  741     datalen = strlen (ph->data);
  742     buflen = sizeof (*icmp6) + datalen;
  743     if (sizeof (buf) < buflen)
  744         return (EINVAL);
  745 
  746     data  = buf + ICMP_MINLEN;
  747     memcpy (data, ph->data, datalen);
  748 
  749     /* The checksum will be calculated by the TCP/IP stack. */
  750 
  751     dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
  752 
  753     status = ping_sendto (obj, ph, buf, buflen, fd);
  754     if (status < 0)
  755     {
  756         perror ("ping_sendto");
  757         return (-1);
  758     }
  759 
  760     dprintf ("sendto: status = %i\n", status);
  761 
  762     return (0);
  763 }
  764 
  765 static int ping_send_one (pingobj_t *obj, pinghost_t *ptr, int fd)
  766 {
  767     if (gettimeofday (ptr->timer, NULL) == -1)
  768     {
  769         /* start timer.. The GNU `ping6' starts the timer before
  770          * sending the packet, so I will do that too */
  771 #if WITH_DEBUG
  772         char errbuf[PING_ERRMSG_LEN];
  773         dprintf ("gettimeofday: %s\n",
  774                 sstrerror (errno, errbuf, sizeof (errbuf)));
  775 #endif
  776         timerclear (ptr->timer);
  777         return (-1);
  778     }
  779     else
  780     {
  781         dprintf ("timer set for hostname = %s\n", ptr->hostname);
  782     }
  783 
  784     if (ptr->addrfamily == AF_INET6)
  785     {
  786         dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname);
  787         if (ping_send_one_ipv6 (obj, ptr, fd) != 0)
  788         {
  789             timerclear (ptr->timer);
  790             return (-1);
  791         }
  792     }
  793     else if (ptr->addrfamily == AF_INET)
  794     {
  795         dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
  796         if (ping_send_one_ipv4 (obj, ptr, fd) != 0)
  797         {
  798             timerclear (ptr->timer);
  799             return (-1);
  800         }
  801     }
  802     else /* this should not happen */
  803     {
  804         dprintf ("Unknown address family: %i\n", ptr->addrfamily);
  805         timerclear (ptr->timer);
  806         return (-1);
  807     }
  808 
  809     ptr->sequence++;
  810 
  811     return (0);
  812 }
  813 
  814 /*
  815  * Set the TTL of a socket protocol independently.
  816  */
  817 static int ping_set_ttl (pingobj_t *obj, int ttl)
  818 {
  819     int ret = 0;
  820     char errbuf[PING_ERRMSG_LEN];
  821 
  822     if (obj->fd4 != -1)
  823     {
  824         if (setsockopt (obj->fd4, IPPROTO_IP, IP_TTL,
  825                 &ttl, sizeof (ttl)))
  826         {
  827             ret = errno;
  828             ping_set_error (obj, "ping_set_ttl",
  829                     sstrerror (ret, errbuf, sizeof (errbuf)));
  830             dprintf ("Setting TTLv4 failed: %s\n", errbuf);
  831         }
  832     }
  833 
  834     if (obj->fd6 != -1)
  835     {
  836         dprintf ("Setting TTLv6 to %i\n", ttl);
  837         if (setsockopt (obj->fd6, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
  838                 &ttl, sizeof (ttl)))
  839         {
  840             ret = errno;
  841             ping_set_error (obj, "ping_set_ttl",
  842                     sstrerror (ret, errbuf, sizeof (errbuf)));
  843             dprintf ("Setting TTLv6 failed: %s\n", errbuf);
  844         }
  845     }
  846 
  847     return (ret);
  848 }
  849 
  850 /*
  851  * Set the TOS of a socket protocol independently.
  852  *
  853  * Using SOL_SOCKET / SO_PRIORITY might be a protocol independent way to
  854  * set this. See socket(7) for details.
  855  */
  856 static int ping_set_qos (pingobj_t *obj, uint8_t qos)
  857 {
  858     int ret = 0;
  859     char errbuf[PING_ERRMSG_LEN];
  860 
  861     if (obj->fd4 != -1)
  862     {
  863         dprintf ("Setting TP_TOS to %#04"PRIx8"\n", qos);
  864         if (setsockopt (obj->fd4, IPPROTO_IP, IP_TOS,
  865                 &qos, sizeof (qos)))
  866         {
  867             ret = errno;
  868             ping_set_error (obj, "ping_set_qos",
  869                     sstrerror (ret, errbuf, sizeof (errbuf)));
  870             dprintf ("Setting TP_TOS failed: %s\n", errbuf);
  871         }
  872     }
  873 
  874     if (obj->fd6 != -1)
  875     {
  876         /* IPV6_TCLASS requires an "int". */
  877         int tmp = (int) qos;
  878 
  879         dprintf ("Setting IPV6_TCLASS to %#04"PRIx8" (%i)\n", qos, tmp);
  880         if (setsockopt (obj->fd6, IPPROTO_IPV6, IPV6_TCLASS,
  881             &tmp, sizeof (tmp)))
  882         {
  883             ret = errno;
  884             ping_set_error (obj, "ping_set_qos",
  885                     sstrerror (ret, errbuf, sizeof (errbuf)));
  886             dprintf ("Setting IPV6_TCLASS failed: %s\n", errbuf);
  887         }
  888     }
  889 
  890     return (ret);
  891 }
  892 
  893 static int ping_get_ident (void)
  894 {
  895     int fd;
  896     static int did_seed = 0;
  897 
  898     int retval;
  899 
  900     if (did_seed == 0)
  901     {
  902         if ((fd = open ("/dev/urandom", O_RDONLY)) != -1)
  903         {
  904             unsigned int seed;
  905 
  906             if (read (fd, &seed, sizeof (seed)) != -1)
  907             {
  908                 did_seed = 1;
  909                 dprintf ("Random seed:   %#x\n", seed);
  910                 srandom (seed);
  911             }
  912 
  913             close (fd);
  914         }
  915 #if WITH_DEBUG
  916         else
  917         {
  918             char errbuf[PING_ERRMSG_LEN];
  919             dprintf ("open (/dev/urandom): %s\n",
  920                     sstrerror (errno, errbuf, sizeof (errbuf)));
  921         }
  922 #endif
  923     }
  924 
  925     retval = (int) random ();
  926 
  927     dprintf ("Random number: %#x\n", retval);
  928 
  929     return (retval);
  930 }
  931 
  932 static pinghost_t *ping_alloc (void)
  933 {
  934     pinghost_t *ph;
  935     size_t      ph_size;
  936 
  937     ph_size = sizeof (pinghost_t)
  938         + sizeof (struct sockaddr_storage)
  939         + sizeof (struct timeval);
  940 
  941     ph = (pinghost_t *) malloc (ph_size);
  942     if (ph == NULL)
  943         return (NULL);
  944 
  945     memset (ph, '\0', ph_size);
  946 
  947     ph->timer   = (struct timeval *) (ph + 1);
  948     ph->addr    = (struct sockaddr_storage *) (ph->timer + 1);
  949 
  950     ph->addrlen = sizeof (struct sockaddr_storage);
  951     ph->latency = -1.0;
  952     ph->dropped = 0;
  953     ph->ident   = ping_get_ident () & 0xFFFF;
  954 
  955     return (ph);
  956 }
  957 
  958 static void ping_free (pinghost_t *ph)
  959 {
  960     if (ph == NULL)
  961         return;
  962 
  963     free (ph->username);
  964     free (ph->hostname);
  965     free (ph->data);
  966 
  967     free (ph);
  968 }
  969 
  970 /* ping_open_socket opens, initializes and returns a new raw socket to use for
  971  * ICMPv4 or ICMPv6 packets. addrfam must be either AF_INET or AF_INET6. On
  972  * error, -1 is returned and obj->errmsg is set appropriately. */
  973 static int ping_open_socket(pingobj_t *obj, int addrfam)
  974 {
  975     int fd;
  976     if (addrfam == AF_INET6)
  977     {
  978         fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMPV6);
  979     }
  980     else if (addrfam == AF_INET)
  981     {
  982         fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMP);
  983     }
  984     else /* this should not happen */
  985     {
  986         ping_set_error (obj, "ping_open_socket", "Unknown address family");
  987         dprintf ("Unknown address family: %i\n", addrfam);
  988         return -1;
  989     }
  990 
  991     if (fd == -1)
  992     {
  993         ping_set_errno (obj, errno);
  994 #if WITH_DEBUG
  995         char errbuf[PING_ERRMSG_LEN];
  996         dprintf ("socket: %s\n",
  997                 sstrerror (errno, errbuf, sizeof (errbuf)));
  998 #endif
  999         return -1;
 1000     }
 1001     else if (fd >= FD_SETSIZE)
 1002     {
 1003         ping_set_errno (obj, EMFILE);
 1004         dprintf ("socket(2) returned file descriptor %d, which is above the file "
 1005              "descriptor limit for select(2) (FD_SETSIZE = %d)\n",
 1006              fd, FD_SETSIZE);
 1007         close (fd);
 1008         return -1;
 1009     }
 1010 
 1011     if (obj->srcaddr != NULL)
 1012     {
 1013         assert (obj->srcaddrlen > 0);
 1014         assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
 1015 
 1016         if (bind (fd, obj->srcaddr, obj->srcaddrlen) == -1)
 1017         {
 1018             ping_set_errno (obj, errno);
 1019 #if WITH_DEBUG
 1020             char errbuf[PING_ERRMSG_LEN];
 1021             dprintf ("bind: %s\n",
 1022                     sstrerror (errno, errbuf, sizeof (errbuf)));
 1023 #endif
 1024             close (fd);
 1025             return -1;
 1026         }
 1027     }
 1028 
 1029 #ifdef SO_BINDTODEVICE
 1030     if (obj->device != NULL)
 1031     {
 1032         if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
 1033                 obj->device, strlen (obj->device) + 1) != 0)
 1034         {
 1035             ping_set_errno (obj, errno);
 1036 #if WITH_DEBUG
 1037             char errbuf[PING_ERRMSG_LEN];
 1038             dprintf ("setsockopt (SO_BINDTODEVICE): %s\n",
 1039                     sstrerror (errno, errbuf, sizeof (errbuf)));
 1040 #endif
 1041             close (fd);
 1042             return -1;
 1043         }
 1044     }
 1045 #endif /* SO_BINDTODEVICE */
 1046 #ifdef SO_MARK
 1047     if (obj->set_mark)
 1048     {
 1049         if (setsockopt(fd, SOL_SOCKET, SO_MARK,
 1050                 &obj->mark, sizeof(obj->mark)) != 0)
 1051         {
 1052             ping_set_errno (obj, errno);
 1053 #if WITH_DEBUG
 1054             char errbuf[PING_ERRMSG_LEN];
 1055             dprintf ("setsockopt (SO_MARK): %s\n",
 1056                  sstrerror (errno, errbuf, sizeof (errbuf)));
 1057 #endif
 1058             close (fd);
 1059             return -1;
 1060         }
 1061     }
 1062 #endif
 1063 #ifdef SO_TIMESTAMP
 1064     if (1) /* {{{ */
 1065     {
 1066         int status = setsockopt (fd, SOL_SOCKET, SO_TIMESTAMP,
 1067                                  &(int){1}, sizeof(int));
 1068         if (status != 0)
 1069         {
 1070             ping_set_errno (obj, errno);
 1071 #if WITH_DEBUG
 1072             char errbuf[PING_ERRMSG_LEN];
 1073             dprintf ("setsockopt (SO_TIMESTAMP): %s\n",
 1074                     sstrerror (errno, errbuf, sizeof (errbuf)));
 1075 #endif
 1076             close (fd);
 1077             return -1;
 1078         }
 1079     } /* }}} if (1) */
 1080 #endif /* SO_TIMESTAMP */
 1081 
 1082     if (addrfam == AF_INET)
 1083     {
 1084 #ifdef IP_RECVTOS
 1085         /* Enable receiving the TOS field */
 1086         setsockopt (fd, IPPROTO_IP, IP_RECVTOS, &(int){1}, sizeof(int));
 1087 #endif /* IP_RECVTOS */
 1088 
 1089         /* Enable receiving the TTL field */
 1090         setsockopt (fd, IPPROTO_IP, IP_RECVTTL, &(int){1}, sizeof(int));
 1091     }
 1092 #if defined(IPV6_RECVHOPLIMIT) || defined(IPV6_RECVTCLASS)
 1093     else if (addrfam == AF_INET6)
 1094     {
 1095 # if defined(IPV6_RECVHOPLIMIT)
 1096         /* For details see RFC 3542, section 6.3. */
 1097         setsockopt (fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
 1098                     &(int){1}, sizeof(int));
 1099 # endif /* IPV6_RECVHOPLIMIT */
 1100 
 1101 # if defined(IPV6_RECVTCLASS)
 1102         /* For details see RFC 3542, section 6.5. */
 1103         setsockopt (fd, IPPROTO_IPV6, IPV6_RECVTCLASS,
 1104                     &(int){1}, sizeof(int));
 1105 # endif /* IPV6_RECVTCLASS */
 1106     }
 1107 #endif /* IPV6_RECVHOPLIMIT || IPV6_RECVTCLASS */
 1108 
 1109     return fd;
 1110 }
 1111 
 1112 /*
 1113  * public methods
 1114  */
 1115 const char *ping_get_error (pingobj_t *obj)
 1116 {
 1117     if (obj == NULL)
 1118         return (NULL);
 1119     return (obj->errmsg);
 1120 }
 1121 
 1122 pingobj_t *ping_construct (void)
 1123 {
 1124     pingobj_t *obj;
 1125 
 1126     if ((obj = malloc (sizeof (*obj))) == NULL)
 1127         return (NULL);
 1128     memset (obj, 0, sizeof (*obj));
 1129 
 1130     obj->timeout    = PING_DEF_TIMEOUT;
 1131     obj->ttl        = PING_DEF_TTL;
 1132     obj->addrfamily = PING_DEF_AF;
 1133     obj->data       = strdup (PING_DEF_DATA);
 1134     obj->qos        = 0;
 1135     obj->fd4        = -1;
 1136     obj->fd6        = -1;
 1137 
 1138     return (obj);
 1139 }
 1140 
 1141 void ping_destroy (pingobj_t *obj)
 1142 {
 1143     pinghost_t *current;
 1144 
 1145     if (obj == NULL)
 1146         return;
 1147 
 1148     current = obj->head;
 1149 
 1150     while (current != NULL)
 1151     {
 1152         pinghost_t *next = current->next;
 1153         ping_free (current);
 1154         current = next;
 1155     }
 1156 
 1157     free (obj->data);
 1158     free (obj->srcaddr);
 1159     free (obj->device);
 1160 
 1161     if (obj->fd4 != -1)
 1162         close(obj->fd4);
 1163 
 1164     if (obj->fd6 != -1)
 1165         close(obj->fd6);
 1166 
 1167     free (obj);
 1168 
 1169     return;
 1170 }
 1171 
 1172 int ping_setopt (pingobj_t *obj, int option, void *value)
 1173 {
 1174     int ret = 0;
 1175 
 1176     if ((obj == NULL) || (value == NULL))
 1177         return (-1);
 1178 
 1179     switch (option)
 1180     {
 1181         case PING_OPT_QOS:
 1182         {
 1183             obj->qos = *((uint8_t *) value);
 1184             ret = ping_set_qos (obj, obj->qos);
 1185             break;
 1186         }
 1187 
 1188         case PING_OPT_TIMEOUT:
 1189             obj->timeout = *((double *) value);
 1190             if (obj->timeout < 0.0)
 1191             {
 1192                 obj->timeout = PING_DEF_TIMEOUT;
 1193                 ret = -1;
 1194             }
 1195             break;
 1196 
 1197         case PING_OPT_TTL:
 1198             ret = *((int *) value);
 1199             if ((ret < 1) || (ret > 255))
 1200             {
 1201                 obj->ttl = PING_DEF_TTL;
 1202                 ret = -1;
 1203             }
 1204             else
 1205             {
 1206                 obj->ttl = ret;
 1207                 ret = ping_set_ttl (obj, obj->ttl);
 1208             }
 1209             break;
 1210 
 1211         case PING_OPT_AF:
 1212             obj->addrfamily = *((int *) value);
 1213             if ((obj->addrfamily != AF_UNSPEC)
 1214                     && (obj->addrfamily != AF_INET)
 1215                     && (obj->addrfamily != AF_INET6))
 1216             {
 1217                 obj->addrfamily = PING_DEF_AF;
 1218                 ret = -1;
 1219             }
 1220             if (obj->srcaddr != NULL)
 1221             {
 1222                 free (obj->srcaddr);
 1223                 obj->srcaddr = NULL;
 1224             }
 1225             break;
 1226 
 1227         case PING_OPT_DATA:
 1228             if (obj->data != NULL)
 1229             {
 1230                 free (obj->data);
 1231                 obj->data = NULL;
 1232             }
 1233             obj->data = strdup ((const char *) value);
 1234             break;
 1235 
 1236         case PING_OPT_SOURCE:
 1237         {
 1238             char            *hostname = (char *) value;
 1239             struct addrinfo  ai_hints;
 1240             struct addrinfo *ai_list;
 1241             int              status;
 1242 #if WITH_DEBUG
 1243             if (obj->addrfamily != AF_UNSPEC)
 1244             {
 1245                 dprintf ("Resetting obj->addrfamily to AF_UNSPEC.\n");
 1246             }
 1247 #endif
 1248             memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
 1249             ai_hints.ai_family = obj->addrfamily = AF_UNSPEC;
 1250 #if defined(AI_ADDRCONFIG)
 1251             ai_hints.ai_flags = AI_ADDRCONFIG;
 1252 #endif
 1253             status = getaddrinfo (hostname, NULL, &ai_hints, &ai_list);
 1254             if (status != 0)
 1255             {
 1256 #if defined(EAI_SYSTEM)
 1257                 char errbuf[PING_ERRMSG_LEN];
 1258 #endif
 1259                 ping_set_error (obj, "getaddrinfo",
 1260 #if defined(EAI_SYSTEM)
 1261                         (status == EAI_SYSTEM)
 1262                         ? sstrerror (errno, errbuf, sizeof (errbuf)) :
 1263 #endif
 1264                         gai_strerror (status));
 1265                 ret = -1;
 1266                 break;
 1267             }
 1268 #if WITH_DEBUG
 1269             if (ai_list->ai_next != NULL)
 1270             {
 1271                 dprintf ("hostname = `%s' is ambiguous.\n", hostname);
 1272             }
 1273 #endif
 1274             if (obj->srcaddr == NULL)
 1275             {
 1276                 obj->srcaddrlen = 0;
 1277                 obj->srcaddr = malloc (sizeof (struct sockaddr_storage));
 1278                 if (obj->srcaddr == NULL)
 1279                 {
 1280                     ping_set_errno (obj, errno);
 1281                     ret = -1;
 1282                     freeaddrinfo (ai_list);
 1283                     break;
 1284                 }
 1285             }
 1286             memset ((void *) obj->srcaddr, 0, sizeof (struct sockaddr_storage));
 1287             assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
 1288             memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
 1289                     ai_list->ai_addrlen);
 1290             obj->srcaddrlen = ai_list->ai_addrlen;
 1291             obj->addrfamily = ai_list->ai_family;
 1292 
 1293             freeaddrinfo (ai_list);
 1294         } /* case PING_OPT_SOURCE */
 1295         break;
 1296 
 1297         case PING_OPT_DEVICE:
 1298         {
 1299 #ifdef SO_BINDTODEVICE
 1300             char *device = strdup ((char *) value);
 1301 
 1302             if (device == NULL)
 1303             {
 1304                 ping_set_errno (obj, errno);
 1305                 ret = -1;
 1306                 break;
 1307             }
 1308 
 1309             if (obj->device != NULL)
 1310                 free (obj->device);
 1311             obj->device = device;
 1312 #else /* ! SO_BINDTODEVICE */
 1313             ping_set_errno (obj, ENOTSUP);
 1314             ret = -1;
 1315 #endif /* ! SO_BINDTODEVICE */
 1316         } /* case PING_OPT_DEVICE */
 1317         break;
 1318 
 1319         case PING_OPT_MARK:
 1320         {
 1321 #ifdef SO_MARK
 1322             obj->mark     = *(int*)(value);
 1323             obj->set_mark = 1;
 1324 #else /* SO_MARK */
 1325             ping_set_errno (obj, ENOTSUP);
 1326             ret = -1;
 1327 #endif /* !SO_MARK */
 1328 
 1329         } /* case PING_OPT_MARK */
 1330         break;
 1331 
 1332         default:
 1333             ret = -2;
 1334     } /* switch (option) */
 1335 
 1336     return (ret);
 1337 } /* int ping_setopt */
 1338 
 1339 int ping_send (pingobj_t *obj)
 1340 {
 1341     pinghost_t *ptr;
 1342 
 1343     struct timeval endtime;
 1344     struct timeval nowtime;
 1345     struct timeval timeout;
 1346 
 1347     _Bool need_ipv4_socket = 0;
 1348     _Bool need_ipv6_socket = 0;
 1349 
 1350     for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
 1351     {
 1352         ptr->latency  = -1.0;
 1353         ptr->recv_ttl = -1;
 1354 
 1355         if (ptr->addrfamily == AF_INET)
 1356             need_ipv4_socket = 1;
 1357         else if (ptr->addrfamily == AF_INET6)
 1358             need_ipv6_socket = 1;
 1359     }
 1360 
 1361     if (!need_ipv4_socket && !need_ipv6_socket)
 1362     {
 1363         ping_set_error (obj, "ping_send", "No hosts to ping");
 1364         return (-1);
 1365     }
 1366 
 1367     if (need_ipv4_socket && obj->fd4 == -1)
 1368     {
 1369         obj->fd4 = ping_open_socket(obj, AF_INET);
 1370         if (obj->fd4 == -1)
 1371             return (-1);
 1372         ping_set_ttl (obj, obj->ttl);
 1373         ping_set_qos (obj, obj->qos);
 1374     }
 1375     if (need_ipv6_socket && obj->fd6 == -1)
 1376     {
 1377         obj->fd6 = ping_open_socket(obj, AF_INET6);
 1378         if (obj->fd6 == -1)
 1379             return (-1);
 1380         ping_set_ttl (obj, obj->ttl);
 1381         ping_set_qos (obj, obj->qos);
 1382     }
 1383 
 1384     if (gettimeofday (&nowtime, NULL) == -1)
 1385     {
 1386         ping_set_errno (obj, errno);
 1387         return (-1);
 1388     }
 1389 
 1390     /* Set up timeout */
 1391     timeout.tv_sec = (time_t) obj->timeout;
 1392     timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec)));
 1393 
 1394     dprintf ("Set timeout to %i.%06i seconds\n",
 1395             (int) timeout.tv_sec,
 1396             (int) timeout.tv_usec);
 1397 
 1398     ping_timeval_add (&nowtime, &timeout, &endtime);
 1399 
 1400     /* host_to_ping points to the host to which to send the next ping. The
 1401      * pointer is advanced to the next host in the linked list after the
 1402      * ping has been sent. If host_to_ping is NULL, no more pings need to be
 1403      * send out. */
 1404     pinghost_t *host_to_ping = obj->head;
 1405 
 1406     /* pings_in_flight is the number of hosts we sent a "ping" to but didn't
 1407      * receive a "pong" yet. */
 1408     int pings_in_flight = 0;
 1409 
 1410     /* pongs_received is the number of echo replies received. Unless there
 1411      * is an error, this is used as the return value of ping_send(). */
 1412     int pongs_received = 0;
 1413 
 1414     int error_count = 0;
 1415 
 1416     while (pings_in_flight > 0 || host_to_ping != NULL)
 1417     {
 1418         fd_set read_fds;
 1419         fd_set write_fds;
 1420 
 1421         int write_fd = -1;
 1422         int max_fd = -1;
 1423 
 1424         FD_ZERO (&read_fds);
 1425         FD_ZERO (&write_fds);
 1426 
 1427         if (obj->fd4 != -1)
 1428         {
 1429             FD_SET(obj->fd4, &read_fds);
 1430             if (host_to_ping != NULL && host_to_ping->addrfamily == AF_INET)
 1431                 write_fd = obj->fd4;
 1432 
 1433             if (max_fd < obj->fd4)
 1434                 max_fd = obj->fd4;
 1435         }
 1436 
 1437         if (obj->fd6 != -1)
 1438         {
 1439             FD_SET(obj->fd6, &read_fds);
 1440             if (host_to_ping != NULL && host_to_ping->addrfamily == AF_INET6)
 1441                 write_fd = obj->fd6;
 1442 
 1443             if (max_fd < obj->fd6)
 1444                 max_fd = obj->fd6;
 1445         }
 1446 
 1447         if (write_fd != -1)
 1448             FD_SET(write_fd, &write_fds);
 1449 
 1450         assert (max_fd != -1);
 1451         assert (max_fd < FD_SETSIZE);
 1452 
 1453         if (gettimeofday (&nowtime, NULL) == -1)
 1454         {
 1455             ping_set_errno (obj, errno);
 1456             return (-1);
 1457         }
 1458 
 1459         if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
 1460             break;
 1461 
 1462         dprintf ("Waiting on %i sockets for %u.%06u seconds\n",
 1463                 ((obj->fd4 != -1) ? 1 : 0) + ((obj->fd6 != -1) ? 1 : 0),
 1464                 (unsigned) timeout.tv_sec,
 1465                 (unsigned) timeout.tv_usec);
 1466 
 1467         int status = select (max_fd + 1, &read_fds, &write_fds, NULL, &timeout);
 1468 
 1469         if (gettimeofday (&nowtime, NULL) == -1)
 1470         {
 1471             ping_set_errno (obj, errno);
 1472             return (-1);
 1473         }
 1474 
 1475         if (status == -1)
 1476         {
 1477             ping_set_errno (obj, errno);
 1478             dprintf ("select: %s\n", obj->errmsg);
 1479             return (-1);
 1480         }
 1481         else if (status == 0)
 1482         {
 1483             dprintf ("select timed out\n");
 1484 
 1485             pinghost_t *ph;
 1486             for (ph = obj->head; ph != NULL; ph = ph->next)
 1487                 if (ph->latency < 0.0)
 1488                     ph->dropped++;
 1489             break;
 1490         }
 1491 
 1492         /* first, check if we can receive a reply ... */
 1493         if (obj->fd6  != -1 && FD_ISSET (obj->fd6, &read_fds))
 1494         {
 1495             if (ping_receive_one (obj, &nowtime, AF_INET6) == 0)
 1496             {
 1497                 pings_in_flight--;
 1498                 pongs_received++;
 1499             }
 1500             continue;
 1501         }
 1502         if (obj->fd4 != -1 && FD_ISSET (obj->fd4, &read_fds))
 1503         {
 1504             if (ping_receive_one (obj, &nowtime, AF_INET) == 0)
 1505             {
 1506                 pings_in_flight--;
 1507                 pongs_received++;
 1508             }
 1509             continue;
 1510         }
 1511 
 1512         /* ... and if no reply is available to read, continue sending
 1513          * out pings. */
 1514 
 1515         /* this condition should always be true. We keep it for
 1516          * consistency with the read blocks above and just to be on the
 1517          * safe side. */
 1518         if (write_fd != -1 && FD_ISSET (write_fd, &write_fds))
 1519         {
 1520             if (ping_send_one (obj, host_to_ping, write_fd) == 0)
 1521                 pings_in_flight++;
 1522             else
 1523                 error_count++;
 1524             host_to_ping = host_to_ping->next;
 1525             continue;
 1526         }
 1527     } /* while (1) */
 1528 
 1529     if (error_count)
 1530         return (-1 * error_count);
 1531     return (pongs_received);
 1532 } /* int ping_send */
 1533 
 1534 static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
 1535 {
 1536     while (ph != NULL)
 1537     {
 1538         if (strcasecmp (ph->username, host) == 0)
 1539             break;
 1540 
 1541         ph = ph->next;
 1542     }
 1543 
 1544     return (ph);
 1545 }
 1546 
 1547 int ping_host_add (pingobj_t *obj, const char *host)
 1548 {
 1549     pinghost_t *ph;
 1550 
 1551     struct addrinfo  ai_hints;
 1552     struct addrinfo *ai_list, *ai_ptr;
 1553     int              ai_return;
 1554 
 1555     if ((obj == NULL) || (host == NULL))
 1556         return (-1);
 1557 
 1558     dprintf ("host = %s\n", host);
 1559 
 1560     if (ping_host_search (obj->head, host) != NULL)
 1561         return (0);
 1562 
 1563     memset (&ai_hints, '\0', sizeof (ai_hints));
 1564     ai_hints.ai_flags     = 0;
 1565 #ifdef AI_ADDRCONFIG
 1566     ai_hints.ai_flags    |= AI_ADDRCONFIG;
 1567 #endif
 1568 #ifdef AI_CANONNAME
 1569     ai_hints.ai_flags    |= AI_CANONNAME;
 1570 #endif
 1571     ai_hints.ai_family    = obj->addrfamily;
 1572     ai_hints.ai_socktype  = SOCK_RAW;
 1573 
 1574     if ((ph = ping_alloc ()) == NULL)
 1575     {
 1576         dprintf ("Out of memory!\n");
 1577         return (-1);
 1578     }
 1579 
 1580     if ((ph->username = strdup (host)) == NULL)
 1581     {
 1582         dprintf ("Out of memory!\n");
 1583         ping_set_errno (obj, errno);
 1584         ping_free (ph);
 1585         return (-1);
 1586     }
 1587 
 1588     if ((ph->hostname = strdup (host)) == NULL)
 1589     {
 1590         dprintf ("Out of memory!\n");
 1591         ping_set_errno (obj, errno);
 1592         ping_free (ph);
 1593         return (-1);
 1594     }
 1595 
 1596     /* obj->data is not garuanteed to be != NULL */
 1597     if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL)
 1598     {
 1599         dprintf ("Out of memory!\n");
 1600         ping_set_errno (obj, errno);
 1601         ping_free (ph);
 1602         return (-1);
 1603     }
 1604 
 1605     if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
 1606     {
 1607 #if defined(EAI_SYSTEM)
 1608         char errbuf[PING_ERRMSG_LEN];
 1609 #endif
 1610         dprintf ("getaddrinfo failed\n");
 1611         ping_set_error (obj, "getaddrinfo",
 1612 #if defined(EAI_SYSTEM)
 1613                         (ai_return == EAI_SYSTEM)
 1614                         ? sstrerror (errno, errbuf, sizeof (errbuf)) :
 1615 #endif
 1616                 gai_strerror (ai_return));
 1617         ping_free (ph);
 1618         return (-1);
 1619     }
 1620 
 1621     if (ai_list == NULL)
 1622         ping_set_error (obj, "getaddrinfo", "No hosts returned");
 1623 
 1624     for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
 1625     {
 1626         if (ai_ptr->ai_family == AF_INET)
 1627         {
 1628             ai_ptr->ai_socktype = SOCK_RAW;
 1629             ai_ptr->ai_protocol = IPPROTO_ICMP;
 1630         }
 1631         else if (ai_ptr->ai_family == AF_INET6)
 1632         {
 1633             ai_ptr->ai_socktype = SOCK_RAW;
 1634             ai_ptr->ai_protocol = IPPROTO_ICMPV6;
 1635         }
 1636         else
 1637         {
 1638             char errmsg[PING_ERRMSG_LEN];
 1639 
 1640             snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family);
 1641             errmsg[PING_ERRMSG_LEN - 1] = '\0';
 1642 
 1643             dprintf ("%s", errmsg);
 1644             ping_set_error (obj, "getaddrinfo", errmsg);
 1645             continue;
 1646         }
 1647 
 1648         assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
 1649         memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
 1650         memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
 1651         ph->addrlen = ai_ptr->ai_addrlen;
 1652         ph->addrfamily = ai_ptr->ai_family;
 1653 
 1654 #ifdef AI_CANONNAME
 1655         if ((ai_ptr->ai_canonname != NULL)
 1656                 && (strcmp (ph->hostname, ai_ptr->ai_canonname) != 0))
 1657         {
 1658             char *old_hostname;
 1659 
 1660             dprintf ("ph->hostname = %s; ai_ptr->ai_canonname = %s;\n",
 1661                     ph->hostname, ai_ptr->ai_canonname);
 1662 
 1663             old_hostname = ph->hostname;
 1664             if ((ph->hostname = strdup (ai_ptr->ai_canonname)) == NULL)
 1665             {
 1666                 /* strdup failed, falling back to old hostname */
 1667                 ph->hostname = old_hostname;
 1668             }
 1669             else if (old_hostname != NULL)
 1670             {
 1671                 free (old_hostname);
 1672             }
 1673         }
 1674 #endif /* AI_CANONNAME */
 1675     } /* for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */
 1676 
 1677     freeaddrinfo (ai_list);
 1678 
 1679     /*
 1680      * Adding in the front is much easier, but then the iterator will
 1681      * return the host that was added last as first host. That's just not
 1682      * nice. -octo
 1683      */
 1684     if (obj->head == NULL)
 1685     {
 1686         obj->head = ph;
 1687     }
 1688     else
 1689     {
 1690         pinghost_t *hptr;
 1691 
 1692         hptr = obj->head;
 1693         while (hptr->next != NULL)
 1694             hptr = hptr->next;
 1695 
 1696         assert ((hptr != NULL) && (hptr->next == NULL));
 1697         hptr->next = ph;
 1698     }
 1699 
 1700     ph->table_next = obj->table[ph->ident % PING_TABLE_LEN];
 1701     obj->table[ph->ident % PING_TABLE_LEN] = ph;
 1702 
 1703     return (0);
 1704 } /* int ping_host_add */
 1705 
 1706 int ping_host_remove (pingobj_t *obj, const char *host)
 1707 {
 1708     pinghost_t *pre, *cur, *target;
 1709 
 1710     if ((obj == NULL) || (host == NULL))
 1711         return (-1);
 1712 
 1713     pre = NULL;
 1714     cur = obj->head;
 1715 
 1716     while (cur != NULL)
 1717     {
 1718         if (strcasecmp (host, cur->username) == 0)
 1719             break;
 1720 
 1721         pre = cur;
 1722         cur = cur->next;
 1723     }
 1724 
 1725     if (cur == NULL)
 1726     {
 1727         ping_set_error (obj, "ping_host_remove", "Host not found");
 1728         return (-1);
 1729     }
 1730 
 1731     if (pre == NULL)
 1732         obj->head = cur->next;
 1733     else
 1734         pre->next = cur->next;
 1735 
 1736     target = cur;
 1737     pre = NULL;
 1738 
 1739     cur = obj->table[target->ident % PING_TABLE_LEN];
 1740 
 1741     while (cur != NULL)
 1742     {
 1743         if (cur == target)
 1744             break;
 1745 
 1746         pre = cur;
 1747         cur = cur->table_next;
 1748     }
 1749 
 1750     if (cur == NULL)
 1751     {
 1752         ping_set_error(obj, "ping_host_remove", "Host not found (T)");
 1753         ping_free(target);
 1754         return (-1);
 1755     }
 1756 
 1757     if (pre == NULL)
 1758         obj->table[target->ident % PING_TABLE_LEN] = cur->table_next;
 1759     else
 1760         pre->table_next = cur->table_next;
 1761 
 1762     ping_free (cur);
 1763 
 1764     return (0);
 1765 }
 1766 
 1767 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
 1768 {
 1769     if (obj == NULL)
 1770         return (NULL);
 1771     return ((pingobj_iter_t *) obj->head);
 1772 }
 1773 
 1774 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
 1775 {
 1776     if (iter == NULL)
 1777         return (NULL);
 1778     return ((pingobj_iter_t *) iter->next);
 1779 }
 1780 
 1781 int ping_iterator_count (pingobj_t *obj)
 1782 {
 1783     if (obj == NULL)
 1784         return 0;
 1785 
 1786     int count = 0;
 1787     pingobj_iter_t *iter = obj->head;
 1788     while (iter) {
 1789         count++;
 1790         iter = iter->next;
 1791     }
 1792     return count;
 1793 }
 1794 
 1795 int ping_iterator_get_info (pingobj_iter_t *iter, int info,
 1796         void *buffer, size_t *buffer_len)
 1797 {
 1798     int ret = EINVAL;
 1799 
 1800     size_t orig_buffer_len = *buffer_len;
 1801 
 1802     if ((iter == NULL) || (buffer_len == NULL))
 1803         return (-1);
 1804 
 1805     if ((buffer == NULL) && (*buffer_len != 0 ))
 1806         return (-1);
 1807 
 1808     switch (info)
 1809     {
 1810         case PING_INFO_USERNAME:
 1811             ret = ENOMEM;
 1812             *buffer_len = strlen (iter->username) + 1;
 1813             if (orig_buffer_len <= *buffer_len)
 1814                 break;
 1815             /* Since (orig_buffer_len > *buffer_len) `strncpy'
 1816              * will copy `*buffer_len' and pad the rest of
 1817              * `buffer' with null-bytes */
 1818             strncpy (buffer, iter->username, orig_buffer_len);
 1819             ret = 0;
 1820             break;
 1821 
 1822         case PING_INFO_HOSTNAME:
 1823             ret = ENOMEM;
 1824             *buffer_len = strlen (iter->hostname) + 1;
 1825             if (orig_buffer_len < *buffer_len)
 1826                 break;
 1827             /* Since (orig_buffer_len > *buffer_len) `strncpy'
 1828              * will copy `*buffer_len' and pad the rest of
 1829              * `buffer' with null-bytes */
 1830             strncpy (buffer, iter->hostname, orig_buffer_len);
 1831             ret = 0;
 1832             break;
 1833 
 1834         case PING_INFO_ADDRESS:
 1835             ret = getnameinfo ((struct sockaddr *) iter->addr,
 1836                     iter->addrlen,
 1837                     (char *) buffer,
 1838                     *buffer_len,
 1839                     NULL, 0,
 1840                     NI_NUMERICHOST);
 1841             if (ret != 0)
 1842             {
 1843                 if ((ret == EAI_MEMORY)
 1844 #ifdef EAI_OVERFLOW
 1845                         || (ret == EAI_OVERFLOW)
 1846 #endif
 1847                    )
 1848                     ret = ENOMEM;
 1849 #if defined(EAI_SYSTEM)
 1850                 else if (ret == EAI_SYSTEM)
 1851                     ret = errno;
 1852 #endif
 1853                 else
 1854                     ret = EINVAL;
 1855             }
 1856             break;
 1857 
 1858         case PING_INFO_FAMILY:
 1859             ret = ENOMEM;
 1860             *buffer_len = sizeof (int);
 1861             if (orig_buffer_len < sizeof (int))
 1862                 break;
 1863             *((int *) buffer) = iter->addrfamily;
 1864             ret = 0;
 1865             break;
 1866 
 1867         case PING_INFO_LATENCY:
 1868             ret = ENOMEM;
 1869             *buffer_len = sizeof (double);
 1870             if (orig_buffer_len < sizeof (double))
 1871                 break;
 1872             *((double *) buffer) = iter->latency;
 1873             ret = 0;
 1874             break;
 1875 
 1876         case PING_INFO_DROPPED:
 1877             ret = ENOMEM;
 1878             *buffer_len = sizeof (uint32_t);
 1879             if (orig_buffer_len < sizeof (uint32_t))
 1880                 break;
 1881             *((uint32_t *) buffer) = iter->dropped;
 1882             ret = 0;
 1883             break;
 1884 
 1885         case PING_INFO_SEQUENCE:
 1886             ret = ENOMEM;
 1887             *buffer_len = sizeof (unsigned int);
 1888             if (orig_buffer_len < sizeof (unsigned int))
 1889                 break;
 1890             *((unsigned int *) buffer) = (unsigned int) iter->sequence;
 1891             ret = 0;
 1892             break;
 1893 
 1894         case PING_INFO_IDENT:
 1895             ret = ENOMEM;
 1896             *buffer_len = sizeof (uint16_t);
 1897             if (orig_buffer_len < sizeof (uint16_t))
 1898                 break;
 1899             *((uint16_t *) buffer) = (uint16_t) iter->ident;
 1900             ret = 0;
 1901             break;
 1902 
 1903         case PING_INFO_DATA:
 1904             ret = ENOMEM;
 1905             *buffer_len = strlen (iter->data);
 1906             if (orig_buffer_len < *buffer_len)
 1907                 break;
 1908             strncpy ((char *) buffer, iter->data, orig_buffer_len);
 1909             ret = 0;
 1910             break;
 1911 
 1912         case PING_INFO_RECV_TTL:
 1913             ret = ENOMEM;
 1914             *buffer_len = sizeof (int);
 1915             if (orig_buffer_len < sizeof (int))
 1916                 break;
 1917             *((int *) buffer) = iter->recv_ttl;
 1918             ret = 0;
 1919             break;
 1920 
 1921         case PING_INFO_RECV_QOS:
 1922             ret = ENOMEM;
 1923             if (*buffer_len>sizeof(unsigned)) *buffer_len=sizeof(unsigned);
 1924             if (!*buffer_len) *buffer_len=1;
 1925             if (orig_buffer_len < *buffer_len)
 1926                 break;
 1927             memcpy(buffer,&iter->recv_qos,*buffer_len);
 1928             ret = 0;
 1929             break;
 1930     }
 1931 
 1932     return (ret);
 1933 } /* ping_iterator_get_info */
 1934 
 1935 void *ping_iterator_get_context (pingobj_iter_t *iter)
 1936 {
 1937     if (iter == NULL)
 1938         return (NULL);
 1939     return (iter->context);
 1940 }
 1941 
 1942 void ping_iterator_set_context (pingobj_iter_t *iter, void *context)
 1943 {
 1944     if (iter == NULL)
 1945         return;
 1946     iter->context = context;
 1947 }