* be_misc.c: helper functions shared between main network backends.
*/
+#define DEFINE_PLUG_METHOD_MACROS
#include "putty.h"
#include "network.h"
}
}
+void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len)
+{
+ const char *data = (const char *)vdata;
+ int pos = 0;
+ int msglen;
+ char *nlpos, *msg, *fullmsg;
+
+ /*
+ * This helper function allows us to collect the data written to a
+ * local proxy command's standard error in whatever size chunks we
+ * happen to get from its pipe, and whenever we have a complete
+ * line, we pass it to plug_log.
+ *
+ * Prerequisites: a plug to log to, and a bufchain stored
+ * somewhere to collect the data in.
+ */
+
+ while (pos < len && (nlpos = memchr(data+pos, '\n', len-pos)) != NULL) {
+ /*
+ * Found a newline in the current input buffer. Append it to
+ * the bufchain (which may contain a partial line from last
+ * time).
+ */
+ bufchain_add(buf, data + pos, nlpos - (data + pos));
+
+ /*
+ * Collect the resulting line of data and pass it to plug_log.
+ */
+ msglen = bufchain_size(buf);
+ msg = snewn(msglen+1, char);
+ bufchain_fetch(buf, msg, msglen);
+ bufchain_consume(buf, msglen);
+ msg[msglen] = '\0';
+ fullmsg = dupprintf("proxy: %s", msg);
+ plug_log(plug, 2, NULL, 0, fullmsg, 0);
+ sfree(fullmsg);
+ sfree(msg);
+
+ /*
+ * Advance past the newline.
+ */
+ pos += nlpos+1 - (data + pos);
+ }
+
+ /*
+ * Now any remaining data is a partial line, which we save for
+ * next time.
+ */
+ bufchain_add(buf, data + pos, len - pos);
+}
*/
void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
const char *error_msg, int error_code);
+typedef struct bufchain_tag bufchain; /* rest of declaration in misc.c */
+void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len);
#endif
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;
bufchain pending_output_data;
bufchain pending_input_data;
+ bufchain pending_error_data;
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
};
/*
* 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;
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 */
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);
}
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))
};
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)
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);
* 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();
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);
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);
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);
const struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
- HANDLE send_H, recv_H;
- struct handle *send_h, *recv_h;
+ HANDLE send_H, recv_H, stderr_H;
+ struct handle *send_h, *recv_h, *stderr_h;
/*
* Freezing one of these sockets is a slightly fiddly business,
/* We buffer data here if we receive it from winhandl while frozen. */
bufchain inputdata;
+ /* Data received from stderr_H, if we have one. */
+ bufchain stderrdata;
+
char *error;
Plug plug;
}
}
+static int handle_stderr(struct handle *h, void *data, int len)
+{
+ Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+
+ if (len > 0)
+ log_proxy_stderr(ps->plug, &ps->stderrdata, data, len);
+
+ return 0;
+}
+
static void handle_sentdata(struct handle *h, int new_backlog)
{
Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
if (ps->recv_H != ps->send_H)
CloseHandle(ps->recv_H);
bufchain_clear(&ps->inputdata);
+ bufchain_clear(&ps->stderrdata);
sfree(ps);
}
return NULL;
}
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped)
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped)
{
static const struct socket_function_table socket_fn_table = {
sk_handle_plug,
ret->error = NULL;
ret->frozen = UNFROZEN;
bufchain_init(&ret->inputdata);
+ bufchain_init(&ret->stderrdata);
ret->recv_H = recv_H;
ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags);
ret->send_H = send_H;
ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags);
+ ret->stderr_H = stderr_H;
+ if (ret->stderr_H)
+ ret->stderr_h = handle_input_new(ret->stderr_H, handle_stderr,
+ ret, flags);
return (Socket) ret;
}
#include "winsecur.h"
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped);
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped);
Socket new_named_pipe_client(const char *pipename, Plug plug)
{
LocalFree(psd);
sfree(usersid);
- return make_handle_socket(pipehandle, pipehandle, plug, TRUE);
+ return make_handle_socket(pipehandle, pipehandle, NULL, plug, TRUE);
}
#endif /* !defined NO_SECURITY */
#include "winsecur.h"
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
- int overlapped);
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+ Plug plug, int overlapped);
typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket;
struct Socket_named_pipe_server_tag {
{
HANDLE conn = (HANDLE)ctx.p;
- return make_handle_socket(conn, conn, plug, TRUE);
+ return make_handle_socket(conn, conn, NULL, plug, TRUE);
}
/*
Plug plug, Conf *conf)
{
char *cmd;
- HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us;
+ HANDLE us_to_cmd, cmd_from_us;
+ HANDLE us_from_cmd, cmd_to_us;
+ HANDLE us_from_cmd_err, cmd_err_to_us;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
return ret;
}
+ 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. */
+ us_from_cmd_err = cmd_err_to_us = NULL;
+ } else {
+ /* If we don't have a sensible stderr, we should catch the
+ * proxy command's standard error to put in our event log. */
+ if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
+ Socket ret = new_error_socket
+ ("Unable to create pipes for proxy command", plug);
+ sfree(cmd);
+ return ret;
+ }
+ }
+
SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0);
+ if (us_from_cmd_err != NULL)
+ SetHandleInformation(us_from_cmd_err, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(si);
si.lpReserved = NULL;
si.lpReserved2 = NULL;
si.hStdInput = cmd_from_us;
si.hStdOutput = cmd_to_us;
- si.hStdError = NULL;
+ si.hStdError = cmd_err_to_us;
CreateProcess(NULL, cmd, NULL, NULL, TRUE,
CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
CloseHandle(cmd_from_us);
CloseHandle(cmd_to_us);
- return make_handle_socket(us_to_cmd, us_from_cmd, plug, FALSE);
+ if (cmd_err_to_us != NULL)
+ CloseHandle(cmd_err_to_us);
+
+ return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err,
+ plug, FALSE);
}