From 3f13ea22641d6b49bb59709dade6e6dcdd5f06e5 Mon Sep 17 00:00:00 2001 From: James Devine Date: Mon, 24 Mar 2014 16:09:25 +0800 Subject: [PATCH] add ipv6 support (changed to support runtime disable + integration by andy@warmcat.com) --- CMakeLists.txt | 9 +- changelog | 5 + config.h.cmake | 3 + lib/client-handshake.c | 133 ++++++++++++++++++++----- lib/libwebsockets.c | 238 +++++++++++++++++++++++++++++++------------- lib/libwebsockets.h | 3 +- lib/private-libwebsockets.h | 9 +- 7 files changed, 299 insertions(+), 101 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d58ee67..d1e7eab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,8 @@ option(LWS_WITHOUT_DEBUG "Don't compile debug related code" OFF) option(LWS_WITHOUT_EXTENSIONS "Don't compile with extensions" OFF) option(LWS_WITH_LATENCY "Build latency measuring code into the library" OFF) option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" OFF) -option(LWS_WITH_LIBEV "Compile without support for libev" OFF) +option(LWS_WITH_LIBEV "Compile with support for libev" OFF) +option(LWS_WITHOUT_IPV6 "Compile without support for ipv6" OFF) # Allow the user to override installation directories. set(LWS_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") @@ -121,6 +122,11 @@ if (LWS_WITH_LIBEV) set(LWS_NO_EXTERNAL_POLL 1) endif() +if (LWS_WITHOUT_IPV6) +else() + set(LWS_WITH_IPV6 1) +endif() + if (MINGW) set(LWS_MINGW_SUPPORT 1) endif() @@ -877,6 +883,7 @@ message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}") message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}") message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") message(" LWS_USE_LIBEV = ${LWS_USE_LIBEV}") +message(" LWS_WITH_IPV6 = ${LWS_WITH_IPV6}") message("---------------------------------------------------------------------") # These will be available to parent projects including libwebsockets using add_subdirectory() diff --git a/changelog b/changelog index e3c464f..88f9c16 100644 --- a/changelog +++ b/changelog @@ -44,6 +44,11 @@ eventloop instead of the default poll() one will also be compiled in. But to use it, you must also set the LWS_SERVER_OPTION_LIBEV flag on the context creation info struct options member. +IPV6 is supported and enabled by default, you can disable the support at +build-time by giving -DLWS_WITHOUT_IPV6, and disable use of it even if +compiled in by making sure the flag LWS_SERVER_OPTION_DISABLE_IPV6 is set on +the context creation info struct options member. + User api changes ---------------- diff --git a/config.h.cmake b/config.h.cmake index a8247a9..6ca3460 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -29,6 +29,9 @@ /* Enable libev io loop */ #cmakedefine LWS_USE_LIBEV +/* Build with support for ipv6 */ +#cmakedefine LWS_WITH_IPV6 + /* Turn on latency measuring code */ #cmakedefine LWS_LATENCY diff --git a/lib/client-handshake.c b/lib/client-handshake.c index d87d210..4c50af8 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -5,9 +5,16 @@ struct libwebsocket *libwebsocket_client_connect_2( struct libwebsocket *wsi ) { struct pollfd pfd; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 server_addr6; + struct sockaddr_in6 client_addr6; + struct addrinfo hints, *result; +#endif + struct sockaddr_in server_addr4; + struct sockaddr_in client_addr4; struct hostent *server_hostent; - struct sockaddr_in server_addr; - struct sockaddr_in client_addr; + + struct sockaddr *v; int n; int plen = 0; const char *ads; @@ -27,10 +34,22 @@ struct libwebsocket *libwebsocket_client_connect_2( lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), wsi->u.hdr.ah->c_port); ads = context->http_proxy_address; - server_addr.sin_port = htons(context->http_proxy_port); + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) + server_addr6.sin6_port = htons(context->http_proxy_port); + else +#endif + server_addr4.sin_port = htons(context->http_proxy_port); + } else { ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); - server_addr.sin_port = htons(wsi->u.hdr.ah->c_port); +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) + server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port); + else +#endif + server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port); } /* @@ -38,15 +57,61 @@ struct libwebsocket *libwebsocket_client_connect_2( */ lwsl_client("libwebsocket_client_connect_2: address %s\n", ads); - server_hostent = gethostbyname(ads); - if (server_hostent == NULL) { - lwsl_err("Unable to get host name from %s\n", ads); - goto oom4; +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { + memset(&hints, 0, sizeof(struct addrinfo)); + n = getaddrinfo(ads, NULL, &hints, &result); + if (n) { + lwsl_err("getaddrinfo: %s\n", gai_strerror(n)); + goto oom4; + } + + server_addr6.sin6_family = AF_INET6; + switch (result->ai_family) { + case AF_INET: + /* map IPv4 to IPv6 */ + bzero((char *)&server_addr6.sin6_addr, + sizeof(struct in6_addr)); + server_addr6.sin6_addr.s6_addr16[5] = 0xffff; + bcopy(&((struct sockaddr_in *)result->ai_addr)->sin_addr, + &server_addr6.sin6_addr.s6_addr16[6], + sizeof(struct in_addr)); + break; + case AF_INET6: + memcpy(&server_addr6.sin6_addr, + &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + default: + lwsl_err("Unknown address family\n"); + freeaddrinfo(result); + goto oom4; + } + + freeaddrinfo(result); + } else +#endif + { + server_hostent = gethostbyname(ads); + if (!server_hostent) { + lwsl_err("Unable to get host name from %s\n", ads); + goto oom4; + } + + server_addr4.sin_family = AF_INET; + server_addr4.sin_addr = + *((struct in_addr *)server_hostent->h_addr); + bzero(&server_addr4.sin_zero, 8); } if (wsi->sock < 0) { - wsi->sock = socket(AF_INET, SOCK_STREAM, 0); +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) + wsi->sock = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + wsi->sock = socket(AF_INET, SOCK_STREAM, 0); if (wsi->sock < 0) { lwsl_warn("Unable to open socket\n"); @@ -66,34 +131,51 @@ struct libwebsocket *libwebsocket_client_connect_2( libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, AWAITING_TIMEOUT); +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&client_addr6; + n = sizeof(client_addr6); + bzero((char *)v, n); + client_addr6.sin6_family = AF_INET6; + } else +#endif + { + v = (struct sockaddr *)&client_addr4; + n = sizeof(client_addr4); + bzero((char *)v, n); + client_addr4.sin_family = AF_INET; + } - bzero((char *) &client_addr, sizeof(client_addr)); - client_addr.sin_family = AF_INET; - - if (context->iface != NULL) { - if (interface_to_sa(context->iface, &client_addr, - sizeof(client_addr)) < 0) { - lwsl_err("Unable to find interface %s\n", context->iface); + if (context->iface) { + if (interface_to_sa(context, context->iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", + context->iface); compatible_close(wsi->sock); goto failed; } - if (bind(wsi->sock, (struct sockaddr *) &client_addr, - sizeof(client_addr)) < 0) { - lwsl_err("Error binding to interface %s", context->iface); + if (bind(wsi->sock, v, n) < 0) { + lwsl_err("Error binding to interface %s", + context->iface); compatible_close(wsi->sock); goto failed; } } } - server_addr.sin_family = AF_INET; - server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr); - - bzero(&server_addr.sin_zero, 8); +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&server_addr6; + n = sizeof(struct sockaddr_in6); + } else +#endif + { + v = (struct sockaddr *)&server_addr4; + n = sizeof(struct sockaddr); + } - if (connect(wsi->sock, (struct sockaddr *)&server_addr, - sizeof(struct sockaddr)) == -1 || LWS_ERRNO == LWS_EISCONN) { + if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) { if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS) { lwsl_client("nonblocking connect retry\n"); @@ -108,7 +190,6 @@ struct libwebsocket *libwebsocket_client_connect_2( } if (LWS_ERRNO != LWS_EISCONN) { - lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO); goto failed; } diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 08a1844..845335f 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -576,66 +576,96 @@ libwebsockets_get_peer_addresses(struct libwebsocket_context *context, char *rip, int rip_len) { socklen_t len; - struct sockaddr_in sin; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; struct hostent *host; struct hostent *host1; char ip[128]; unsigned char *p; int n; - int ret = -1; #ifdef AF_LOCAL struct sockaddr_un *un; #endif + int ret = -1; rip[0] = '\0'; name[0] = '\0'; lws_latency_pre(context, wsi); - len = sizeof(sin); - if (getpeername(fd, (struct sockaddr *) &sin, &len) < 0) { - lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); - goto bail; - } +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { - host = gethostbyaddr((char *) &sin.sin_addr, sizeof(sin.sin_addr), - AF_INET); - if (host == NULL) { - lwsl_warn("gethostbyaddr: %s\n", strerror(LWS_ERRNO)); - goto bail; - } + len = sizeof(sin6); + if (getpeername(fd, (struct sockaddr *) &sin6, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } - strncpy(name, host->h_name, name_len); - name[name_len - 1] = '\0'; + if (inet_ntop(AF_INET6, &sin6.sin6_addr, rip, rip_len) == NULL) { + perror("inet_ntop"); + goto bail; + } - host1 = gethostbyname(host->h_name); - if (host1 == NULL) - goto bail; - p = (unsigned char *)host1; - n = 0; - while (p != NULL) { - p = (unsigned char *)host1->h_addr_list[n++]; - if (p == NULL) - continue; - if ((host1->h_addrtype != AF_INET) -#ifdef AF_LOCAL - && (host1->h_addrtype != AF_LOCAL) + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) { + memmove(rip, rip + 7, strlen(rip) - 6); + } + + getnameinfo((struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), name, + name_len, NULL, 0, 0); + + } else #endif - ) - continue; + { + len = sizeof(sin4); + if (getpeername(fd, (struct sockaddr *) &sin4, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + host = gethostbyaddr((char *) &sin4.sin_addr, + sizeof(sin4.sin_addr), AF_INET); + if (host == NULL) { + lwsl_warn("gethostbyaddr: %s\n", strerror(LWS_ERRNO)); + goto bail; + } - if (host1->h_addrtype == AF_INET) - sprintf(ip, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); -#ifdef AF_LOCAL - else { - un = (struct sockaddr_un *)p; - strncpy(ip, un->sun_path, sizeof(ip) - 1); - ip[sizeof(ip) - 1] = '\0'; + strncpy(name, host->h_name, name_len); + name[name_len - 1] = '\0'; + + host1 = gethostbyname(host->h_name); + if (host1 == NULL) + goto bail; + p = (unsigned char *)host1; + n = 0; + while (p != NULL) { + p = (unsigned char *)host1->h_addr_list[n++]; + if (p == NULL) + continue; + if ((host1->h_addrtype != AF_INET) + #ifdef AF_LOCAL + && (host1->h_addrtype != AF_LOCAL) + #endif + ) + continue; + + if (host1->h_addrtype == AF_INET) + sprintf(ip, "%u.%u.%u.%u", + p[0], p[1], p[2], p[3]); + #ifdef AF_LOCAL + else { + un = (struct sockaddr_un *)p; + strncpy(ip, un->sun_path, sizeof(ip) - 1); + ip[sizeof(ip) - 1] = '\0'; + } + #endif + p = NULL; + strncpy(rip, ip, rip_len); + rip[rip_len - 1] = '\0'; } -#endif - p = NULL; - strncpy(rip, ip, rip_len); - rip[rip_len - 1] = '\0'; } ret = 0; @@ -2053,7 +2083,11 @@ libwebsocket_create_context(struct lws_context_creation_info *info) #ifndef LWS_NO_SERVER int opt = 1; struct libwebsocket *wsi; - struct sockaddr_in serv_addr; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; + struct sockaddr *v; #endif #ifndef LWS_NO_EXTENSIONS int m; @@ -2070,6 +2104,14 @@ libwebsocket_create_context(struct lws_context_creation_info *info) lwsl_notice("Initial logging level %d\n", log_level); lwsl_notice("Library version: %s\n", library_version); +#ifdef LWS_WITH_IPV6 + if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_notice("IPV6 compiled in and enabled\n"); + else + lwsl_notice("IPV6 compiled in but disabled\n"); +#else + lwsl_notice("IPV6 not compiled in\n"); +#endif lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN); lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS); #ifndef LWS_NO_EXTENSIONS @@ -2489,7 +2531,7 @@ libwebsocket_create_context(struct lws_context_creation_info *info) context->ssl_ctx, NULL, 0); } - if(info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { + if (info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { /* Normally SSL listener rejects non-ssl, optionally allow */ context->allow_non_ssl_on_ssl_port = 1; } @@ -2538,7 +2580,13 @@ libwebsocket_create_context(struct lws_context_creation_info *info) if (info->port != CONTEXT_PORT_NO_LISTEN) { int sockfd; - sockfd = socket(AF_INET, SOCK_STREAM, 0); +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { lwsl_err("ERROR opening socket\n"); goto bail; @@ -2562,37 +2610,51 @@ libwebsocket_create_context(struct lws_context_creation_info *info) fcntl(sockfd, F_SETFL, O_NONBLOCK); #endif - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - if (info->iface == NULL) - serv_addr.sin_addr.s_addr = INADDR_ANY; - else - if (interface_to_sa(info->iface, &serv_addr, - sizeof(serv_addr)) < 0) { - lwsl_err("Unable to find interface %s\n", - info->iface); - compatible_close(sockfd); - goto bail; +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + serv_addr6.sin6_addr = in6addr_any; + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(info->port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + serv_addr4.sin_port = htons(info->port); + + if (info->iface) { + if (interface_to_sa(context, info->iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", + info->iface); + compatible_close(sockfd); + goto bail; + } } - serv_addr.sin_port = htons(info->port); + } /* ipv4 */ - n = bind(sockfd, (struct sockaddr *) &serv_addr, - sizeof(serv_addr)); + n = bind(sockfd, v, n); if (n < 0) { lwsl_err("ERROR on binding to port %d (%d %d)\n", - info->port, n, LWS_ERRNO); + info->port, n, LWS_ERRNO); compatible_close(sockfd); goto bail; } - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) - perror("getsockname"); - else - info->port = ntohs(sin.sin_port); + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + perror("getsockname"); + else + info->port = ntohs(sin.sin_port); - context->listen_port = info->port; + context->listen_port = info->port; wsi = (struct libwebsocket *)malloc( sizeof(struct libwebsocket)); @@ -2861,8 +2923,11 @@ LWS_VISIBLE void lws_set_log_level(int level, void (*log_emit_function)(int leve lwsl_emit = log_emit_function; } +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + int -interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen) +interface_to_sa(struct libwebsocket_context *context, + const char *ifname, struct sockaddr_in *addr, size_t addrlen) { int rc = -1; #if defined(WIN32) || defined(_WIN32) @@ -2870,19 +2935,50 @@ interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen) #else struct ifaddrs *ifr; struct ifaddrs *ifc; - struct sockaddr_in *sin; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif getifaddrs(&ifr); for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { - if (ifc->ifa_addr == NULL) + if (!ifc->ifa_addr) continue; + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + if (strcmp(ifc->ifa_name, ifname)) continue; - sin = (struct sockaddr_in *)ifc->ifa_addr; - if (sin->sin_family != AF_INET) + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(context)) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr16[5] = 0xffff; + bcopy(&((struct sockaddr_in *)ifc->ifa_addr)-> + sin_addr, + &addr6->sin6_addr.s6_addr16[6], + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_WITH_IPV6 + case AF_INET6: + if (rc >= 0) + break; + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: continue; - memcpy(addr, sin, addrlen); + } rc = 0; } diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 5bdd8b6..5c41a57 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -149,7 +149,8 @@ enum libwebsocket_context_options { LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = 8, - LWS_SERVER_OPTION_LIBEV = 16 + LWS_SERVER_OPTION_LIBEV = 16, + LWS_SERVER_OPTION_DISABLE_IPV6 = 32, }; enum libwebsocket_callback_reasons { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index f56aba5..d2fdf95 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -354,6 +354,11 @@ struct libwebsocket_context { #define LWS_LIBEV_ENABLED(context) (0) #endif +#ifdef LWS_WITH_IPV6 +#define LWS_IPV6_ENABLED(context) (context->options & LWS_SERVER_OPTION_DISABLE_IPV6) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif enum uri_path_states { URIPS_IDLE, @@ -618,8 +623,8 @@ LWS_EXTERN int handshake_0405(struct libwebsocket_context *context, LWS_EXTERN int get_daemonize_pid(); #endif -extern int interface_to_sa(const char *ifname, - struct sockaddr_in *addr, size_t addrlen); +extern int interface_to_sa(struct libwebsocket_context *context, + const char *ifname, struct sockaddr_in *addr, size_t addrlen); #ifndef LWS_OPENSSL_SUPPORT -- 2.7.4