Code

src/liboping.c: Use \#ifdef's around `EAI_SYSTEM'.
[liboping.git] / src / liboping.c
index 6131107a03509c320168e0c36d9cffd0edcc2fff..22ab3a8672167b4d53248c4b92b9646ab82c05ee 100644 (file)
@@ -1,11 +1,11 @@
 /**
  * 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
- * 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
@@ -97,6 +97,9 @@
 
 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;
@@ -115,14 +118,17 @@ struct pinghost
 
 struct pingobj
 {
-       double      timeout;
-       int         ttl;
-       int         addrfamily;
-       char       *data;
+       double                   timeout;
+       int                      ttl;
+       int                      addrfamily;
+       char                    *data;
+
+       struct sockaddr_storage *srcaddr;
+       socklen_t                srcaddrlen;
 
-       char        errmsg[PING_ERRMSG_LEN];
+       char                     errmsg[PING_ERRMSG_LEN];
 
-       pinghost_t *head;
+       pinghost_t              *head;
 };
 
 /*
@@ -285,6 +291,22 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
        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;
@@ -537,7 +559,17 @@ static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
                        (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);
 }
@@ -765,6 +797,7 @@ static pinghost_t *ping_alloc (void)
        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;
 
@@ -773,6 +806,12 @@ static pinghost_t *ping_alloc (void)
 
 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);
 
@@ -824,6 +863,9 @@ void ping_destroy (pingobj_t *obj)
        if (obj->data != NULL)
                free (obj->data);
 
+       if (obj->srcaddr != NULL)
+               free (obj->srcaddr);
+
        free (obj);
 
        return;
@@ -862,6 +904,11 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
                                obj->addrfamily = PING_DEF_AF;
                                ret = -1;
                        }
+                       if (obj->srcaddr != NULL)
+                       {
+                               free (obj->srcaddr);
+                               obj->srcaddr = NULL;
+                       }
                        break;
 
                case PING_OPT_DATA:
@@ -873,6 +920,65 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
                        obj->data = strdup ((const char *) value);
                        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) */
@@ -898,7 +1004,7 @@ static pinghost_t *ping_host_search (pinghost_t *ph, const char *host)
 {
        while (ph != NULL)
        {
-               if (strcasecmp (ph->hostname, host) == 0)
+               if (strcasecmp (ph->username, host) == 0)
                        break;
 
                ph = ph->next;
@@ -940,6 +1046,14 @@ int ping_host_add (pingobj_t *obj, const char *host)
                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");
@@ -961,9 +1075,11 @@ int ping_host_add (pingobj_t *obj, const char *host)
        {
                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);
        }
@@ -1024,21 +1140,20 @@ int ping_host_add (pingobj_t *obj, const char *host)
                        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)
+               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;
+                       }
                }
-#endif
 
                assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
                memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
@@ -1075,8 +1190,7 @@ int ping_host_add (pingobj_t *obj, const char *host)
 
        if (ph->fd < 0)
        {
-               free (ph->hostname);
-               free (ph);
+               ping_free (ph);
                return (-1);
        }
 
@@ -1115,7 +1229,7 @@ int ping_host_remove (pingobj_t *obj, const char *host)
 
        while (cur != NULL)
        {
-               if (strcasecmp (host, cur->hostname))
+               if (strcasecmp (host, cur->username) == 0)
                        break;
 
                pre = cur;
@@ -1133,9 +1247,6 @@ int ping_host_remove (pingobj_t *obj, const char *host)
        else
                pre->next = cur->next;
        
-       if (cur->fd >= 0)
-               close (cur->fd);
-
        ping_free (cur);
 
        return (0);
@@ -1160,9 +1271,21 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int 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;
-                       *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'
@@ -1187,9 +1310,10 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
 #endif
                                   )
                                        ret = ENOMEM;
+#if defined(EAI_SYSTEM)
                                else if (ret == EAI_SYSTEM)
-                                       /* XXX: Not thread-safe! */
                                        ret = errno;
+#endif
                                else
                                        ret = EINVAL;
                        }