Subject: windows: support to bind to a specific IPv6 address
authorLeonardo Maccari Rufino <eulmr1@gmail.com>
Mon, 5 Jun 2017 16:59:22 +0000 (13:59 -0300)
committerAndy Green <andy@warmcat.com>
Mon, 5 Jun 2017 23:44:50 +0000 (07:44 +0800)
lib/libwebsockets.c
lib/lws-plat-esp32.c
lib/lws-plat-esp8266.c
lib/lws-plat-optee.c
lib/lws-plat-unix.c
lib/lws-plat-win.c
lib/private-libwebsockets.h

index 1f9182a..53cb12b 100755 (executable)
@@ -1906,46 +1906,14 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
                v = (struct sockaddr *)&serv_addr6;
                n = sizeof(struct sockaddr_in6);
                bzero((char *) &serv_addr6, sizeof(serv_addr6));
-               if (iface &&
-                   interface_to_sa(vhost, iface,
-                                   (struct sockaddr_in *)v, n) < 0) {
-                       lwsl_err("Unable to find interface %s\n", iface);
-                       return -1;
-               }
-
-#ifndef WIN32
                if (iface) {
-                       struct ifaddrs *addrs, *addr;
-                       char ip[NI_MAXHOST];
-                       unsigned int i;
-
-                       getifaddrs(&addrs);
-                       for (addr = addrs; addr; addr = addr->ifa_next) {
-                               if (!addr->ifa_addr ||
-                                   addr->ifa_addr->sa_family != AF_INET6)
-                                       continue;
-
-                               getnameinfo(addr->ifa_addr,
-                                           sizeof(struct sockaddr_in6),
-                                           ip, sizeof(ip),
-                                           NULL, 0, NI_NUMERICHOST);
-
-                               i = 0;
-                               while (ip[i])
-                                       if (ip[i++] == '%') {
-                                               ip[i - 1] = '\0';
-                                               break;
-                                       }
-
-                               if (!strcmp(ip, iface)) {
-                                       serv_addr6.sin6_scope_id =
-                                               if_nametoindex(addr->ifa_name);
-                                       break;
-                               }
+                       if (interface_to_sa(vhost, iface,
+                                   (struct sockaddr_in *)v, n) < 0) {
+                               lwsl_err("Unable to find interface %s\n", iface);
+                               return -1;
                        }
-                       freeifaddrs(addrs);
+                       serv_addr6.sin6_scope_id = lws_get_addr_scope(iface);
                }
-#endif
 
                serv_addr6.sin6_family = AF_INET6;
                serv_addr6.sin6_port = htons(port);
@@ -2000,6 +1968,108 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
        return port;
 }
 
+#if defined(LWS_USE_IPV6)
+LWS_EXTERN unsigned long
+lws_get_addr_scope(const char *ipaddr)
+{
+       unsigned long scope = 0;
+
+#ifndef WIN32
+       struct ifaddrs *addrs, *addr;
+       char ip[NI_MAXHOST];
+       unsigned int i;
+
+       getifaddrs(&addrs);
+       for (addr = addrs; addr; addr = addr->ifa_next) {
+               if (!addr->ifa_addr ||
+                       addr->ifa_addr->sa_family != AF_INET6)
+                       continue;
+
+               getnameinfo(addr->ifa_addr,
+                               sizeof(struct sockaddr_in6),
+                               ip, sizeof(ip),
+                               NULL, 0, NI_NUMERICHOST);
+
+               i = 0;
+               while (ip[i])
+                       if (ip[i++] == '%') {
+                               ip[i - 1] = '\0';
+                               break;
+                       }
+
+               if (!strcmp(ip, ipaddr)) {
+                       scope = if_nametoindex(addr->ifa_name);
+                       break;
+               }
+       }
+       freeifaddrs(addrs);
+#else
+       PIP_ADAPTER_ADDRESSES adapter, addrs = NULL;
+       PIP_ADAPTER_UNICAST_ADDRESS addr;
+       ULONG size = 0;
+       DWORD ret;
+       struct sockaddr_in6 *sockaddr;
+       char ip[NI_MAXHOST];
+       unsigned int i;
+       int found = 0;
+
+       for (i = 0; i < 5; i++)
+       {
+               ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX,
+                               NULL, addrs, &size);
+               if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) {
+                       break;
+               } else if (ret == ERROR_BUFFER_OVERFLOW)
+               {
+                       if (addrs)
+                               free(addrs);
+                       addrs = (IP_ADAPTER_ADDRESSES *) malloc(size);
+               } else
+               {
+                       if (addrs)
+                       {
+                               free(addrs);
+                               addrs = NULL;
+                       }
+                       lwsl_err("Failed to get IPv6 address table (%d)", ret);
+                       break;
+               }
+       }
+
+       if ((ret == NO_ERROR) && (addrs))
+       {
+               adapter = addrs;
+               while ((adapter) && (!found))
+               {
+                       addr = adapter->FirstUnicastAddress;
+                       while ((addr) && (!found))
+                       {
+                               if (addr->Address.lpSockaddr->sa_family == AF_INET6)
+                               {
+                                       sockaddr = (struct sockaddr_in6 *) (addr->Address.lpSockaddr);
+
+                                       lws_plat_inet_ntop(sockaddr->sin6_family, &sockaddr->sin6_addr,
+                                                       ip, sizeof(ip));
+
+                                       if (!strcmp(ip, ipaddr)) {
+                                               scope = sockaddr->sin6_scope_id;
+                                               found = 1;
+                                               break;
+                                       }
+                               }
+                               addr = addr->Next;
+                       }
+                       adapter = adapter->Next;
+               }
+       }
+       if (addrs)
+               free(addrs);
+#endif
+
+       return scope;
+}
+#endif
+
 LWS_EXTERN void
 lws_restart_ws_ping_pong_timer(struct lws *wsi)
 {
index 3356963..37439ad 100644 (file)
@@ -433,6 +433,12 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
        return inet_ntop(af, src, dst, cnt);
 }
 
+LWS_VISIBLE int
+lws_plat_inet_pton(int af, const char *src, void *dst)
+{
+       return 1; //  inet_pton(af, src, dst);
+}
+
 LWS_VISIBLE lws_fop_fd_t IRAM_ATTR
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
                    const char *vpath, lws_fop_flags_t *flags)
index d7f7e70..39f6930 100644 (file)
@@ -631,6 +631,13 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
 }
 
 LWS_VISIBLE int
+lws_plat_inet_pton(int af, const char *src, void *dst)
+{
+       //return inet_pton(af, src, dst);
+       return 1;
+}
+
+LWS_VISIBLE int
 lws_plat_init(struct lws_context *context,
              struct lws_context_creation_info *info)
 {
index 20379bc..3006a6d 100644 (file)
@@ -261,6 +261,13 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
        return "lws_plat_inet_ntop";
 }
 
+LWS_VISIBLE int
+lws_plat_inet_pton(int af, const char *src, void *dst)
+{
+       //return inet_pton(af, src, dst);
+       return 1;
+}
+
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
                    const char *filename, lws_fop_flags_t *flags)
index b9f317b..b2679b6 100644 (file)
@@ -616,6 +616,12 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
        return inet_ntop(af, src, dst, cnt);
 }
 
+LWS_VISIBLE int
+lws_plat_inet_pton(int af, const char *src, void *dst)
+{
+       return inet_pton(af, src, dst);
+}
+
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
                    const char *vpath, lws_fop_flags_t *flags)
index 2c74a18..2d8b8eb 100644 (file)
@@ -398,6 +398,16 @@ LWS_VISIBLE LWS_EXTERN int
 lws_interface_to_sa(int ipv6,
                const char *ifname, struct sockaddr_in *addr, size_t addrlen)
 {
+#ifdef LWS_USE_IPV6
+       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
+
+       if (ipv6) {
+               if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
+                       return 0;
+               }
+       }
+#endif
+
        long long address = inet_addr(ifname);
 
        if (address == INADDR_NONE) {
@@ -525,6 +535,59 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
        return ok ? dst : NULL;
 }
 
+LWS_VISIBLE int
+lws_plat_inet_pton(int af, const char *src, void *dst)
+{
+       WCHAR *buffer;
+       DWORD bufferlen = strlen(src) + 1;
+       BOOL ok = FALSE;
+
+       buffer = lws_malloc(bufferlen * 2);
+       if (!buffer) {
+               lwsl_err("Out of memory\n");
+               return -1;
+       }
+
+       if (MultiByteToWideChar(CP_ACP, 0, src, bufferlen, buffer, bufferlen) <= 0) {
+               lwsl_err("Failed to convert multi byte to wide char\n");
+               lws_free(buffer);
+               return -1;
+       }
+
+       if (af == AF_INET) {
+               struct sockaddr_in dstaddr;
+               int dstaddrlen = sizeof(dstaddr);
+               bzero(&dstaddr, sizeof(dstaddr));
+               dstaddr.sin_family = AF_INET;
+
+               if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
+                       ok = TRUE;
+                       memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
+               }
+#ifdef LWS_USE_IPV6
+       } else if (af == AF_INET6) {
+               struct sockaddr_in6 dstaddr;
+               int dstaddrlen = sizeof(dstaddr);
+               bzero(&dstaddr, sizeof(dstaddr));
+               dstaddr.sin6_family = AF_INET6;
+
+               if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
+                       ok = TRUE;
+                       memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr));
+               }
+#endif
+       } else
+               lwsl_err("Unsupported type\n");
+
+       if (!ok) {
+               int rv = WSAGetLastError();
+               lwsl_err("WSAAddressToString() : %d\n", rv);
+       }
+
+       lws_free(buffer);
+       return ok ? 1 : -1;
+}
+
 LWS_VISIBLE lws_fop_fd_t
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
                    const char *vpath, lws_fop_flags_t *flags)
index 8f7f796..dcee160 100644 (file)
@@ -1674,6 +1674,11 @@ LWS_EXTERN int
 lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
                const char *iface);
 
+#if defined(LWS_USE_IPV6)
+LWS_EXTERN unsigned long
+lws_get_addr_scope(const char *ipaddr);
+#endif
+
 LWS_EXTERN void
 lws_close_free_wsi(struct lws *wsi, enum lws_close_status);
 
@@ -2163,6 +2168,8 @@ LWS_EXTERN unsigned long long
 time_in_microseconds(void);
 LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
 lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_plat_inet_pton(int af, const char *src, void *dst);
 
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);