Actually bind to the configured address.
[liboping.git] / src / liboping.c
index 3c63521..21855e1 100644 (file)
@@ -87,7 +87,7 @@
 
 #include "oping.h"
 
-#if DEBUG
+#if WITH_DEBUG
 # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
 #else
 # define dprintf(...) /**/
@@ -106,6 +106,7 @@ struct pinghost
        int                      sequence;
        struct timeval          *timer;
        double                   latency;
+       char                    *data;
 
        void                    *context;
 
@@ -114,14 +115,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;
 };
 
 /*
@@ -369,7 +373,7 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
                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)
        {
@@ -565,10 +569,7 @@ static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph)
        icmp4->icmp_seq   = htons (ph->sequence);
 
        buflen = 4096 - sizeof (struct icmp);
-       if (obj->data != NULL)
-               strncpy (data, obj->data, buflen);
-       else
-               strncpy (data, PING_DEF_DATA, buflen);
+       strncpy (data, ph->data, buflen);
        datalen = strlen (data);
 
        buflen = datalen + sizeof (struct icmp);
@@ -609,15 +610,13 @@ static int ping_send_one_ipv6 (pingobj_t *obj, 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);
 
        buflen = 4096 - sizeof (struct icmp6_hdr);
-       if (obj->data != NULL)
-               strncpy (data, obj->data, buflen);
-       else
-               strncpy (data, PING_DEF_DATA, buflen);
+       strncpy (data, ph->data, buflen);
        datalen = strlen (data);
 
        buflen = datalen + sizeof (struct icmp6_hdr);
@@ -780,6 +779,9 @@ static void ping_free (pinghost_t *ph)
        if (ph->hostname != NULL)
                free (ph->hostname);
 
+       if (ph->data != NULL)
+               free (ph->data);
+
        free (ph);
 }
 
@@ -802,6 +804,7 @@ pingobj_t *ping_construct (void)
        obj->timeout    = PING_DEF_TIMEOUT;
        obj->ttl        = PING_DEF_TTL;
        obj->addrfamily = PING_DEF_AF;
+       obj->data       = strdup (PING_DEF_DATA);
 
        return (obj);
 }
@@ -821,6 +824,12 @@ void ping_destroy (pingobj_t *obj)
                current = next;
        }
 
+       if (obj->data != NULL)
+               free (obj->data);
+
+       if (obj->srcaddr != NULL)
+               free (obj->srcaddr);
+
        free (obj);
 
        return;
@@ -859,6 +868,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:
@@ -870,6 +884,63 @@ 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",
+                                               status == EAI_SYSTEM
+                                               ? strerror (errno)
+                                               : 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) */
@@ -925,6 +996,9 @@ int ping_host_add (pingobj_t *obj, const char *host)
 #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;
 
@@ -942,6 +1016,15 @@ int ping_host_add (pingobj_t *obj, const char *host)
                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");
@@ -1009,13 +1092,19 @@ int ping_host_add (pingobj_t *obj, const char *host)
                        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);
@@ -1024,6 +1113,28 @@ int ping_host_add (pingobj_t *obj, const char *host)
                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;
        }
 
@@ -1036,8 +1147,26 @@ 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);
 
@@ -1151,7 +1280,6 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
                        ret = 0;
                        break;
 
-               /* FIXME Return the sequence as an unsigned int */
                case PING_INFO_SEQUENCE:
                        ret = ENOMEM;
                        *buffer_len = sizeof (unsigned int);
@@ -1169,6 +1297,15 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
                        *((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);