X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fliboping%2Fliboping.c;h=fb3f843178ba800c6230e67e0d780d90bd284fb4;hb=2d8915c37b7be045e6e638ff67e0b1168b4136a6;hp=a6d2c5dbcab768b32ce11abc986d6ec64b2c31f9;hpb=61d0abe95ab3c7461ca948fdc8824d08ad48cc6d;p=collectd.git diff --git a/src/liboping/liboping.c b/src/liboping/liboping.c index a6d2c5db..fb3f8431 100644 --- a/src/liboping/liboping.c +++ b/src/liboping/liboping.c @@ -17,35 +17,138 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include -#include -#include +#if HAVE_CONFIG_H +# include +#endif -#include +#if STDC_HEADERS +# include +# include +# include +# include +# include +#else +# error "You don't have the standard C99 header files installed" +#endif /* STDC_HEADERS */ -#include -#include -#include +#if HAVE_UNISTD_H +# include +#endif -#include -#include -#include -#include +#if HAVE_FCNTL_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif -#include -#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_NETDB_H +# include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +#if HAVE_NETINET_IP_ICMP_H +# include +#endif +#ifdef HAVE_NETINET_IP_VAR_H +# include +#endif +#if HAVE_NETINET_IP6_H +# include +#endif +#if HAVE_NETINET_ICMP6_H +# include +#endif -#include "liboping.h" +#include "oping.h" -#if DEBUG +#if WITH_DEBUG # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__) #else -# define dprintf(format, ...) /**/ +# define dprintf(...) /**/ #endif -#define PING_DATA "Florian Forster http://verplant.org/" +#define PING_ERRMSG_LEN 256 + +struct pinghost +{ + char *hostname; + struct sockaddr_storage *addr; + socklen_t addrlen; + int addrfamily; + int fd; + int ident; + int sequence; + struct timeval *timer; + double latency; + char *data; + + void *context; + + struct pinghost *next; +}; + +struct pingobj +{ + double timeout; + int ttl; + int addrfamily; + char *data; + + char errmsg[PING_ERRMSG_LEN]; + + pinghost_t *head; +}; + +/* + * private (static) functions + */ +static void ping_set_error (pingobj_t *obj, const char *function, + const char *message) +{ + snprintf (obj->errmsg, PING_ERRMSG_LEN, "%s: %s", function, message); + obj->errmsg[PING_ERRMSG_LEN - 1] = '\0'; +} + +static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2, + struct timeval *res) +{ + res->tv_sec = tv1->tv_sec + tv2->tv_sec; + res->tv_usec = tv1->tv_usec + tv2->tv_usec; + + while (res->tv_usec > 1000000) + { + res->tv_usec -= 1000000; + res->tv_sec++; + } + + return (0); +} static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2, struct timeval *res) @@ -98,7 +201,7 @@ static uint16_t ping_icmp4_checksum (char *buf, size_t len) static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len) { struct ip *ip_hdr; - struct icmphdr *icmp_hdr; + struct icmp *icmp_hdr; size_t ip_hdr_len; @@ -122,23 +225,23 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe buffer += ip_hdr_len; buffer_len -= ip_hdr_len; - if (buffer_len < sizeof (struct icmphdr)) + if (buffer_len < sizeof (struct icmp)) return (NULL); - icmp_hdr = (struct icmphdr *) buffer; - buffer += sizeof (struct icmphdr); - buffer_len -= sizeof (struct icmphdr); + icmp_hdr = (struct icmp *) buffer; + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); - if (icmp_hdr->type != ICMP_ECHOREPLY) + if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) { - dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->type); + dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type); return (NULL); } - recv_checksum = icmp_hdr->checksum; - icmp_hdr->checksum = 0; + recv_checksum = icmp_hdr->icmp_cksum; + icmp_hdr->icmp_cksum = 0; calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr, - sizeof (struct icmphdr) + buffer_len); + sizeof (struct icmp) + buffer_len); if (recv_checksum != calc_checksum) { @@ -147,13 +250,13 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe return (NULL); } - ident = ntohs (icmp_hdr->un.echo.id); - seq = ntohs (icmp_hdr->un.echo.sequence); + ident = ntohs (icmp_hdr->icmp_id); + seq = ntohs (icmp_hdr->icmp_seq); for (ptr = ph; ptr != NULL; ptr = ptr->next) { dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ptr->ident, ptr->sequence - 1); + ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF)); if (ptr->addrfamily != AF_INET) continue; @@ -164,7 +267,7 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe if (ptr->ident != ident) continue; - if ((ptr->sequence - 1) != seq) + if (((ptr->sequence - 1) & 0xFFFF) != seq) continue; dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", @@ -195,8 +298,8 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe return (NULL); icmp_hdr = (struct icmp6_hdr *) buffer; - buffer += sizeof (struct icmphdr); - buffer_len -= sizeof (struct icmphdr); + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY) { @@ -216,7 +319,7 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe for (ptr = ph; ptr != NULL; ptr = ptr->next) { dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ptr->ident, ptr->sequence - 1); + ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF)); if (ptr->addrfamily != AF_INET6) continue; @@ -227,7 +330,7 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe if (ptr->ident != ident) continue; - if ((ptr->sequence - 1) != seq) + if (((ptr->sequence - 1) & 0xFFFF) != seq) continue; dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", @@ -245,12 +348,11 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe return (ptr); } -static void ping_receive_one (int fd, pinghost_t *ph) +static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now) { char buffer[4096]; size_t buffer_len; - struct timeval now; struct timeval diff; pinghost_t *host = NULL; @@ -265,40 +367,33 @@ static void ping_receive_one (int fd, pinghost_t *ph) if (buffer_len == -1) { dprintf ("recvfrom: %s\n", strerror (errno)); - return; + return (-1); } - dprintf ("Read %i bytes from fd = %i\n", buffer_len, fd); + dprintf ("Read %u bytes from fd = %i\n", (unsigned int) buffer_len, fd); if (sa.ss_family == AF_INET) { if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL) - return; + return (-1); } else if (sa.ss_family == AF_INET6) { if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL) - return; - } - - if (gettimeofday (&now, NULL) == -1) - { - dprintf ("gettimeofday: %s\n", strerror (errno)); - timerclear (host->timer); - return; + return (-1); } + dprintf ("rcvd: %12i.%06i\n", + (int) now->tv_sec, + (int) now->tv_usec); dprintf ("sent: %12i.%06i\n", (int) host->timer->tv_sec, (int) host->timer->tv_usec); - dprintf ("rcvd: %12i.%06i\n", - (int) now.tv_sec, - (int) now.tv_usec); - if (ping_timeval_sub (&now, host->timer, &diff) < 0) + if (ping_timeval_sub (now, host->timer, &diff) < 0) { timerclear (host->timer); - return; + return (-1); } dprintf ("diff: %12i.%06i\n", @@ -309,14 +404,17 @@ static void ping_receive_one (int fd, pinghost_t *ph) host->latency += ((double) diff.tv_sec) * 1000.0; timerclear (host->timer); + + return (0); } -static int ping_receive_all (pinghost_t *ph) +static int ping_receive_all (pingobj_t *obj) { fd_set readfds; int num_readfds; int max_readfds; + pinghost_t *ph; pinghost_t *ptr; struct timeval endtime; @@ -324,25 +422,36 @@ static int ping_receive_all (pinghost_t *ph) struct timeval timeout; int status; - if (gettimeofday (&endtime, NULL) == -1) - return (-1); - endtime.tv_sec += 1; + int ret; + + ph = obj->head; + ret = 0; for (ptr = ph; ptr != NULL; ptr = ptr->next) ptr->latency = -1.0; + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + /* Set up timeout */ + timeout.tv_sec = (time_t) obj->timeout; + timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec))); + + dprintf ("Set timeout to %i.%06i seconds\n", + (int) timeout.tv_sec, + (int) timeout.tv_usec); + + ping_timeval_add (&nowtime, &timeout, &endtime); + while (1) { FD_ZERO (&readfds); num_readfds = 0; max_readfds = -1; - if (gettimeofday (&nowtime, NULL) == -1) - return (-1); - - if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1) - return (0); - for (ptr = ph; ptr != NULL; ptr = ptr->next) { if (!timerisset (ptr->timer)) @@ -358,16 +467,30 @@ static int ping_receive_all (pinghost_t *ph) if (num_readfds == 0) break; + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1) + break; + dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds, (int) timeout.tv_sec, (int) timeout.tv_usec); status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout); + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } - if (status == EINTR) + if ((status == -1) && (errno == EINTR)) { dprintf ("select was interrupted by signal..\n"); - break; /* XXX */ continue; } else if (status < 0) @@ -384,12 +507,12 @@ static int ping_receive_all (pinghost_t *ph) for (ptr = ph; ptr != NULL; ptr = ptr->next) { if (FD_ISSET (ptr->fd, &readfds)) - ping_receive_one (ptr->fd, ph); + if (ping_receive_one (ptr->fd, ph, &nowtime) == 0) + ret++; } - } + } /* while (1) */ - /* FIXME - return correct status */ - return (0); + return (ret); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -399,9 +522,39 @@ static int ping_receive_all (pinghost_t *ph) * +-> ping_send_one_ipv4 * * `-> ping_send_one_ipv6 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -static int ping_send_one_ipv4 (pinghost_t *ph) +static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph, + const void *buf, size_t buflen) +{ + ssize_t ret; + + if (gettimeofday (ph->timer, NULL) == -1) + { + timerclear (ph->timer); + return (-1); + } + + ret = sendto (ph->fd, buf, buflen, 0, + (struct sockaddr *) ph->addr, ph->addrlen); + + if (ret < 0) + { +#if defined(EHOSTUNREACH) + if (errno == EHOSTUNREACH) + return (0); +#endif +#if defined(ENETUNREACH) + if (errno == ENETUNREACH) + return (0); +#endif + ping_set_error (obj, "sendto", strerror (errno)); + } + + return (ret); +} + +static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph) { - struct icmphdr *icmp4; + struct icmp *icmp4; int status; char buf[4096]; @@ -413,30 +566,29 @@ static int ping_send_one_ipv4 (pinghost_t *ph) dprintf ("ph->hostname = %s\n", ph->hostname); memset (buf, '\0', sizeof (buf)); - icmp4 = (struct icmphdr *) buf; + icmp4 = (struct icmp *) buf; data = (char *) (icmp4 + 1); - icmp4->type = ICMP_ECHO; - icmp4->code = 0; - /* The checksum will be calculated by the TCP/IP stack. */ - icmp4->checksum = 0; - icmp4->un.echo.id = htons (ph->ident); - icmp4->un.echo.sequence = htons (ph->sequence); + icmp4->icmp_type = ICMP_ECHO; + icmp4->icmp_code = 0; + icmp4->icmp_cksum = 0; + icmp4->icmp_id = htons (ph->ident); + icmp4->icmp_seq = htons (ph->sequence); - strcpy (data, PING_DATA); + buflen = 4096 - sizeof (struct icmp); + strncpy (data, ph->data, buflen); datalen = strlen (data); - buflen = datalen + sizeof (struct icmphdr); + buflen = datalen + sizeof (struct icmp); - icmp4->checksum = ping_icmp4_checksum (buf, buflen); + icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen); dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident); - status = sendto (ph->fd, buf, buflen, 0, - (struct sockaddr *) ph->addr, ph->addrlen); + status = ping_sendto (obj, ph, buf, buflen); if (status < 0) { - perror ("sendto"); + perror ("ping_sendto"); return (-1); } @@ -445,7 +597,7 @@ static int ping_send_one_ipv4 (pinghost_t *ph) return (0); } -static int ping_send_one_ipv6 (pinghost_t *ph) +static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph) { struct icmp6_hdr *icmp6; int status; @@ -465,22 +617,23 @@ static int ping_send_one_ipv6 (pinghost_t *ph) icmp6->icmp6_type = ICMP6_ECHO_REQUEST; icmp6->icmp6_code = 0; /* The checksum will be calculated by the TCP/IP stack. */ + /* FIXME */ icmp6->icmp6_cksum = 0; icmp6->icmp6_id = htons (ph->ident); icmp6->icmp6_seq = htons (ph->sequence); - strcpy (data, PING_DATA); + buflen = 4096 - sizeof (struct icmp6_hdr); + strncpy (data, ph->data, buflen); datalen = strlen (data); buflen = datalen + sizeof (struct icmp6_hdr); dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident); - status = sendto (ph->fd, buf, buflen, 0, - (struct sockaddr *) ph->addr, ph->addrlen); + status = ping_sendto (obj, ph, buf, buflen); if (status < 0) { - perror ("sendto"); + perror ("ping_sendto"); return (-1); } @@ -489,10 +642,16 @@ static int ping_send_one_ipv6 (pinghost_t *ph) return (0); } -static int ping_send_all (pinghost_t *ph) +static int ping_send_all (pingobj_t *obj) { + pinghost_t *ph; pinghost_t *ptr; + int ret; + + ret = 0; + ph = obj->head; + for (ptr = ph; ptr != NULL; ptr = ptr->next) { /* start timer.. The GNU `ping6' starts the timer before @@ -501,6 +660,7 @@ static int ping_send_all (pinghost_t *ph) { dprintf ("gettimeofday: %s\n", strerror (errno)); timerclear (ptr->timer); + ret--; continue; } else @@ -511,18 +671,20 @@ static int ping_send_all (pinghost_t *ph) if (ptr->addrfamily == AF_INET6) { dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname); - if (ping_send_one_ipv6 (ptr) != 0) + if (ping_send_one_ipv6 (obj, ptr) != 0) { timerclear (ptr->timer); + ret--; continue; } } else if (ptr->addrfamily == AF_INET) { dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname); - if (ping_send_one_ipv4 (ptr) != 0) + if (ping_send_one_ipv4 (obj, ptr) != 0) { timerclear (ptr->timer); + ret--; continue; } } @@ -530,14 +692,33 @@ static int ping_send_all (pinghost_t *ph) { dprintf ("Unknown address family: %i\n", ptr->addrfamily); timerclear (ptr->timer); + ret--; continue; } ptr->sequence++; } - /* FIXME */ - return (0); + return (ret); +} + +/* + * Set the TTL of a socket protocol independently. + */ +static int ping_set_ttl (pinghost_t *ph, int ttl) +{ + int ret = -2; + + if (ph->addrfamily == AF_INET) + { + ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl)); + } + else if (ph->addrfamily == AF_INET6) + { + ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl)); + } + + return (ret); } static int ping_get_ident (void) @@ -605,21 +786,32 @@ static void ping_free (pinghost_t *ph) if (ph->hostname != NULL) free (ph->hostname); + if (ph->data != NULL) + free (ph->data); + free (ph); } /* * public methods */ -pingobj_t *ping_construct (int flags) +const char *ping_get_error (pingobj_t *obj) +{ + return (obj->errmsg); +} + +pingobj_t *ping_construct (void) { pingobj_t *obj; if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL) return (NULL); + memset (obj, '\0', sizeof (pingobj_t)); - obj->flags = flags; - obj->head = NULL; + obj->timeout = PING_DEF_TIMEOUT; + obj->ttl = PING_DEF_TTL; + obj->addrfamily = PING_DEF_AF; + obj->data = strdup (PING_DEF_DATA); return (obj); } @@ -639,20 +831,77 @@ void ping_destroy (pingobj_t *obj) current = next; } + if (obj->data != NULL) + free (obj->data); + free (obj); return; } +int ping_setopt (pingobj_t *obj, int option, void *value) +{ + int ret = 0; + + switch (option) + { + case PING_OPT_TIMEOUT: + obj->timeout = *((double *) value); + if (obj->timeout < 0.0) + { + obj->timeout = PING_DEF_TIMEOUT; + ret = -1; + } + break; + + case PING_OPT_TTL: + obj->ttl = *((int *) value); + if ((obj->ttl < 1) || (obj->ttl > 255)) + { + obj->ttl = PING_DEF_TTL; + ret = -1; + } + break; + + case PING_OPT_AF: + obj->addrfamily = *((int *) value); + if ((obj->addrfamily != AF_UNSPEC) + && (obj->addrfamily != AF_INET) + && (obj->addrfamily != AF_INET6)) + { + obj->addrfamily = PING_DEF_AF; + ret = -1; + } + break; + + case PING_OPT_DATA: + if (obj->data != NULL) + { + free (obj->data); + obj->data = NULL; + } + obj->data = strdup ((const char *) value); + break; + + default: + ret = -2; + } /* switch (option) */ + + return (ret); +} /* int ping_setopt */ + + int ping_send (pingobj_t *obj) { - if (ping_send_all (obj->head) < 0) + int ret; + + if (ping_send_all (obj) < 0) return (-1); - if (ping_receive_all (obj->head) < 0) + if ((ret = ping_receive_all (obj)) < 0) return (-2); - return (0); + return (ret); } static pinghost_t *ping_host_search (pinghost_t *ph, const char *host) @@ -672,9 +921,6 @@ int ping_host_add (pingobj_t *obj, const char *host) { pinghost_t *ph; - struct sockaddr_storage sockaddr; - socklen_t sockaddr_len; - struct addrinfo ai_hints; struct addrinfo *ai_list, *ai_ptr; int ai_return; @@ -685,9 +931,12 @@ int ping_host_add (pingobj_t *obj, const char *host) return (0); memset (&ai_hints, '\0', sizeof (ai_hints)); - ai_hints.ai_flags = AI_ADDRCONFIG; - ai_hints.ai_family = PF_UNSPEC; - ai_hints.ai_socktype = SOCK_RAW; + ai_hints.ai_flags = 0; +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = obj->addrfamily; + ai_hints.ai_socktype = SOCK_RAW; if ((ph = ping_alloc ()) == NULL) { @@ -698,6 +947,16 @@ int ping_host_add (pingobj_t *obj, const char *host) if ((ph->hostname = strdup (host)) == NULL) { dprintf ("Out of memory!\n"); + ping_set_error (obj, "strdup", strerror (errno)); + ping_free (ph); + return (-1); + } + + /* obj->data is not garuanteed to be != NULL */ + if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL) + { + dprintf ("Out of memory!\n"); + ping_set_error (obj, "strdup", strerror (errno)); ping_free (ph); return (-1); } @@ -705,59 +964,68 @@ int ping_host_add (pingobj_t *obj, const char *host) if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0) { dprintf ("getaddrinfo failed\n"); + ping_set_error (obj, "getaddrinfo", + (ai_return == EAI_SYSTEM) + ? strerror (errno) + : gai_strerror (ai_return)); ping_free (ph); return (-1); } + if (ai_list == NULL) + ping_set_error (obj, "getaddrinfo", "No hosts returned"); + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { ph->fd = -1; - sockaddr_len = sizeof (sockaddr); - memset (&sockaddr, '\0', sockaddr_len); - if (ai_ptr->ai_family == AF_INET) { - struct sockaddr_in *si; - - si = (struct sockaddr_in *) &sockaddr; - si->sin_family = AF_INET; - si->sin_port = htons (ph->ident); - si->sin_addr.s_addr = htonl (INADDR_ANY); - + ai_ptr->ai_socktype = SOCK_RAW; ai_ptr->ai_protocol = IPPROTO_ICMP; } else if (ai_ptr->ai_family == AF_INET6) { - struct sockaddr_in6 *si; - - si = (struct sockaddr_in6 *) &sockaddr; - si->sin6_family = AF_INET6; - si->sin6_port = htons (ph->ident); - si->sin6_addr = in6addr_any; - + ai_ptr->ai_socktype = SOCK_RAW; ai_ptr->ai_protocol = IPPROTO_ICMPV6; } else { - dprintf ("Unknown `ai_family': %i\n", ai_ptr->ai_family); + char errmsg[PING_ERRMSG_LEN]; + + snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family); + errmsg[PING_ERRMSG_LEN - 1] = '\0'; + + dprintf (errmsg); + ping_set_error (obj, "getaddrinfo", errmsg); continue; } + /* TODO: Move this to a static function `ping_open_socket' and + * call it whenever the socket dies. */ ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (ph->fd == -1) { dprintf ("socket: %s\n", strerror (errno)); + ping_set_error (obj, "socket", strerror (errno)); continue; } +/* + * The majority vote of operating systems has decided that you don't need to + * bind here. This code should be reactivated to bind to a specific address, + * though. See the `-I' option of `ping(1)' (GNU). -octo + */ +#if 0 if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1) { dprintf ("bind: %s\n", strerror (errno)); + ping_set_error (obj, "bind", strerror (errno)); close (ph->fd); ph->fd = -1; continue; } +#endif assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen); memset (ph->addr, '\0', sizeof (struct sockaddr_storage)); @@ -777,8 +1045,28 @@ int ping_host_add (pingobj_t *obj, const char *host) return (-1); } - ph->next = obj->head; - obj->head = ph; + /* + * Adding in the front is much easier, but then the iterator will + * return the host that was added last as first host. That's just not + * nice. -octo + */ + if (obj->head == NULL) + { + obj->head = ph; + } + else + { + pinghost_t *hptr; + + hptr = obj->head; + while (hptr->next != NULL) + hptr = hptr->next; + + assert ((hptr != NULL) && (hptr->next == NULL)); + hptr->next = ph; + } + + ping_set_ttl (ph, obj->ttl); return (0); } @@ -800,7 +1088,10 @@ int ping_host_remove (pingobj_t *obj, const char *host) } if (cur == NULL) + { + ping_set_error (obj, "ping_host_remove", "Host not found"); return (-1); + } if (pre == NULL) obj->head = cur->next; @@ -825,12 +1116,105 @@ pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) return ((pingobj_iter_t *) iter->next); } -const char *ping_iterator_get_host (pingobj_iter_t *iter) +int ping_iterator_get_info (pingobj_iter_t *iter, int info, + void *buffer, size_t *buffer_len) +{ + int ret = EINVAL; + + size_t orig_buffer_len = *buffer_len; + + switch (info) + { + case PING_INFO_HOSTNAME: + ret = ENOMEM; + *buffer_len = strlen (iter->hostname); + if (orig_buffer_len <= *buffer_len) + break; + /* Since (orig_buffer_len > *buffer_len) `strncpy' + * will copy `*buffer_len' and pad the rest of + * `buffer' with null-bytes */ + strncpy (buffer, iter->hostname, orig_buffer_len); + ret = 0; + break; + + case PING_INFO_ADDRESS: + ret = getnameinfo ((struct sockaddr *) iter->addr, + iter->addrlen, + (char *) buffer, + *buffer_len, + NULL, 0, + NI_NUMERICHOST); + if (ret != 0) + { + if ((ret == EAI_MEMORY) +#ifdef EAI_OVERFLOW + || (ret == EAI_OVERFLOW) +#endif + ) + ret = ENOMEM; + else if (ret == EAI_SYSTEM) + /* XXX: Not thread-safe! */ + ret = errno; + else + ret = EINVAL; + } + break; + + case PING_INFO_FAMILY: + ret = ENOMEM; + *buffer_len = sizeof (int); + if (orig_buffer_len < sizeof (int)) + break; + *((int *) buffer) = iter->addrfamily; + ret = 0; + break; + + case PING_INFO_LATENCY: + ret = ENOMEM; + *buffer_len = sizeof (double); + if (orig_buffer_len < sizeof (double)) + break; + *((double *) buffer) = iter->latency; + ret = 0; + break; + + case PING_INFO_SEQUENCE: + ret = ENOMEM; + *buffer_len = sizeof (unsigned int); + if (orig_buffer_len < sizeof (unsigned int)) + break; + *((unsigned int *) buffer) = (unsigned int) iter->sequence; + ret = 0; + break; + + case PING_INFO_IDENT: + ret = ENOMEM; + *buffer_len = sizeof (uint16_t); + if (orig_buffer_len < sizeof (uint16_t)) + break; + *((uint16_t *) buffer) = (uint16_t) iter->ident; + ret = 0; + break; + + case PING_INFO_DATA: + ret = ENOMEM; + *buffer_len = strlen (iter->data); + if (orig_buffer_len < *buffer_len) + break; + strncpy ((char *) buffer, iter->data, orig_buffer_len); + ret = 0; + break; + } + + return (ret); +} + +void *ping_iterator_get_context (pingobj_iter_t *iter) { - return (iter->hostname); + return (iter->context); } -double ping_iterator_get_latency (pingobj_iter_t *iter) +void ping_iterator_set_context (pingobj_iter_t *iter, void *context) { - return (iter->latency); + iter->context = context; }