From cc766a85f460ebb7f8c915508447548b5f5b99bc Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 4 Apr 2022 15:46:58 +0100 Subject: patch 8.2.4684: cannot open a channel on a Unix domain socket Problem: Cannot open a channel on a Unix domain socket. Solution: Add Unix domain socket support. (closes #10062) --- src/channel.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 19 deletions(-) (limited to 'src/channel.c') diff --git a/src/channel.c b/src/channel.c index 66ba55df65..75fc89560c 100644 --- a/src/channel.c +++ b/src/channel.c @@ -44,11 +44,18 @@ # define sock_write(sd, buf, len) send((SOCKET)sd, buf, len, 0) # define sock_read(sd, buf, len) recv((SOCKET)sd, buf, len, 0) # define sock_close(sd) closesocket((SOCKET)sd) +// Support for Unix-domain sockets was added in Windows SDK 17061. +# define UNIX_PATH_MAX 108 +typedef struct sockaddr_un { + ADDRESS_FAMILY sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; #else # include # include # include # include +# include # ifdef HAVE_LIBGEN_H # include # endif @@ -928,6 +935,67 @@ channel_connect( return sd; } +/* + * Open a socket channel to the UNIX socket at "path". + * Returns the channel for success. + * Returns NULL for failure. + */ + static channel_T * +channel_open_unix( + const char *path, + void (*nb_close_cb)(void)) +{ + channel_T *channel = NULL; + int sd = -1; + size_t path_len = STRLEN(path); + struct sockaddr_un server; + size_t server_len; + int waittime = -1; + + if (*path == NUL || path_len >= sizeof(server.sun_path)) + { + semsg(_(e_invalid_argument_str), path); + return NULL; + } + + channel = add_channel(); + if (channel == NULL) + { + ch_error(NULL, "Cannot allocate channel."); + return NULL; + } + + CLEAR_FIELD(server); + server.sun_family = AF_UNIX; + STRNCPY(server.sun_path, path, sizeof(server.sun_path) - 1); + + ch_log(channel, "Trying to connect to %s", path); + + server_len = offsetof(struct sockaddr_un, sun_path) + path_len + 1; + sd = channel_connect(channel, (struct sockaddr *)&server, (int)server_len, + &waittime); + + 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 *)path); + channel->ch_port = 0; + channel->ch_to_be_closed |= (1U << PART_SOCK); + +#ifdef FEAT_GUI + channel_gui_register_one(channel, PART_SOCK); +#endif + + return channel; +} + /* * Open a socket channel to "hostname":"port". * "waittime" is the time in msec to wait for the connection. @@ -1301,8 +1369,9 @@ channel_open_func(typval_T *argvars) char_u *address; char_u *p; char *rest; - int port; + int port = 0; int is_ipv6 = FALSE; + int is_unix = FALSE; jobopt_T opt; channel_T *channel = NULL; @@ -1319,8 +1388,18 @@ channel_open_func(typval_T *argvars) return NULL; } - // parse address - if (*address == '[') + if (*address == NUL) + { + semsg(_(e_invalid_argument_str), address); + return NULL; + } + + if (!STRNCMP(address, "unix:", 5)) + { + is_unix = TRUE; + address += 5; + } + else if (*address == '[') { // ipv6 address is_ipv6 = TRUE; @@ -1333,6 +1412,7 @@ channel_open_func(typval_T *argvars) } else { + // ipv4 address p = vim_strchr(address, ':'); if (p == NULL) { @@ -1340,27 +1420,32 @@ channel_open_func(typval_T *argvars) return NULL; } } - port = strtol((char *)(p + 1), &rest, 10); - if (*address == NUL || port <= 0 || port >= 65536 || *rest != NUL) - { - semsg(_(e_invalid_argument_str), address); - return NULL; - } - if (is_ipv6) + + if (!is_unix) { - // strip '[' and ']' - ++address; - *(p - 1) = NUL; + port = strtol((char *)(p + 1), &rest, 10); + if (port <= 0 || port >= 65536 || *rest != NUL) + { + semsg(_(e_invalid_argument_str), address); + return NULL; + } + if (is_ipv6) + { + // strip '[' and ']' + ++address; + *(p - 1) = NUL; + } + else + *p = NUL; } - else - *p = NUL; // parse options clear_job_options(&opt); opt.jo_mode = MODE_JSON; opt.jo_timeout = 2000; if (get_job_options(&argvars[1], &opt, - JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL, 0) == FAIL) + JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + + (is_unix? 0 : JO_WAITTIME), 0) == FAIL) goto theend; if (opt.jo_timeout < 0) { @@ -1368,7 +1453,10 @@ channel_open_func(typval_T *argvars) goto theend; } - channel = channel_open((char *)address, port, opt.jo_waittime, NULL); + if (is_unix) + channel = channel_open_unix((char *)address, NULL); + else + channel = channel_open((char *)address, port, opt.jo_waittime, NULL); if (channel != NULL) { opt.jo_set = JO_ALL; @@ -3268,8 +3356,14 @@ channel_info(channel_T *channel, dict_T *dict) if (channel->ch_hostname != NULL) { - dict_add_string(dict, "hostname", (char_u *)channel->ch_hostname); - dict_add_number(dict, "port", channel->ch_port); + if (channel->ch_port) + { + dict_add_string(dict, "hostname", (char_u *)channel->ch_hostname); + dict_add_number(dict, "port", channel->ch_port); + } + else + // Unix-domain socket. + dict_add_string(dict, "path", (char_u *)channel->ch_hostname); channel_part_info(channel, dict, "sock", PART_SOCK); } else -- cgit v1.2.3