From c2ea70c3e9ac41a5dcb4a643f6872b4ecf8dce47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 19 Aug 2016 20:06:04 +0200 Subject: [PATCH] api: fix connection to servers with hook_connect() on Windows 10 with Windows subsystem for Linux (issue #770) The test on socketpair() function is now made when hooks are initialized (instead of doing the test at compilation time). --- ChangeLog.adoc | 1 + src/core/wee-hook.c | 88 ++++++++++----- src/core/wee-hook.h | 15 +-- src/core/wee-network.c | 245 ++++++++++++++++++++--------------------- 4 files changed, 189 insertions(+), 160 deletions(-) diff --git a/ChangeLog.adoc b/ChangeLog.adoc index 6c2425adb..c0d44ebf4 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -36,6 +36,7 @@ Improvements:: Bug fixes:: * core, irc, xfer: refresh domain name and name server addresses before connection to servers (issue #771) + * api: fix connection to servers with hook_connect() on Windows 10 with Windows subsystem for Linux (issue #770) * api: fix crash in function string_split_command() when the separator is not a semicolon (issue #731) * irc: fix NULL pointer dereference in 734 command callback (issue #738) * relay: return an empty hdata when the requested hdata or pointer is not found (issue #767) diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index 40396facc..2e12faa1d 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -78,20 +79,22 @@ struct pollfd *hook_fd_pollfd = NULL; /* file descriptors for poll() */ int hook_fd_pollfd_count = 0; /* number of file descriptors */ int hook_process_pending = 0; /* 1 if there are some process to */ /* run (via fork) */ +int hook_socketpair_ok = 0; /* 1 if socketpair() is OK */ void hook_process_run (struct t_hook *hook_process); /* - * Initializes lists of hooks. + * Initializes hooks. */ void hook_init () { - int type; + int type, sock[2], rc; + /* initialize list of hooks */ for (type = 0; type < HOOK_NUM_TYPES; type++) { weechat_hooks[type] = NULL; @@ -100,6 +103,40 @@ hook_init () } hooks_count_total = 0; hook_last_system_time = time (NULL); + + /* + * Set a flag to 0 if socketpair() function is not available. + * + * For the connect hook, when this is defined an array of sockets will + * be passed from the parent process to the child process instead of using + * SCM_RIGHTS to pass a socket back from the child process to parent + * process. + * + * This allows connections to work on Windows but it limits the number of + * IPs that can be attempted each time. + */ + hook_socketpair_ok = 1; + +#if defined(__CYGWIN__) || defined(__APPLE__) || defined(__MACH__) + hook_socketpair_ok = 0; +#else + /* + * Test if socketpair() function is working fine: this is NOT the case + * on Windows with Ubuntu bash + * (errno == 94: ESOCKTNOSUPPORT: socket type not supported) + */ + rc = socketpair (AF_LOCAL, SOCK_DGRAM, 0, sock); + if (rc < 0) + { + /* Windows/Ubuntu */ + hook_socketpair_ok = 0; + } + else + { + close (sock[0]); + close (sock[1]); + } +#endif } /* @@ -2209,9 +2246,7 @@ hook_connect (struct t_weechat_plugin *plugin, const char *proxy, { struct t_hook *new_hook; struct t_hook_connect *new_hook_connect; -#ifdef HOOK_CONNECT_MAX_SOCKETS int i; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ #ifndef HAVE_GNUTLS /* make C compiler happy */ @@ -2265,13 +2300,14 @@ hook_connect (struct t_weechat_plugin *plugin, const char *proxy, new_hook_connect->handshake_hook_timer = NULL; new_hook_connect->handshake_fd_flags = 0; new_hook_connect->handshake_ip_address = NULL; -#ifdef HOOK_CONNECT_MAX_SOCKETS - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + if (!hook_socketpair_ok) { - new_hook_connect->sock_v4[i] = -1; - new_hook_connect->sock_v6[i] = -1; + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + new_hook_connect->sock_v4[i] = -1; + new_hook_connect->sock_v6[i] = -1; + } } -#endif /* HOOK_CONNECT_MAX_SOCKETS */ hook_add_to_list (new_hook); @@ -3939,21 +3975,22 @@ unhook (struct t_hook *hook) close (HOOK_CONNECT(hook, child_send)); HOOK_CONNECT(hook, child_send) = -1; } -#ifdef HOOK_CONNECT_MAX_SOCKETS - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + if (!hook_socketpair_ok) { - if (HOOK_CONNECT(hook, sock_v4[i]) != -1) + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) { - close (HOOK_CONNECT(hook, sock_v4[i])); - HOOK_CONNECT(hook, sock_v4[i]) = -1; - } - if (HOOK_CONNECT(hook, sock_v6[i]) != -1) - { - close (HOOK_CONNECT(hook, sock_v6[i])); - HOOK_CONNECT(hook, sock_v6[i]) = -1; + if (HOOK_CONNECT(hook, sock_v4[i]) != -1) + { + close (HOOK_CONNECT(hook, sock_v4[i])); + HOOK_CONNECT(hook, sock_v4[i]) = -1; + } + if (HOOK_CONNECT(hook, sock_v6[i]) != -1) + { + close (HOOK_CONNECT(hook, sock_v6[i])); + HOOK_CONNECT(hook, sock_v6[i]) = -1; + } } } -#endif /* HOOK_CONNECT_MAX_SOCKETS */ break; case HOOK_TYPE_PRINT: if (HOOK_PRINT(hook, tags_array)) @@ -4822,13 +4859,14 @@ hook_print_log () log_printf (" handshake_hook_timer. : 0x%lx", HOOK_CONNECT(ptr_hook, handshake_hook_timer)); log_printf (" handshake_fd_flags. . : %d", HOOK_CONNECT(ptr_hook, handshake_fd_flags)); log_printf (" handshake_ip_address. : '%s'", HOOK_CONNECT(ptr_hook, handshake_ip_address)); -#ifdef HOOK_CONNECT_MAX_SOCKETS - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + if (!hook_socketpair_ok) { - log_printf (" sock_v4[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v4[i])); - log_printf (" sock_v6[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v6[i])); + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + log_printf (" sock_v4[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v4[i])); + log_printf (" sock_v6[%d]. . . . . . : '%d'", HOOK_CONNECT(ptr_hook, sock_v6[i])); + } } -#endif /* HOOK_CONNECT_MAX_SOCKETS */ break; case HOOK_TYPE_PRINT: log_printf (" print data:"); diff --git a/src/core/wee-hook.h b/src/core/wee-hook.h index cdbba4027..c8db3be4c 100644 --- a/src/core/wee-hook.h +++ b/src/core/wee-hook.h @@ -27,17 +27,8 @@ #include #endif -#if defined(__CYGWIN__) || defined(__APPLE__) || defined(__MACH__) -/* - * For the connect hook, when this is defined an array of sockets will - * be passed from the parent process to the child process instead of using - * SCM_RIGHTS to pass a socket back from the child process to parent process. - * - * This allows connections to work on Windows but it limits the number of - * IPs that can be attempted each time. - */ +/* used if socketpair() function is NOT available */ #define HOOK_CONNECT_MAX_SOCKETS 4 -#endif struct t_gui_bar; struct t_gui_buffer; @@ -285,10 +276,9 @@ struct t_hook_connect struct t_hook *handshake_hook_timer; /* timer for handshake timeout */ int handshake_fd_flags; /* socket flags saved for handshake */ char *handshake_ip_address; /* ip address (used for handshake) */ -#ifdef HOOK_CONNECT_MAX_SOCKETS + /* sockets used if socketpair() is NOT available */ int sock_v4[HOOK_CONNECT_MAX_SOCKETS]; /* IPv4 sockets for connecting */ int sock_v6[HOOK_CONNECT_MAX_SOCKETS]; /* IPv6 sockets for connecting */ -#endif /* HOOK_CONNECT_MAX_SOCKETS */ }; /* hook print */ @@ -454,6 +444,7 @@ extern struct t_hook *weechat_hooks[]; extern struct t_hook *last_weechat_hook[]; extern int hooks_count[]; extern int hooks_count_total; +extern int hook_socketpair_ok; /* hook functions */ diff --git a/src/core/wee-network.c b/src/core/wee-network.c index a221ff134..b61d18faf 100644 --- a/src/core/wee-network.c +++ b/src/core/wee-network.c @@ -754,16 +754,12 @@ network_connect_child (struct t_hook *hook_connect) char status_without_string[1 + 5 + 1]; const char *error; int rc, length, num_written; - int sock, set, flags; -#ifdef HOOK_CONNECT_MAX_SOCKETS - int j; -#else + int sock, set, flags, j; struct msghdr msg; struct cmsghdr *cmsg; char msg_buf[CMSG_SPACE(sizeof (sock))]; struct iovec iov[1]; char iov_data[1] = { 0 }; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ /* * indicates that something is wrong with whichever group of * servers is being tried first after connecting, so start at @@ -1050,38 +1046,41 @@ network_connect_child (struct t_hook *hook_connect) { ptr_res = res_reorder[i]; -#ifdef HOOK_CONNECT_MAX_SOCKETS - /* use pre-created socket pool */ - sock = -1; - for (j = 0; j < HOOK_CONNECT_MAX_SOCKETS; j++) + if (hook_socketpair_ok) { - if (ptr_res->ai_family == AF_INET) - { - sock = HOOK_CONNECT(hook_connect, sock_v4[j]); - if (sock != -1) - { - HOOK_CONNECT(hook_connect, sock_v4[j]) = -1; - break; - } - } - else if (ptr_res->ai_family == AF_INET6) - { - sock = HOOK_CONNECT(hook_connect, sock_v6[j]); - if (sock != -1) - { - HOOK_CONNECT(hook_connect, sock_v6[j]) = -1; - break; - } - } + /* create a socket */ + sock = socket (ptr_res->ai_family, + ptr_res->ai_socktype, + ptr_res->ai_protocol); + } + else + { + /* use pre-created socket pool */ + sock = -1; + for (j = 0; j < HOOK_CONNECT_MAX_SOCKETS; j++) + { + if (ptr_res->ai_family == AF_INET) + { + sock = HOOK_CONNECT(hook_connect, sock_v4[j]); + if (sock != -1) + { + HOOK_CONNECT(hook_connect, sock_v4[j]) = -1; + break; + } + } + else if (ptr_res->ai_family == AF_INET6) + { + sock = HOOK_CONNECT(hook_connect, sock_v6[j]); + if (sock != -1) + { + HOOK_CONNECT(hook_connect, sock_v6[j]) = -1; + break; + } + } + } + if (sock < 0) + continue; } - if (sock < 0) - continue; -#else - /* create a socket */ - sock = socket (ptr_res->ai_family, - ptr_res->ai_socktype, - ptr_res->ai_protocol); -#endif /* HOOK_CONNECT_MAX_SOCKETS */ if (sock < 0) { status_str[0] = '0' + WEECHAT_HOOK_CONNECT_SOCKET_ERROR; @@ -1189,30 +1188,33 @@ network_connect_child (struct t_hook *hook_connect) } /* send the socket to the parent process */ -#ifndef HOOK_CONNECT_MAX_SOCKETS - memset (&msg, 0, sizeof (msg)); - msg.msg_control = msg_buf; - msg.msg_controllen = sizeof (msg_buf); + if (hook_socketpair_ok) + { + memset (&msg, 0, sizeof (msg)); + msg.msg_control = msg_buf; + msg.msg_controllen = sizeof (msg_buf); - /* send 1 byte of data (not required on Linux, required by BSD/OSX) */ - memset (iov, 0, sizeof (iov)); - iov[0].iov_base = iov_data; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; + /* send 1 byte of data (not required on Linux, required by BSD/OSX) */ + memset (iov, 0, sizeof (iov)); + iov[0].iov_base = iov_data; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof (sock)); - memcpy(CMSG_DATA(cmsg), &sock, sizeof (sock)); - msg.msg_controllen = cmsg->cmsg_len; - num_written = sendmsg (HOOK_CONNECT(hook_connect, child_send), &msg, 0); - (void) num_written; -#else - num_written = write (HOOK_CONNECT(hook_connect, child_write), &sock, sizeof (sock)); - (void) num_written; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof (sock)); + memcpy(CMSG_DATA(cmsg), &sock, sizeof (sock)); + msg.msg_controllen = cmsg->cmsg_len; + num_written = sendmsg (HOOK_CONNECT(hook_connect, child_send), &msg, 0); + (void) num_written; + } + else + { + num_written = write (HOOK_CONNECT(hook_connect, child_write), &sock, sizeof (sock)); + (void) num_written; + } } else { @@ -1394,16 +1396,12 @@ network_connect_child_read_cb (const void *pointer, void *data, int fd) #ifdef HAVE_GNUTLS int rc, direction; #endif /* HAVE_GNUTLS */ - int sock; -#ifdef HOOK_CONNECT_MAX_SOCKETS - int i; -#else + int sock, i; struct msghdr msg; struct cmsghdr *cmsg; char msg_buf[CMSG_SPACE(sizeof (sock))]; struct iovec iov[1]; char iov_data[1]; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ /* make C compiler happy */ (void) data; @@ -1447,44 +1445,47 @@ network_connect_child_read_cb (const void *pointer, void *data, int fd) } } -#ifndef HOOK_CONNECT_MAX_SOCKETS - /* receive the socket from the child process */ - memset (&msg, 0, sizeof (msg)); - msg.msg_control = msg_buf; - msg.msg_controllen = sizeof (msg_buf); - - /* recv 1 byte of data (not required on Linux, required by BSD/OSX) */ - memset (iov, 0, sizeof (iov)); - iov[0].iov_base = iov_data; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - if (recvmsg (HOOK_CONNECT(hook_connect, child_recv), &msg, 0) >= 0) + if (hook_socketpair_ok) { - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg != NULL - && cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS - && cmsg->cmsg_len >= sizeof (sock)) + /* receive the socket from the child process */ + memset (&msg, 0, sizeof (msg)); + msg.msg_control = msg_buf; + msg.msg_controllen = sizeof (msg_buf); + + /* recv 1 byte of data (not required on Linux, required by BSD/OSX) */ + memset (iov, 0, sizeof (iov)); + iov[0].iov_base = iov_data; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (recvmsg (HOOK_CONNECT(hook_connect, child_recv), &msg, 0) >= 0) { - memcpy(&sock, CMSG_DATA(cmsg), sizeof (sock)); + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg != NULL + && cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS + && cmsg->cmsg_len >= sizeof (sock)) + { + memcpy(&sock, CMSG_DATA(cmsg), sizeof (sock)); + } } } -#else - num_read = read (HOOK_CONNECT(hook_connect, child_read), &sock, sizeof (sock)); - (void) num_read; - - /* prevent unhook process from closing the socket */ - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + else { - if (HOOK_CONNECT(hook_connect, sock_v4[i]) == sock) - HOOK_CONNECT(hook_connect, sock_v4[i]) = -1; + num_read = read (HOOK_CONNECT(hook_connect, child_read), &sock, sizeof (sock)); + (void) num_read; - if (HOOK_CONNECT(hook_connect, sock_v6[i]) == sock) - HOOK_CONNECT(hook_connect, sock_v6[i]) = -1; + /* prevent unhook process from closing the socket */ + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + if (HOOK_CONNECT(hook_connect, sock_v4[i]) == sock) + HOOK_CONNECT(hook_connect, sock_v4[i]) = -1; + + if (HOOK_CONNECT(hook_connect, sock_v6[i]) == sock) + HOOK_CONNECT(hook_connect, sock_v6[i]) = -1; + } } -#endif /* HOOK_CONNECT_MAX_SOCKETS */ HOOK_CONNECT(hook_connect, sock) = sock; @@ -1634,12 +1635,7 @@ network_connect_child_read_cb (const void *pointer, void *data, int fd) void network_connect_with_fork (struct t_hook *hook_connect) { - int child_pipe[2], rc; -#ifdef HOOK_CONNECT_MAX_SOCKETS - int i; -#else - int child_socket[2]; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ + int child_pipe[2], child_socket[2], rc, i; #ifdef HAVE_GNUTLS const char *pos_error; #endif /* HAVE_GNUTLS */ @@ -1708,27 +1704,30 @@ network_connect_with_fork (struct t_hook *hook_connect) HOOK_CONNECT(hook_connect, child_read) = child_pipe[0]; HOOK_CONNECT(hook_connect, child_write) = child_pipe[1]; -#ifndef HOOK_CONNECT_MAX_SOCKETS - /* create socket for child process */ - if (socketpair (AF_LOCAL, SOCK_DGRAM, 0, child_socket) < 0) + if (hook_socketpair_ok) { - (void) (HOOK_CONNECT(hook_connect, callback)) - (hook_connect->callback_pointer, - hook_connect->callback_data, - WEECHAT_HOOK_CONNECT_MEMORY_ERROR, - 0, -1, "socketpair", NULL); - unhook (hook_connect); - return; + /* create socket for child process */ + if (socketpair (AF_LOCAL, SOCK_DGRAM, 0, child_socket) < 0) + { + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_pointer, + hook_connect->callback_data, + WEECHAT_HOOK_CONNECT_MEMORY_ERROR, + 0, -1, "socketpair", NULL); + unhook (hook_connect); + return; + } + HOOK_CONNECT(hook_connect, child_recv) = child_socket[0]; + HOOK_CONNECT(hook_connect, child_send) = child_socket[1]; } - HOOK_CONNECT(hook_connect, child_recv) = child_socket[0]; - HOOK_CONNECT(hook_connect, child_send) = child_socket[1]; -#else - for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + else { - HOOK_CONNECT(hook_connect, sock_v4[i]) = socket (AF_INET, SOCK_STREAM, 0); - HOOK_CONNECT(hook_connect, sock_v6[i]) = socket (AF_INET6, SOCK_STREAM, 0); + for (i = 0; i < HOOK_CONNECT_MAX_SOCKETS; i++) + { + HOOK_CONNECT(hook_connect, sock_v4[i]) = socket (AF_INET, SOCK_STREAM, 0); + HOOK_CONNECT(hook_connect, sock_v6[i]) = socket (AF_INET6, SOCK_STREAM, 0); + } } -#endif /* HOOK_CONNECT_MAX_SOCKETS */ switch (pid = fork ()) { @@ -1746,9 +1745,8 @@ network_connect_with_fork (struct t_hook *hook_connect) rc = setuid (getuid ()); (void) rc; close (HOOK_CONNECT(hook_connect, child_read)); -#ifndef HOOK_CONNECT_MAX_SOCKETS - close (HOOK_CONNECT(hook_connect, child_recv)); -#endif /* HOOK_CONNECT_MAX_SOCKETS */ + if (hook_socketpair_ok) + close (HOOK_CONNECT(hook_connect, child_recv)); network_connect_child (hook_connect); _exit (EXIT_SUCCESS); } @@ -1756,10 +1754,11 @@ network_connect_with_fork (struct t_hook *hook_connect) HOOK_CONNECT(hook_connect, child_pid) = pid; close (HOOK_CONNECT(hook_connect, child_write)); HOOK_CONNECT(hook_connect, child_write) = -1; -#ifndef HOOK_CONNECT_MAX_SOCKETS - close (HOOK_CONNECT(hook_connect, child_send)); - HOOK_CONNECT(hook_connect, child_send) = -1; -#endif /* HOOK_CONNECT_MAX_SOCKETS */ + if (hook_socketpair_ok) + { + close (HOOK_CONNECT(hook_connect, child_send)); + HOOK_CONNECT(hook_connect, child_send) = -1; + } HOOK_CONNECT(hook_connect, hook_child_timer) = hook_timer (hook_connect->plugin, CONFIG_INTEGER(config_network_connection_timeout) * 1000, 0, 1,