#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
#endif
#if HAVE_SYS_SOCKET_H
# include <netinet/icmp6.h>
#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(...) /**/
#define PING_ERRMSG_LEN 256
-#define PING_DATA "Florian Forster <octo@verplant.org> http://verplant.org/"
-
struct pinghost
{
char *hostname;
int sequence;
struct timeval *timer;
double latency;
+ char *data;
+
+ void *context;
struct pinghost *next;
};
double timeout;
int ttl;
int addrfamily;
+ char *data;
char errmsg[PING_ERRMSG_LEN];
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)
{
* +-> ping_send_one_ipv4 *
* `-> ping_send_one_ipv6 *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-static ssize_t ping_sendto (pinghost_t *ph, const void *buf, size_t buflen)
+static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
+ const void *buf, size_t buflen)
{
ssize_t ret;
ret = sendto (ph->fd, buf, buflen, 0,
(struct sockaddr *) ph->addr, ph->addrlen);
+ if (ret < 0)
+ ping_set_error (obj, "sendto", strerror (errno));
+
return (ret);
}
-static int ping_send_one_ipv4 (pinghost_t *ph)
+static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph)
{
struct icmp *icmp4;
int status;
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);
dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
- status = ping_sendto (ph, buf, buflen);
+ status = ping_sendto (obj, ph, buf, buflen);
if (status < 0)
{
perror ("ping_sendto");
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;
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 = ping_sendto (ph, buf, buflen);
+ status = ping_sendto (obj, ph, buf, buflen);
if (status < 0)
{
perror ("ping_sendto");
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
{
dprintf ("gettimeofday: %s\n", strerror (errno));
timerclear (ptr->timer);
+ ret--;
continue;
}
else
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;
}
}
{
dprintf ("Unknown address family: %i\n", ptr->addrfamily);
timerclear (ptr->timer);
+ ret--;
continue;
}
ptr->sequence++;
}
- /* FIXME */
- return (0);
+ return (ret);
}
/*
if (ph->hostname != NULL)
free (ph->hostname);
+ if (ph->data != NULL)
+ free (ph->data);
+
free (ph);
}
obj->timeout = PING_DEF_TIMEOUT;
obj->ttl = PING_DEF_TTL;
obj->addrfamily = PING_DEF_AF;
+ obj->data = strdup (PING_DEF_DATA);
return (obj);
}
current = next;
}
+ if (obj->data != NULL)
+ free (obj->data);
+
free (obj);
return;
}
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) */
{
int ret;
- if (ping_send_all (obj->head) < 0)
+ if (ping_send_all (obj) < 0)
return (-1);
if ((ret = ping_receive_all (obj)) < 0)
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");
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)
{
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)
{
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 ((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;
}