Subject: windows: support to bind to a specific IPv6 address
[platform/upstream/libwebsockets.git] / lib / libwebsockets.c
index 8781eec..53cb12b 100755 (executable)
@@ -302,8 +302,10 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                        }
                        pcgi = &(*pcgi)->cgi_list;
                }
-               if (wsi->cgi->headers_buf)
+               if (wsi->cgi->headers_buf) {
+                       lwsl_debug("close: freed cgi headers\n");
                        lws_free_set_NULL(wsi->cgi->headers_buf);
+               }
                /* we have a cgi going, we must kill it */
                wsi->cgi->being_closed = 1;
                lws_cgi_kill(wsi);
@@ -1876,10 +1878,10 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
 #endif
        struct sockaddr_in serv_addr4;
 #ifndef LWS_PLAT_OPTEE
-       socklen_t len = sizeof(struct sockaddr);
+       socklen_t len = sizeof(struct sockaddr_storage);
 #endif
        int n;
-       struct sockaddr_in sin;
+       struct sockaddr_storage sin;
        struct sockaddr *v;
 
 #ifdef LWS_USE_UNIX_SOCK
@@ -1904,43 +1906,13 @@ 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;
-               }
-
                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);
                }
 
                serv_addr6.sin6_family = AF_INET6;
@@ -1984,12 +1956,120 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
                lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
        else
 #endif
-               port = ntohs(sin.sin_port);
+#if defined(LWS_USE_IPV6)
+               port = (sin.ss_family == AF_INET6) ?
+                                 ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) :
+                                 ntohs(((struct sockaddr_in *) &sin)->sin_port);
+#else
+               port = ntohs(((struct sockaddr_in *) &sin)->sin_port);
+#endif
 #endif
 
        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)
 {
@@ -2504,11 +2584,13 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
         * process is OK.  Stuff that happens after the execvpe() is OK.
         */
 
-       for (n = 0; n < 3; n++)
+       for (n = 0; n < 3; n++) {
                if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
                        lwsl_err("%s: stdin dup2 failed\n", __func__);
                        goto bail3;
                }
+               close(cgi->pipe_fds[n][!(n == 0)]);
+       }
 
 #if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
        for (m = 0; m < n; m++) {
@@ -2555,6 +2637,7 @@ static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
        "content-length: ",
        "location: ",
        "status: ",
+       "transfer-encoding: chunked",
 };
 
 LWS_VISIBLE LWS_EXTERN int
@@ -2608,6 +2691,8 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
                        if (n > 512)
                                n = 512;
 
+                       lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);
+
                        m = lws_write(wsi, (unsigned char *)wsi->cgi->headers_dumped,
                                      n, LWS_WRITE_HTTP_HEADERS);
                        if (m < 0) {
@@ -2618,6 +2703,7 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
                        if (wsi->cgi->headers_dumped == wsi->cgi->headers_pos) {
                                wsi->hdr_state = LHCS_PAYLOAD;
                                lws_free_set_NULL(wsi->cgi->headers_buf);
+                               lwsl_debug("freed cgi headers\n");
                        } else {
                                wsi->reason_bf |= 8;
                                lws_callback_on_writable(wsi);
@@ -2637,6 +2723,8 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
                                lwsl_err("OOM\n");
                                return -1;
                        }
+
+                       lwsl_debug("allocated cgi hdrs\n");
                        wsi->cgi->headers_pos = wsi->cgi->headers_buf;
                        wsi->cgi->headers_dumped = wsi->cgi->headers_pos;
                        wsi->cgi->headers_end = wsi->cgi->headers_buf + n - 1;
@@ -2706,6 +2794,12 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
                                if (c == '\x0d')
                                        wsi->hdr_state = LCHS_LF1;
 
+                               if (wsi->hdr_state != LCHS_HEADER &&
+                                   !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING][wsi->cgi->match[SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
+                                       lwsl_debug("cgi produced chunked\n");
+                                       wsi->cgi->explicitly_chunked = 1;
+                               }
+
                                /* presence of Location: mandates 302 retcode */
                                if (wsi->hdr_state != LCHS_HEADER &&
                                    !significant_hdr[SIGNIFICANT_HDR_LOCATION][wsi->cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
@@ -2905,7 +2999,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
                                lwsl_debug("%s: found PID %d on cgi list\n",
                                            __func__, n);
 
-                               if (!cgi->content_length) {
+                               if (!cgi->content_length && cgi->explicitly_chunked) {
                                        /*
                                         * well, if he sends chunked... give him 5s after the
                                         * cgi terminated to send buffered