Code

de66cd532f6ae940fde370e9a227647c835b6ca0
[collectd.git] / src / liboping / liboping.c
1 /**
2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
3  * Copyright (C) 2006  Florian octo Forster <octo at verplant.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  */
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
24 #if STDC_HEADERS
25 # include <stdlib.h>
26 # include <stdio.h>
27 # include <string.h>
28 # include <errno.h>
29 # include <assert.h>
30 #else
31 # error "You don't have the standard C99 header files installed"
32 #endif /* STDC_HEADERS */
34 #if HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
38 #if HAVE_FCNTL_H
39 # include <fcntl.h>
40 #endif
41 #if HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 #endif
44 #if HAVE_SYS_STAT_H
45 # include <sys/stat.h>
46 #endif
48 #if TIME_WITH_SYS_TIME
49 # include <sys/time.h>
50 # include <time.h>
51 #endif
53 #if HAVE_SYS_SOCKET_H
54 # include <sys/socket.h>
55 #endif
56 #if HAVE_NETDB_H
57 # include <netdb.h>
58 #endif
60 #if HAVE_NETINET_IN_SYSTM_H
61 # include <netinet/in_systm.h>
62 #endif
63 #if HAVE_NETINET_IN_H
64 # include <netinet/in.h>
65 #endif
66 #if HAVE_NETINET_IP_H
67 # include <netinet/ip.h>
68 #endif
69 #if HAVE_NETINET_IP_ICMP_H
70 # include <netinet/ip_icmp.h>
71 #endif
72 #ifdef HAVE_NETINET_IP_VAR_H
73 # include <netinet/ip_var.h>
74 #endif
75 #if HAVE_NETINET_IP6_H
76 # include <netinet/ip6.h>
77 #endif
78 #if HAVE_NETINET_ICMP6_H
79 # include <netinet/icmp6.h>
80 #endif
82 #include "liboping.h"
84 #if DEBUG
85 # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
86 #else
87 # define dprintf(...) /**/
88 #endif
90 #define PING_ERRMSG_LEN 256
92 #define PING_DATA "Florian Forster <octo@verplant.org> http://verplant.org/"
94 struct pinghost
95 {
96         char                    *hostname;
97         struct sockaddr_storage *addr;
98         socklen_t                addrlen;
99         int                      addrfamily;
100         int                      fd;
101         int                      ident;
102         int                      sequence;
103         struct timeval          *timer;
104         double                   latency;
106         struct pinghost         *next;
107 };
109 struct pingobj
111         double      timeout;
112         int         ttl;
113         int         addrfamily;
115         char        errmsg[PING_ERRMSG_LEN];
117         pinghost_t *head;
118 };
120 /*
121  * private (static) functions
122  */
123 static void ping_set_error (pingobj_t *obj, const char *function,
124                 const char *message)
126         snprintf (obj->errmsg, PING_ERRMSG_LEN, "%s: %s", function, message);
127         obj->errmsg[PING_ERRMSG_LEN - 1] = '\0';
130 static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
131                 struct timeval *res)
133         res->tv_sec  = tv1->tv_sec  + tv2->tv_sec;
134         res->tv_usec = tv1->tv_usec + tv2->tv_usec;
136         while (res->tv_usec > 1000000)
137         {
138                 res->tv_usec -= 1000000;
139                 res->tv_sec++;
140         }
142         return (0);
145 static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
146                 struct timeval *res)
149         if ((tv1->tv_sec < tv2->tv_sec)
150                         || ((tv1->tv_sec == tv2->tv_sec)
151                                 && (tv1->tv_usec < tv2->tv_usec)))
152                 return (-1);
154         res->tv_sec  = tv1->tv_sec  - tv2->tv_sec;
155         res->tv_usec = tv1->tv_usec - tv2->tv_usec;
157         assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0)));
159         while (res->tv_usec < 0)
160         {
161                 res->tv_usec += 1000000;
162                 res->tv_sec--;
163         }
165         return (0);
168 static uint16_t ping_icmp4_checksum (char *buf, size_t len)
170         uint32_t sum = 0;
171         uint16_t ret = 0;
173         uint16_t *ptr;
175         for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
176                 sum += *ptr;
178         if (len == 1)
179         {
180                 *(char *) &ret = *(char *) ptr;
181                 sum += ret;
182         }
184         /* Do this twice to get all possible carries.. */
185         sum = (sum >> 16) + (sum & 0xFFFF);
186         sum = (sum >> 16) + (sum & 0xFFFF);
188         ret = ~sum;
190         return (ret);
193 static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len)
195         struct ip *ip_hdr;
196         struct icmp *icmp_hdr;
198         size_t ip_hdr_len;
200         uint16_t recv_checksum;
201         uint16_t calc_checksum;
203         uint16_t ident;
204         uint16_t seq;
206         pinghost_t *ptr;
208         if (buffer_len < sizeof (struct ip))
209                 return (NULL);
211         ip_hdr     = (struct ip *) buffer;
212         ip_hdr_len = ip_hdr->ip_hl << 2;
214         if (buffer_len < ip_hdr_len)
215                 return (NULL);
217         buffer     += ip_hdr_len;
218         buffer_len -= ip_hdr_len;
220         if (buffer_len < sizeof (struct icmp))
221                 return (NULL);
223         icmp_hdr = (struct icmp *) buffer;
224         buffer     += sizeof (struct icmp);
225         buffer_len -= sizeof (struct icmp);
227         if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
228         {
229                 dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type);
230                 return (NULL);
231         }
233         recv_checksum = icmp_hdr->icmp_cksum;
234         icmp_hdr->icmp_cksum = 0;
235         calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr,
236                         sizeof (struct icmp) + buffer_len);
238         if (recv_checksum != calc_checksum)
239         {
240                 dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n",
241                                 recv_checksum, calc_checksum);
242                 return (NULL);
243         }
245         ident = ntohs (icmp_hdr->icmp_id);
246         seq   = ntohs (icmp_hdr->icmp_seq);
248         for (ptr = ph; ptr != NULL; ptr = ptr->next)
249         {
250                 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
251                                 ptr->hostname, ptr->ident, ptr->sequence - 1);
253                 if (ptr->addrfamily != AF_INET)
254                         continue;
256                 if (!timerisset (ptr->timer))
257                         continue;
259                 if (ptr->ident != ident)
260                         continue;
262                 if ((ptr->sequence - 1) != seq)
263                         continue;
265                 dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
266                                 ptr->hostname, ident, seq);
268                 break;
269         }
271         if (ptr == NULL)
272         {
273                 dprintf ("No match found for ident = 0x%04x, seq = %i\n",
274                                 ident, seq);
275         }
277         return (ptr);
280 static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
282         struct icmp6_hdr *icmp_hdr;
284         uint16_t ident;
285         uint16_t seq;
287         pinghost_t *ptr;
289         if (buffer_len < sizeof (struct icmp6_hdr))
290                 return (NULL);
292         icmp_hdr = (struct icmp6_hdr *) buffer;
293         buffer     += sizeof (struct icmp);
294         buffer_len -= sizeof (struct icmp);
296         if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
297         {
298                 dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type);
299                 return (NULL);
300         }
302         if (icmp_hdr->icmp6_code != 0)
303         {
304                 dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code);
305                 return (NULL);
306         }
308         ident = ntohs (icmp_hdr->icmp6_id);
309         seq   = ntohs (icmp_hdr->icmp6_seq);
311         for (ptr = ph; ptr != NULL; ptr = ptr->next)
312         {
313                 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
314                                 ptr->hostname, ptr->ident, ptr->sequence - 1);
316                 if (ptr->addrfamily != AF_INET6)
317                         continue;
319                 if (!timerisset (ptr->timer))
320                         continue;
322                 if (ptr->ident != ident)
323                         continue;
325                 if ((ptr->sequence - 1) != seq)
326                         continue;
328                 dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
329                                 ptr->hostname, ident, seq);
331                 break;
332         }
334         if (ptr == NULL)
335         {
336                 dprintf ("No match found for ident = 0x%04x, seq = %i\n",
337                                 ident, seq);
338         }
340         return (ptr);
343 static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
345         char   buffer[4096];
346         size_t buffer_len;
348         struct timeval diff;
350         pinghost_t *host = NULL;
352         struct sockaddr_storage sa;
353         socklen_t               sa_len;
355         sa_len = sizeof (sa);
357         buffer_len = recvfrom (fd, buffer, sizeof (buffer), 0,
358                         (struct sockaddr *) &sa, &sa_len);
359         if (buffer_len == -1)
360         {
361                 dprintf ("recvfrom: %s\n", strerror (errno));
362                 return (-1);
363         }
365         dprintf ("Read %i bytes from fd = %i\n", buffer_len, fd);
367         if (sa.ss_family == AF_INET)
368         {
369                 if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL)
370                         return (-1);
371         }
372         else if (sa.ss_family == AF_INET6)
373         {
374                 if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL)
375                         return (-1);
376         }
378         dprintf ("rcvd: %12i.%06i\n",
379                         (int) now->tv_sec,
380                         (int) now->tv_usec);
381         dprintf ("sent: %12i.%06i\n",
382                         (int) host->timer->tv_sec,
383                         (int) host->timer->tv_usec);
385         if (ping_timeval_sub (now, host->timer, &diff) < 0)
386         {
387                 timerclear (host->timer);
388                 return (-1);
389         }
391         dprintf ("diff: %12i.%06i\n",
392                         (int) diff.tv_sec,
393                         (int) diff.tv_usec);
395         host->latency  = ((double) diff.tv_usec) / 1000.0;
396         host->latency += ((double) diff.tv_sec)  * 1000.0;
398         timerclear (host->timer);
400         return (0);
403 static int ping_receive_all (pingobj_t *obj)
405         fd_set readfds;
406         int num_readfds;
407         int max_readfds;
409         pinghost_t *ph;
410         pinghost_t *ptr;
412         struct timeval endtime;
413         struct timeval nowtime;
414         struct timeval timeout;
415         int status;
417         int ret;
419         ph = obj->head;
420         ret = 0;
422         for (ptr = ph; ptr != NULL; ptr = ptr->next)
423                 ptr->latency = -1.0;
425         if (gettimeofday (&nowtime, NULL) == -1)
426         {
427                 ping_set_error (obj, "gettimeofday", strerror (errno));
428                 return (-1);
429         }
431         /* Set up timeout */
432         timeout.tv_sec = (time_t) obj->timeout;
433         timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec)));
435         dprintf ("Set timeout to %i.%06i seconds\n",
436                         (int) timeout.tv_sec,
437                         (int) timeout.tv_usec);
439         ping_timeval_add (&nowtime, &timeout, &endtime);
441         while (1)
442         {
443                 FD_ZERO (&readfds);
444                 num_readfds =  0;
445                 max_readfds = -1;
447                 for (ptr = ph; ptr != NULL; ptr = ptr->next)
448                 {
449                         if (!timerisset (ptr->timer))
450                                 continue;
452                         FD_SET (ptr->fd, &readfds);
453                         num_readfds++;
455                         if (max_readfds < ptr->fd)
456                                 max_readfds = ptr->fd;
457                 }
459                 if (num_readfds == 0)
460                         break;
462                 if (gettimeofday (&nowtime, NULL) == -1)
463                 {
464                         ping_set_error (obj, "gettimeofday", strerror (errno));
465                         return (-1);
466                 }
468                 if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
469                         break;
471                 dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds,
472                                 (int) timeout.tv_sec,
473                                 (int) timeout.tv_usec);
475                 status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout);
477                 if (gettimeofday (&nowtime, NULL) == -1)
478                 {
479                         ping_set_error (obj, "gettimeofday", strerror (errno));
480                         return (-1);
481                 }
482                 
483                 if ((status == -1) && (errno == EINTR))
484                 {
485                         dprintf ("select was interrupted by signal..\n");
486                         continue;
487                 }
488                 else if (status < 0)
489                 {
490                         dprintf ("select: %s\n", strerror (errno));
491                         break;
492                 }
493                 else if (status == 0)
494                 {
495                         dprintf ("select timed out\n");
496                         break;
497                 }
499                 for (ptr = ph; ptr != NULL; ptr = ptr->next)
500                 {
501                         if (FD_ISSET (ptr->fd, &readfds))
502                                 if (ping_receive_one (ptr->fd, ph, &nowtime) == 0)
503                                         ret++;
504                 }
505         } /* while (1) */
506         
507         return (ret);
510 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
511  * Sending functions:                                                        *
512  *                                                                           *
513  * ping_send_all                                                             *
514  * +-> ping_send_one_ipv4                                                    *
515  * `-> ping_send_one_ipv6                                                    *
516  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
517 static ssize_t ping_sendto (pinghost_t *ph, const void *buf, size_t buflen)
519         ssize_t ret;
521         if (gettimeofday (ph->timer, NULL) == -1)
522         {
523                 timerclear (ph->timer);
524                 return (-1);
525         }
527         ret = sendto (ph->fd, buf, buflen, 0,
528                         (struct sockaddr *) ph->addr, ph->addrlen);
530         return (ret);
533 static int ping_send_one_ipv4 (pinghost_t *ph)
535         struct icmp *icmp4;
536         int status;
538         char buf[4096];
539         int  buflen;
541         char *data;
542         int   datalen;
544         dprintf ("ph->hostname = %s\n", ph->hostname);
546         memset (buf, '\0', sizeof (buf));
547         icmp4 = (struct icmp *) buf;
548         data  = (char *) (icmp4 + 1);
550         icmp4->icmp_type  = ICMP_ECHO;
551         icmp4->icmp_code  = 0;
552         icmp4->icmp_cksum = 0;
553         icmp4->icmp_id    = htons (ph->ident);
554         icmp4->icmp_seq   = htons (ph->sequence);
556         strcpy (data, PING_DATA);
557         datalen = strlen (data);
559         buflen = datalen + sizeof (struct icmp);
561         icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
563         dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
565         status = ping_sendto (ph, buf, buflen);
566         if (status < 0)
567         {
568                 perror ("ping_sendto");
569                 return (-1);
570         }
572         dprintf ("sendto: status = %i\n", status);
574         return (0);
577 static int ping_send_one_ipv6 (pinghost_t *ph)
579         struct icmp6_hdr *icmp6;
580         int status;
582         char buf[4096];
583         int  buflen;
585         char *data;
586         int   datalen;
588         dprintf ("ph->hostname = %s\n", ph->hostname);
590         memset (buf, '\0', sizeof (buf));
591         icmp6 = (struct icmp6_hdr *) buf;
592         data  = (char *) (icmp6 + 1);
594         icmp6->icmp6_type  = ICMP6_ECHO_REQUEST;
595         icmp6->icmp6_code  = 0;
596         /* The checksum will be calculated by the TCP/IP stack.  */
597         icmp6->icmp6_cksum = 0;
598         icmp6->icmp6_id    = htons (ph->ident);
599         icmp6->icmp6_seq   = htons (ph->sequence);
601         strcpy (data, PING_DATA);
602         datalen = strlen (data);
604         buflen = datalen + sizeof (struct icmp6_hdr);
606         dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
608         status = ping_sendto (ph, buf, buflen);
609         if (status < 0)
610         {
611                 perror ("ping_sendto");
612                 return (-1);
613         }
615         dprintf ("sendto: status = %i\n", status);
617         return (0);
620 static int ping_send_all (pinghost_t *ph)
622         pinghost_t *ptr;
624         for (ptr = ph; ptr != NULL; ptr = ptr->next)
625         {
626                 /* start timer.. The GNU `ping6' starts the timer before
627                  * sending the packet, so I will do that too */
628                 if (gettimeofday (ptr->timer, NULL) == -1)
629                 {
630                         dprintf ("gettimeofday: %s\n", strerror (errno));
631                         timerclear (ptr->timer);
632                         continue;
633                 }
634                 else
635                 {
636                         dprintf ("timer set for hostname = %s\n", ptr->hostname);
637                 }
639                 if (ptr->addrfamily == AF_INET6)
640                 {       
641                         dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname);
642                         if (ping_send_one_ipv6 (ptr) != 0)
643                         {
644                                 timerclear (ptr->timer);
645                                 continue;
646                         }
647                 }
648                 else if (ptr->addrfamily == AF_INET)
649                 {
650                         dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
651                         if (ping_send_one_ipv4 (ptr) != 0)
652                         {
653                                 timerclear (ptr->timer);
654                                 continue;
655                         }
656                 }
657                 else /* this should not happen */
658                 {
659                         dprintf ("Unknown address family: %i\n", ptr->addrfamily);
660                         timerclear (ptr->timer);
661                         continue;
662                 }
664                 ptr->sequence++;
665         }
667         /* FIXME */
668         return (0);
671 /*
672  * Set the TTL of a socket protocol independently.
673  */
674 static int ping_set_ttl (pinghost_t *ph, int ttl)
676         int ret = -2;
678         if (ph->addrfamily == AF_INET)
679         {
680                 ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl));
681         }
682         else if (ph->addrfamily == AF_INET6)
683         {
684                 ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl));
685         }
687         return (ret);
690 static int ping_get_ident (void)
692         int fd;
693         static int did_seed = 0;
695         int retval;
697         if (did_seed == 0)
698         {
699                 if ((fd = open ("/dev/urandom", O_RDONLY)) != -1)
700                 {
701                         unsigned int seed;
703                         if (read (fd, &seed, sizeof (seed)) != -1)
704                         {
705                                 did_seed = 1;
706                                 dprintf ("Random seed: %i\n", seed);
707                                 srandom (seed);
708                         }
710                         close (fd);
711                 }
712                 else
713                 {
714                         dprintf ("open (/dev/urandom): %s\n", strerror (errno));
715                 }
716         }
718         retval = (int) random ();
720         dprintf ("Random number: %i\n", retval);
721         
722         return (retval);
725 static pinghost_t *ping_alloc (void)
727         pinghost_t *ph;
728         size_t      ph_size;
730         ph_size = sizeof (pinghost_t)
731                 + sizeof (struct sockaddr_storage)
732                 + sizeof (struct timeval);
734         ph = (pinghost_t *) malloc (ph_size);
735         if (ph == NULL)
736                 return (NULL);
738         memset (ph, '\0', ph_size);
740         ph->timer   = (struct timeval *) (ph + 1);
741         ph->addr    = (struct sockaddr_storage *) (ph->timer + 1);
743         ph->addrlen = sizeof (struct sockaddr_storage);
744         ph->latency = -1.0;
745         ph->ident   = ping_get_ident () & 0xFFFF;
747         return (ph);
750 static void ping_free (pinghost_t *ph)
752         if (ph->hostname != NULL)
753                 free (ph->hostname);
755         free (ph);
758 /*
759  * public methods
760  */
761 const char *ping_get_error (pingobj_t *obj)
763         return (obj->errmsg);
766 pingobj_t *ping_construct (void)
768         pingobj_t *obj;
770         if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL)
771                 return (NULL);
772         memset (obj, '\0', sizeof (pingobj_t));
774         obj->timeout    = PING_DEF_TIMEOUT;
775         obj->ttl        = PING_DEF_TTL;
776         obj->addrfamily = PING_DEF_AF;
778         return (obj);
781 void ping_destroy (pingobj_t *obj)
783         pinghost_t *current;
784         pinghost_t *next;
786         current = obj->head;
787         next = NULL;
789         while (current != NULL)
790         {
791                 next = current->next;
792                 ping_free (current);
793                 current = next;
794         }
796         free (obj);
798         return;
801 int ping_setopt (pingobj_t *obj, int option, void *value)
803         int ret = 0;
805         switch (option)
806         {
807                 case PING_OPT_TIMEOUT:
808                         obj->timeout = *((double *) value);
809                         if (obj->timeout < 0.0)
810                         {
811                                 obj->timeout = PING_DEF_TIMEOUT;
812                                 ret = -1;
813                         }
814                         break;
816                 case PING_OPT_TTL:
817                         obj->ttl = *((int *) value);
818                         if ((obj->ttl < 1) || (obj->ttl > 255))
819                         {
820                                 obj->ttl = PING_DEF_TTL;
821                                 ret = -1;
822                         }
823                         break;
825                 case PING_OPT_AF:
826                         obj->addrfamily = *((int *) value);
827                         if ((obj->addrfamily != AF_UNSPEC)
828                                         && (obj->addrfamily != AF_INET)
829                                         && (obj->addrfamily != AF_INET6))
830                         {
831                                 obj->addrfamily = PING_DEF_AF;
832                                 ret = -1;
833                         }
834                         break;
836                 default:
837                         ret = -2;
838         } /* switch (option) */
840         return (ret);
841 } /* int ping_setopt */
844 int ping_send (pingobj_t *obj)
846         int ret;
848         if (ping_send_all (obj->head) < 0)
849                 return (-1);
851         if ((ret = ping_receive_all (obj)) < 0)
852                 return (-2);
854         return (ret);
857 static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
859         while (ph != NULL)
860         {
861                 if (strcasecmp (ph->hostname, host) == 0)
862                         break;
864                 ph = ph->next;
865         }
867         return (ph);
870 int ping_host_add (pingobj_t *obj, const char *host)
872         pinghost_t *ph;
874         struct sockaddr_storage sockaddr;
875         socklen_t               sockaddr_len;
877         struct addrinfo  ai_hints;
878         struct addrinfo *ai_list, *ai_ptr;
879         int              ai_return;
881         dprintf ("host = %s\n", host);
883         if (ping_host_search (obj->head, host) != NULL)
884                 return (0);
886         memset (&ai_hints, '\0', sizeof (ai_hints));
887         ai_hints.ai_flags     = 0;
888 #ifdef AI_ADDRCONFIG
889         ai_hints.ai_flags    |= AI_ADDRCONFIG;
890 #endif
891         ai_hints.ai_family    = obj->addrfamily;
892         ai_hints.ai_socktype  = SOCK_RAW;
894         if ((ph = ping_alloc ()) == NULL)
895         {
896                 dprintf ("Out of memory!\n");
897                 return (-1);
898         }
900         if ((ph->hostname = strdup (host)) == NULL)
901         {
902                 dprintf ("Out of memory!\n");
903                 ping_set_error (obj, "strdup", strerror (errno));
904                 ping_free (ph);
905                 return (-1);
906         }
908         if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
909         {
910                 dprintf ("getaddrinfo failed\n");
911                 ping_set_error (obj, "getaddrinfo",
912                                 (ai_return == EAI_SYSTEM)
913                                 ? strerror (errno)
914                                 : gai_strerror (ai_return));
915                 ping_free (ph);
916                 return (-1);
917         }
919         if (ai_list == NULL)
920                 ping_set_error (obj, "getaddrinfo", "No hosts returned");
922         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
923         {
924                 ph->fd = -1;
926                 sockaddr_len = sizeof (sockaddr);
927                 memset (&sockaddr, '\0', sockaddr_len);
929                 if (ai_ptr->ai_family == AF_INET)
930                 {
931                         struct sockaddr_in *si;
933                         si = (struct sockaddr_in *) &sockaddr;
934                         si->sin_family = AF_INET;
935                         si->sin_port   = htons (ph->ident);
936                         si->sin_addr.s_addr = htonl (INADDR_ANY);
938                         ai_ptr->ai_socktype = SOCK_RAW;
939                         ai_ptr->ai_protocol = IPPROTO_ICMP;
940                 }
941                 else if (ai_ptr->ai_family == AF_INET6)
942                 {
943                         struct sockaddr_in6 *si;
945                         si = (struct sockaddr_in6 *) &sockaddr;
946                         si->sin6_family = AF_INET6;
947                         si->sin6_port   = htons (ph->ident);
948                         si->sin6_addr   = in6addr_any;
950                         ai_ptr->ai_socktype = SOCK_RAW;
951                         ai_ptr->ai_protocol = IPPROTO_ICMPV6;
952                 }
953                 else
954                 {
955                         char errmsg[PING_ERRMSG_LEN];
957                         snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family);
958                         errmsg[PING_ERRMSG_LEN - 1] = '\0';
960                         dprintf (errmsg);
961                         ping_set_error (obj, "getaddrinfo", errmsg);
962                         continue;
963                 }
965                 ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
966                 if (ph->fd == -1)
967                 {
968                         dprintf ("socket: %s\n", strerror (errno));
969                         ping_set_error (obj, "socket", strerror (errno));
970                         continue;
971                 }
973                 if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1)
974                 {
975                         dprintf ("bind: %s\n", strerror (errno));
976                         ping_set_error (obj, "bind", strerror (errno));
977                         close (ph->fd);
978                         ph->fd = -1;
979                         continue;
980                 }
982                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
983                 memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
984                 memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
985                 ph->addrlen = ai_ptr->ai_addrlen;
986                 ph->addrfamily = ai_ptr->ai_family;
988                 break;
989         }
991         freeaddrinfo (ai_list);
993         if (ph->fd < 0)
994         {
995                 free (ph->hostname);
996                 free (ph);
997                 return (-1);
998         }
1000         ph->next  = obj->head;
1001         obj->head = ph;
1003         ping_set_ttl (ph, obj->ttl);
1005         return (0);
1008 int ping_host_remove (pingobj_t *obj, const char *host)
1010         pinghost_t *pre, *cur;
1012         pre = NULL;
1013         cur = obj->head;
1015         while (cur != NULL)
1016         {
1017                 if (strcasecmp (host, cur->hostname))
1018                         break;
1020                 pre = cur;
1021                 cur = cur->next;
1022         }
1024         if (cur == NULL)
1025         {
1026                 ping_set_error (obj, "ping_host_remove", "Host not found");
1027                 return (-1);
1028         }
1030         if (pre == NULL)
1031                 obj->head = cur->next;
1032         else
1033                 pre->next = cur->next;
1034         
1035         if (cur->fd >= 0)
1036                 close (cur->fd);
1038         ping_free (cur);
1040         return (0);
1043 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
1045         return ((pingobj_iter_t *) obj->head);
1048 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
1050         return ((pingobj_iter_t *) iter->next);
1053 const char *ping_iterator_get_host (pingobj_iter_t *iter)
1055         return (iter->hostname);
1058 double ping_iterator_get_latency (pingobj_iter_t *iter)
1060         return (iter->latency);