add ipv6 support

(changed to support runtime disable + integration by andy@warmcat.com)
This commit is contained in:
James Devine 2014-03-24 16:09:25 +08:00 committed by Andy Green
parent 9ed754d46e
commit 3f13ea2264
7 changed files with 303 additions and 105 deletions

View file

@ -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()

View file

@ -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
----------------

View file

@ -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

View file

@ -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);
#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);
}
bzero(&server_addr.sin_zero, 8);
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;
}

View file

@ -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;
}
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';
len = sizeof(sin6);
if (getpeername(fd, (struct sockaddr *) &sin6, &len) < 0) {
lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
goto bail;
}
if (inet_ntop(AF_INET6, &sin6.sin6_addr, rip, rip_len) == NULL) {
perror("inet_ntop");
goto bail;
}
// 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
p = NULL;
strncpy(rip, ip, rip_len);
rip[rip_len - 1] = '\0';
{
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;
}
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';
}
}
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;
}
serv_addr.sin_port = htons(info->port);
#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);
n = bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr));
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;
}
}
} /* ipv4 */
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;
}

View file

@ -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 {

View file

@ -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