Merge branch 'liboping-1.2'
[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         if (obj == NULL)
1011                 return (NULL);
1012         return (obj->errmsg);
1013 }
1014
1015 pingobj_t *ping_construct (void)
1016 {
1017         pingobj_t *obj;
1018
1019         if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL)
1020                 return (NULL);
1021         memset (obj, '\0', sizeof (pingobj_t));
1022
1023         obj->timeout    = PING_DEF_TIMEOUT;
1024         obj->ttl        = PING_DEF_TTL;
1025         obj->addrfamily = PING_DEF_AF;
1026         obj->data       = strdup (PING_DEF_DATA);
1027
1028         return (obj);
1029 }
1030
1031 void ping_destroy (pingobj_t *obj)
1032 {
1033         pinghost_t *current;
1034         pinghost_t *next;
1035
1036         if (obj == NULL)
1037                 return;
1038
1039         current = obj->head;
1040         next = NULL;
1041
1042         while (current != NULL)
1043         {
1044                 next = current->next;
1045                 ping_free (current);
1046                 current = next;
1047         }
1048
1049         if (obj->data != NULL)
1050                 free (obj->data);
1051
1052         if (obj->srcaddr != NULL)
1053                 free (obj->srcaddr);
1054
1055         if (obj->device != NULL)
1056                 free (obj->device);
1057
1058         free (obj);
1059
1060         return;
1061 }
1062
1063 int ping_setopt (pingobj_t *obj, int option, void *value)
1064 {
1065         int ret = 0;
1066
1067         if ((obj == NULL) || (value == NULL))
1068                 return (-1);
1069
1070         switch (option)
1071         {
1072                 case PING_OPT_TIMEOUT:
1073                         obj->timeout = *((double *) value);
1074                         if (obj->timeout < 0.0)
1075                         {
1076                                 obj->timeout = PING_DEF_TIMEOUT;
1077                                 ret = -1;
1078                         }
1079                         break;
1080
1081                 case PING_OPT_TTL:
1082                         obj->ttl = *((int *) value);
1083                         if ((obj->ttl < 1) || (obj->ttl > 255))
1084                         {
1085                                 obj->ttl = PING_DEF_TTL;
1086                                 ret = -1;
1087                         }
1088                         else
1089                         {
1090                                 pinghost_t *ph;
1091
1092                                 for (ph = obj->head; ph != NULL; ph = ph->next)
1093                                         ping_set_ttl (ph, obj->ttl);
1094                         }
1095                         break;
1096
1097                 case PING_OPT_AF:
1098                         obj->addrfamily = *((int *) value);
1099                         if ((obj->addrfamily != AF_UNSPEC)
1100                                         && (obj->addrfamily != AF_INET)
1101                                         && (obj->addrfamily != AF_INET6))
1102                         {
1103                                 obj->addrfamily = PING_DEF_AF;
1104                                 ret = -1;
1105                         }
1106                         if (obj->srcaddr != NULL)
1107                         {
1108                                 free (obj->srcaddr);
1109                                 obj->srcaddr = NULL;
1110                         }
1111                         break;
1112
1113                 case PING_OPT_DATA:
1114                         if (obj->data != NULL)
1115                         {
1116                                 free (obj->data);
1117                                 obj->data = NULL;
1118                         }
1119                         obj->data = strdup ((const char *) value);
1120                         break;
1121
1122                 case PING_OPT_SOURCE:
1123                 {
1124                         char            *hostname = (char *) value;
1125                         struct addrinfo  ai_hints;
1126                         struct addrinfo *ai_list;
1127                         int              status;
1128 #if WITH_DEBUG
1129                         if (obj->addrfamily != AF_UNSPEC)
1130                         {
1131                                 dprintf ("Resetting obj->addrfamily to AF_UNSPEC.\n");
1132                         }
1133 #endif
1134                         memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
1135                         ai_hints.ai_family = obj->addrfamily = AF_UNSPEC;
1136 #if defined(AI_ADDRCONFIG)
1137                         ai_hints.ai_flags = AI_ADDRCONFIG;
1138 #endif
1139                         status = getaddrinfo (hostname, NULL, &ai_hints, &ai_list);
1140                         if (status != 0)
1141                         {
1142 #if defined(EAI_SYSTEM)
1143                                 char errbuf[PING_ERRMSG_LEN];
1144 #endif
1145                                 ping_set_error (obj, "getaddrinfo",
1146 #if defined(EAI_SYSTEM)
1147                                                 (status == EAI_SYSTEM)
1148                                                 ? sstrerror (errno, errbuf, sizeof (errbuf)) :
1149 #endif
1150                                                 gai_strerror (status));
1151                                 ret = -1;
1152                                 break;
1153                         }
1154 #if WITH_DEBUG
1155                         if (ai_list->ai_next != NULL)
1156                         {
1157                                 dprintf ("hostname = `%s' is ambiguous.\n", hostname);
1158                         }
1159 #endif
1160                         if (obj->srcaddr == NULL)
1161                         {
1162                                 obj->srcaddrlen = 0;
1163                                 obj->srcaddr = malloc (sizeof (struct sockaddr_storage));
1164                                 if (obj->srcaddr == NULL)
1165                                 {
1166                                         ping_set_errno (obj, errno);
1167                                         ret = -1;
1168                                         freeaddrinfo (ai_list);
1169                                         break;
1170                                 }
1171                         }
1172                         memset ((void *) obj->srcaddr, 0, sizeof (struct sockaddr_storage));
1173                         assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
1174                         memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
1175                                         ai_list->ai_addrlen);
1176                         obj->srcaddrlen = ai_list->ai_addrlen;
1177                         obj->addrfamily = ai_list->ai_family;
1178
1179                         freeaddrinfo (ai_list);
1180                 } /* case PING_OPT_SOURCE */
1181                 break;
1182
1183                 case PING_OPT_DEVICE:
1184                 {
1185 #ifdef SO_BINDTODEVICE
1186                         char *device = strdup ((char *) value);
1187
1188                         if (device == NULL)
1189                         {
1190                                 ping_set_errno (obj, errno);
1191                                 ret = -1;
1192                                 break;
1193                         }
1194
1195                         if (obj->device != NULL)
1196                                 free (obj->device);
1197                         obj->device = device;
1198 #else /* ! SO_BINDTODEVICE */
1199                         ping_set_errno (obj, ENOTSUP);
1200                         ret = -1;
1201 #endif /* ! SO_BINDTODEVICE */
1202                 } /* case PING_OPT_DEVICE */
1203                 break;
1204
1205                 default:
1206                         ret = -2;
1207         } /* switch (option) */
1208
1209         return (ret);
1210 } /* int ping_setopt */
1211
1212
1213 int ping_send (pingobj_t *obj)
1214 {
1215         int ret;
1216
1217         if (obj == NULL)
1218                 return (-1);
1219
1220         if (ping_send_all (obj) < 0)
1221                 return (-1);
1222
1223         if ((ret = ping_receive_all (obj)) < 0)
1224                 return (-2);
1225
1226         return (ret);
1227 }
1228
1229 static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
1230 {
1231         while (ph != NULL)
1232         {
1233                 if (strcasecmp (ph->username, host) == 0)
1234                         break;
1235
1236                 ph = ph->next;
1237         }
1238
1239         return (ph);
1240 }
1241
1242 int ping_host_add (pingobj_t *obj, const char *host)
1243 {
1244         pinghost_t *ph;
1245
1246         struct addrinfo  ai_hints;
1247         struct addrinfo *ai_list, *ai_ptr;
1248         int              ai_return;
1249
1250         if ((obj == NULL) || (host == NULL))
1251                 return (-1);
1252
1253         dprintf ("host = %s\n", host);
1254
1255         if (ping_host_search (obj->head, host) != NULL)
1256                 return (0);
1257
1258         memset (&ai_hints, '\0', sizeof (ai_hints));
1259         ai_hints.ai_flags     = 0;
1260 #ifdef AI_ADDRCONFIG
1261         ai_hints.ai_flags    |= AI_ADDRCONFIG;
1262 #endif
1263 #ifdef AI_CANONNAME
1264         ai_hints.ai_flags    |= AI_CANONNAME;
1265 #endif
1266         ai_hints.ai_family    = obj->addrfamily;
1267         ai_hints.ai_socktype  = SOCK_RAW;
1268
1269         if ((ph = ping_alloc ()) == NULL)
1270         {
1271                 dprintf ("Out of memory!\n");
1272                 return (-1);
1273         }
1274
1275         if ((ph->username = strdup (host)) == NULL)
1276         {
1277                 dprintf ("Out of memory!\n");
1278                 ping_set_errno (obj, errno);
1279                 ping_free (ph);
1280                 return (-1);
1281         }
1282
1283         if ((ph->hostname = strdup (host)) == NULL)
1284         {
1285                 dprintf ("Out of memory!\n");
1286                 ping_set_errno (obj, errno);
1287                 ping_free (ph);
1288                 return (-1);
1289         }
1290
1291         /* obj->data is not garuanteed to be != NULL */
1292         if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL)
1293         {
1294                 dprintf ("Out of memory!\n");
1295                 ping_set_errno (obj, errno);
1296                 ping_free (ph);
1297                 return (-1);
1298         }
1299
1300         if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
1301         {
1302 #if defined(EAI_SYSTEM)
1303                 char errbuf[PING_ERRMSG_LEN];
1304 #endif
1305                 dprintf ("getaddrinfo failed\n");
1306                 ping_set_error (obj, "getaddrinfo",
1307 #if defined(EAI_SYSTEM)
1308                                                 (ai_return == EAI_SYSTEM)
1309                                                 ? sstrerror (errno, errbuf, sizeof (errbuf)) :
1310 #endif
1311                                 gai_strerror (ai_return));
1312                 ping_free (ph);
1313                 return (-1);
1314         }
1315
1316         if (ai_list == NULL)
1317                 ping_set_error (obj, "getaddrinfo", "No hosts returned");
1318
1319         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
1320         {
1321                 ph->fd = -1;
1322
1323                 if (ai_ptr->ai_family == AF_INET)
1324                 {
1325                         ai_ptr->ai_socktype = SOCK_RAW;
1326                         ai_ptr->ai_protocol = IPPROTO_ICMP;
1327                 }
1328                 else if (ai_ptr->ai_family == AF_INET6)
1329                 {
1330                         ai_ptr->ai_socktype = SOCK_RAW;
1331                         ai_ptr->ai_protocol = IPPROTO_ICMPV6;
1332                 }
1333                 else
1334                 {
1335                         char errmsg[PING_ERRMSG_LEN];
1336
1337                         snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family);
1338                         errmsg[PING_ERRMSG_LEN - 1] = '\0';
1339
1340                         dprintf (errmsg);
1341                         ping_set_error (obj, "getaddrinfo", errmsg);
1342                         continue;
1343                 }
1344
1345                 /* TODO: Move this to a static function `ping_open_socket' and
1346                  * call it whenever the socket dies. */
1347                 ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
1348                 if (ph->fd == -1)
1349                 {
1350 #if WITH_DEBUG
1351                         char errbuf[PING_ERRMSG_LEN];
1352                         dprintf ("socket: %s\n",
1353                                         sstrerror (errno, errbuf, sizeof (errbuf)));
1354 #endif
1355                         ping_set_errno (obj, errno);
1356                         continue;
1357                 }
1358
1359                 if (obj->srcaddr != NULL)
1360                 {
1361                         assert (obj->srcaddrlen > 0);
1362                         assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
1363
1364                         if (bind (ph->fd, obj->srcaddr, obj->srcaddrlen) == -1)
1365                         {
1366 #if WITH_DEBUG
1367                                 char errbuf[PING_ERRMSG_LEN];
1368                                 dprintf ("bind: %s\n",
1369                                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1370 #endif
1371                                 ping_set_errno (obj, errno);
1372                                 close (ph->fd);
1373                                 ph->fd = -1;
1374                                 continue;
1375                         }
1376                 }
1377
1378 #ifdef SO_BINDTODEVICE
1379                 if (obj->device != NULL)
1380                 {
1381                         if (setsockopt (ph->fd, SOL_SOCKET, SO_BINDTODEVICE,
1382                                         obj->device, strlen (obj->device) + 1) != 0)
1383                         {
1384 #if WITH_DEBUG
1385                                 char errbuf[PING_ERRMSG_LEN];
1386                                 dprintf ("setsockopt: %s\n",
1387                                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1388 #endif
1389                                 ping_set_errno (obj, errno);
1390                                 close (ph->fd);
1391                                 ph->fd = -1;
1392                                 continue;
1393                         }
1394                 }
1395 #endif /* SO_BINDTODEVICE */
1396
1397                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
1398                 memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
1399                 memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
1400                 ph->addrlen = ai_ptr->ai_addrlen;
1401                 ph->addrfamily = ai_ptr->ai_family;
1402
1403 #ifdef AI_CANONNAME
1404                 if ((ai_ptr->ai_canonname != NULL)
1405                                 && (strcmp (ph->hostname, ai_ptr->ai_canonname) != 0))
1406                 {
1407                         char *old_hostname;
1408
1409                         dprintf ("ph->hostname = %s; ai_ptr->ai_canonname = %s;\n",
1410                                         ph->hostname, ai_ptr->ai_canonname);
1411
1412                         old_hostname = ph->hostname;
1413                         if ((ph->hostname = strdup (ai_ptr->ai_canonname)) == NULL)
1414                         {
1415                                 /* strdup failed, falling back to old hostname */
1416                                 ph->hostname = old_hostname;
1417                         }
1418                         else if (old_hostname != NULL)
1419                         {
1420                                 free (old_hostname);
1421                         }
1422                 }
1423 #endif /* AI_CANONNAME */
1424
1425                 if (ph->addrfamily == AF_INET)
1426                 {
1427                         int opt = 1;
1428
1429                         setsockopt (ph->fd, IPPROTO_IP, IP_RECVTTL,
1430                                         &opt, sizeof (opt));
1431                 }
1432 #if defined(IPPROTO_IPV6) && defined(IPV6_RECVHOPLIMIT)
1433                 else if (ph->addrfamily == AF_INET6)
1434                 {
1435                         int opt = 1;
1436
1437                         setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
1438                                         &opt, sizeof (opt));
1439                 }
1440 #endif
1441
1442                 break;
1443         }
1444
1445         freeaddrinfo (ai_list);
1446
1447         if (ph->fd < 0)
1448         {
1449                 ping_free (ph);
1450                 return (-1);
1451         }
1452
1453         /*
1454          * Adding in the front is much easier, but then the iterator will
1455          * return the host that was added last as first host. That's just not
1456          * nice. -octo
1457          */
1458         if (obj->head == NULL)
1459         {
1460                 obj->head = ph;
1461         }
1462         else
1463         {
1464                 pinghost_t *hptr;
1465
1466                 hptr = obj->head;
1467                 while (hptr->next != NULL)
1468                         hptr = hptr->next;
1469
1470                 assert ((hptr != NULL) && (hptr->next == NULL));
1471                 hptr->next = ph;
1472         }
1473
1474         ping_set_ttl (ph, obj->ttl);
1475
1476         return (0);
1477 } /* int ping_host_add */
1478
1479 int ping_host_remove (pingobj_t *obj, const char *host)
1480 {
1481         pinghost_t *pre, *cur;
1482
1483         if ((obj == NULL) || (host == NULL))
1484                 return (-1);
1485
1486         pre = NULL;
1487         cur = obj->head;
1488
1489         while (cur != NULL)
1490         {
1491                 if (strcasecmp (host, cur->username) == 0)
1492                         break;
1493
1494                 pre = cur;
1495                 cur = cur->next;
1496         }
1497
1498         if (cur == NULL)
1499         {
1500                 ping_set_error (obj, "ping_host_remove", "Host not found");
1501                 return (-1);
1502         }
1503
1504         if (pre == NULL)
1505                 obj->head = cur->next;
1506         else
1507                 pre->next = cur->next;
1508         
1509         ping_free (cur);
1510
1511         return (0);
1512 }
1513
1514 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
1515 {
1516         if (obj == NULL)
1517                 return (NULL);
1518         return ((pingobj_iter_t *) obj->head);
1519 }
1520
1521 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
1522 {
1523         if (iter == NULL)
1524                 return (NULL);
1525         return ((pingobj_iter_t *) iter->next);
1526 }
1527
1528 int ping_iterator_get_info (pingobj_iter_t *iter, int info,
1529                 void *buffer, size_t *buffer_len)
1530 {
1531         int ret = EINVAL;
1532
1533         size_t orig_buffer_len = *buffer_len;
1534
1535         if ((iter == NULL) || (buffer == NULL) || (buffer_len == NULL))
1536                 return (-1);
1537
1538         switch (info)
1539         {
1540                 case PING_INFO_USERNAME:
1541                         ret = ENOMEM;
1542                         *buffer_len = strlen (iter->username) + 1;
1543                         if (orig_buffer_len <= *buffer_len)
1544                                 break;
1545                         /* Since (orig_buffer_len > *buffer_len) `strncpy'
1546                          * will copy `*buffer_len' and pad the rest of
1547                          * `buffer' with null-bytes */
1548                         strncpy (buffer, iter->username, orig_buffer_len);
1549                         ret = 0;
1550                         break;
1551
1552                 case PING_INFO_HOSTNAME:
1553                         ret = ENOMEM;
1554                         *buffer_len = strlen (iter->hostname) + 1;
1555                         if (orig_buffer_len < *buffer_len)
1556                                 break;
1557                         /* Since (orig_buffer_len > *buffer_len) `strncpy'
1558                          * will copy `*buffer_len' and pad the rest of
1559                          * `buffer' with null-bytes */
1560                         strncpy (buffer, iter->hostname, orig_buffer_len);
1561                         ret = 0;
1562                         break;
1563
1564                 case PING_INFO_ADDRESS:
1565                         ret = getnameinfo ((struct sockaddr *) iter->addr,
1566                                         iter->addrlen,
1567                                         (char *) buffer,
1568                                         *buffer_len,
1569                                         NULL, 0,
1570                                         NI_NUMERICHOST);
1571                         if (ret != 0)
1572                         {
1573                                 if ((ret == EAI_MEMORY)
1574 #ifdef EAI_OVERFLOW
1575                                                 || (ret == EAI_OVERFLOW)
1576 #endif
1577                                    )
1578                                         ret = ENOMEM;
1579 #if defined(EAI_SYSTEM)
1580                                 else if (ret == EAI_SYSTEM)
1581                                         ret = errno;
1582 #endif
1583                                 else
1584                                         ret = EINVAL;
1585                         }
1586                         break;
1587
1588                 case PING_INFO_FAMILY:
1589                         ret = ENOMEM;
1590                         *buffer_len = sizeof (int);
1591                         if (orig_buffer_len < sizeof (int))
1592                                 break;
1593                         *((int *) buffer) = iter->addrfamily;
1594                         ret = 0;
1595                         break;
1596
1597                 case PING_INFO_LATENCY:
1598                         ret = ENOMEM;
1599                         *buffer_len = sizeof (double);
1600                         if (orig_buffer_len < sizeof (double))
1601                                 break;
1602                         *((double *) buffer) = iter->latency;
1603                         ret = 0;
1604                         break;
1605
1606                 case PING_INFO_DROPPED:
1607                         ret = ENOMEM;
1608                         *buffer_len = sizeof (uint32_t);
1609                         if (orig_buffer_len < sizeof (uint32_t))
1610                                 break;
1611                         *((uint32_t *) buffer) = iter->dropped;
1612                         ret = 0;
1613                         break;
1614
1615                 case PING_INFO_SEQUENCE:
1616                         ret = ENOMEM;
1617                         *buffer_len = sizeof (unsigned int);
1618                         if (orig_buffer_len < sizeof (unsigned int))
1619                                 break;
1620                         *((unsigned int *) buffer) = (unsigned int) iter->sequence;
1621                         ret = 0;
1622                         break;
1623
1624                 case PING_INFO_IDENT:
1625                         ret = ENOMEM;
1626                         *buffer_len = sizeof (uint16_t);
1627                         if (orig_buffer_len < sizeof (uint16_t))
1628                                 break;
1629                         *((uint16_t *) buffer) = (uint16_t) iter->ident;
1630                         ret = 0;
1631                         break;
1632
1633                 case PING_INFO_DATA:
1634                         ret = ENOMEM;
1635                         *buffer_len = strlen (iter->data);
1636                         if (orig_buffer_len < *buffer_len)
1637                                 break;
1638                         strncpy ((char *) buffer, iter->data, orig_buffer_len);
1639                         ret = 0;
1640                         break;
1641
1642                 case PING_INFO_RECV_TTL:
1643                         ret = ENOMEM;
1644                         *buffer_len = sizeof (int);
1645                         if (orig_buffer_len < sizeof (int))
1646                                 break;
1647                         *((int *) buffer) = iter->recv_ttl;
1648                         ret = 0;
1649                         break;
1650         }
1651
1652         return (ret);
1653 } /* ping_iterator_get_info */
1654
1655 void *ping_iterator_get_context (pingobj_iter_t *iter)
1656 {
1657         if (iter == NULL)
1658                 return (NULL);
1659         return (iter->context);
1660 }
1661
1662 void ping_iterator_set_context (pingobj_iter_t *iter, void *context)
1663 {
1664         if (iter == NULL)
1665                 return;
1666         iter->context = context;
1667 }