emacsclient config+sending long-line fixes

Do not mishandle long lines, or lines containing NUL,
when getting configuration or sending data.
* lib-src/emacsclient.c (send_to_emacs_len, quote_argument_len):
New functions, generalizing the old send_to_emacs and quote_argument.
Rewrite the old functions to use the new ones.
(get_server_config): Do not mishandle long lines in the config file.
(set_tcp_socket): No longer a need to null-terminate auth string.
(main): Do not mishandle long lines from stdin, or lines with NUL.
This commit is contained in:
Paul Eggert
2026-04-12 15:35:40 -07:00
parent ca33663e50
commit 180953c8f6

View File

@@ -811,12 +811,12 @@ sock_err_message (const char *function_name)
}
/* Send to S the data in *DATA when either
/* Send to S the DATA, of size DLEN, when either
- the data's last byte is '\n', or
- the buffer is full (but this shouldn't happen)
- the buffer is full.
Otherwise, just accumulate the data. */
static void
send_to_emacs (HSOCKET s, const char *data)
send_to_emacs_len (HSOCKET s, const char *data, ptrdiff_t dlen)
{
enum { SEND_BUFFER_SIZE = 4096 };
@@ -826,7 +826,7 @@ send_to_emacs (HSOCKET s, const char *data)
/* Fill pointer for the send buffer. */
static int sblen;
for (ptrdiff_t dlen = strlen (data); dlen != 0; )
while (dlen != 0)
{
int part = min (dlen, SEND_BUFFER_SIZE - sblen);
memcpy (&send_buffer[sblen], data, part);
@@ -858,20 +858,27 @@ send_to_emacs (HSOCKET s, const char *data)
}
}
/* In STR, insert a & before each &, each space, each newline, and
any initial -. Change spaces to underscores, too, so that the
return value never contains a space.
Does not change the string. Outputs the result to S. */
/* Send to S the data in the string DATA. */
static void
quote_argument (HSOCKET s, const char *str)
send_to_emacs (HSOCKET s, const char *data)
{
char *copy = xmalloc (strlen (str) * 2 + 1);
send_to_emacs_len (s, data, strlen (data));
}
/* Output to S a quoted copy of the array of bytes STR with length LEN.
Insert a & before each &, each space, each newline, and
any initial -. Change spaces to underscores, too, so that the
output never contains a space. */
static void
quote_argument_len (HSOCKET s, const char *str, ptrdiff_t len)
{
char const *lim = str + len;
char *copy = xmalloc (len * 2);
char *q = copy;
if (*str == '-')
*q++ = '&', *q++ = *str++;
for (; *str; str++)
if (str < lim && *str == '-')
*q++ = '&';
for (; str < lim; str++)
{
char c = *str;
if (c == ' ')
@@ -882,13 +889,21 @@ quote_argument (HSOCKET s, const char *str)
*q++ = '&';
*q++ = c;
}
*q = 0;
send_to_emacs (s, copy);
send_to_emacs_len (s, copy, q - copy);
free (copy);
}
/* Output to S a quoted copy of the string STR.
Insert a & before each &, each space, each newline, and
any initial -. Change spaces to underscores, too, so that the
output never contains a space. */
static void
quote_argument (HSOCKET s, const char *str)
{
return quote_argument_len (s, str, strlen (str));
}
/* The inverse of quote_argument. Remove quoting in string STR by
modifying the addressed string in place. Return STR. */
@@ -990,8 +1005,6 @@ static bool
get_server_config (const char *config_file, struct sockaddr_in *server,
char *authentication)
{
char dotted[32];
char *port;
FILE *config;
if (IS_ABSOLUTE_FILE_NAME (config_file))
@@ -1009,8 +1022,11 @@ get_server_config (const char *config_file, struct sockaddr_in *server,
if (! config)
return false;
if (fgets (dotted, sizeof dotted, config)
&& (port = strchr (dotted, ':')))
char *dotted = NULL;
size_t dottedsize;
ssize_t dottedlen = getline (&dotted, &dottedsize, config);
char *port = dottedlen < 0 ? NULL : strchr (dotted, ':');
if (port)
*port++ = '\0';
else
{
@@ -1022,6 +1038,7 @@ get_server_config (const char *config_file, struct sockaddr_in *server,
server->sin_family = AF_INET;
server->sin_addr.s_addr = inet_addr (dotted);
server->sin_port = htons (atoi (port));
free (dotted);
if (! fread (authentication, AUTH_KEY_LENGTH, 1, config))
{
@@ -1060,9 +1077,9 @@ set_tcp_socket (const char *local_server_file)
struct sockaddr sa;
} server;
struct linger l_arg = { .l_onoff = 1, .l_linger = 1 };
char auth_string[AUTH_KEY_LENGTH + 1];
char auth_buf[AUTH_KEY_LENGTH];
if (! get_server_config (local_server_file, &server.in, auth_string))
if (! get_server_config (local_server_file, &server.in, auth_buf))
return INVALID_SOCKET;
if (server.in.sin_addr.s_addr != inet_addr ("127.0.0.1") && !quiet)
@@ -1096,10 +1113,8 @@ set_tcp_socket (const char *local_server_file)
sock_err_message ("setsockopt");
/* Send the authentication. */
auth_string[AUTH_KEY_LENGTH] = '\0';
send_to_emacs (s, "-auth ");
send_to_emacs (s, auth_string);
send_to_emacs_len (s, auth_buf, sizeof auth_buf);
send_to_emacs (s, " ");
return s;
@@ -2170,11 +2185,14 @@ main (int argc, char **argv)
else if (eval)
{
/* Read expressions interactively. */
while (fgets (string, BUFSIZ, stdin))
char *line = NULL;
size_t linesize;
for (ssize_t len; 0 <= (len = getline (&line, &linesize, stdin)); )
{
send_to_emacs (emacs_socket, "-eval ");
quote_argument (emacs_socket, string);
quote_argument_len (emacs_socket, line, len);
}
free (line);
send_to_emacs (emacs_socket, " ");
}