.gitignore: Add tarballs.
[liboping.git] / src / liboping.c
index 2ccbf4d..6950103 100644 (file)
@@ -134,6 +134,8 @@ struct pingobj
        struct sockaddr         *srcaddr;
        socklen_t                srcaddrlen;
 
+       char                    *device;
+
        char                     errmsg[PING_ERRMSG_LEN];
 
        pinghost_t              *head;
@@ -259,7 +261,8 @@ static uint16_t ping_icmp4_checksum (char *buf, size_t len)
        return (ret);
 }
 
-static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len)
+static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
+               size_t buffer_len)
 {
        struct ip *ip_hdr;
        struct icmp *icmp_hdr;
@@ -306,7 +309,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
 
        if (recv_checksum != calc_checksum)
        {
-               dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n",
+               dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
+                               "calculated 0x%04"PRIx16"\n",
                                recv_checksum, calc_checksum);
                return (NULL);
        }
@@ -314,7 +318,9 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
        ident = ntohs (icmp_hdr->icmp_id);
        seq   = ntohs (icmp_hdr->icmp_seq);
 
-       for (ptr = ph; ptr != NULL; ptr = ptr->next)
+       /* We have to iterate over all hosts, since ICMPv4 packets may
+        * be received on any raw v4 socket. */
+       for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
        {
                dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
                                ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
@@ -331,7 +337,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
                if (((ptr->sequence - 1) & 0xFFFF) != seq)
                        continue;
 
-               dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
+               dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
+                               "seq = %"PRIu16"\n",
                                ptr->hostname, ident, seq);
 
                break;
@@ -339,7 +346,7 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
 
        if (ptr == NULL)
        {
-               dprintf ("No match found for ident = 0x%04x, seq = %i\n",
+               dprintf ("No match found for ident = 0x%04"PRIx16", seq = %"PRIu16"\n",
                                ident, seq);
        }
 
@@ -365,7 +372,8 @@ static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffe
 # endif
 #endif
 
-static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len)
+static pinghost_t *ping_receive_ipv6 (pingobj_t *obj, char *buffer,
+               size_t buffer_len)
 {
        struct icmp6_hdr *icmp_hdr;
 
@@ -396,7 +404,9 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
        ident = ntohs (icmp_hdr->icmp6_id);
        seq   = ntohs (icmp_hdr->icmp6_seq);
 
-       for (ptr = ph; ptr != NULL; ptr = ptr->next)
+       /* We have to iterate over all hosts, since ICMPv6 packets may
+        * be received on any raw v6 socket. */
+       for (ptr = obj->head; ptr != NULL; ptr = ptr->next)
        {
                dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n",
                                ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF));
@@ -413,7 +423,8 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
                if (((ptr->sequence - 1) & 0xFFFF) != seq)
                        continue;
 
-               dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n",
+               dprintf ("Match found: hostname = %s, ident = 0x%04"PRIx16", "
+                               "seq = %"PRIu16"\n",
                                ptr->hostname, ident, seq);
 
                break;
@@ -421,15 +432,21 @@ static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffe
 
        if (ptr == NULL)
        {
-               dprintf ("No match found for ident = 0x%04x, seq = %i\n",
+               dprintf ("No match found for ident = 0x%04"PRIx16", "
+                               "seq = %"PRIu16"\n",
                                ident, seq);
        }
 
        return (ptr);
 }
 
-static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
+static int ping_receive_one (pingobj_t *obj, const pinghost_t *ph,
+               struct timeval *now)
 {
+       /* Note: 'ph' is not necessarily the host object for which we receive a
+        * reply. The right object will be returned by ping_receive_ipv*(). For
+        * now, we can only rely on ph->fd and ph->addrfamily. */
+
        struct timeval diff;
        pinghost_t *host = NULL;
        int recv_ttl;
@@ -464,7 +481,7 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
        msghdr.msg_flags |= MSG_XPG4_2;
 #endif
 
-       payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
+       payload_buffer_len = recvmsg (ph->fd, &msghdr, /* flags = */ 0);
        if (payload_buffer_len < 0)
        {
 #if WITH_DEBUG
@@ -474,7 +491,7 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
 #endif
                return (-1);
        }
-       dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, fd);
+       dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, ph->fd);
 
        /* Iterate over all auxiliary data in msghdr */
        recv_ttl = -1;
@@ -526,13 +543,13 @@ static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now)
 
        if (ph->addrfamily == AF_INET)
        {
-               host = ping_receive_ipv4 (ph, payload_buffer, payload_buffer_len);
+               host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
                if (host == NULL)
                        return (-1);
        }
        else if (ph->addrfamily == AF_INET6)
        {
-               host = ping_receive_ipv6 (ph, payload_buffer, payload_buffer_len);
+               host = ping_receive_ipv6 (obj, payload_buffer, payload_buffer_len);
                if (host == NULL)
                        return (-1);
        }
@@ -680,7 +697,7 @@ static int ping_receive_all (pingobj_t *obj)
                for (ptr = ph; ptr != NULL; ptr = ptr->next)
                {
                        if (FD_ISSET (ptr->fd, &readfds))
-                               if (ping_receive_one (ptr->fd, ph, &nowtime) == 0)
+                               if (ping_receive_one (obj, ptr, &nowtime) == 0)
                                        ret++;
                }
        } /* while (1) */
@@ -990,6 +1007,8 @@ static void ping_free (pinghost_t *ph)
  */
 const char *ping_get_error (pingobj_t *obj)
 {
+       if (obj == NULL)
+               return (NULL);
        return (obj->errmsg);
 }
 
@@ -1014,6 +1033,9 @@ void ping_destroy (pingobj_t *obj)
        pinghost_t *current;
        pinghost_t *next;
 
+       if (obj == NULL)
+               return;
+
        current = obj->head;
        next = NULL;
 
@@ -1030,6 +1052,9 @@ void ping_destroy (pingobj_t *obj)
        if (obj->srcaddr != NULL)
                free (obj->srcaddr);
 
+       if (obj->device != NULL)
+               free (obj->device);
+
        free (obj);
 
        return;
@@ -1039,6 +1064,9 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
 {
        int ret = 0;
 
+       if ((obj == NULL) || (value == NULL))
+               return (-1);
+
        switch (option)
        {
                case PING_OPT_TIMEOUT:
@@ -1152,6 +1180,28 @@ int ping_setopt (pingobj_t *obj, int option, void *value)
                } /* case PING_OPT_SOURCE */
                break;
 
+               case PING_OPT_DEVICE:
+               {
+#ifdef SO_BINDTODEVICE
+                       char *device = strdup ((char *) value);
+
+                       if (device == NULL)
+                       {
+                               ping_set_errno (obj, errno);
+                               ret = -1;
+                               break;
+                       }
+
+                       if (obj->device != NULL)
+                               free (obj->device);
+                       obj->device = device;
+#else /* ! SO_BINDTODEVICE */
+                       ping_set_errno (obj, ENOTSUP);
+                       ret = -1;
+#endif /* ! SO_BINDTODEVICE */
+               } /* case PING_OPT_DEVICE */
+               break;
+
                default:
                        ret = -2;
        } /* switch (option) */
@@ -1164,6 +1214,9 @@ int ping_send (pingobj_t *obj)
 {
        int ret;
 
+       if (obj == NULL)
+               return (-1);
+
        if (ping_send_all (obj) < 0)
                return (-1);
 
@@ -1194,6 +1247,9 @@ int ping_host_add (pingobj_t *obj, const char *host)
        struct addrinfo *ai_list, *ai_ptr;
        int              ai_return;
 
+       if ((obj == NULL) || (host == NULL))
+               return (-1);
+
        dprintf ("host = %s\n", host);
 
        if (ping_host_search (obj->head, host) != NULL)
@@ -1319,6 +1375,25 @@ int ping_host_add (pingobj_t *obj, const char *host)
                        }
                }
 
+#ifdef SO_BINDTODEVICE
+               if (obj->device != NULL)
+               {
+                       if (setsockopt (ph->fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                       obj->device, strlen (obj->device) + 1) != 0)
+                       {
+#if WITH_DEBUG
+                               char errbuf[PING_ERRMSG_LEN];
+                               dprintf ("setsockopt: %s\n",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+                               ping_set_errno (obj, errno);
+                               close (ph->fd);
+                               ph->fd = -1;
+                               continue;
+                       }
+               }
+#endif /* SO_BINDTODEVICE */
+
                assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
                memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
                memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
@@ -1405,6 +1480,9 @@ int ping_host_remove (pingobj_t *obj, const char *host)
 {
        pinghost_t *pre, *cur;
 
+       if ((obj == NULL) || (host == NULL))
+               return (-1);
+
        pre = NULL;
        cur = obj->head;
 
@@ -1435,11 +1513,15 @@ int ping_host_remove (pingobj_t *obj, const char *host)
 
 pingobj_iter_t *ping_iterator_get (pingobj_t *obj)
 {
+       if (obj == NULL)
+               return (NULL);
        return ((pingobj_iter_t *) obj->head);
 }
 
 pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter)
 {
+       if (iter == NULL)
+               return (NULL);
        return ((pingobj_iter_t *) iter->next);
 }
 
@@ -1450,6 +1532,9 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
 
        size_t orig_buffer_len = *buffer_len;
 
+       if ((iter == NULL) || (buffer == NULL) || (buffer_len == NULL))
+               return (-1);
+
        switch (info)
        {
                case PING_INFO_USERNAME:
@@ -1569,10 +1654,14 @@ int ping_iterator_get_info (pingobj_iter_t *iter, int info,
 
 void *ping_iterator_get_context (pingobj_iter_t *iter)
 {
+       if (iter == NULL)
+               return (NULL);
        return (iter->context);
 }
 
 void ping_iterator_set_context (pingobj_iter_t *iter, void *context)
 {
+       if (iter == NULL)
+               return;
        iter->context = context;
 }