Merge branch 'master' of github.com:jclehner/nmrp-flash
[oweals/nmrpflash.git] / ethsock.c
index 28eb0d58a8cbaa7cc0975ca97ec4ecb67364461b..9a0cd1705cfd9d55417c17ffbdfb06f7ee708d18 100644 (file)
--- a/ethsock.c
+++ b/ethsock.c
@@ -3,14 +3,14 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <pcap.h>
-#include "ethsock.h"
 #include "nmrpd.h"
 
 #if defined(NMRPFLASH_WINDOWS)
-#include <windows.h>
-#define NMRPFLASH_ALIAS_PREFIX "net"
+#define NMRPFLASH_NETALIAS_PREFIX "net"
+#define WPCAP
+#include <pcap.h>
 #else
+#include <pcap.h>
 #include <ifaddrs.h>
 #if defined(NMRPFLASH_LINUX)
 #include <linux/if_packet.h>
@@ -28,15 +28,22 @@ struct ethsock
 {
        pcap_t *pcap;
 #ifndef NMRPFLASH_WINDOWS
-       struct timeval timeout;
        int fd;
 #else
-       DWORD timeout;
        HANDLE handle;
 #endif
+       unsigned timeout;
        uint8_t hwaddr[6];
 };
 
+const char *mac_to_str(uint8_t *mac)
+{
+       static char buf[18];
+       snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+                       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+       return buf;
+}
+
 static int x_pcap_findalldevs(pcap_if_t **devs)
 {
        char errbuf[PCAP_ERRBUF_SIZE];
@@ -86,14 +93,21 @@ static bool get_hwaddr(uint8_t *hwaddr, const char *intf)
 }
 #else
 
-static void win_perror2(const char *msg, int err)
-{
-       fprintf(stderr, "%s: error %d\n", msg, err);
-}
-
-static void win_perror(const char *msg)
+void win_perror2(const char *msg, DWORD err)
 {
-       win_perror2(msg, (int)GetLastError());
+       char *buf = NULL;
+       FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       (LPTSTR)&buf, 0, NULL);
+
+       if (buf) {
+               /* FormatMessageA terminates buf with CRLF! */
+               fprintf(stderr, "%s: %s", msg, buf);
+               LocalFree(buf);
+       } else {
+               fprintf(stderr, "%s: error %d\n", msg, (int)err);
+       }
 }
 
 static bool get_hwaddr(uint8_t *hwaddr, const char *intf)
@@ -154,7 +168,7 @@ static const char *intf_alias_to_wpcap(const char *intf)
 
        if (intf[0] == '\\') {
                return intf;
-       } else if (sscanf(intf, NMRPFLASH_ALIAS_PREFIX "%u", &dev_num) != 1) {
+       } else if (sscanf(intf, NMRPFLASH_NETALIAS_PREFIX "%u", &dev_num) != 1) {
                fprintf(stderr, "Invalid interface alias.\n");
                return NULL;
        }
@@ -163,9 +177,11 @@ static const char *intf_alias_to_wpcap(const char *intf)
                return NULL;
        }
 
-       for (dev = devs; dev; dev = dev->next) {
+       for (dev = devs; dev; dev = dev->next, ++i) {
                if (i == dev_num) {
-                       printf(NMRPFLASH_ALIAS_PREFIX "%u: %s\n", i, dev->name);
+                       if (verbosity) {
+                               printf("%s%u: %s\n", NMRPFLASH_NETALIAS_PREFIX, i, dev->name);
+                       }
                        strncpy(buf, dev->name, sizeof(buf) - 1);
                        buf[sizeof(buf) - 1] = '\0';
                        break;
@@ -203,7 +219,9 @@ static const char *intf_get_pretty_name(const char *intf)
                        "%s\\Connection", guid);
        err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hkey);
        if (err != ERROR_SUCCESS) {
-               win_perror2("RegOpenKeyExA", err);
+               if (verbosity > 1) {
+                       win_perror2("RegOpenKeyExA", err);
+               }
                return NULL;
        }
 
@@ -212,7 +230,9 @@ static const char *intf_get_pretty_name(const char *intf)
        if (err == ERROR_SUCCESS) {
                intf = buf;
        } else {
-               win_perror2("RegQueryValueExA", err);
+               if (verbosity > 1) {
+                       win_perror2("RegQueryValueExA", err);
+               }
                intf = NULL;
        }
 
@@ -282,16 +302,27 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
                fprintf(stderr, "No event handle available.\n");
                goto cleanup_pcap;
        }
+
+       err = pcap_setmintocopy(sock->pcap, 1);
+       if (err) {
+               pcap_perror(sock->pcap, "pcap_setmintocopy");
+               goto cleanup_pcap;
+       }
 #endif
 
-       snprintf(buf, sizeof(buf), "ether proto %04x", protocol);
+       snprintf(buf, sizeof(buf), "ether proto 0x%04x and not ether src %s",
+                       protocol, mac_to_str(sock->hwaddr));
+
        err = pcap_compile(sock->pcap, &fp, buf, 0, 0);
        if (err) {
                pcap_perror(sock->pcap, "pcap_compile");
                goto cleanup_pcap;
        }
 
-       if ((err = pcap_setfilter(sock->pcap, &fp))) {
+       err = pcap_setfilter(sock->pcap, &fp);
+       pcap_freecode(&fp);
+
+       if (err) {
                pcap_perror(sock->pcap, "pcap_setfilter");
                goto cleanup_pcap;
        }
@@ -305,40 +336,52 @@ cleanup_malloc:
        return NULL;
 }
 
+int select_fd(int fd, unsigned timeout)
+{
+       struct timeval tv;
+       int status;
+       fd_set fds;
+
+       FD_ZERO(&fds);
+       FD_SET(fd, &fds);
+
+       tv.tv_sec = timeout / 1000;
+       tv.tv_usec = 1000 * (timeout % 1000);
+
+       status = select(fd + 1, &fds, NULL, NULL, &tv);
+       if (status < 0) {
+               sock_perror("select");
+       }
+
+       return status;
+}
+
 ssize_t ethsock_recv(struct ethsock *sock, void *buf, size_t len)
 {
        struct pcap_pkthdr* hdr;
        const u_char *capbuf;
        int status;
-#ifndef NMRPFLASH_WINDOWS
-       fd_set fds;
-#else
+#ifdef NMRPFLASH_WINDOWS
        DWORD ret;
-#endif
-
-#ifndef NMRPFLASH_WINDOWS
-       if (sock->timeout.tv_sec || sock->timeout.tv_usec) {
-               FD_ZERO(&fds);
-               FD_SET(sock->fd, &fds);
 
-               status = select(sock->fd + 1, &fds, NULL, NULL, &sock->timeout);
-               if (status == -1) {
-                       perror("select");
-                       return -1;
-               } else if (status == 0) {
-                       return 0;
-               }
-       }
-#else
        if (sock->timeout) {
                ret = WaitForSingleObject(sock->handle, sock->timeout);
                if (ret == WAIT_TIMEOUT) {
                        return 0;
                } else if (ret != WAIT_OBJECT_0) {
-                       fprintf(stderr, "WaitForSingleObject: returned %d\n", (int)ret);
+                       win_perror2("WaitForSingleObject", ret);
                        return -1;
                }
        }
+#else
+       if (sock->timeout) {
+               status = select_fd(sock->fd, sock->timeout);
+               if (status < 0) {
+                       return -1;
+               } else if (status == 0) {
+                       return 0;
+               }
+       }
 #endif
 
        status = pcap_next_ex(sock->pcap, &hdr, &capbuf);
@@ -383,14 +426,9 @@ int ethsock_close(struct ethsock *sock)
        return 0;
 }
 
-int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
+inline int ethsock_set_timeout(struct ethsock *sock, unsigned msec)
 {
-#ifndef NMRPFLASH_WINDOWS
-       sock->timeout.tv_sec = msec / 1000;
-       sock->timeout.tv_usec = (msec % 1000) * 1000;
-#else
        sock->timeout = msec;
-#endif
        return 0;
 }
 
@@ -413,8 +451,9 @@ static bool is_ethernet(const char *intf)
 int ethsock_list_all(void)
 {
        pcap_if_t *devs, *dev;
+       pcap_addr_t *addr;
        uint8_t hwaddr[6];
-       unsigned dev_num = 0;
+       unsigned dev_num = 0, dev_ok = 0;
 #ifdef NMRPFLASH_WINDOWS
        const char *pretty;
 #endif
@@ -425,36 +464,70 @@ int ethsock_list_all(void)
 
        memset(hwaddr, 0, 6);
 
-       for (dev = devs; dev; dev = dev->next) {
+       for (dev = devs; dev; dev = dev->next, ++dev_num) {
+               if (dev->flags & PCAP_IF_LOOPBACK) {
+                       if (verbosity) {
+                               printf("%-15s  (loopback device)\n", dev->name);
+                       }
+                       continue;
+               }
+
                if (!is_ethernet(dev->name)) {
+                       if (verbosity) {
+                               printf("%-15s  (not an ethernet device)\n",
+                                               dev->name);
+                       }
                        continue;
                }
 
                if (!get_hwaddr(hwaddr, dev->name)) {
+                       if (verbosity) {
+                               printf("%-15s  (failed to get hardware address)\n",
+                                               dev->name);
+                       }
                        continue;
                }
 
 #ifndef NMRPFLASH_WINDOWS
-               printf("%s", dev->name);
+               printf("%-15s", dev->name);
 #else
-               printf(NMRPFLASH_ALIAS_PREFIX "%u", dev_num);
+               /* Call this here so *_perror() calls don't happen within a line */
+               pretty = intf_get_pretty_name(dev->name);
+
+               if (!verbosity) {
+                       printf("%s%u", NMRPFLASH_NETALIAS_PREFIX, dev_num);
+               } else {
+                       printf("%s", dev->name);
+               }
 #endif
-               printf("  %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], hwaddr[1],
-                               hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
+
+               for (addr = dev->addresses; addr; addr = addr->next) {
+                       if (addr->addr->sa_family == AF_INET) {
+                               printf("  %-15s",
+                                               inet_ntoa(((struct sockaddr_in*)addr->addr)->sin_addr));
+                               break;
+                       }
+               }
+
+               if (!addr) {
+                       printf("  %-15s", "0.0.0.0");
+               }
+
+               printf("  %s", mac_to_str(hwaddr));
 
 #ifdef NMRPFLASH_WINDOWS
-               pretty = intf_get_pretty_name(dev->name);
                if (pretty) {
                        printf("  (%s)", pretty);
                } else if (dev->description) {
                        printf("  (%s)", dev->description);
                }
+
 #endif
                printf("\n");
-               ++dev_num;
+               ++dev_ok;
        }
 
-       if (!dev_num) {
+       if (!dev_ok) {
                printf("No suitable network interfaces found.\n");
        }