diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-04-12 17:53:12 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-04-12 17:53:12 +0200 |
commit | bfe13ccc58ccb96f243a58309800410db1ccb52c (patch) | |
tree | 9b11760d9a0a5bfbdfeda140c0f795e80844a556 | |
parent | c5f1ef53c24cc0c9f7b2131609e916f913634feb (diff) |
patch 8.2.0557: no IPv6 support for channelsv8.2.0557
Problem: No IPv6 support for channels.
Solution: Add IPv6 support. (Ozaki Kiichi, closes #5893)
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | runtime/doc/channel.txt | 6 | ||||
-rw-r--r-- | runtime/doc/various.txt | 1 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 2 | ||||
-rw-r--r-- | src/Make_mvc.mak | 2 | ||||
-rwxr-xr-x | src/auto/configure | 78 | ||||
-rw-r--r-- | src/channel.c | 343 | ||||
-rw-r--r-- | src/config.h.in | 3 | ||||
-rw-r--r-- | src/configure.ac | 48 | ||||
-rw-r--r-- | src/evalfunc.c | 7 | ||||
-rw-r--r-- | src/proto/channel.pro | 2 | ||||
-rw-r--r-- | src/testdir/check.vim | 33 | ||||
-rw-r--r-- | src/testdir/runtest.vim | 27 | ||||
-rw-r--r-- | src/testdir/test_cdo.vim | 2 | ||||
-rw-r--r-- | src/testdir/test_channel.py | 19 | ||||
-rw-r--r-- | src/testdir/test_channel.vim | 138 | ||||
-rw-r--r-- | src/testdir/test_channel_6.py | 15 | ||||
-rw-r--r-- | src/testdir/test_escaped_glob.vim | 2 | ||||
-rw-r--r-- | src/testdir/test_getcwd.vim | 2 | ||||
-rw-r--r-- | src/testdir/test_hide.vim | 2 | ||||
-rw-r--r-- | src/version.c | 2 |
21 files changed, 546 insertions, 192 deletions
diff --git a/.travis.yml b/.travis.yml index 356702cdfc..2c5f5ff304 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,9 +62,7 @@ _anchors: sudo update-alternatives --install /usr/bin/lua lua /usr/bin/lua5.3 10 fi before_script: - # On travis bionic-amd64 gethostbyname() resolves "localhost" to 127.0.1.1 - # so that makes various channel tests fail. - - sudo sed -i '/^127\.0\.1\.1\s/s/\blocalhost\b//g' /etc/hosts + - sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 - sudo bash ci/load-snd-dummy.sh || true - sudo usermod -a -G audio $USER - do_test() { sg audio "sg $(id -gn) '$*'"; } diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 3fa05083cb..42e00c7ae7 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -120,6 +120,9 @@ Use |ch_status()| to see if the channel could be opened. {address} has the form "hostname:port". E.g., "localhost:8765". +When using an IPv6 address, enclose it within square brackets. E.g., +"[2001:db8::1]:8765". + {options} is a dictionary with optional entries: *channel-open-options* "mode" can be: *channel-mode* @@ -621,6 +624,9 @@ ch_open({address} [, {options}]) *ch_open()* {address} has the form "hostname:port", e.g., "localhost:8765". + When using an IPv6 address, enclose it within square brackets. + E.g., "[2001:db8::1]:8765". + If {options} is given it must be a |Dictionary|. See |channel-open-options|. diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index b6867bcabd..a88cc81cc1 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -375,6 +375,7 @@ m *+hangul_input* Hangul input support |hangul| *+iconv* Compiled with the |iconv()| function *+iconv/dyn* Likewise |iconv-dynamic| |/dyn| T *+insert_expand* |insert_expand| Insert mode completion +m *+ipv6* Support for IPv6 networking |channel| m *+job* starting and stopping jobs |job| S *+jumplist* |jumplist| B *+keymap* |'keymap'| diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 86986482ae..091102e661 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -850,7 +850,7 @@ endif ifeq ($(CHANNEL),yes) OBJ += $(OUTDIR)/channel.o -LIB += -lwsock32 +LIB += -lwsock32 -lws2_32 endif ifeq ($(DIRECTX),yes) diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 1d7a3ed112..fa14c9e646 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -469,7 +469,7 @@ CHANNEL_PRO = proto/channel.pro CHANNEL_OBJ = $(OBJDIR)/channel.obj CHANNEL_DEFS = -DFEAT_JOB_CHANNEL -NETBEANS_LIB = WSock32.lib +NETBEANS_LIB = WSock32.lib Ws2_32.lib !endif # Set which version of the CRT to use diff --git a/src/auto/configure b/src/auto/configure index 0a140eed8b..33bf663224 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -7723,8 +7723,7 @@ $as_echo "yes" >&6; } fi if test "$enable_channel" = "yes"; then - - if test "x$HAIKU" = "xyes"; then + if test "x$HAIKU" = "xyes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lnetwork" >&5 $as_echo_n "checking for socket in -lnetwork... " >&6; } if ${ac_cv_lib_network_socket+:} false; then : @@ -7818,7 +7817,63 @@ fi fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling with IPv6 networking is possible" >&5 +$as_echo_n "checking whether compiling with IPv6 networking is possible... " >&6; } +if ${vim_cv_ipv6_networking+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + /* Check bitfields */ + struct nbbuf { + unsigned int initDone:1; + unsigned short signmaplen; + }; + +int +main () +{ + + /* Check creating a socket. */ + struct sockaddr_in server; + struct addrinfo *res; + (void)socket(AF_INET, SOCK_STREAM, 0); + (void)htons(100); + (void)getaddrinfo("microsoft.com", NULL, NULL, &res); + if (errno == ECONNREFUSED) + (void)connect(1, (struct sockaddr *)&server, sizeof(server)); + (void)freeaddrinfo(res); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + vim_cv_ipv6_networking="yes" +else + vim_cv_ipv6_networking="no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_ipv6_networking" >&5 +$as_echo "$vim_cv_ipv6_networking" >&6; } + + if test "x$vim_cv_ipv6_networking" = "xyes"; then + $as_echo "#define FEAT_IPV6 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 $as_echo_n "checking for gethostbyname in -lnsl... " >&6; } if ${ac_cv_lib_nsl_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 @@ -7863,8 +7918,11 @@ _ACEOF fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling with process communication is possible" >&5 -$as_echo_n "checking whether compiling with process communication is possible... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling with IPv4 networking is possible" >&5 +$as_echo_n "checking whether compiling with IPv4 networking is possible... " >&6; } +if ${vim_cv_ipv4_networking+:} false; then : + $as_echo_n "(cached) " >&6 +else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -7900,15 +7958,17 @@ main () } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } + vim_cv_ipv4_networking="yes" else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; }; enable_netbeans="no"; enable_channel="no" + vim_cv_ipv4_networking="no"; enable_netbeans="no"; enable_channel="no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vim_cv_ipv4_networking" >&5 +$as_echo "$vim_cv_ipv4_networking" >&6; } + fi +fi if test "$enable_netbeans" = "yes"; then $as_echo "#define FEAT_NETBEANS_INTG 1" >>confdefs.h diff --git a/src/channel.c b/src/channel.c index 1dfb28f854..8f15c659bf 100644 --- a/src/channel.c +++ b/src/channel.c @@ -10,6 +10,13 @@ * Implements communication through a socket or any file handle. */ +#ifdef WIN32 +// Must include winsock2.h before windows.h since it conflicts with winsock.h +// (included in windows.h). +# include <winsock2.h> +# include <ws2tcpip.h> +#endif + #include "vim.h" #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) @@ -40,7 +47,7 @@ #else # include <netdb.h> # include <netinet/in.h> - +# include <arpa/inet.h> # include <sys/socket.h> # ifdef HAVE_LIBGEN_H # include <libgen.h> @@ -711,90 +718,38 @@ channel_gui_unregister(channel_T *channel) static char *e_cannot_connect = N_("E902: Cannot connect to port"); /* - * Open a socket channel to "hostname":"port". - * "waittime" is the time in msec to wait for the connection. - * When negative wait forever. - * Returns the channel for success. - * Returns NULL for failure. + * For Unix we need to call connect() again after connect() failed. + * On Win32 one time is sufficient. */ - channel_T * -channel_open( - char *hostname, - int port_in, - int waittime, - void (*nb_close_cb)(void)) + static int +channel_connect( + channel_T *channel, + const struct sockaddr *server_addr, + int server_addrlen, + int *waittime) { - int sd = -1; - struct sockaddr_in server; - struct hostent *host; + int sd = -1; #ifdef MSWIN - u_short port = port_in; - u_long val = 1; -#else - int port = port_in; + u_long val = 1; #endif - channel_T *channel; - int ret; - -#ifdef MSWIN - channel_init_winsock(); -#endif - - channel = add_channel(); - if (channel == NULL) - { - ch_error(NULL, "Cannot allocate channel."); - return NULL; - } - - // Get the server internet address and put into addr structure - // fill in the socket address structure and connect to server - vim_memset((char *)&server, 0, sizeof(server)); - server.sin_family = AF_INET; - server.sin_port = htons(port); - if ((host = gethostbyname(hostname)) == NULL) - { - ch_error(channel, "in gethostbyname() in channel_open()"); - PERROR(_("E901: gethostbyname() in channel_open()")); - channel_free(channel); - return NULL; - } - { - char *p; - // When using host->h_addr_list[0] directly ubsan warns for it to not - // be aligned. First copy the pointer to avoid that. - memcpy(&p, &host->h_addr_list[0], sizeof(p)); - memcpy((char *)&server.sin_addr, p, host->h_length); - } - - // On Mac and Solaris a zero timeout almost never works. At least wait - // one millisecond. Let's do it for all systems, because we don't know why - // this is needed. - if (waittime == 0) - waittime = 1; - - /* - * For Unix we need to call connect() again after connect() failed. - * On Win32 one time is sufficient. - */ while (TRUE) { long elapsed_msec = 0; int waitnow; + int ret; if (sd >= 0) sock_close(sd); - sd = socket(AF_INET, SOCK_STREAM, 0); + sd = socket(server_addr->sa_family, SOCK_STREAM, 0); if (sd == -1) { - ch_error(channel, "in socket() in channel_open()."); - PERROR(_("E898: socket() in channel_open()")); - channel_free(channel); - return NULL; + ch_error(channel, "in socket() in channel_connect()."); + PERROR(_("E898: socket() in channel_connect()")); + return -1; } - if (waittime >= 0) + if (*waittime >= 0) { // Make connect() non-blocking. if ( @@ -807,23 +762,22 @@ channel_open( { SOCK_ERRNO; ch_error(channel, - "channel_open: Connect failed with errno %d", errno); + "channel_connect: Connect failed with errno %d", errno); sock_close(sd); - channel_free(channel); - return NULL; + return -1; } } // Try connecting to the server. - ch_log(channel, "Connecting to %s port %d", hostname, port); - ret = connect(sd, (struct sockaddr *)&server, sizeof(server)); + ch_log(channel, "Connecting..."); + ret = connect(sd, server_addr, server_addrlen); if (ret == 0) // The connection could be established. break; SOCK_ERRNO; - if (waittime < 0 || (errno != EWOULDBLOCK + if (*waittime < 0 || (errno != EWOULDBLOCK && errno != ECONNREFUSED #ifdef EINPROGRESS && errno != EINPROGRESS @@ -831,22 +785,24 @@ channel_open( )) { ch_error(channel, - "channel_open: Connect failed with errno %d", errno); + "channel_connect: Connect failed with errno %d", errno); PERROR(_(e_cannot_connect)); sock_close(sd); - channel_free(channel); - return NULL; + return -1; + } + else if (errno == ECONNREFUSED) + { + ch_error(channel, "channel_connect: Connection refused"); + sock_close(sd); + return -1; } // Limit the waittime to 50 msec. If it doesn't work within this // time we close the socket and try creating it again. - waitnow = waittime > 50 ? 50 : waittime; + waitnow = *waittime > 50 ? 50 : *waittime; // If connect() didn't finish then try using select() to wait for the // connection to be made. For Win32 always use select() to wait. -#ifndef MSWIN - if (errno != ECONNREFUSED) -#endif { struct timeval tv; fd_set rfds; @@ -868,18 +824,17 @@ channel_open( gettimeofday(&start_tv, NULL); #endif ch_log(channel, - "Waiting for connection (waiting %d msec)...", waitnow); - ret = select((int)sd + 1, &rfds, &wfds, NULL, &tv); + "Waiting for connection (waiting %d msec)...", waitnow); + ret = select((int)sd + 1, &rfds, &wfds, NULL, &tv); if (ret < 0) { SOCK_ERRNO; ch_error(channel, - "channel_open: Connect failed with errno %d", errno); + "channel_connect: Connect failed with errno %d", errno); PERROR(_(e_cannot_connect)); sock_close(sd); - channel_free(channel); - return NULL; + return -1; } #ifdef MSWIN @@ -888,9 +843,9 @@ channel_open( if (FD_ISSET(sd, &wfds)) break; elapsed_msec = waitnow; - if (waittime > 1 && elapsed_msec < waittime) + if (*waittime > 1 && elapsed_msec < *waittime) { - waittime -= elapsed_msec; + *waittime -= elapsed_msec; continue; } #else @@ -914,12 +869,17 @@ channel_open( )) { ch_error(channel, - "channel_open: Connect failed with errno %d", + "channel_connect: Connect failed with errno %d", so_error); PERROR(_(e_cannot_connect)); sock_close(sd); - channel_free(channel); - return NULL; + return -1; + } + else if (errno == ECONNREFUSED) + { + ch_error(channel, "channel_connect: Connection refused"); + sock_close(sd); + return -1; } } @@ -929,30 +889,30 @@ channel_open( gettimeofday(&end_tv, NULL); elapsed_msec = (end_tv.tv_sec - start_tv.tv_sec) * 1000 - + (end_tv.tv_usec - start_tv.tv_usec) / 1000; + + (end_tv.tv_usec - start_tv.tv_usec) / 1000; #endif } #ifndef MSWIN - if (waittime > 1 && elapsed_msec < waittime) + if (*waittime > 1 && elapsed_msec < *waittime) { // The port isn't ready but we also didn't get an error. // This happens when the server didn't open the socket // yet. Select() may return early, wait until the remaining // "waitnow" and try again. waitnow -= elapsed_msec; - waittime -= elapsed_msec; + *waittime -= elapsed_msec; if (waitnow > 0) { mch_delay((long)waitnow, TRUE); ui_breakcheck(); - waittime -= waitnow; + *waittime -= waitnow; } if (!got_int) { - if (waittime <= 0) + if (*waittime <= 0) // give it one more try - waittime = 1; + *waittime = 1; continue; } // we were interrupted, behave as if timed out @@ -962,13 +922,10 @@ channel_open( // We timed out. ch_error(channel, "Connection timed out"); sock_close(sd); - channel_free(channel); - return NULL; + return -1; } - ch_log(channel, "Connection made"); - - if (waittime >= 0) + if (*waittime >= 0) { #ifdef MSWIN val = 0; @@ -978,10 +935,151 @@ channel_open( #endif } + return sd; +} + +/* + * Open a socket channel to "hostname":"port". + * "waittime" is the time in msec to wait for the connection. + * When negative wait forever. + * Returns the channel for success. + * Returns NULL for failure. + */ + channel_T * +channel_open( + const char *hostname, + int port, + int waittime, + void (*nb_close_cb)(void)) +{ + int sd = -1; + channel_T *channel = NULL; +#ifdef FEAT_IPV6 + struct addrinfo hints; + struct addrinfo *res = NULL; + struct addrinfo *addr = NULL; +#else + struct sockaddr_in server; + struct hostent *host = NULL; +#endif + +#ifdef MSWIN + channel_init_winsock(); +#endif + + channel = add_channel(); + if (channel == NULL) + { + ch_error(NULL, "Cannot allocate channel."); + return NULL; + } + + // Get the server internet address and put into addr structure fill in the + // socket address structure and connect to server. +#ifdef FEAT_IPV6 + vim_memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; +# ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +# endif + // Set port number manually in order to prevent name resolution services + // from being invoked in the environment where AI_NUMERICSERV is not + // defined. + if (getaddrinfo(hostname, NULL, &hints, &res) != 0) + { + ch_error(channel, "in getaddrinfo() in channel_open()"); + PERROR(_("E901: getaddrinfo() in channel_open()")); + channel_free(channel); + return NULL; + } + + for (addr = res; addr != NULL; addr = addr->ai_next) + { + const char *dst = hostname; + const void *src = NULL; + char buf[NUMBUFLEN]; + + if (addr->ai_family == AF_INET6) + { + struct sockaddr_in6 *sai = (struct sockaddr_in6 *)addr->ai_addr; + + sai->sin6_port = htons(port); + src = &sai->sin6_addr; + } + else if (addr->ai_family == AF_INET) + { + struct sockaddr_in *sai = (struct sockaddr_in *)addr->ai_addr; + + sai->sin_port = htons(port); + src = &sai->sin_addr; + } + if (src != NULL) + { + dst = inet_ntop(addr->ai_family, src, buf, sizeof(buf)); + if (dst != NULL && STRCMP(hostname, dst) != 0) + ch_log(channel, "Resolved %s to %s", hostname, dst); + } + + ch_log(channel, "Trying to connect to %s port %d", dst, port); + + // On Mac and Solaris a zero timeout almost never works. At least wait + // one millisecond. Let's do it for all systems, because we don't know + // why this is needed. + if (waittime == 0) + waittime = 1; + + sd = channel_connect(channel, addr->ai_addr, addr->ai_addrlen, + &waittime); + if (sd >= 0) + break; + } + + freeaddrinfo(res); +#else + vim_memset((char *)&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + if ((host = gethostbyname(hostname)) == NULL) + { + ch_error(channel, "in gethostbyname() in channel_open()"); + PERROR(_("E901: gethostbyname() in channel_open()")); + channel_free(channel); + return NULL; + } + { + char *p; + + // When using host->h_addr_list[0] directly ubsan warns for it to not + // be aligned. First copy the pointer to avoid that. + memcpy(&p, &host->h_addr_list[0], sizeof(p)); + memcpy((char *)&server.sin_addr, p, host->h_length); + } + + ch_log(channel, "Trying to connect to %s port %d", hostname, port); + + // On Mac and Solaris a zero timeout almost never works. At least wait one + // millisecond. Let's do it for all systems, because we don't know why + // this is needed. + if (waittime == 0) + waittime = 1; + + sd = channel_connect(channel, (struct sockaddr *)&server, sizeof(server), + &waittime); +#endif + + if (sd < 0) + { + channel_free(channel); + return NULL; + } + + ch_log(channel, "Connection made"); + channel->CH_SOCK_FD = (sock_T)sd; channel->ch_nb_close_cb = nb_close_cb; channel->ch_hostname = (char *)vim_strsave((char_u *)hostname); - channel->ch_port = port_in; + channel->ch_port = port; channel->ch_to_be_closed |= (1U << PART_SOCK); #ifdef FEAT_GUI @@ -1222,6 +1320,7 @@ channel_open_func(typval_T *argvars) char_u *p; char *rest; int port; + int is_ipv6 = FALSE; jobopt_T opt; channel_T *channel = NULL; @@ -1234,20 +1333,40 @@ channel_open_func(typval_T *argvars) } // parse address - p = vim_strchr(address, ':'); - if (p == NULL) + if (*address == '[') { - semsg(_(e_invarg2), address); - return NULL; + // ipv6 address + is_ipv6 = TRUE; + p = vim_strchr(address + 1, ']'); + if (p == NULL || *++p != ':') + { + semsg(_(e_invarg2), address); + return NULL; + } } - *p++ = NUL; - port = strtol((char *)p, &rest, 10); - if (*address == NUL || port <= 0 || *rest != NUL) + else + { + p = vim_strchr(address, ':'); + if (p == NULL) + { + semsg(_(e_invarg2), address); + return NULL; + } + } + port = strtol((char *)(p + 1), &rest, 10); + if (*address == NUL || port <= 0 || port >= 65536 || *rest != NUL) { - p[-1] = ':'; semsg(_(e_invarg2), address); return NULL; } + if (is_ipv6) + { + // strip '[' and ']' + ++address; + *(p - 1) = NUL; + } + else + *p = NUL; // parse options clear_job_options(&opt); diff --git a/src/config.h.in b/src/config.h.in index deeceb7a94..166724c916 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -438,6 +438,9 @@ /* Define if we have shl_load() */ #undef HAVE_SHL_LOAD +/* Define if we can use IPv6 networking. */ +#undef FEAT_IPV6 + /* Define if you want to include NetBeans integration. */ #undef FEAT_NETBEANS_INTG diff --git a/src/configure.ac b/src/configure.ac index 89ae06f739..47502a99c4 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2038,17 +2038,50 @@ else fi if test "$enable_channel" = "yes"; then - dnl On Solaris we need the socket and nsl library. - + dnl On Solaris we need the socket library, or on Haiku the network library. if test "x$HAIKU" = "xyes"; then AC_CHECK_LIB(network, socket) else AC_CHECK_LIB(socket, socket) fi - AC_CHECK_LIB(nsl, gethostbyname) - AC_MSG_CHECKING(whether compiling with process communication is possible) - AC_TRY_LINK([ + AC_CACHE_CHECK([whether compiling with IPv6 networking is possible], [vim_cv_ipv6_networking], + [AC_TRY_LINK([ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + /* Check bitfields */ + struct nbbuf { + unsigned int initDone:1; + unsigned short signmaplen; + }; + ], [ + /* Check creating a socket. */ + struct sockaddr_in server; + struct addrinfo *res; + (void)socket(AF_INET, SOCK_STREAM, 0); + (void)htons(100); + (void)getaddrinfo("microsoft.com", NULL, NULL, &res); + if (errno == ECONNREFUSED) + (void)connect(1, (struct sockaddr *)&server, sizeof(server)); + (void)freeaddrinfo(res); + ], + [vim_cv_ipv6_networking="yes"], + [vim_cv_ipv6_networking="no"])]) + + if test "x$vim_cv_ipv6_networking" = "xyes"; then + AC_DEFINE(FEAT_IPV6) + else + dnl On Solaris we need the nsl library. + AC_CHECK_LIB(nsl, gethostbyname) + AC_CACHE_CHECK([whether compiling with IPv4 networking is possible], [vim_cv_ipv4_networking], + [AC_TRY_LINK([ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -2072,8 +2105,9 @@ if test "$enable_channel" = "yes"; then if (errno == ECONNREFUSED) (void)connect(1, (struct sockaddr *)&server, sizeof(server)); ], - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no); enable_netbeans="no"; enable_channel="no") + [vim_cv_ipv4_networking="yes"], + [vim_cv_ipv4_networking="no"; enable_netbeans="no"; enable_channel="no"])]) + fi fi if test "$enable_netbeans" = "yes"; then AC_DEFINE(FEAT_NETBEANS_INTG) diff --git a/src/evalfunc.c b/src/evalfunc.c index 7170c0e1fe..8ce8c8751d 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -3943,6 +3943,13 @@ f_has(typval_T *argvars, typval_T *rettv) #endif }, {"insert_expand", 1}, + {"ipv6", +#ifdef FEAT_IPV6 + 1 +#else + 0 +#endif + }, {"job", #ifdef FEAT_JOB_CHANNEL 1 diff --git a/src/proto/channel.pro b/src/proto/channel.pro index a441a484f4..56a0167b82 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -7,7 +7,7 @@ int channel_unref(channel_T *channel); int free_unused_channels_contents(int copyID, int mask); void free_unused_channels(int copyID, int mask); void channel_gui_register_all(void); -channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_close_cb)(void)); +channel_T *channel_open(const char *hostname, int port, int waittime, void (*nb_close_cb)(void)); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_buffer_free(buf_T *buf); diff --git a/src/testdir/check.vim b/src/testdir/check.vim index 34bf5b319d..efb273b005 100644 --- a/src/testdir/check.vim +++ b/src/testdir/check.vim @@ -142,4 +142,37 @@ func CheckEnglish() endif endfunc +" Command to check that loopback device has IPv6 address +command CheckIPv6 call CheckIPv6() +func CheckIPv6() + if !has('ipv6') + throw 'Skipped: cannot use IPv6 networking' + endif + if !exists('s:ipv6_loopback') + let s:ipv6_loopback = s:CheckIPv6Loopback() + endif + if !s:ipv6_loopback + throw 'Skipped: no IPv6 address for loopback device' + endif +endfunc + +func s:CheckIPv6Loopback() + if has('win32') + return system('netsh interface ipv6 show interface') =~? '\<Loopback\>' + elseif filereadable('/proc/net/if_inet6') + return (match(readfile('/proc/net/if_inet6'), '\slo$') >= 0) + elseif executable('ifconfig') + for dev in ['lo0', 'lo', 'loop'] + " NOTE: On SunOS, need specify address family 'inet6' to get IPv6 info. + if system('ifconfig ' .. dev .. ' inet6 2>/dev/null') =~? '\<inet6\>' + \ || system('ifconfig ' .. dev .. ' 2>/dev/null') =~? '\<inet6\>' + return v:true + endif + endfor + else + " TODO: How to check it in other platforms? + endif + return v:false +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index 0d603231aa..3e7e5115f4 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -161,8 +161,7 @@ func RunTheTest(test) exe 'call ' . a:test else try - let s:test = a:test - au VimLeavePre * call EarlyExit(s:test) + au VimLeavePre * call EarlyExit(g:testfunc) exe 'call ' . a:test au! VimLeavePre catch /^\cskipped/ @@ -226,11 +225,11 @@ func AfterTheTest(func_name) if len(v:errors) > 0 if match(s:may_fail_list, '^' .. a:func_name) >= 0 let s:fail_expected += 1 - call add(s:errors_expected, 'Found errors in ' . s:test . ':') + call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':') call extend(s:errors_expected, v:errors) else let s:fail += 1 - call add(s:errors, 'Found errors in ' . s:test . ':') + call add(s:errors, 'Found errors in ' . g:testfunc . ':') call extend(s:errors, v:errors) endif let v:errors = [] @@ -396,31 +395,31 @@ endif let s:may_fail_list = [] if $TEST_MAY_FAIL != '' - " Split the list at commas and add () to make it match s:test. + " Split the list at commas and add () to make it match g:testfunc. let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'}) endif " Execute the tests in alphabetical order. -for s:test in sort(s:tests) +for g:testfunc in sort(s:tests) " Silence, please! set belloff=all let prev_error = '' let total_errors = [] let g:run_nr = 1 - " A test can set test_is_flaky to retry running the test. - let test_is_flaky = 0 + " A test can set g:test_is_flaky to retry running the test. + let g:test_is_flaky = 0 - call RunTheTest(s:test) + call RunTheTest(g:testfunc) " Repeat a flaky test. Give up when: " - it fails again with the same message " - it fails five times (with a different message) if len(v:errors) > 0 - \ && (index(s:flaky_tests, s:test) >= 0 - \ || test_is_flaky) + \ && (index(s:flaky_tests, g:testfunc) >= 0 + \ || g:test_is_flaky) while 1 - call add(s:messages, 'Found errors in ' . s:test . ':') + call add(s:messages, 'Found errors in ' . g:testfunc . ':') call extend(s:messages, v:errors) call add(total_errors, 'Run ' . g:run_nr . ':') @@ -443,7 +442,7 @@ for s:test in sort(s:tests) let v:errors = [] let g:run_nr += 1 - call RunTheTest(s:test) + call RunTheTest(g:testfunc) if len(v:errors) == 0 " Test passed on rerun. @@ -452,7 +451,7 @@ for s:test in sort(s:tests) endwhile endif - call AfterTheTest(s:test) + call AfterTheTest(g:testfu |