diff --git a/src/liboping.c b/src/liboping.c
index af29a64fe1b367e0497c3918638b4f9b68ce57ba..22ab3a8672167b4d53248c4b92b9646ab82c05ee 100644 (file)
--- a/src/liboping.c
+++ b/src/liboping.c
/**
* Object oriented C module to send ICMP and ICMPv6 `echo's.
/**
* Object oriented C module to send ICMP and ICMPv6 `echo's.
- * Copyright (C) 2006 Florian octo Forster <octo at verplant.org>
+ * Copyright (C) 2006-2008 Florian octo Forster <octo at verplant.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation; only version 2 of the License is
+ * applicable.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
#include "oping.h"
#include "oping.h"
-#if DEBUG
+#if WITH_DEBUG
# define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
#else
# define dprintf(...) /**/
# define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
#else
# define dprintf(...) /**/
#define PING_ERRMSG_LEN 256
#define PING_ERRMSG_LEN 256
-#define PING_DATA "Florian Forster <octo@verplant.org> http://verplant.org/"
-
struct pinghost
{
struct pinghost
{
+ /* username: name passed in by the user */
+ char *username;
+ /* hostname: name returned by the reverse lookup */
char *hostname;
struct sockaddr_storage *addr;
socklen_t addrlen;
char *hostname;
struct sockaddr_storage *addr;
socklen_t addrlen;
int sequence;
struct timeval *timer;
double latency;
int sequence;
struct timeval *timer;
double latency;
+ char *data;
void *context;
void *context;
struct pingobj
{
struct pingobj
{
- double timeout;
- int ttl;
- int addrfamily;
+ double timeout;
+ int ttl;
+ int addrfamily;
+ char *data;
- char errmsg[PING_ERRMSG_LEN];
+ struct sockaddr_storage *srcaddr;
+ socklen_t srcaddrlen;
- pinghost_t *head;
+ char errmsg[PING_ERRMSG_LEN];
+
+ pinghost_t *head;
};
/*
};
/*
@@ -256,7 +262,7 @@ static pinghost_t *ping_receive_ipv4 (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",
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;
if (ptr->addrfamily != AF_INET)
continue;
@@ -267,7 +273,7 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
if (ptr->ident != ident)
continue;
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",
continue;
dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
@@ -285,6 +291,22 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
return (ptr);
}
return (ptr);
}
+#ifndef ICMP6_ECHO_REQUEST
+# ifdef ICMP6_ECHO /* AIX netinet/ip6_icmp.h */
+# define ICMP6_ECHO_REQUEST ICMP6_ECHO
+# else
+# define ICMP6_ECHO_REQUEST 128
+# endif
+#endif
+
+#ifndef ICMP6_ECHO_REPLY
+# ifdef ICMP6_ECHOREPLY /* AIX netinet/ip6_icmp.h */
+# define ICMP6_ECHO_REPLY ICMP6_ECHOREPLY
+# else
+# define ICMP6_ECHO_REPLY 129
+# endif
+#endif
+
static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
{
struct icmp6_hdr *icmp_hdr;
static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
{
struct icmp6_hdr *icmp_hdr;
@@ -319,7 +341,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",
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;
if (ptr->addrfamily != AF_INET6)
continue;
@@ -330,7 +352,7 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
if (ptr->ident != ident)
continue;
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",
continue;
dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
return (-1);
}
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 (sa.ss_family == AF_INET)
{
(struct sockaddr *) ph->addr, ph->addrlen);
if (ret < 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));
ping_set_error (obj, "sendto", strerror (errno));
+ }
return (ret);
}
return (ret);
}
icmp4->icmp_id = htons (ph->ident);
icmp4->icmp_seq = htons (ph->sequence);
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 icmp);
datalen = strlen (data);
buflen = datalen + sizeof (struct icmp);
icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
icmp6->icmp6_code = 0;
/* The checksum will be calculated by the TCP/IP stack. */
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);
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);
datalen = strlen (data);
buflen = datalen + sizeof (struct icmp6_hdr);
ph->addr = (struct sockaddr_storage *) (ph->timer + 1);
ph->addrlen = sizeof (struct sockaddr_storage);
ph->addr = (struct sockaddr_storage *) (ph->timer + 1);
ph->addrlen = sizeof (struct sockaddr_storage);
+ ph->fd = -1;
ph->latency = -1.0;
ph->ident = ping_get_ident () & 0xFFFF;
ph->latency = -1.0;
ph->ident = ping_get_ident () & 0xFFFF;
static void ping_free (pinghost_t *ph)
{
static void ping_free (pinghost_t *ph)
{
+ if (ph->fd >= 0)
+ close (ph->fd);
+
+ if (ph->username != NULL)
+ free (ph->username);
+
if (ph->hostname != NULL)
free (ph->hostname);
if (ph->hostname != NULL)
free (ph->hostname);
+ if (ph->data != NULL)
+ free (ph->data);
+
free (ph);
}
free (ph);
}
obj->timeout = PING_DEF_TIMEOUT;
obj->ttl = PING_DEF_TTL;
obj->addrfamily = PING_DEF_AF;
obj->timeout = PING_DEF_TIMEOUT;
obj->ttl = PING_DEF_TTL;
obj->addrfamily = PING_DEF_AF;
+ obj->data = strdup (PING_DEF_DATA);
return (obj);
}
return (obj);
}
current = next;
}
current = next;
}
+ if (obj->data != NULL)
+ free (obj->data);
+
+ if (obj->srcaddr != NULL)
+ free (obj->srcaddr);
+
free (obj);
return;
free (obj);
return;
obj->addrfamily = PING_DEF_AF;
ret = -1;
}
obj->addrfamily = PING_DEF_AF;
ret = -1;
}
+ if (obj->srcaddr != NULL)
+ {
+ free (obj->srcaddr);
+ obj->srcaddr = NULL;
+ }
+ break;
+
+ case PING_OPT_DATA:
+ if (obj->data != NULL)
+ {
+ free (obj->data);
+ obj->data = NULL;
+ }
+ obj->data = strdup ((const char *) value);
break;
break;
+ case PING_OPT_SOURCE:
+ {
+ char *hostname = (char *) value;
+ struct addrinfo ai_hints;
+ struct addrinfo *ai_list;
+ int status;
+#if WITH_DEBUG
+ if (obj->addrfamily != AF_UNSPEC)
+ {
+ dprintf ("Resetting obj->addrfamily to AF_UNSPEC.\n");
+ }
+#endif
+ memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
+ ai_hints.ai_family = obj->addrfamily = AF_UNSPEC;
+#if defined(AI_ADDRCONFIG)
+ ai_hints.ai_flags = AI_ADDRCONFIG;
+#endif
+ status = getaddrinfo (hostname, NULL, &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ ping_set_error (obj, "getaddrinfo",
+#if defined(EAI_SYSTEM)
+ (status == EAI_SYSTEM)
+ ? strerror (errno) :
+#endif
+ gai_strerror (status));
+ ret = -1;
+ break;
+ }
+#if WITH_DEBUG
+ if (ai_list->ai_next != NULL)
+ {
+ dprintf ("hostname = `%s' is ambiguous.\n", hostname);
+ }
+#endif
+ if (obj->srcaddr == NULL)
+ {
+ obj->srcaddrlen = 0;
+ obj->srcaddr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage));
+ if (obj->srcaddr == NULL)
+ {
+ ping_set_error (obj, "malloc",
+ strerror (errno));
+ ret = -1;
+ freeaddrinfo (ai_list);
+ break;
+ }
+ }
+ memset ((void *) obj->srcaddr, '\0', sizeof (struct sockaddr_storage));
+ assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
+ memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
+ ai_list->ai_addrlen);
+ obj->srcaddrlen = ai_list->ai_addrlen;
+ obj->addrfamily = ai_list->ai_family;
+
+ freeaddrinfo (ai_list);
+ } /* case PING_OPT_SOURCE */
+ break;
+
default:
ret = -2;
} /* switch (option) */
default:
ret = -2;
} /* switch (option) */
{
while (ph != NULL)
{
{
while (ph != NULL)
{
- if (strcasecmp (ph->hostname, host) == 0)
+ if (strcasecmp (ph->username, host) == 0)
break;
ph = ph->next;
break;
ph = ph->next;
ai_hints.ai_flags = 0;
#ifdef AI_ADDRCONFIG
ai_hints.ai_flags |= AI_ADDRCONFIG;
ai_hints.ai_flags = 0;
#ifdef AI_ADDRCONFIG
ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+#ifdef AI_CANONNAME
+ ai_hints.ai_flags |= AI_CANONNAME;
#endif
ai_hints.ai_family = obj->addrfamily;
ai_hints.ai_socktype = SOCK_RAW;
#endif
ai_hints.ai_family = obj->addrfamily;
ai_hints.ai_socktype = SOCK_RAW;
return (-1);
}
return (-1);
}
+ if ((ph->username = strdup (host)) == NULL)
+ {
+ dprintf ("Out of memory!\n");
+ ping_set_error (obj, "strdup", strerror (errno));
+ ping_free (ph);
+ return (-1);
+ }
+
if ((ph->hostname = strdup (host)) == NULL)
{
dprintf ("Out of memory!\n");
if ((ph->hostname = strdup (host)) == NULL)
{
dprintf ("Out of memory!\n");
return (-1);
}
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);
+ }
+
if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0)
{
dprintf ("getaddrinfo failed\n");
ping_set_error (obj, "getaddrinfo",
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));
+#if defined(EAI_SYSTEM)
+ (ai_return == EAI_SYSTEM)
+ ? strerror (errno) :
+#endif
+ gai_strerror (ai_return));
ping_free (ph);
return (-1);
}
ping_free (ph);
return (-1);
}
continue;
}
continue;
}
- if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1)
+ if (obj->srcaddr != NULL)
{
{
- dprintf ("bind: %s\n", strerror (errno));
- ping_set_error (obj, "bind", strerror (errno));
- close (ph->fd);
- ph->fd = -1;
- continue;
+ assert (obj->srcaddrlen > 0);
+ assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
+
+ if (bind (ph->fd, (struct sockaddr *) obj->srcaddr, obj->srcaddrlen) == -1)
+ {
+ dprintf ("bind: %s\n", strerror (errno));
+ ping_set_error (obj, "bind", strerror (errno));
+ close (ph->fd);
+ ph->fd = -1;
+ continue;
+ }
}
assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
}
assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
ph->addrlen = ai_ptr->ai_addrlen;
ph->addrfamily = ai_ptr->ai_family;
ph->addrlen = ai_ptr->ai_addrlen;
ph->addrfamily = ai_ptr->ai_family;
+#ifdef AI_CANONNAME
+ if ((ai_ptr->ai_canonname != NULL)
+ && (strcmp (ph->hostname, ai_ptr->ai_canonname) != 0))
+ {
+ char *old_hostname;
+
+ dprintf ("ph->hostname = %s; ai_ptr->ai_canonname = %s;\n",
+ ph->hostname, ai_ptr->ai_canonname);
+
+ old_hostname = ph->hostname;
+ if ((ph->hostname = strdup (ai_ptr->ai_canonname)) == NULL)
+ {
+ /* strdup failed, falling back to old hostname */
+ ph->hostname = old_hostname;
+ }
+ else if (old_hostname != NULL)
+ {
+ free (old_hostname);
+ }
+ }
+#endif /* AI_CANONNAME */
+
break;
}
break;
}
if (ph->fd < 0)
{
if (ph->fd < 0)
{
- free (ph->hostname);
- free (ph);
+ ping_free (ph);
return (-1);
}
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);
ping_set_ttl (ph, obj->ttl);
while (cur != NULL)
{
while (cur != NULL)
{
- if (strcasecmp (host, cur->hostname))
+ if (strcasecmp (host, cur->username) == 0)
break;
pre = cur;
break;
pre = cur;
else
pre->next = cur->next;
else
pre->next = cur->next;
- if (cur->fd >= 0)
- close (cur->fd);
-
ping_free (cur);
return (0);
ping_free (cur);
return (0);
switch (info)
{
switch (info)
{
+ case PING_INFO_USERNAME:
+ ret = ENOMEM;
+ *buffer_len = strlen (iter->username) + 1;
+ 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->username, orig_buffer_len);
+ ret = 0;
+ break;
+
case PING_INFO_HOSTNAME:
ret = ENOMEM;
case PING_INFO_HOSTNAME:
ret = ENOMEM;
- *buffer_len = strlen (iter->hostname);
+ *buffer_len = strlen (iter->hostname) + 1;
if (orig_buffer_len <= *buffer_len)
break;
/* Since (orig_buffer_len > *buffer_len) `strncpy'
if (orig_buffer_len <= *buffer_len)
break;
/* Since (orig_buffer_len > *buffer_len) `strncpy'
#endif
)
ret = ENOMEM;
#endif
)
ret = ENOMEM;
+#if defined(EAI_SYSTEM)
else if (ret == EAI_SYSTEM)
else if (ret == EAI_SYSTEM)
- /* XXX: Not thread-safe! */
ret = errno;
ret = errno;
+#endif
else
ret = EINVAL;
}
else
ret = EINVAL;
}
case PING_INFO_SEQUENCE:
ret = ENOMEM;
case PING_INFO_SEQUENCE:
ret = ENOMEM;
- *buffer_len = sizeof (uint16_t);
- if (orig_buffer_len < sizeof (uint16_t))
+ *buffer_len = sizeof (unsigned int);
+ if (orig_buffer_len < sizeof (unsigned int))
break;
break;
- *((uint16_t *) buffer) = (uint16_t) iter->sequence;
+ *((unsigned int *) buffer) = (unsigned int) iter->sequence;
ret = 0;
break;
ret = 0;
break;
*((uint16_t *) buffer) = (uint16_t) iter->ident;
ret = 0;
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);
}
return (ret);