summaryrefslogtreecommitdiffstats
path: root/src/channel.c
diff options
context:
space:
mode:
authorLemonBoy <thatlemon@gmail.com>2022-04-04 15:46:58 +0100
committerBram Moolenaar <Bram@vim.org>2022-04-04 15:46:58 +0100
commitcc766a85f460ebb7f8c915508447548b5f5b99bc (patch)
tree063d92f755b260af22cd1d40bd95379b7339e9d2 /src/channel.c
parent4829c1c9e9095a3303caec9af7d02f6547f6df0e (diff)
patch 8.2.4684: cannot open a channel on a Unix domain socketv8.2.4684
Problem: Cannot open a channel on a Unix domain socket. Solution: Add Unix domain socket support. (closes #10062)
Diffstat (limited to 'src/channel.c')
-rw-r--r--src/channel.c132
1 files changed, 113 insertions, 19 deletions
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 <netdb.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/socket.h>
+# include <sys/un.h>
# ifdef HAVE_LIBGEN_H
# include <libgen.h>
# endif
@@ -929,6 +936,67 @@ channel_connect(
}
/*
+ * 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.
* When negative wait forever.
@@ -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