Applied all the changes that `git-svnimport' missed.
[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  */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
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 */
33
34 #if HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37
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
47
48 #if TIME_WITH_SYS_TIME
49 # include <sys/time.h>
50 # include <time.h>
51 #endif
52
53 #if HAVE_SYS_SOCKET_H
54 # include <sys/socket.h>
55 #endif
56 #if HAVE_NETDB_H
57 # include <netdb.h>
58 #endif
59
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
81
82 #include "liboping.h"
83
84 #if DEBUG
85 # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
86 #else
87 # define dprintf(...) /**/
88 #endif
89
90 #define PING_DATA "Florian Forster <octo@verplant.org> http://verplant.org/"
91
92 /*
93  * private (static) functions
94  */
95 static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
96                 struct timeval *res)
97 {
98         res->tv_sec  = tv1->tv_sec  + tv2->tv_sec;
99         res->tv_usec = tv1->tv_usec + tv2->tv_usec;
100
101         while (res->tv_usec > 1000000)
102         {
103                 res->tv_usec -= 1000000;
104                 res->tv_sec++;
105         }
106
107         return (0);
108 }
109
110 static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
111                 struct timeval *res)
112 {
113
114         if ((tv1->tv_sec < tv2->tv_sec)
115                         || ((tv1->tv_sec == tv2->tv_sec)
116                                 && (tv1->tv_usec < tv2->tv_usec)))
117                 return (-1);
118
119         res->tv_sec  = tv1->tv_sec  - tv2->tv_sec;
120         res->tv_usec = tv1->tv_usec - tv2->tv_usec;
121
122         assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0)));
123
124         while (res->tv_usec < 0)
125         {
126                 res->tv_usec += 1000000;
127                 res->tv_sec--;
128         }
129
130         return (0);
131 }
132
133 static uint16_t ping_icmp4_checksum (char *buf, size_t len)
134 {
135         uint32_t sum = 0;
136         uint16_t ret = 0;
137
138         uint16_t *ptr;
139
140         for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
141                 sum += *ptr;
142
143         if (len == 1)
144         {
145                 *(char *) &ret = *(char *) ptr;
146                 sum += ret;
147         }
148
149         /* Do this twice to get all possible carries.. */
150         sum = (sum >> 16) + (sum & 0xFFFF);
151         sum = (sum >> 16) + (sum & 0xFFFF);
152
153         ret = ~sum;
154
155         return (ret);
156 }
157
158 static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len)
159 {
160         struct ip *ip_hdr;
161         struct icmp *icmp_hdr;
162
163         size_t ip_hdr_len;
164
165         uint16_t recv_checksum;
166         uint16_t calc_checksum;
167
168         uint16_t ident;
169         uint16_t seq;
170
171         pinghost_t *ptr;
172
173         if (buffer_len < sizeof (struct ip))
174                 return (NULL);
175
176         ip_hdr     = (struct ip *) buffer;
177         ip_hdr_len = ip_hdr->ip_hl << 2;
178
179         if (buffer_len < ip_hdr_len)
180                 return (NULL);
181
182         buffer     += ip_hdr_len;
183         buffer_len -= ip_hdr_len;
184
185         if (buffer_len < sizeof (struct icmp))
186                 return (NULL);
187
188         icmp_hdr = (struct icmp *) buffer;
189         buffer     += sizeof (struct icmp);
190         buffer_len -= sizeof (struct icmp);
191
192         if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
193         {
194                 dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type);
195                 return (NULL);
196         }
197
198         recv_checksum = icmp_hdr->icmp_cksum;
199         icmp_hdr->icmp_cksum = 0;
200         calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr,
201                         sizeof (struct icmp) + buffer_len);
202
203         if (recv_checksum != calc_checksum)
204         {
205                 dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n",
206                                 recv_checksum, calc_checksum);
207                 return (NULL);
208         }
209
210         ident = ntohs (icmp_hdr->icmp_id);
211         seq   = ntohs (icmp_hdr->icmp_seq);
212
213         for (ptr = ph; ptr != NULL; ptr = ptr->next)
214         {
215                 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
216                                 ptr->hostname, ptr->ident, ptr->sequence - 1);
217
218                 if (ptr->addrfamily != AF_INET)
219                         continue;
220
221                 if (!timerisset (ptr->timer))
222                         continue;
223
224                 if (ptr->ident != ident)
225                         continue;
226
227                 if ((ptr->sequence - 1) != seq)
228                         continue;
229
230                 dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
231                                 ptr->hostname, ident, seq);
232
233                 break;
234         }
235
236         if (ptr == NULL)
237         {
238                 dprintf ("No match found for ident = 0x%04x, seq = %i\n",
239                                 ident, seq);
240         }
241
242         return (ptr);
243 }
244
245 static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
246 {
247         struct icmp6_hdr *icmp_hdr;
248
249         uint16_t ident;
250         uint16_t seq;
251
252         pinghost_t *ptr;
253
254         if (buffer_len < sizeof (struct icmp6_hdr))
255                 return (NULL);
256
257         icmp_hdr = (struct icmp6_hdr *) buffer;
258         buffer     += sizeof (struct icmp);
259         buffer_len -= sizeof (struct icmp);
260
261         if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
262         {
263                 dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type);
264                 return (NULL);
265         }
266
267         if (icmp_hdr->icmp6_code != 0)
268         {
269                 dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code);
270                 return (NULL);
271         }
272
273         ident = ntohs (icmp_hdr->icmp6_id);
274         seq   = ntohs (icmp_hdr->icmp6_seq);
275
276         for (ptr = ph; ptr != NULL; ptr = ptr->next)
277         {
278                 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
279                                 ptr->hostname, ptr->ident, ptr->sequence - 1);
280
281                 if (ptr->addrfamily != AF_INET6)
282                         continue;
283
284                 if (!timerisset (ptr->timer))
285                         continue;
286
287                 if (ptr->ident != ident)
288                         continue;
289
290                 if ((ptr->sequence - 1) != seq)
291                         continue;
292
293                 dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
294                                 ptr->hostname, ident, seq);
295
296                 break;
297         }
298
299         if (ptr == NULL)
300         {
301                 dprintf ("No match found for ident = 0x%04x, seq = %i\n",
302                                 ident, seq);
303         }
304
305         return (ptr);
306 }
307
308 static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
309 {
310         char   buffer[4096];
311         size_t buffer_len;
312
313         struct timeval diff;
314
315         pinghost_t *host = NULL;
316
317         struct sockaddr_storage sa;
318         socklen_t               sa_len;
319
320         sa_len = sizeof (sa);
321
322         buffer_len = recvfrom (fd, buffer, sizeof (buffer), 0,
323                         (struct sockaddr *) &sa, &sa_len);
324         if (buffer_len == -1)
325         {
326                 dprintf ("recvfrom: %s\n", strerror (errno));
327                 return (-1);
328         }
329
330         dprintf ("Read %i bytes from fd = %i\n", buffer_len, fd);
331
332         if (sa.ss_family == AF_INET)
333         {
334                 if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL)
335                         return (-1);
336         }
337         else if (sa.ss_family == AF_INET6)
338         {
339                 if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL)
340                         return (-1);
341         }
342
343         dprintf ("rcvd: %12i.%06i\n",
344                         (int) now->tv_sec,
345                         (int) now->tv_usec);
346         dprintf ("sent: %12i.%06i\n",
347                         (int) host->timer->tv_sec,
348                         (int) host->timer->tv_usec);
349
350         if (ping_timeval_sub (now, host->timer, &diff) < 0)
351         {
352                 timerclear (host->timer);
353                 return (-1);
354         }
355
356         dprintf ("diff: %12i.%06i\n",
357                         (int) diff.tv_sec,
358                         (int) diff.tv_usec);
359
360         host->latency  = ((double) diff.tv_usec) / 1000.0;
361         host->latency += ((double) diff.tv_sec)  * 1000.0;
362
363         timerclear (host->timer);
364
365         return (0);
366 }
367
368 static int ping_receive_all (pingobj_t *obj)
369 {
370         fd_set readfds;
371         int num_readfds;
372         int max_readfds;
373
374         pinghost_t *ph;
375         pinghost_t *ptr;
376
377         struct timeval endtime;
378         struct timeval nowtime;
379         struct timeval timeout;
380         int status;
381
382         int ret;
383
384         ph = obj->head;
385         ret = 0;
386
387         for (ptr = ph; ptr != NULL; ptr = ptr->next)
388                 ptr->latency = -1.0;
389
390         if (gettimeofday (&nowtime, NULL) == -1)
391                 return (-1);
392
393         /* Set up timeout */
394         timeout.tv_sec = (time_t) obj->timeout;
395         timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec)));
396
397         dprintf ("Set timeout to %i.%06i seconds\n",
398                         (int) timeout.tv_sec,
399                         (int) timeout.tv_usec);
400
401         ping_timeval_add (&nowtime, &timeout, &endtime);
402
403         while (1)
404         {
405                 FD_ZERO (&readfds);
406                 num_readfds =  0;
407                 max_readfds = -1;
408
409                 for (ptr = ph; ptr != NULL; ptr = ptr->next)
410                 {
411                         if (!timerisset (ptr->timer))
412                                 continue;
413
414                         FD_SET (ptr->fd, &readfds);
415                         num_readfds++;
416
417                         if (max_readfds < ptr->fd)
418                                 max_readfds = ptr->fd;
419                 }
420
421                 if (num_readfds == 0)
422                         break;
423
424                 if (gettimeofday (&nowtime, NULL) == -1)
425                         return (-1);
426
427                 if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
428                         break;
429
430                 dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds,
431                                 (int) timeout.tv_sec,
432                                 (int) timeout.tv_usec);
433
434                 status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout);
435
436                 if (gettimeofday (&nowtime, NULL) == -1)
437                         return (-1);
438                 
439                 if ((status == -1) && (errno == EINTR))
440                 {
441                         dprintf ("select was interrupted by signal..\n");
442                         continue;
443                 }
444                 else if (status < 0)
445                 {
446                         dprintf ("select: %s\n", strerror (errno));
447                         break;
448                 }
449                 else if (status == 0)
450                 {
451                         dprintf ("select timed out\n");
452                         break;
453                 }
454
455                 for (ptr = ph; ptr != NULL; ptr = ptr->next)
456                 {
457                         if (FD_ISSET (ptr->fd, &readfds))
458                                 if (ping_receive_one (ptr->fd, ph, &nowtime) == 0)
459                                         ret++;
460                 }
461         } /* while (1) */
462         
463         return (ret);
464 }
465
466 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
467  * Sending functions:                                                        *
468  *                                                                           *
469  * ping_send_all                                                             *
470  * +-> ping_send_one_ipv4                                                    *
471  * `-> ping_send_one_ipv6                                                    *
472  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
473 ssize_t ping_sendto (pinghost_t *ph, const void *buf, size_t buflen)
474 {
475         ssize_t ret;
476
477         if (gettimeofday (ph->timer, NULL) == -1)
478         {
479                 timerclear (ph->timer);
480                 return (-1);
481         }
482
483         ret = sendto (ph->fd, buf, buflen, 0,
484                         (struct sockaddr *) ph->addr, ph->addrlen);
485
486         return (ret);
487 }
488
489 static int ping_send_one_ipv4 (pinghost_t *ph)
490 {
491         struct icmp *icmp4;
492         int status;
493
494         char buf[4096];
495         int  buflen;
496
497         char *data;
498         int   datalen;
499
500         dprintf ("ph->hostname = %s\n", ph->hostname);
501
502         memset (buf, '\0', sizeof (buf));
503         icmp4 = (struct icmp *) buf;
504         data  = (char *) (icmp4 + 1);
505
506         icmp4->icmp_type  = ICMP_ECHO;
507         icmp4->icmp_code  = 0;
508         icmp4->icmp_cksum = 0;
509         icmp4->icmp_id    = htons (ph->ident);
510         icmp4->icmp_seq   = htons (ph->sequence);
511
512         strcpy (data, PING_DATA);
513         datalen = strlen (data);
514
515         buflen = datalen + sizeof (struct icmp);
516
517         icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
518
519         dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
520
521         status = ping_sendto (ph, buf, buflen);
522         if (status < 0)
523         {
524                 perror ("ping_sendto");
525                 return (-1);
526         }
527
528         dprintf ("sendto: status = %i\n", status);
529
530         return (0);
531 }
532
533 static int ping_send_one_ipv6 (pinghost_t *ph)
534 {
535         struct icmp6_hdr *icmp6;
536         int status;
537
538         char buf[4096];
539         int  buflen;
540
541         char *data;
542         int   datalen;
543
544         dprintf ("ph->hostname = %s\n", ph->hostname);
545
546         memset (buf, '\0', sizeof (buf));
547         icmp6 = (struct icmp6_hdr *) buf;
548         data  = (char *) (icmp6 + 1);
549
550         icmp6->icmp6_type  = ICMP6_ECHO_REQUEST;
551         icmp6->icmp6_code  = 0;
552         /* The checksum will be calculated by the TCP/IP stack.  */
553         icmp6->icmp6_cksum = 0;
554         icmp6->icmp6_id    = htons (ph->ident);
555         icmp6->icmp6_seq   = htons (ph->sequence);
556
557         strcpy (data, PING_DATA);
558         datalen = strlen (data);
559
560         buflen = datalen + sizeof (struct icmp6_hdr);
561
562         dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
563
564         status = ping_sendto (ph, buf, buflen);
565         if (status < 0)
566         {
567                 perror ("ping_sendto");
568                 return (-1);
569         }
570
571         dprintf ("sendto: status = %i\n", status);
572
573         return (0);
574 }
575
576 static int ping_send_all (pinghost_t *ph)
577 {
578         pinghost_t *ptr;
579
580         for (ptr = ph; ptr != NULL; ptr = ptr->next)
581         {
582                 /* start timer.. The GNU `ping6' starts the timer before
583                  * sending the packet, so I will do that too */
584                 if (gettimeofday (ptr->timer, NULL) == -1)
585                 {
586                         dprintf ("gettimeofday: %s\n", strerror (errno));
587                         timerclear (ptr->timer);
588                         continue;
589                 }
590                 else
591                 {
592                         dprintf ("timer set for hostname = %s\n", ptr->hostname);
593                 }
594
595                 if (ptr->addrfamily == AF_INET6)
596                 {       
597                         dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname);
598                         if (ping_send_one_ipv6 (ptr) != 0)
599                         {
600                                 timerclear (ptr->timer);
601                                 continue;
602                         }
603                 }
604                 else if (ptr->addrfamily == AF_INET)
605                 {
606                         dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
607                         if (ping_send_one_ipv4 (ptr) != 0)
608                         {
609                                 timerclear (ptr->timer);
610                                 continue;
611                         }
612                 }
613                 else /* this should not happen */
614                 {
615                         dprintf ("Unknown address family: %i\n", ptr->addrfamily);
616                         timerclear (ptr->timer);
617                         continue;
618                 }
619
620                 ptr->sequence++;
621         }
622
623         /* FIXME */
624         return (0);
625 }
626
627 /*
628  * Set the TTL of a socket protocol independently.
629  */
630 static int ping_set_ttl (pinghost_t *ph, int ttl)
631 {
632         int ret = -2;
633
634         if (ph->addrfamily == AF_INET)
635         {
636                 ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl));
637         }
638         else if (ph->addrfamily == AF_INET6)
639         {
640                 ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl));
641         }
642
643         return (ret);
644 }
645
646 static int ping_get_ident (void)
647 {
648         int fd;
649         static int did_seed = 0;
650
651         int retval;
652
653         if (did_seed == 0)
654         {
655                 if ((fd = open ("/dev/urandom", O_RDONLY)) != -1)
656                 {
657                         unsigned int seed;
658
659                         if (read (fd, &seed, sizeof (seed)) != -1)
660                         {
661                                 did_seed = 1;
662                                 dprintf ("Random seed: %i\n", seed);
663                                 srandom (seed);
664                         }
665
666                         close (fd);
667                 }
668                 else
669                 {
670                         dprintf ("open (/dev/urandom): %s\n", strerror (errno));
671                 }
672         }
673
674         retval = (int) random ();
675
676         dprintf ("Random number: %i\n", retval);
677         
678         return (retval);
679 }
680
681 static pinghost_t *ping_alloc (void)
682 {
683         pinghost_t *ph;
684         size_t      ph_size;
685
686         ph_size = sizeof (pinghost_t)
687                 + sizeof (struct sockaddr_storage)
688                 + sizeof (struct timeval);
689
690         ph = (pinghost_t *) malloc (ph_size);
691         if (ph == NULL)
692                 return (NULL);
693
694         memset (ph, '\0', ph_size);
695
696         ph->timer   = (struct timeval *) (ph + 1);
697         ph->addr    = (struct sockaddr_storage *) (ph->timer + 1);
698
699         ph->addrlen = sizeof (struct sockaddr_storage);
700         ph->latency = -1.0;
701         ph->ident   = ping_get_ident () & 0xFFFF;
702
703         return (ph);
704 }
705
706 static void ping_free (pinghost_t *ph)
707 {
708         if (ph->hostname != NULL)
709                 free (ph->hostname);
710
711         free (ph);
712 }
713
714 /*
715  * public methods
716  */
717 pingobj_t *ping_construct (void)
718 {
719         pingobj_t *obj;
720
721         if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL)
722                 return (NULL);
723         memset (obj, '\0', sizeof (pingobj_t));
724
725         obj->timeout    = PING_DEF_TIMEOUT;
726         obj->ttl        = PING_DEF_TTL;
727         obj->addrfamily = PING_DEF_AF;
728
729         return (obj);
730 }
731
732 void ping_destroy (pingobj_t *obj)
733 {
734         pinghost_t *current;
735         pinghost_t *next;
736
737         current = obj->head;
738         next = NULL;
739
740         while (current != NULL)
741         {
742                 next = current->next;
743                 ping_free (current);
744                 current = next;
745         }
746
747         free (obj);
748
749         return;
750 }
751
752 int ping_setopt (pingobj_t *obj, int option, void *value)
753 {
754         int ret = 0;
755
756         switch (option)
757         {
758                 case PING_OPT_TIMEOUT:
759                         obj->timeout = *((double *) value);
760                         if (obj->timeout < 0.0)
761                         {
762                                 obj->timeout = PING_DEF_TIMEOUT;
763                                 ret = -1;
764                         }
765                         break;
766
767                 case PING_OPT_TTL:
768                         obj->ttl = *((int *) value);
769                         if ((obj->ttl < 1) || (obj->ttl > 255))
770                         {
771                                 obj->ttl = PING_DEF_TTL;
772                                 ret = -1;
773                         }
774                         break;
775
776                 case PING_OPT_AF:
777                         obj->addrfamily = *((int *) value);
778                         if ((obj->addrfamily != AF_UNSPEC)
779                                         && (obj->addrfamily != AF_INET)
780                                         && (obj->addrfamily != AF_INET6))
781                         {
782                                 obj->addrfamily = PING_DEF_AF;
783                                 ret = -1;
784                         }
785                         break;
786
787                 default:
788                         ret = -2;
789         } /* switch (option) */
790
791         return (ret);
792 } /* int ping_setopt */
793
794
795 int ping_send (pingobj_t *obj)
796 {
797         int ret;
798
799         if (ping_send_all (obj->head) < 0)
800                 return (-1);
801
802         if ((ret = ping_receive_all (obj)) < 0)
803                 return (-2);
804
805         return (ret);
806 }
807
808 static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
809 {
810         while (ph != NULL)
811         {
812                 if (strcasecmp (ph->hostname, host) == 0)
813                         break;
814
815                 ph = ph->next;
816         }
817
818         return (ph);
819 }
820
821 int ping_host_add (pingobj_t *obj, const char *host)
822 {
823         pinghost_t *ph;
824
825         struct sockaddr_storage sockaddr;
826         socklen_t               sockaddr_len;
827
828         struct addrinfo  ai_hints;
829         struct addrinfo *ai_list, *ai_ptr;
830         int              ai_return;
831
832         dprintf ("host = %s\n", host);
833
834         if (ping_host_search (obj->head, host) != NULL)
835                 return (0);
836
837         memset (&ai_hints, '\0', sizeof (ai_hints));
838         ai_hints.ai_flags     = 0;
839 #ifdef AI_ADDRCONFIG
840         ai_hints.ai_flags    |= AI_ADDRCONFIG;
841 #endif
842         ai_hints.ai_family    = obj->addrfamily;
843         ai_hints.ai_socktype  = SOCK_RAW;
844
845         if ((ph = ping_alloc ()) == NULL)
846         {
847                 dprintf ("Out of memory!\n");
848                 return (-1);
849         }
850
851         if ((ph->hostname = strdup (host)) == NULL)
852         {
853                 dprintf ("Out of memory!\n");
854                 ping_free (ph);
855                 return (-1);
856         }
857
858         if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
859         {
860                 dprintf ("getaddrinfo failed\n");
861                 ping_free (ph);
862                 return (-1);
863         }
864
865         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
866         {
867                 ph->fd = -1;
868
869                 sockaddr_len = sizeof (sockaddr);
870                 memset (&sockaddr, '\0', sockaddr_len);
871
872                 if (ai_ptr->ai_family == AF_INET)
873                 {
874                         struct sockaddr_in *si;
875
876                         si = (struct sockaddr_in *) &sockaddr;
877                         si->sin_family = AF_INET;
878                         si->sin_port   = htons (ph->ident);
879                         si->sin_addr.s_addr = htonl (INADDR_ANY);
880
881                         ai_ptr->ai_protocol = IPPROTO_ICMP;
882                 }
883                 else if (ai_ptr->ai_family == AF_INET6)
884                 {
885                         struct sockaddr_in6 *si;
886
887                         si = (struct sockaddr_in6 *) &sockaddr;
888                         si->sin6_family = AF_INET6;
889                         si->sin6_port   = htons (ph->ident);
890                         si->sin6_addr   = in6addr_any;
891
892                         ai_ptr->ai_protocol = IPPROTO_ICMPV6;
893                 }
894                 else
895                 {
896                         dprintf ("Unknown `ai_family': %i\n", ai_ptr->ai_family);
897                         continue;
898                 }
899
900                 ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
901                 if (ph->fd == -1)
902                 {
903                         dprintf ("socket: %s\n", strerror (errno));
904                         continue;
905                 }
906
907                 if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1)
908                 {
909                         dprintf ("bind: %s\n", strerror (errno));
910                         close (ph->fd);
911                         ph->fd = -1;
912                         continue;
913                 }
914
915                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
916                 memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
917                 memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
918                 ph->addrlen = ai_ptr->ai_addrlen;
919                 ph->addrfamily = ai_ptr->ai_family;
920
921                 break;
922         }
923
924         freeaddrinfo (ai_list);
925
926         if (ph->fd < 0)
927         {
928                 free (ph->hostname);
929                 free (ph);
930                 return (-1);
931         }
932
933         ph->next  = obj->head;
934         obj->head = ph;
935
936         ping_set_ttl (ph, obj->ttl);
937
938         return (0);
939 }
940
941 int ping_host_remove (pingobj_t *obj, const char *host)
942 {
943         pinghost_t *pre, *cur;
944
945         pre = NULL;
946         cur = obj->head;
947
948         while (cur != NULL)
949         {
950                 if (strcasecmp (host, cur->hostname))
951                         break;
952
953                 pre = cur;
954                 cur = cur->next;
955         }
956
957         if (cur == NULL)
958                 return (-1);
959
960         if (pre == NULL)
961                 obj->head = cur->next;
962         else
963                 pre->next = cur->next;
964         
965         if (cur->fd >= 0)
966                 close (cur->fd);
967
968         ping_free (cur);
969
970         return (0);
971 }
972
973 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
974 {
975         return ((pingobj_iter_t *) obj->head);
976 }
977
978 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
979 {
980         return ((pingobj_iter_t *) iter->next);
981 }
982
983 const char *ping_iterator_get_host (pingobj_iter_t *iter)
984 {
985         return (iter->hostname);
986 }
987
988 double ping_iterator_get_latency (pingobj_iter_t *iter)
989 {
990         return (iter->latency);
991 }