client: getaddrinfo refactor
authorAndy Green <andy@warmcat.com>
Tue, 20 Jun 2017 07:56:48 +0000 (15:56 +0800)
committerAndy Green <andy@warmcat.com>
Tue, 20 Jun 2017 07:56:48 +0000 (15:56 +0800)
https://github.com/warmcat/libwebsockets/issues/926

lib/client-handshake.c
lib/private-libwebsockets.h

index 26543b9..6286f2d 100644 (file)
@@ -1,20 +1,51 @@
 #include "private-libwebsockets.h"
 
-struct lws *
-lws_client_connect_2(struct lws *wsi)
+static int
+lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
 {
+       struct addrinfo hints;
+
+       memset(&hints, 0, sizeof(hints));
+       *result = NULL;
+
 #ifdef LWS_USE_IPV6
-       struct sockaddr_in6 server_addr6;
-       struct addrinfo hints, *result;
+       if (wsi->ipv6) {
+
+#if !defined(__ANDROID__)
+               hints.ai_family = AF_INET6;
+               hints.ai_flags = AI_V4MAPPED;
+#endif
+       } else
 #endif
+       {
+               hints.ai_family = PF_UNSPEC;
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_flags = AI_CANONNAME;
+       }
+
+       return getaddrinfo(ads, NULL, &hints, result);
+}
+
+struct lws *
+lws_client_connect_2(struct lws *wsi)
+{
+       sockaddr46 sa46;
+       struct addrinfo *result;
        struct lws_context *context = wsi->context;
        struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-       struct sockaddr_in server_addr4;
        struct lws_pollfd pfd;
-       struct sockaddr *v;
        const char *cce = "", *iface;
-       int n, plen = 0;
+       int n, plen = 0, port;
        const char *ads;
+#ifdef LWS_USE_IPV6
+       char ipv6only = lws_check_opt(wsi->vhost->options,
+                       LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
+                       LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
+
+#if defined(__ANDROID__)
+       ipv6only = 0;
+#endif
+#endif
 
        lwsl_client("%s\n", __func__);
 
@@ -24,9 +55,15 @@ lws_client_connect_2(struct lws *wsi)
                goto oom4;
        }
 
-       /* proxy? */
+       /*
+        * start off allowing ipv6 on connection if vhost allows it
+        */
+       wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost);
+
+       /* Decide what it is we need to connect to:
+        *
+        * Priority 1: connect to http proxy */
 
-       /* http proxy */
        if (wsi->vhost->http_proxy_port) {
                plen = sprintf((char *)pt->serv_buf,
                        "CONNECT %s:%u HTTP/1.0\x0d\x0a"
@@ -41,88 +78,63 @@ lws_client_connect_2(struct lws *wsi)
 
                plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
                ads = wsi->vhost->http_proxy_address;
+               port = wsi->vhost->http_proxy_port;
 
-#ifdef LWS_USE_IPV6
-               if (LWS_IPV6_ENABLED(wsi->vhost)) {
-                       memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
-                       server_addr6.sin6_port = htons(wsi->vhost->http_proxy_port);
-               } else
-#endif
-                       server_addr4.sin_port = htons(wsi->vhost->http_proxy_port);
-
-       }
 #if defined(LWS_WITH_SOCKS5)
-       /* socks proxy */
-       else if (wsi->vhost->socks_proxy_port) {
+
+       /* Priority 2: Connect to SOCK5 Proxy */
+
+       } else if (wsi->vhost->socks_proxy_port) {
                socks_generate_msg(wsi, SOCKS_MSG_GREETING, (size_t *)&plen);
                lwsl_client("%s\n", "Sending SOCKS Greeting.");
 
                ads = wsi->vhost->socks_proxy_address;
-
-#ifdef LWS_USE_IPV6
-               if (LWS_IPV6_ENABLED(wsi->vhost)) {
-                       memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
-                       server_addr6.sin6_port = htons(wsi->vhost->socks_proxy_port);
-               } else
+               port = wsi->vhost->socks_proxy_port;
 #endif
-                       server_addr4.sin_port = htons(wsi->vhost->socks_proxy_port);
+       } else {
+
+               /* Priority 3: Connect directly */
 
-       }
-#endif
-       else {
                ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
-#ifdef LWS_USE_IPV6
-               if (LWS_IPV6_ENABLED(wsi->vhost)) {
-                       memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
-                       server_addr6.sin6_port = htons(wsi->c_port);
-               } else
-#endif
-                       server_addr4.sin_port = htons(wsi->c_port);
+               port = wsi->c_port;
        }
 
        /*
-        * prepare the actual connection (to the proxy, if any)
+        * prepare the actual connection
+        * to whatever we decided to connect to
         */
+
        lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads);
 
+       n = lws_getaddrinfo46(wsi, ads, &result);
+
 #ifdef LWS_USE_IPV6
-       if (LWS_IPV6_ENABLED(wsi->vhost)) {
-               memset(&hints, 0, sizeof(struct addrinfo));
-#if !defined(__ANDROID__)
-               hints.ai_family = AF_INET6;
-               hints.ai_flags = AI_V4MAPPED;
-#endif
-               n = getaddrinfo(ads, NULL, &hints, &result);
-               if (n) {
-#ifdef _WIN32
-                       lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n));
-#else
-                       lwsl_err("getaddrinfo: %s\n", gai_strerror(n));
-#endif
-                       cce = "getaddrinfo (ipv6) failed";
-                       goto oom4;
-               }
+       if (wsi->ipv6) {
 
-               server_addr6.sin6_family = AF_INET6;
+               memset(&sa46, 0, sizeof(sa46));
+
+               sa46.sa6.sin6_family = AF_INET6;
                switch (result->ai_family) {
-#if defined(__ANDROID__)
                case AF_INET:
+                       if (ipv6only)
+                               break;
                        /* map IPv4 to IPv6 */
-                       bzero((char *)&server_addr6.sin6_addr,
-                                               sizeof(struct in6_addr));
-                       server_addr6.sin6_addr.s6_addr[10] = 0xff;
-                       server_addr6.sin6_addr.s6_addr[11] = 0xff;
-                       memcpy(&server_addr6.sin6_addr.s6_addr[12],
+                       bzero((char *)&sa46.sa6.sin6_addr,
+                                               sizeof(sa46.sa6.sin6_addr));
+                       sa46.sa6.sin6_addr.s6_addr[10] = 0xff;
+                       sa46.sa6.sin6_addr.s6_addr[11] = 0xff;
+                       memcpy(&sa46.sa6.sin6_addr.s6_addr[12],
                                &((struct sockaddr_in *)result->ai_addr)->sin_addr,
                                                        sizeof(struct in_addr));
+                       lwsl_notice("uplevelling AF_INET to AF_INET6\n");
                        break;
-#endif
+
                case AF_INET6:
-                       memcpy(&server_addr6.sin6_addr,
+                       memcpy(&sa46.sa6.sin6_addr,
                          &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
                                                sizeof(struct in6_addr));
-                       server_addr6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id;
-                       server_addr6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo;
+                       sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id;
+                       sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo;
                        break;
                default:
                        lwsl_err("Unknown address family\n");
@@ -130,23 +142,18 @@ lws_client_connect_2(struct lws *wsi)
                        cce = "unknown address family";
                        goto oom4;
                }
-
-               freeaddrinfo(result);
        } else
-#endif
+#endif /* use ipv6 */
+
+       /* use ipv4 */
        {
-               struct addrinfo ai, *res, *result = NULL;
                void *p = NULL;
-               int addr_rv;
 
-               memset (&ai, 0, sizeof ai);
-               ai.ai_family = PF_UNSPEC;
-               ai.ai_socktype = SOCK_STREAM;
-               ai.ai_flags = AI_CANONNAME;
+               if (!n) {
+                       struct addrinfo *res = result;
+
+                       /* pick the first AF_INET (IPv4) result */
 
-               addr_rv = getaddrinfo(ads, NULL, &ai, &result);
-               if (!addr_rv) {
-                       res = result;
                        while (!p && res) {
                                switch (res->ai_family) {
                                case AF_INET:
@@ -157,7 +164,7 @@ lws_client_connect_2(struct lws *wsi)
                                res = res->ai_next;
                        }
 #if defined(LWS_FALLBACK_GETHOSTBYNAME)
-               } else if (addr_rv == EAI_SYSTEM) {
+               } else if (n == EAI_SYSTEM) {
                        struct hostent *host;
 
                        lwsl_info("getaddrinfo (ipv4) failed, trying gethostbyname\n");
@@ -172,7 +179,7 @@ lws_client_connect_2(struct lws *wsi)
 #endif
                } else {
                        lwsl_err("getaddrinfo failed\n");
-                       cce = "getaddrinfo (ipv4) failed";
+                       cce = "getaddrinfo failed";
                        goto oom4;
                }
 
@@ -184,21 +191,28 @@ lws_client_connect_2(struct lws *wsi)
                        goto oom4;
                }
 
-               server_addr4.sin_family = AF_INET;
-               server_addr4.sin_addr = *((struct in_addr *)p);
-               bzero(&server_addr4.sin_zero, 8);
-               if (result)
-                       freeaddrinfo(result);
+               sa46.sa4.sin_family = AF_INET;
+               sa46.sa4.sin_addr = *((struct in_addr *)p);
+               bzero(&sa46.sa4.sin_zero, 8);
        }
 
+       if (result)
+               freeaddrinfo(result);
+
+       /* now we decided on ipv4 or ipv6, set the port */
+
        if (!lws_socket_is_valid(wsi->desc.sockfd)) {
 
 #ifdef LWS_USE_IPV6
-               if (LWS_IPV6_ENABLED(wsi->vhost))
+               if (wsi->ipv6) {
+                       sa46.sa6.sin6_port = htons(port);
                        wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0);
-               else
+               else
 #endif
+               {
+                       sa46.sa4.sin_port = htons(port);
                        wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0);
+               }
 
                if (!lws_socket_is_valid(wsi->desc.sockfd)) {
                        lwsl_warn("Unable to open socket\n");
@@ -252,17 +266,13 @@ lws_client_connect_2(struct lws *wsi)
        }
 
 #ifdef LWS_USE_IPV6
-       if (LWS_IPV6_ENABLED(wsi->vhost)) {
-               v = (struct sockaddr *)&server_addr6;
+       if (wsi->ipv6)
                n = sizeof(struct sockaddr_in6);
-       else
+       else
 #endif
-       {
-               v = (struct sockaddr *)&server_addr4;
                n = sizeof(struct sockaddr);
-       }
 
-       if (connect(wsi->desc.sockfd, v, n) == -1 ||
+       if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 ||
            LWS_ERRNO == LWS_EISCONN) {
                if (LWS_ERRNO == LWS_EALREADY ||
                    LWS_ERRNO == LWS_EINPROGRESS ||
index 420076e..dc6146c 100644 (file)
@@ -1160,6 +1160,13 @@ LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *in
 #define LWS_UNIX_SOCK_ENABLED(vhost) (0)
 #endif
 
+typedef union {
+#ifdef LWS_USE_IPV6
+       struct sockaddr_in6 sa6;
+#endif
+       struct sockaddr_in sa4;
+} sockaddr46;
+
 enum uri_path_states {
        URIPS_IDLE,
        URIPS_SEEN_SLASH,
@@ -1623,6 +1630,7 @@ struct lws {
        unsigned int sending_chunked:1;
        unsigned int already_did_cce:1;
        unsigned int told_user_closed:1;
+       unsigned int ipv6:1;
 
 #if defined(LWS_WITH_ESP8266)
        unsigned int pending_send_completion:3;