summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-04-12 17:53:12 +0200
committerBram Moolenaar <Bram@vim.org>2020-04-12 17:53:12 +0200
commitbfe13ccc58ccb96f243a58309800410db1ccb52c (patch)
tree9b11760d9a0a5bfbdfeda140c0f795e80844a556
parentc5f1ef53c24cc0c9f7b2131609e916f913634feb (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.yml4
-rw-r--r--runtime/doc/channel.txt6
-rw-r--r--runtime/doc/various.txt1
-rw-r--r--src/Make_cyg_ming.mak2
-rw-r--r--src/Make_mvc.mak2
-rwxr-xr-xsrc/auto/configure78
-rw-r--r--src/channel.c343
-rw-r--r--src/config.h.in3
-rw-r--r--src/configure.ac48
-rw-r--r--src/evalfunc.c7
-rw-r--r--src/proto/channel.pro2
-rw-r--r--src/testdir/check.vim33
-rw-r--r--src/testdir/runtest.vim27
-rw-r--r--src/testdir/test_cdo.vim2
-rw-r--r--src/testdir/test_channel.py19
-rw-r--r--src/testdir/test_channel.vim138
-rw-r--r--src/testdir/test_channel_6.py15
-rw-r--r--src/testdir/test_escaped_glob.vim2
-rw-r--r--src/testdir/test_getcwd.vim2
-rw-r--r--src/testdir/test_hide.vim2
-rw-r--r--src/version.c2
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:testfunc)
endfor
call FinishTesting()
diff --git a/src/testdir/test_cdo.vim b/src/testdir/test_cdo.vim
index 8a6b746718..51800a5e6b 100644
--- a/src/testdir/test_cdo.vim
+++ b/src/testdir/test_cdo.vim
@@ -4,7 +4,7 @@ source check.vim
CheckFeature quickfix
" Create the files used by the tests
-function SetUp()
+func SetUp()
call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1')
call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2')
call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3')
diff -