1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-06-30 19:12:48 -05:00

In GUI PuTTY, log standard error from local proxy commands.

On both Unix and Windows, we now redirect the local proxy command's
standard error into a third pipe; data received from that pipe is
broken up at newlines and logged in the Event Log. So if the proxy
command emits any error messages in the course of failing to connect
to something, you now have a fighting chance of finding out what went
wrong.

This feature is disabled in command-line tools like PSFTP and Plink,
on the basis that in that situation it seems more likely that the user
would expect standard-error output to go to the ordinary standard
error in the ordinary way. Only GUI PuTTY catches it and logs it like
this, because it either doesn't have a standard error at all (on
Windows) or is likely to be pointing it at some completely unhelpful
session log file (under X).
This commit is contained in:
Simon Tatham
2015-11-22 11:50:37 +00:00
parent 3d4d4004e8
commit 297efff303
7 changed files with 186 additions and 26 deletions

View File

@ -21,7 +21,7 @@ struct Socket_localproxy_tag {
const struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
int to_cmd, from_cmd; /* fds */
int to_cmd, from_cmd, cmd_err; /* fds */
char *error;
@ -29,6 +29,7 @@ struct Socket_localproxy_tag {
bufchain pending_output_data;
bufchain pending_input_data;
bufchain pending_error_data;
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
};
@ -37,7 +38,9 @@ static int localproxy_select_result(int fd, int event);
/*
* Trees to look up the pipe fds in.
*/
static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
static tree234 *localproxy_by_fromfd;
static tree234 *localproxy_by_tofd;
static tree234 *localproxy_by_errfd;
static int localproxy_fromfd_cmp(void *av, void *bv)
{
Local_Proxy_Socket a = (Local_Proxy_Socket)av;
@ -78,6 +81,26 @@ static int localproxy_tofd_find(void *av, void *bv)
return +1;
return 0;
}
static int localproxy_errfd_cmp(void *av, void *bv)
{
Local_Proxy_Socket a = (Local_Proxy_Socket)av;
Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
if (a->cmd_err < b->cmd_err)
return -1;
if (a->cmd_err > b->cmd_err)
return +1;
return 0;
}
static int localproxy_errfd_find(void *av, void *bv)
{
int a = *(int *)av;
Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
if (a < b->cmd_err)
return -1;
if (a > b->cmd_err)
return +1;
return 0;
}
/* basic proxy socket functions */
@ -104,8 +127,14 @@ static void sk_localproxy_close (Socket s)
uxsel_del(ps->from_cmd);
close(ps->from_cmd);
del234(localproxy_by_errfd, ps);
uxsel_del(ps->cmd_err);
close(ps->cmd_err);
bufchain_clear(&ps->pending_input_data);
bufchain_clear(&ps->pending_output_data);
bufchain_clear(&ps->pending_error_data);
sfree(ps);
}
@ -209,19 +238,26 @@ static int localproxy_select_result(int fd, int event)
int ret;
if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
!(s = find234(localproxy_by_fromfd, &fd, localproxy_errfd_find)) &&
!(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
return 1; /* boggle */
if (event == 1) {
assert(fd == s->from_cmd);
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
return plug_closing(s->plug, strerror(errno), errno, 0);
} else if (ret == 0) {
return plug_closing(s->plug, NULL, 0, 0);
} else {
return plug_receive(s->plug, 0, buf, ret);
}
if (fd == s->cmd_err) {
ret = read(fd, buf, sizeof(buf));
if (ret > 0)
log_proxy_stderr(s->plug, &s->pending_error_data, buf, ret);
} else {
assert(fd == s->from_cmd);
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
return plug_closing(s->plug, strerror(errno), errno, 0);
} else if (ret == 0) {
return plug_closing(s->plug, NULL, 0, 0);
} else {
return plug_receive(s->plug, 0, buf, ret);
}
}
} else if (event == 2) {
assert(fd == s->to_cmd);
if (localproxy_try_send(s))
@ -252,7 +288,7 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
};
Local_Proxy_Socket ret;
int to_cmd_pipe[2], from_cmd_pipe[2], pid, proxytype;
int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
proxytype = conf_get_int(conf, CONF_proxy_type);
if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
@ -266,10 +302,23 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
bufchain_init(&ret->pending_input_data);
bufchain_init(&ret->pending_output_data);
bufchain_init(&ret->pending_error_data);
if (proxytype == PROXY_CMD) {
cmd = format_telnet_command(addr, port, conf);
if (flags & FLAG_STDERR) {
/* If we have a sensible stderr, the proxy command can
* send its own standard error there, so we won't
* interfere. */
cmd_err_pipe[0] = cmd_err_pipe[1] = -1;
} else {
/* If we don't have a sensible stderr, we should catch the
* proxy command's standard error to put in our event
* log. */
cmd_err_pipe[0] = cmd_err_pipe[1] = 0;
}
{
char *logmsg = dupprintf("Starting local proxy command: %s", cmd);
plug_log(plug, 2, NULL, 0, logmsg, 0);
@ -281,13 +330,16 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
* command process.
*/
if (pipe(to_cmd_pipe) < 0 ||
pipe(from_cmd_pipe) < 0) {
pipe(from_cmd_pipe) < 0 ||
(cmd_err_pipe[0] == 0 && pipe(cmd_err_pipe) < 0)) {
ret->error = dupprintf("pipe: %s", strerror(errno));
sfree(cmd);
return (Socket)ret;
}
cloexec(to_cmd_pipe[1]);
cloexec(from_cmd_pipe[0]);
if (cmd_err_pipe[0] >= 0)
cloexec(cmd_err_pipe[0]);
pid = fork();
@ -302,6 +354,10 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
dup2(from_cmd_pipe[1], 1);
close(to_cmd_pipe[0]);
close(from_cmd_pipe[1]);
if (cmd_err_pipe[0] >= 0) {
dup2(cmd_err_pipe[1], 2);
close(cmd_err_pipe[1]);
}
noncloexec(0);
noncloexec(1);
execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
@ -312,9 +368,12 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
close(to_cmd_pipe[0]);
close(from_cmd_pipe[1]);
if (cmd_err_pipe[0] >= 0)
close(cmd_err_pipe[1]);
ret->to_cmd = to_cmd_pipe[1];
ret->from_cmd = from_cmd_pipe[0];
ret->cmd_err = cmd_err_pipe[0];
} else {
cmd = format_telnet_command(addr, port, conf);
ret->to_cmd = open("/dev/null", O_WRONLY);
@ -330,17 +389,23 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
return (Socket)ret;
}
sfree(cmd);
ret->cmd_err = -1;
}
if (!localproxy_by_fromfd)
localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
if (!localproxy_by_tofd)
localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
if (!localproxy_by_errfd)
localproxy_by_errfd = newtree234(localproxy_errfd_cmp);
add234(localproxy_by_fromfd, ret);
add234(localproxy_by_tofd, ret);
if (ret->cmd_err >= 0)
add234(localproxy_by_errfd, ret);
uxsel_set(ret->from_cmd, 1, localproxy_select_result);
uxsel_set(ret->cmd_err, 1, localproxy_select_result);
/* We are responsible for this and don't need it any more */
sk_addr_free(addr);