1 /**
2 * Object oriented C module to send ICMP and ICMPv6 `echo's.
3 * Copyright (C) 2006-2010 Florian octo Forster <octo at verplant.org>
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by the
7 * Free Software Foundation; either version 2.1 of the License, or (at your
8 * option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
24 #if STDC_HEADERS
25 # include <stdlib.h>
26 # include <stdio.h>
27 # include <string.h>
28 # include <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 */
35 #ifdef HAVE_STDINT_H
36 # include <stdint.h>
37 #endif
39 #if HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
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
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
64 #if HAVE_SYS_SOCKET_H
65 # include <sys/socket.h>
66 #endif
68 #if HAVE_NETDB_H
69 # include <netdb.h>
70 #endif
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
94 #include "oping.h"
96 #if WITH_DEBUG
97 # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
98 #else
99 # define dprintf(...) /**/
100 #endif
102 #define PING_ERRMSG_LEN 256
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 uint8_t recv_qos;
121 char *data;
123 void *context;
125 struct pinghost *next;
126 };
128 struct pingobj
129 {
130 double timeout;
131 int ttl;
132 int addrfamily;
133 uint8_t qos;
134 char *data;
136 struct sockaddr *srcaddr;
137 socklen_t srcaddrlen;
139 char *device;
141 char errmsg[PING_ERRMSG_LEN];
143 pinghost_t *head;
144 };
146 /*
147 * private (static) functions
148 */
149 /* Even though Posix requires "strerror_r" to return an "int",
150 * some systems (e.g. the GNU libc) return a "char *" _and_
151 * ignore the second argument ... -tokkee */
152 static char *sstrerror (int errnum, char *buf, size_t buflen)
153 {
154 buf[0] = 0;
156 #if !HAVE_STRERROR_R
157 {
158 snprintf (buf, buflen, "Error %i (%#x)", errnum, errnum);
159 }
160 /* #endif !HAVE_STRERROR_R */
162 #elif STRERROR_R_CHAR_P
163 {
164 char *temp;
165 temp = strerror_r (errnum, buf, buflen);
166 if (buf[0] == 0)
167 {
168 if ((temp != NULL) && (temp != buf) && (temp[0] != 0))
169 strncpy (buf, temp, buflen);
170 else
171 strncpy (buf, "strerror_r did not return "
172 "an error message", buflen);
173 }
174 }
175 /* #endif STRERROR_R_CHAR_P */
177 #else
178 if (strerror_r (errnum, buf, buflen) != 0)
179 {
180 snprintf (buf, buflen, "Error %i (%#x); "
181 "Additionally, strerror_r failed.",
182 errnum, errnum);
183 }
184 #endif /* STRERROR_R_CHAR_P */
186 buf[buflen - 1] = 0;
188 return (buf);
189 } /* char *sstrerror */
191 static void ping_set_error (pingobj_t *obj, const char *function,
192 const char *message)
193 {
194 snprintf (obj->errmsg, sizeof (obj->errmsg),
195 "%s: %s", function, message);
196 obj->errmsg[sizeof (obj->errmsg) - 1] = 0;
197 }
199 static void ping_set_errno (pingobj_t *obj, int error_number)
200 {
201 sstrerror (error_number, obj->errmsg, sizeof (obj->errmsg));
202 }
204 static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
205 struct timeval *res)
206 {
207 res->tv_sec = tv1->tv_sec + tv2->tv_sec;
208 res->tv_usec = tv1->tv_usec + tv2->tv_usec;
210 while (res->tv_usec > 1000000)
211 {
212 res->tv_usec -= 1000000;
213 res->tv_sec++;
214 }
216 return (0);
217 }
219 static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
220 struct timeval *res)
221 {
222 if ((tv1->tv_sec < tv2->tv_sec)
223 || ((tv1->tv_sec == tv2->tv_sec)
224 && (tv1->tv_usec < tv2->tv_usec)))
225 return (-1);
227 res->tv_sec = tv1->tv_sec - tv2->tv_sec;
228 res->tv_usec = tv1->tv_usec - tv2->tv_usec;
230 assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec >= 0)));
232 while (res->tv_usec < 0)
233 {
234 res->tv_usec += 1000000;
235 res->tv_sec--;
236 }
238 return (0);
239 }
241 static uint16_t ping_icmp4_checksum (char *buf, size_t len)
242 {
243 uint32_t sum = 0;
244 uint16_t ret = 0;
246 uint16_t *ptr;
248 for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
249 sum += *ptr;
251 if (len == 1)
252 {
253 *(char *) &ret = *(char *) ptr;
254 sum += ret;
255 }
257 /* Do this twice to get all possible carries.. */
258 sum = (sum >> 16) + (sum & 0xFFFF);
259 sum = (sum >> 16) + (sum & 0xFFFF);
261 ret = ~sum;
263 return (ret);
264 }
266 static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
267 size_t buffer_len)
268 {
269 struct ip *ip_hdr;
270 struct icmp *icmp_hdr;
272 size_t ip_hdr_len;
274 uint16_t recv_checksum;
275 uint16_t calc_checksum;
277 uint16_t ident;
278 uint16_t seq;
280 pinghost_t *ptr;
282 if (buffer_len < sizeof (struct ip))
283 return (NULL);
285 ip_hdr = (struct ip *) buffer;
286 ip_hdr_len = ip_hdr->ip_hl << 2;
288 if (buffer_len < ip_hdr_len)
289 return (NULL);
291 buffer += ip_hdr_len;
292 buffer_len -= ip_hdr_len;
294 if (buffer_len < sizeof (struct icmp))
295 return (NULL);
297 icmp_hdr = (struct icmp *) buffer;
298 buffer += sizeof (struct icmp);
299 buffer_len -= sizeof (struct icmp);
301 if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
302 {
303 dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type);
304 return (NULL);
305 }
307 recv_checksum = icmp_hdr->icmp_cksum;
308 icmp_hdr->icmp_cksum = 0;
309 calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr,
310 sizeof (struct icmp) + buffer_len);
312 if (recv_checksum != calc_checksum)
313 {
314 dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
315 "calculated 0x%04"PRIx16"\n",
316 recv_checksum, calc_checksum);
317 return (NULL);
318 }
320 ident = ntohs (icmp_hdr->icmp_id);
321 seq = ntohs (icmp_hdr->icmp_seq);
323 /* We have to iterate over all hosts, since ICMPv4 packets may
324 * be received on any raw v4 socket. */
325 for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
326 {
327 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
328 ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
330 if (ptr->addrfamily != AF_INET)
331 continue;
333 if (!timerisset (ptr->timer))
334 continue;
336 if (ptr->ident != ident)
337 continue;
339 if (((ptr->sequence - 1) & 0xFFFF) != seq)
340 continue;
342 dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
343 "seq = %"PRIu16"\n",
344 ptr->hostname, ident, seq);
346 break;
347 }
349 if (ptr == NULL)
350 {
351 dprintf ("No match found for ident = 0x%04"PRIx16", seq = %"PRIu16"\n",
352 ident, seq);
353 }
355 if (ptr != NULL){
356 ptr->recv_ttl = (int) ip_hdr->ip_ttl;
357 ptr->recv_qos = (uint8_t) ip_hdr->ip_tos;
358 }
359 return (ptr);
360 }
362 #ifndef ICMP6_ECHO_REQUEST
363 # ifdef ICMP6_ECHO /* AIX netinet/ip6_icmp.h */
364 # define ICMP6_ECHO_REQUEST ICMP6_ECHO
365 # else
366 # define ICMP6_ECHO_REQUEST 128
367 # endif
368 #endif
370 #ifndef ICMP6_ECHO_REPLY
371 # ifdef ICMP6_ECHOREPLY /* AIX netinet/ip6_icmp.h */
372 # define ICMP6_ECHO_REPLY ICMP6_ECHOREPLY
373 # else
374 # define ICMP6_ECHO_REPLY 129
375 # endif
376 #endif
378 static pinghost_t *ping_receive_ipv6 (pingobj_t *obj, char *buffer,
379 size_t buffer_len)
380 {
381 struct icmp6_hdr *icmp_hdr;
383 uint16_t ident;
384 uint16_t seq;
386 pinghost_t *ptr;
388 if (buffer_len < sizeof (struct icmp6_hdr))
389 return (NULL);
391 icmp_hdr = (struct icmp6_hdr *) buffer;
392 buffer += sizeof (struct icmp);
393 buffer_len -= sizeof (struct icmp);
395 if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
396 {
397 dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type);
398 return (NULL);
399 }
401 if (icmp_hdr->icmp6_code != 0)
402 {
403 dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code);
404 return (NULL);
405 }
407 ident = ntohs (icmp_hdr->icmp6_id);
408 seq = ntohs (icmp_hdr->icmp6_seq);
410 /* We have to iterate over all hosts, since ICMPv6 packets may
411 * be received on any raw v6 socket. */
412 for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
413 {
414 dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
415 ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
417 if (ptr->addrfamily != AF_INET6)
418 continue;
420 if (!timerisset (ptr->timer))
421 continue;
423 if (ptr->ident != ident)
424 continue;
426 if (((ptr->sequence - 1) & 0xFFFF) != seq)
427 continue;
429 dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
430 "seq = %"PRIu16"\n",
431 ptr->hostname, ident, seq);
433 break;
434 }
436 if (ptr == NULL)
437 {
438 dprintf ("No match found for ident = 0x%04"PRIx16", "
439 "seq = %"PRIu16"\n",
440 ident, seq);
441 }
443 return (ptr);
444 }
446 static int ping_receive_one (pingobj_t *obj, const pinghost_t *ph,
447 struct timeval *now)
448 {
449 /* Note: 'ph' is not necessarily the host object for which we receive a
450 * reply. The right object will be returned by ping_receive_ipv*(). For
451 * now, we can only rely on ph->fd and ph->addrfamily. */
453 struct timeval diff;
454 pinghost_t *host = NULL;
455 int recv_ttl;
456 uint8_t recv_qos;
458 /*
459 * Set up the receive buffer..
460 */
461 struct msghdr msghdr;
462 struct cmsghdr *cmsg;
463 char payload_buffer[4096];
464 ssize_t payload_buffer_len;
465 char control_buffer[4096];
466 struct iovec payload_iovec;
468 memset (&payload_iovec, 0, sizeof (payload_iovec));
469 payload_iovec.iov_base = payload_buffer;
470 payload_iovec.iov_len = sizeof (payload_buffer);
472 memset (&msghdr, 0, sizeof (msghdr));
473 /* unspecified source address */
474 msghdr.msg_name = NULL;
475 msghdr.msg_namelen = 0;
476 /* output buffer vector, see readv(2) */
477 msghdr.msg_iov = &payload_iovec;
478 msghdr.msg_iovlen = 1;
479 /* output buffer for control messages */
480 msghdr.msg_control = control_buffer;
481 msghdr.msg_controllen = sizeof (control_buffer);
482 /* flags; this is an output only field.. */
483 msghdr.msg_flags = 0;
484 #ifdef MSG_XPG4_2
485 msghdr.msg_flags |= MSG_XPG4_2;
486 #endif
488 payload_buffer_len = recvmsg (ph->fd, &msghdr, /* flags = */ 0);
489 if (payload_buffer_len < 0)
490 {
491 #if WITH_DEBUG
492 char errbuf[PING_ERRMSG_LEN];
493 dprintf ("recvfrom: %s\n",
494 sstrerror (errno, errbuf, sizeof (errbuf)));
495 #endif
496 return (-1);
497 }
498 dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, ph->fd);
500 /* Iterate over all auxiliary data in msghdr */
501 recv_ttl = -1;
502 recv_qos = 0;
503 for (cmsg = CMSG_FIRSTHDR (&msghdr); /* {{{ */
504 cmsg != NULL;
505 cmsg = CMSG_NXTHDR (&msghdr, cmsg))
506 {
507 if (ph->addrfamily == AF_INET) /* {{{ */
508 {
509 if (cmsg->cmsg_level != IPPROTO_IP)
510 continue;
512 if (cmsg->cmsg_type == IP_TOS)
513 {
514 memcpy (&recv_qos, CMSG_DATA (cmsg),
515 sizeof (recv_qos));
516 dprintf ("TOSv4 = 0x%02"PRIx8";\n", recv_qos);
517 } else
518 if (cmsg->cmsg_type == IP_TTL)
519 {
520 memcpy (&recv_ttl, CMSG_DATA (cmsg),
521 sizeof (recv_ttl));
522 dprintf ("TTLv4 = %i;\n", recv_ttl);
523 }
524 else
525 {
526 dprintf ("Not handling option %i.\n",
527 cmsg->cmsg_type);
528 }
529 } /* }}} */
530 else if (ph->addrfamily == AF_INET6) /* {{{ */
531 {
532 if (cmsg->cmsg_level != IPPROTO_IPV6)
533 continue;
535 if (cmsg->cmsg_type == IPV6_TCLASS)
536 {
537 memcpy (&recv_qos, CMSG_DATA (cmsg),
538 sizeof (recv_qos));
539 dprintf ("TOSv6 = 0x%02"PRIx8";\n", recv_qos);
540 } else
541 if (cmsg->cmsg_type == IPV6_HOPLIMIT)
542 {
543 memcpy (&recv_ttl, CMSG_DATA (cmsg),
544 sizeof (recv_ttl));
545 dprintf ("TTLv6 = %i;\n", recv_ttl);
546 }
547 else
548 {
549 dprintf ("Not handling option %i.\n",
550 cmsg->cmsg_type);
551 }
552 } /* }}} */
553 else
554 {
555 dprintf ("Don't know how to handle "
556 "unknown protocol %i.\n",
557 cmsg->cmsg_level);
558 }
559 } /* }}} for (cmsg) */
561 if (ph->addrfamily == AF_INET)
562 {
563 host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
564 if (host == NULL)
565 return (-1);
566 }
567 else if (ph->addrfamily == AF_INET6)
568 {
569 host = ping_receive_ipv6 (obj, payload_buffer, payload_buffer_len);
570 if (host == NULL)
571 return (-1);
572 }
573 else
574 {
575 dprintf ("ping_receive_one: Unknown address family %i.\n",
576 ph->addrfamily);
577 return (-1);
578 }
580 dprintf ("rcvd: %12i.%06i\n",
581 (int) now->tv_sec,
582 (int) now->tv_usec);
583 dprintf ("sent: %12i.%06i\n",
584 (int) host->timer->tv_sec,
585 (int) host->timer->tv_usec);
587 if (ping_timeval_sub (now, host->timer, &diff) < 0)
588 {
589 timerclear (host->timer);
590 return (-1);
591 }
593 dprintf ("diff: %12i.%06i\n",
594 (int) diff.tv_sec,
595 (int) diff.tv_usec);
597 if (recv_ttl >= 0)
598 host->recv_ttl = recv_ttl;
599 host->recv_qos = recv_qos;
601 host->latency = ((double) diff.tv_usec) / 1000.0;
602 host->latency += ((double) diff.tv_sec) * 1000.0;
604 timerclear (host->timer);
606 return (0);
607 }
609 static int ping_receive_all (pingobj_t *obj)
610 {
611 fd_set read_fds;
612 fd_set err_fds;
613 int num_fds;
614 int max_fd;
616 pinghost_t *ph;
617 pinghost_t *ptr;
619 struct timeval endtime;
620 struct timeval nowtime;
621 struct timeval timeout;
622 int status;
624 int ret;
626 ph = obj->head;
627 ret = 0;
629 for (ptr = ph; ptr != NULL; ptr = ptr->next)
630 {
631 ptr->latency = -1.0;
632 ptr->recv_ttl = -1;
633 }
635 if (gettimeofday (&nowtime, NULL) == -1)
636 {
637 ping_set_errno (obj, errno);
638 return (-1);
639 }
641 /* Set up timeout */
642 timeout.tv_sec = (time_t) obj->timeout;
643 timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec)));
645 dprintf ("Set timeout to %i.%06i seconds\n",
646 (int) timeout.tv_sec,
647 (int) timeout.tv_usec);
649 ping_timeval_add (&nowtime, &timeout, &endtime);
651 while (1)
652 {
653 FD_ZERO (&read_fds);
654 FD_ZERO (&err_fds);
655 num_fds = 0;
656 max_fd = -1;
658 for (ptr = ph; ptr != NULL; ptr = ptr->next)
659 {
660 if (!timerisset (ptr->timer))
661 continue;
663 FD_SET (ptr->fd, &read_fds);
664 FD_SET (ptr->fd, &err_fds);
665 num_fds++;
667 if (max_fd < ptr->fd)
668 max_fd = ptr->fd;
669 }
671 if (num_fds == 0)
672 break;
674 if (gettimeofday (&nowtime, NULL) == -1)
675 {
676 ping_set_errno (obj, errno);
677 return (-1);
678 }
680 if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1)
681 break;
683 dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_fds,
684 (int) timeout.tv_sec,
685 (int) timeout.tv_usec);
687 status = select (max_fd + 1, &read_fds, NULL, &err_fds, &timeout);
689 if (gettimeofday (&nowtime, NULL) == -1)
690 {
691 ping_set_errno (obj, errno);
692 return (-1);
693 }
695 if ((status == -1) && (errno == EINTR))
696 {
697 dprintf ("select was interrupted by signal..\n");
698 continue;
699 }
700 else if (status < 0)
701 {
702 #if WITH_DEBUG
703 char errbuf[PING_ERRMSG_LEN];
704 dprintf ("select: %s\n",
705 sstrerror (errno, errbuf, sizeof (errbuf)));
706 #endif
707 break;
708 }
709 else if (status == 0)
710 {
711 dprintf ("select timed out\n");
712 for (ptr = ph; ptr != NULL; ptr = ptr->next)
713 if (ptr->latency < 0.0)
714 ptr->dropped++;
715 break;
716 }
718 for (ptr = ph; ptr != NULL; ptr = ptr->next)
719 {
720 if (FD_ISSET (ptr->fd, &read_fds))
721 {
722 if (ping_receive_one (obj, ptr, &nowtime) == 0)
723 ret++;
724 }
725 else if (FD_ISSET (ptr->fd, &err_fds))
726 {
727 /* clear the timer in this case so that we
728 * don't run into an endless loop. */
729 /* TODO: Set an error flag in this case. */
730 timerclear (ptr->timer);
731 }
732 }
733 } /* while (1) */
735 return (ret);
736 }
738 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
739 * Sending functions: *
740 * *
741 * ping_send_all *
742 * +-> ping_send_one_ipv4 *
743 * `-> ping_send_one_ipv6 *
744 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
745 static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
746 const void *buf, size_t buflen)
747 {
748 ssize_t ret;
750 if (gettimeofday (ph->timer, NULL) == -1)
751 {
752 timerclear (ph->timer);
753 return (-1);
754 }
756 ret = sendto (ph->fd, buf, buflen, 0,
757 (struct sockaddr *) ph->addr, ph->addrlen);
759 if (ret < 0)
760 {
761 #if defined(EHOSTUNREACH)
762 if (errno == EHOSTUNREACH)
763 return (0);
764 #endif
765 #if defined(ENETUNREACH)
766 if (errno == ENETUNREACH)
767 return (0);
768 #endif
769 ping_set_errno (obj, errno);
770 }
772 return (ret);
773 }
775 static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph)
776 {
777 struct icmp *icmp4;
778 int status;
780 char buf[4096];
781 int buflen;
783 char *data;
784 int datalen;
786 dprintf ("ph->hostname = %s\n", ph->hostname);
788 memset (buf, '\0', sizeof (buf));
789 icmp4 = (struct icmp *) buf;
790 data = (char *) (icmp4 + 1);
792 icmp4->icmp_type = ICMP_ECHO;
793 icmp4->icmp_code = 0;
794 icmp4->icmp_cksum = 0;
795 icmp4->icmp_id = htons (ph->ident);
796 icmp4->icmp_seq = htons (ph->sequence);
798 buflen = 4096 - sizeof (struct icmp);
799 strncpy (data, ph->data, buflen);
800 datalen = strlen (data);
802 buflen = datalen + sizeof (struct icmp);
804 icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
806 dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
808 status = ping_sendto (obj, ph, buf, buflen);
809 if (status < 0)
810 {
811 perror ("ping_sendto");
812 return (-1);
813 }
815 dprintf ("sendto: status = %i\n", status);
817 return (0);
818 }
820 static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph)
821 {
822 struct icmp6_hdr *icmp6;
823 int status;
825 char buf[4096];
826 int buflen;
828 char *data;
829 int datalen;
831 dprintf ("ph->hostname = %s\n", ph->hostname);
833 memset (buf, '\0', sizeof (buf));
834 icmp6 = (struct icmp6_hdr *) buf;
835 data = (char *) (icmp6 + 1);
837 icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
838 icmp6->icmp6_code = 0;
839 /* The checksum will be calculated by the TCP/IP stack. */
840 /* FIXME */
841 icmp6->icmp6_cksum = 0;
842 icmp6->icmp6_id = htons (ph->ident);
843 icmp6->icmp6_seq = htons (ph->sequence);
845 buflen = 4096 - sizeof (struct icmp6_hdr);
846 strncpy (data, ph->data, buflen);
847 datalen = strlen (data);
849 buflen = datalen + sizeof (struct icmp6_hdr);
851 dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
853 status = ping_sendto (obj, ph, buf, buflen);
854 if (status < 0)
855 {
856 perror ("ping_sendto");
857 return (-1);
858 }
860 dprintf ("sendto: status = %i\n", status);
862 return (0);
863 }
865 static int ping_send_all (pingobj_t *obj)
866 {
867 pinghost_t *ph;
868 pinghost_t *ptr;
870 int ret;
872 ret = 0;
873 ph = obj->head;
875 for (ptr = ph; ptr != NULL; ptr = ptr->next)
876 {
877 /* start timer.. The GNU `ping6' starts the timer before
878 * sending the packet, so I will do that too */
879 if (gettimeofday (ptr->timer, NULL) == -1)
880 {
881 #if WITH_DEBUG
882 char errbuf[PING_ERRMSG_LEN];
883 dprintf ("gettimeofday: %s\n",
884 sstrerror (errno, errbuf, sizeof (errbuf)));
885 #endif
886 timerclear (ptr->timer);
887 ret--;
888 continue;
889 }
890 else
891 {
892 dprintf ("timer set for hostname = %s\n", ptr->hostname);
893 }
895 if (ptr->addrfamily == AF_INET6)
896 {
897 dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname);
898 if (ping_send_one_ipv6 (obj, ptr) != 0)
899 {
900 timerclear (ptr->timer);
901 ret--;
902 continue;
903 }
904 }
905 else if (ptr->addrfamily == AF_INET)
906 {
907 dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
908 if (ping_send_one_ipv4 (obj, ptr) != 0)
909 {
910 timerclear (ptr->timer);
911 ret--;
912 continue;
913 }
914 }
915 else /* this should not happen */
916 {
917 dprintf ("Unknown address family: %i\n", ptr->addrfamily);
918 timerclear (ptr->timer);
919 ret--;
920 continue;
921 }
923 ptr->sequence++;
924 }
926 return (ret);
927 }
929 /*
930 * Set the TTL of a socket protocol independently.
931 */
932 static int ping_set_ttl (pinghost_t *ph, int ttl)
933 {
934 int ret = -2;
936 if (ph->addrfamily == AF_INET)
937 {
938 dprintf ("Setting TTLv4 to %i\n", ttl);
939 ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL,
940 &ttl, sizeof (ttl));
941 }
942 else if (ph->addrfamily == AF_INET6)
943 {
944 dprintf ("Setting TTLv6 to %i\n", ttl);
945 ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
946 &ttl, sizeof (ttl));
947 }
949 return (ret);
950 }
952 /*
953 * Set the TOS of a socket protocol independently.
954 *
955 * Using SOL_SOCKET / SO_PRIORITY might be a protocol independent way to
956 * set this. See socket(7) for details.
957 */
958 static int ping_set_qos (pingobj_t *obj, pinghost_t *ph, uint8_t qos)
959 {
960 int ret = EINVAL;
961 char errbuf[PING_ERRMSG_LEN];
963 if (ph->addrfamily == AF_INET)
964 {
965 dprintf ("Setting TP_TOS to %#04"PRIx8"\n", qos);
966 ret = setsockopt (ph->fd, IPPROTO_IP, IP_TOS,
967 &qos, sizeof (qos));
968 if (ret != 0)
969 {
970 ret = errno;
971 ping_set_error (obj, "ping_set_qos",
972 sstrerror (ret, errbuf, sizeof (errbuf)));
973 dprintf ("Setting TP_TOS failed: %s\n", errbuf);
974 }
975 }
976 else if (ph->addrfamily == AF_INET6)
977 {
978 /* IPV6_TCLASS requires an "int". */
979 int tmp = (int) qos;
981 dprintf ("Setting IPV6_TCLASS to %#04"PRIx8" (%i)\n", qos, tmp);
982 ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_TCLASS,
983 &tmp, sizeof (tmp));
984 if (ret != 0)
985 {
986 ret = errno;
987 ping_set_error (obj, "ping_set_qos",
988 sstrerror (ret, errbuf, sizeof (errbuf)));
989 dprintf ("Setting IPV6_TCLASS failed: %s\n", errbuf);
990 }
991 }
993 return (ret);
994 }
996 static int ping_get_ident (void)
997 {
998 int fd;
999 static int did_seed = 0;
1001 int retval;
1003 if (did_seed == 0)
1004 {
1005 if ((fd = open ("/dev/urandom", O_RDONLY)) != -1)
1006 {
1007 unsigned int seed;
1009 if (read (fd, &seed, sizeof (seed)) != -1)
1010 {
1011 did_seed = 1;
1012 dprintf ("Random seed: %#x\n", seed);
1013 srandom (seed);
1014 }
1016 close (fd);
1017 }
1018 #if WITH_DEBUG
1019 else
1020 {
1021 char errbuf[PING_ERRMSG_LEN];
1022 dprintf ("open (/dev/urandom): %s\n",
1023 sstrerror (errno, errbuf, sizeof (errbuf)));
1024 }
1025 #endif
1026 }
1028 retval = (int) random ();
1030 dprintf ("Random number: %#x\n", retval);
1032 return (retval);
1033 }
1035 static pinghost_t *ping_alloc (void)
1036 {
1037 pinghost_t *ph;
1038 size_t ph_size;
1040 ph_size = sizeof (pinghost_t)
1041 + sizeof (struct sockaddr_storage)
1042 + sizeof (struct timeval);
1044 ph = (pinghost_t *) malloc (ph_size);
1045 if (ph == NULL)
1046 return (NULL);
1048 memset (ph, '\0', ph_size);
1050 ph->timer = (struct timeval *) (ph + 1);
1051 ph->addr = (struct sockaddr_storage *) (ph->timer + 1);
1053 ph->addrlen = sizeof (struct sockaddr_storage);
1054 ph->fd = -1;
1055 ph->latency = -1.0;
1056 ph->dropped = 0;
1057 ph->ident = ping_get_ident () & 0xFFFF;
1059 return (ph);
1060 }
1062 static void ping_free (pinghost_t *ph)
1063 {
1064 if (ph->fd >= 0)
1065 close (ph->fd);
1067 if (ph->username != NULL)
1068 free (ph->username);
1070 if (ph->hostname != NULL)
1071 free (ph->hostname);
1073 if (ph->data != NULL)
1074 free (ph->data);
1076 free (ph);
1077 }
1079 /*
1080 * public methods
1081 */
1082 const char *ping_get_error (pingobj_t *obj)
1083 {
1084 if (obj == NULL)
1085 return (NULL);
1086 return (obj->errmsg);
1087 }
1089 pingobj_t *ping_construct (void)
1090 {
1091 pingobj_t *obj;
1093 if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL)
1094 return (NULL);
1095 memset (obj, 0, sizeof (pingobj_t));
1097 obj->timeout = PING_DEF_TIMEOUT;
1098 obj->ttl = PING_DEF_TTL;
1099 obj->addrfamily = PING_DEF_AF;
1100 obj->data = strdup (PING_DEF_DATA);
1101 obj->qos = 0;
1103 return (obj);
1104 }
1106 void ping_destroy (pingobj_t *obj)
1107 {
1108 pinghost_t *current;
1109 pinghost_t *next;
1111 if (obj == NULL)
1112 return;
1114 current = obj->head;
1115 next = NULL;
1117 while (current != NULL)
1118 {
1119 next = current->next;
1120 ping_free (current);
1121 current = next;
1122 }
1124 if (obj->data != NULL)
1125 free (obj->data);
1127 if (obj->srcaddr != NULL)
1128 free (obj->srcaddr);
1130 if (obj->device != NULL)
1131 free (obj->device);
1133 free (obj);
1135 return;
1136 }
1138 int ping_setopt (pingobj_t *obj, int option, void *value)
1139 {
1140 int ret = 0;
1142 if ((obj == NULL) || (value == NULL))
1143 return (-1);
1145 switch (option)
1146 {
1147 case PING_OPT_QOS:
1148 {
1149 pinghost_t *ph;
1151 obj->qos = *((uint8_t *) value);
1152 for (ph = obj->head; ph != NULL; ph = ph->next)
1153 ping_set_qos (obj, ph, obj->qos);
1154 break;
1155 }
1157 case PING_OPT_TIMEOUT:
1158 obj->timeout = *((double *) value);
1159 if (obj->timeout < 0.0)
1160 {
1161 obj->timeout = PING_DEF_TIMEOUT;
1162 ret = -1;
1163 }
1164 break;
1166 case PING_OPT_TTL:
1167 obj->ttl = *((int *) value);
1168 if ((obj->ttl < 1) || (obj->ttl > 255))
1169 {
1170 obj->ttl = PING_DEF_TTL;
1171 ret = -1;
1172 }
1173 else
1174 {
1175 pinghost_t *ph;
1177 for (ph = obj->head; ph != NULL; ph = ph->next)
1178 ping_set_ttl (ph, obj->ttl);
1179 }
1180 break;
1182 case PING_OPT_AF:
1183 obj->addrfamily = *((int *) value);
1184 if ((obj->addrfamily != AF_UNSPEC)
1185 && (obj->addrfamily != AF_INET)
1186 && (obj->addrfamily != AF_INET6))
1187 {
1188 obj->addrfamily = PING_DEF_AF;
1189 ret = -1;
1190 }
1191 if (obj->srcaddr != NULL)
1192 {
1193 free (obj->srcaddr);
1194 obj->srcaddr = NULL;
1195 }
1196 break;
1198 case PING_OPT_DATA:
1199 if (obj->data != NULL)
1200 {
1201 free (obj->data);
1202 obj->data = NULL;
1203 }
1204 obj->data = strdup ((const char *) value);
1205 break;
1207 case PING_OPT_SOURCE:
1208 {
1209 char *hostname = (char *) value;
1210 struct addrinfo ai_hints;
1211 struct addrinfo *ai_list;
1212 int status;
1213 #if WITH_DEBUG
1214 if (obj->addrfamily != AF_UNSPEC)
1215 {
1216 dprintf ("Resetting obj->addrfamily to AF_UNSPEC.\n");
1217 }
1218 #endif
1219 memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
1220 ai_hints.ai_family = obj->addrfamily = AF_UNSPEC;
1221 #if defined(AI_ADDRCONFIG)
1222 ai_hints.ai_flags = AI_ADDRCONFIG;
1223 #endif
1224 status = getaddrinfo (hostname, NULL, &ai_hints, &ai_list);
1225 if (status != 0)
1226 {
1227 #if defined(EAI_SYSTEM)
1228 char errbuf[PING_ERRMSG_LEN];
1229 #endif
1230 ping_set_error (obj, "getaddrinfo",
1231 #if defined(EAI_SYSTEM)
1232 (status == EAI_SYSTEM)
1233 ? sstrerror (errno, errbuf, sizeof (errbuf)) :
1234 #endif
1235 gai_strerror (status));
1236 ret = -1;
1237 break;
1238 }
1239 #if WITH_DEBUG
1240 if (ai_list->ai_next != NULL)
1241 {
1242 dprintf ("hostname = `%s' is ambiguous.\n", hostname);
1243 }
1244 #endif
1245 if (obj->srcaddr == NULL)
1246 {
1247 obj->srcaddrlen = 0;
1248 obj->srcaddr = malloc (sizeof (struct sockaddr_storage));
1249 if (obj->srcaddr == NULL)
1250 {
1251 ping_set_errno (obj, errno);
1252 ret = -1;
1253 freeaddrinfo (ai_list);
1254 break;
1255 }
1256 }
1257 memset ((void *) obj->srcaddr, 0, sizeof (struct sockaddr_storage));
1258 assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
1259 memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
1260 ai_list->ai_addrlen);
1261 obj->srcaddrlen = ai_list->ai_addrlen;
1262 obj->addrfamily = ai_list->ai_family;
1264 freeaddrinfo (ai_list);
1265 } /* case PING_OPT_SOURCE */
1266 break;
1268 case PING_OPT_DEVICE:
1269 {
1270 #ifdef SO_BINDTODEVICE
1271 char *device = strdup ((char *) value);
1273 if (device == NULL)
1274 {
1275 ping_set_errno (obj, errno);
1276 ret = -1;
1277 break;
1278 }
1280 if (obj->device != NULL)
1281 free (obj->device);
1282 obj->device = device;
1283 #else /* ! SO_BINDTODEVICE */
1284 ping_set_errno (obj, ENOTSUP);
1285 ret = -1;
1286 #endif /* ! SO_BINDTODEVICE */
1287 } /* case PING_OPT_DEVICE */
1288 break;
1290 default:
1291 ret = -2;
1292 } /* switch (option) */
1294 return (ret);
1295 } /* int ping_setopt */
1298 int ping_send (pingobj_t *obj)
1299 {
1300 int ret;
1302 if (obj == NULL)
1303 return (-1);
1305 if (ping_send_all (obj) < 0)
1306 return (-1);
1308 if ((ret = ping_receive_all (obj)) < 0)
1309 return (-2);
1311 return (ret);
1312 }
1314 static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
1315 {
1316 while (ph != NULL)
1317 {
1318 if (strcasecmp (ph->username, host) == 0)
1319 break;
1321 ph = ph->next;
1322 }
1324 return (ph);
1325 }
1327 int ping_host_add (pingobj_t *obj, const char *host)
1328 {
1329 pinghost_t *ph;
1331 struct addrinfo ai_hints;
1332 struct addrinfo *ai_list, *ai_ptr;
1333 int ai_return;
1335 if ((obj == NULL) || (host == NULL))
1336 return (-1);
1338 dprintf ("host = %s\n", host);
1340 if (ping_host_search (obj->head, host) != NULL)
1341 return (0);
1343 memset (&ai_hints, '\0', sizeof (ai_hints));
1344 ai_hints.ai_flags = 0;
1345 #ifdef AI_ADDRCONFIG
1346 ai_hints.ai_flags |= AI_ADDRCONFIG;
1347 #endif
1348 #ifdef AI_CANONNAME
1349 ai_hints.ai_flags |= AI_CANONNAME;
1350 #endif
1351 ai_hints.ai_family = obj->addrfamily;
1352 ai_hints.ai_socktype = SOCK_RAW;
1354 if ((ph = ping_alloc ()) == NULL)
1355 {
1356 dprintf ("Out of memory!\n");
1357 return (-1);
1358 }
1360 if ((ph->username = strdup (host)) == NULL)
1361 {
1362 dprintf ("Out of memory!\n");
1363 ping_set_errno (obj, errno);
1364 ping_free (ph);
1365 return (-1);
1366 }
1368 if ((ph->hostname = strdup (host)) == NULL)
1369 {
1370 dprintf ("Out of memory!\n");
1371 ping_set_errno (obj, errno);
1372 ping_free (ph);
1373 return (-1);
1374 }
1376 /* obj->data is not garuanteed to be != NULL */
1377 if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL)
1378 {
1379 dprintf ("Out of memory!\n");
1380 ping_set_errno (obj, errno);
1381 ping_free (ph);
1382 return (-1);
1383 }
1385 if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
1386 {
1387 #if defined(EAI_SYSTEM)
1388 char errbuf[PING_ERRMSG_LEN];
1389 #endif
1390 dprintf ("getaddrinfo failed\n");
1391 ping_set_error (obj, "getaddrinfo",
1392 #if defined(EAI_SYSTEM)
1393 (ai_return == EAI_SYSTEM)
1394 ? sstrerror (errno, errbuf, sizeof (errbuf)) :
1395 #endif
1396 gai_strerror (ai_return));
1397 ping_free (ph);
1398 return (-1);
1399 }
1401 if (ai_list == NULL)
1402 ping_set_error (obj, "getaddrinfo", "No hosts returned");
1404 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
1405 {
1406 ph->fd = -1;
1408 if (ai_ptr->ai_family == AF_INET)
1409 {
1410 ai_ptr->ai_socktype = SOCK_RAW;
1411 ai_ptr->ai_protocol = IPPROTO_ICMP;
1412 }
1413 else if (ai_ptr->ai_family == AF_INET6)
1414 {
1415 ai_ptr->ai_socktype = SOCK_RAW;
1416 ai_ptr->ai_protocol = IPPROTO_ICMPV6;
1417 }
1418 else
1419 {
1420 char errmsg[PING_ERRMSG_LEN];
1422 snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family);
1423 errmsg[PING_ERRMSG_LEN - 1] = '\0';
1425 dprintf (errmsg);
1426 ping_set_error (obj, "getaddrinfo", errmsg);
1427 continue;
1428 }
1430 /* TODO: Move this to a static function `ping_open_socket' and
1431 * call it whenever the socket dies. */
1432 ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
1433 if (ph->fd == -1)
1434 {
1435 #if WITH_DEBUG
1436 char errbuf[PING_ERRMSG_LEN];
1437 dprintf ("socket: %s\n",
1438 sstrerror (errno, errbuf, sizeof (errbuf)));
1439 #endif
1440 ping_set_errno (obj, errno);
1441 continue;
1442 }
1444 if (obj->srcaddr != NULL)
1445 {
1446 assert (obj->srcaddrlen > 0);
1447 assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
1449 if (bind (ph->fd, obj->srcaddr, obj->srcaddrlen) == -1)
1450 {
1451 #if WITH_DEBUG
1452 char errbuf[PING_ERRMSG_LEN];
1453 dprintf ("bind: %s\n",
1454 sstrerror (errno, errbuf, sizeof (errbuf)));
1455 #endif
1456 ping_set_errno (obj, errno);
1457 close (ph->fd);
1458 ph->fd = -1;
1459 continue;
1460 }
1461 }
1463 #ifdef SO_BINDTODEVICE
1464 if (obj->device != NULL)
1465 {
1466 if (setsockopt (ph->fd, SOL_SOCKET, SO_BINDTODEVICE,
1467 obj->device, strlen (obj->device) + 1) != 0)
1468 {
1469 #if WITH_DEBUG
1470 char errbuf[PING_ERRMSG_LEN];
1471 dprintf ("setsockopt: %s\n",
1472 sstrerror (errno, errbuf, sizeof (errbuf)));
1473 #endif
1474 ping_set_errno (obj, errno);
1475 close (ph->fd);
1476 ph->fd = -1;
1477 continue;
1478 }
1479 }
1480 #endif /* SO_BINDTODEVICE */
1482 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
1483 memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
1484 memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
1485 ph->addrlen = ai_ptr->ai_addrlen;
1486 ph->addrfamily = ai_ptr->ai_family;
1488 #ifdef AI_CANONNAME
1489 if ((ai_ptr->ai_canonname != NULL)
1490 && (strcmp (ph->hostname, ai_ptr->ai_canonname) != 0))
1491 {
1492 char *old_hostname;
1494 dprintf ("ph->hostname = %s; ai_ptr->ai_canonname = %s;\n",
1495 ph->hostname, ai_ptr->ai_canonname);
1497 old_hostname = ph->hostname;
1498 if ((ph->hostname = strdup (ai_ptr->ai_canonname)) == NULL)
1499 {
1500 /* strdup failed, falling back to old hostname */
1501 ph->hostname = old_hostname;
1502 }
1503 else if (old_hostname != NULL)
1504 {
1505 free (old_hostname);
1506 }
1507 }
1508 #endif /* AI_CANONNAME */
1510 if (ph->addrfamily == AF_INET)
1511 {
1512 int opt;
1514 /* Enable receiving the TOS field */
1515 opt = 1;
1516 setsockopt (ph->fd, IPPROTO_IP, IP_RECVTOS,
1517 &opt, sizeof (opt));
1519 /* Enable receiving the TTL field */
1520 opt = 1;
1521 setsockopt (ph->fd, IPPROTO_IP, IP_RECVTTL,
1522 &opt, sizeof (opt));
1523 }
1524 #if defined(IPV6_RECVHOPLIMIT) || defined(IPV6_RECVTCLASS)
1525 else if (ph->addrfamily == AF_INET6)
1526 {
1527 int opt;
1529 # if defined(IPV6_RECVHOPLIMIT)
1530 /* For details see RFC 3542, section 6.3. */
1531 opt = 1;
1532 setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
1533 &opt, sizeof (opt));
1534 # endif /* IPV6_RECVHOPLIMIT */
1536 # if defined(IPV6_RECVTCLASS)
1537 /* For details see RFC 3542, section 6.5. */
1538 opt = 1;
1539 setsockopt (ph->fd, IPPROTO_IPV6, IPV6_RECVTCLASS,
1540 &opt, sizeof (opt));
1541 # endif /* IPV6_RECVTCLASS */
1542 }
1543 #endif /* IPV6_RECVHOPLIMIT || IPV6_RECVTCLASS */
1545 break;
1546 } /* for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */
1548 freeaddrinfo (ai_list);
1550 if (ph->fd < 0)
1551 {
1552 ping_free (ph);
1553 return (-1);
1554 }
1556 /*
1557 * Adding in the front is much easier, but then the iterator will
1558 * return the host that was added last as first host. That's just not
1559 * nice. -octo
1560 */
1561 if (obj->head == NULL)
1562 {
1563 obj->head = ph;
1564 }
1565 else
1566 {
1567 pinghost_t *hptr;
1569 hptr = obj->head;
1570 while (hptr->next != NULL)
1571 hptr = hptr->next;
1573 assert ((hptr != NULL) && (hptr->next == NULL));
1574 hptr->next = ph;
1575 }
1577 ping_set_ttl (ph, obj->ttl);
1578 ping_set_qos (obj, ph, obj->qos);
1580 return (0);
1581 } /* int ping_host_add */
1583 int ping_host_remove (pingobj_t *obj, const char *host)
1584 {
1585 pinghost_t *pre, *cur;
1587 if ((obj == NULL) || (host == NULL))
1588 return (-1);
1590 pre = NULL;
1591 cur = obj->head;
1593 while (cur != NULL)
1594 {
1595 if (strcasecmp (host, cur->username) == 0)
1596 break;
1598 pre = cur;
1599 cur = cur->next;
1600 }
1602 if (cur == NULL)
1603 {
1604 ping_set_error (obj, "ping_host_remove", "Host not found");
1605 return (-1);
1606 }
1608 if (pre == NULL)
1609 obj->head = cur->next;
1610 else
1611 pre->next = cur->next;
1613 ping_free (cur);
1615 return (0);
1616 }
1618 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
1619 {
1620 if (obj == NULL)
1621 return (NULL);
1622 return ((pingobj_iter_t *) obj->head);
1623 }
1625 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
1626 {
1627 if (iter == NULL)
1628 return (NULL);
1629 return ((pingobj_iter_t *) iter->next);
1630 }
1632 int ping_iterator_get_info (pingobj_iter_t *iter, int info,
1633 void *buffer, size_t *buffer_len)
1634 {
1635 int ret = EINVAL;
1637 size_t orig_buffer_len = *buffer_len;
1639 if ((iter == NULL) || (buffer_len == NULL))
1640 return (-1);
1642 if ((buffer == NULL) && (*buffer_len != 0 ))
1643 return (-1);
1645 switch (info)
1646 {
1647 case PING_INFO_USERNAME:
1648 ret = ENOMEM;
1649 *buffer_len = strlen (iter->username) + 1;
1650 if (orig_buffer_len <= *buffer_len)
1651 break;
1652 /* Since (orig_buffer_len > *buffer_len) `strncpy'
1653 * will copy `*buffer_len' and pad the rest of
1654 * `buffer' with null-bytes */
1655 strncpy (buffer, iter->username, orig_buffer_len);
1656 ret = 0;
1657 break;
1659 case PING_INFO_HOSTNAME:
1660 ret = ENOMEM;
1661 *buffer_len = strlen (iter->hostname) + 1;
1662 if (orig_buffer_len < *buffer_len)
1663 break;
1664 /* Since (orig_buffer_len > *buffer_len) `strncpy'
1665 * will copy `*buffer_len' and pad the rest of
1666 * `buffer' with null-bytes */
1667 strncpy (buffer, iter->hostname, orig_buffer_len);
1668 ret = 0;
1669 break;
1671 case PING_INFO_ADDRESS:
1672 ret = getnameinfo ((struct sockaddr *) iter->addr,
1673 iter->addrlen,
1674 (char *) buffer,
1675 *buffer_len,
1676 NULL, 0,
1677 NI_NUMERICHOST);
1678 if (ret != 0)
1679 {
1680 if ((ret == EAI_MEMORY)
1681 #ifdef EAI_OVERFLOW
1682 || (ret == EAI_OVERFLOW)
1683 #endif
1684 )
1685 ret = ENOMEM;
1686 #if defined(EAI_SYSTEM)
1687 else if (ret == EAI_SYSTEM)
1688 ret = errno;
1689 #endif
1690 else
1691 ret = EINVAL;
1692 }
1693 break;
1695 case PING_INFO_FAMILY:
1696 ret = ENOMEM;
1697 *buffer_len = sizeof (int);
1698 if (orig_buffer_len < sizeof (int))
1699 break;
1700 *((int *) buffer) = iter->addrfamily;
1701 ret = 0;
1702 break;
1704 case PING_INFO_LATENCY:
1705 ret = ENOMEM;
1706 *buffer_len = sizeof (double);
1707 if (orig_buffer_len < sizeof (double))
1708 break;
1709 *((double *) buffer) = iter->latency;
1710 ret = 0;
1711 break;
1713 case PING_INFO_DROPPED:
1714 ret = ENOMEM;
1715 *buffer_len = sizeof (uint32_t);
1716 if (orig_buffer_len < sizeof (uint32_t))
1717 break;
1718 *((uint32_t *) buffer) = iter->dropped;
1719 ret = 0;
1720 break;
1722 case PING_INFO_SEQUENCE:
1723 ret = ENOMEM;
1724 *buffer_len = sizeof (unsigned int);
1725 if (orig_buffer_len < sizeof (unsigned int))
1726 break;
1727 *((unsigned int *) buffer) = (unsigned int) iter->sequence;
1728 ret = 0;
1729 break;
1731 case PING_INFO_IDENT:
1732 ret = ENOMEM;
1733 *buffer_len = sizeof (uint16_t);
1734 if (orig_buffer_len < sizeof (uint16_t))
1735 break;
1736 *((uint16_t *) buffer) = (uint16_t) iter->ident;
1737 ret = 0;
1738 break;
1740 case PING_INFO_DATA:
1741 ret = ENOMEM;
1742 *buffer_len = strlen (iter->data);
1743 if (orig_buffer_len < *buffer_len)
1744 break;
1745 strncpy ((char *) buffer, iter->data, orig_buffer_len);
1746 ret = 0;
1747 break;
1749 case PING_INFO_RECV_TTL:
1750 ret = ENOMEM;
1751 *buffer_len = sizeof (int);
1752 if (orig_buffer_len < sizeof (int))
1753 break;
1754 *((int *) buffer) = iter->recv_ttl;
1755 ret = 0;
1756 break;
1758 case PING_INFO_RECV_QOS:
1759 ret = ENOMEM;
1760 if (*buffer_len>sizeof(unsigned)) *buffer_len=sizeof(unsigned);
1761 if (!*buffer_len) *buffer_len=1;
1762 if (orig_buffer_len < *buffer_len)
1763 break;
1764 memcpy(buffer,&iter->recv_qos,*buffer_len);
1765 ret = 0;
1766 break;
1767 }
1769 return (ret);
1770 } /* ping_iterator_get_info */
1772 void *ping_iterator_get_context (pingobj_iter_t *iter)
1773 {
1774 if (iter == NULL)
1775 return (NULL);
1776 return (iter->context);
1777 }
1779 void ping_iterator_set_context (pingobj_iter_t *iter, void *context)
1780 {
1781 if (iter == NULL)
1782 return;
1783 iter->context = context;
1784 }