patch 9.2.0170: channel: some issues in ch_listen()
Commit:
https://github.com/vim/vim/commit/e05d897258a6c3d0463fb6d76aaeb4f0534cd13f
Author: Yasuhiro Matsumoto <
matt...@gmail.com>
Date: Sun Mar 15 09:22:29 2026 +0000
patch 9.2.0170: channel: some issues in ch_listen()
Problem: channel: some issues in ch_listen()
(char101, after v9.2.0153)
Solution: On MS-Windows, initialize using channel_init_winsock() and use
SO_EXCLUSIVEADDRUSE instead of SO_REUSEADDR, allow to use port
0 to have the OS assign a port (Yasuhiro Matsumoto).
related: #19231
closes: #19690
Co-Authored-By: Claude Opus 4.6 (1M context) <
nor...@anthropic.com>
Signed-off-by: Yasuhiro Matsumoto <
matt...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/channel.c b/src/channel.c
index f35cbf8ea..6f06517fd 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1440,7 +1440,7 @@ channel_listen_func(typval_T *argvars)
return NULL;
}
port = strtol((char *)(p + 1), &rest, 10);
- if (port <= 0 || port >= 65536 || *rest != NUL)
+ if (port < 0 || port >= 65536 || *rest != NUL)
{
semsg(_(e_invalid_argument_str), address);
return NULL;
@@ -1459,7 +1459,7 @@ channel_listen_func(typval_T *argvars)
return NULL;
}
port = strtol((char *)(p + 1), &rest, 10);
- if (port <= 0 || port >= 65536 || *rest != NUL)
+ if (port < 0 || port >= 65536 || *rest != NUL)
{
semsg(_(e_invalid_argument_str), address);
return NULL;
@@ -1511,6 +1511,10 @@ channel_listen(
int val = 1;
channel_T *channel;
+#ifdef MSWIN
+ channel_init_winsock();
+#endif
+
channel = add_channel();
if (channel == NULL)
{
@@ -1555,7 +1559,7 @@ channel_listen(
}
#ifdef MSWIN
- if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
+ if (setsockopt(sd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
(const char *)&val, sizeof(val)) < 0)
#else
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
@@ -1591,6 +1595,16 @@ channel_listen(
return NULL;
}
+ // When port 0 was specified, retrieve the actual port assigned by the OS.
+ if (port_in == 0)
+ {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+
+ if (getsockname(sd, (struct sockaddr *)&addr, &addr_len) == 0)
+ port_in = ntohs(addr.sin_port);
+ }
+
channel->ch_listen = TRUE;
channel->CH_SOCK_FD = (sock_T)sd;
channel->ch_nb_close_cb = nb_close_cb;
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index 431eaab26..94dec0d97 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -2798,8 +2798,11 @@ func Test_listen_invalid_address()
" port number too large
call assert_fails("call ch_listen('localhost:99999')", 'E475:')
- " port number zero
- call assert_fails("call ch_listen('localhost:0')", 'E475:')
+ " port number zero should let the OS assign an available port
+ let ch = ch_listen('localhost:0')
+ call assert_equal('open', ch_status(ch))
+ call assert_notequal(0, ch_info(ch).port)
+ call ch_close(ch)
" port number negative
call assert_fails("call ch_listen('localhost:-1')", 'E475:')
diff --git a/src/version.c b/src/version.c
index 4f9c944a6..38e48f4a1 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 170,
/**/
169,
/**/