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