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;
-
- void *privptr;
+ bufchain pending_error_data;
+ enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
};
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;
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 */
{
Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
- del234(localproxy_by_fromfd, ps);
- del234(localproxy_by_tofd, ps);
+ if (ps->to_cmd >= 0) {
+ del234(localproxy_by_tofd, ps);
+ uxsel_del(ps->to_cmd);
+ close(ps->to_cmd);
+ }
- uxsel_del(ps->to_cmd);
+ del234(localproxy_by_fromfd, ps);
uxsel_del(ps->from_cmd);
- close(ps->to_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);
}
}
}
+ if (ps->outgoingeof == EOF_PENDING) {
+ del234(localproxy_by_tofd, ps);
+ close(ps->to_cmd);
+ uxsel_del(ps->to_cmd);
+ ps->to_cmd = -1;
+ ps->outgoingeof = EOF_SENT;
+ }
+
if (bufchain_size(&ps->pending_output_data) == 0)
uxsel_del(ps->to_cmd);
else
{
Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
+ assert(ps->outgoingeof == EOF_NO);
+
bufchain_add(&ps->pending_output_data, data, len);
localproxy_try_send(ps);
return sk_localproxy_write(s, data, len);
}
-static void sk_localproxy_flush (Socket s)
-{
- /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
- /* do nothing */
-}
-
-static void sk_localproxy_set_private_ptr (Socket s, void *ptr)
+static void sk_localproxy_write_eof (Socket s)
{
Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
- ps->privptr = ptr;
+
+ assert(ps->outgoingeof == EOF_NO);
+ ps->outgoingeof = EOF_PENDING;
+
+ localproxy_try_send(ps);
}
-static void * sk_localproxy_get_private_ptr (Socket s)
+static void sk_localproxy_flush (Socket s)
{
- Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
- return ps->privptr;
+ /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
+ /* do nothing */
}
static void sk_localproxy_set_frozen (Socket s, int is_frozen)
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, 1, 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))
return 1;
}
-Socket platform_new_connection(SockAddr addr, char *hostname,
+Socket platform_new_connection(SockAddr addr, const char *hostname,
int port, int privport,
int oobinline, int nodelay, int keepalive,
- Plug plug, const Config *cfg)
+ Plug plug, Conf *conf)
{
char *cmd;
sk_localproxy_close,
sk_localproxy_write,
sk_localproxy_write_oob,
+ sk_localproxy_write_eof,
sk_localproxy_flush,
- sk_localproxy_set_private_ptr,
- sk_localproxy_get_private_ptr,
sk_localproxy_set_frozen,
- sk_localproxy_socket_error
+ sk_localproxy_socket_error,
+ NULL, /* peer_info */
};
Local_Proxy_Socket ret;
- int to_cmd_pipe[2], from_cmd_pipe[2], pid;
+ int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
- if (cfg->proxy_type != PROXY_CMD)
+ proxytype = conf_get_int(conf, CONF_proxy_type);
+ if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
return NULL;
- cmd = format_telnet_command(addr, port, cfg);
-
ret = snew(struct Socket_localproxy_tag);
ret->fn = &socket_fn_table;
ret->plug = plug;
ret->error = NULL;
+ ret->outgoingeof = EOF_NO;
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);
+ sfree(logmsg);
+ }
+
+ /*
+ * Create the pipes to the proxy command, and spawn the proxy
+ * command process.
+ */
+ if (pipe(to_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();
+
+ if (pid < 0) {
+ ret->error = dupprintf("fork: %s", strerror(errno));
+ sfree(cmd);
+ return (Socket)ret;
+ } else if (pid == 0) {
+ close(0);
+ close(1);
+ dup2(to_cmd_pipe[0], 0);
+ 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);
+ _exit(255);
+ }
- /*
- * Create the pipes to the proxy command, and spawn the proxy
- * command process.
- */
- if (pipe(to_cmd_pipe) < 0 ||
- pipe(from_cmd_pipe) < 0) {
- ret->error = dupprintf("pipe: %s", strerror(errno));
- return (Socket)ret;
- }
-
- pid = fork();
-
- if (pid < 0) {
- ret->error = dupprintf("fork: %s", strerror(errno));
- return (Socket)ret;
- } else if (pid == 0) {
- int i;
- close(0);
- close(1);
- dup2(to_cmd_pipe[0], 0);
- dup2(from_cmd_pipe[1], 1);
- for (i = 3; i < 127; i++)
- close(i);
- fcntl(0, F_SETFD, 0);
- fcntl(1, F_SETFD, 0);
- execl("/bin/sh", "sh", "-c", cmd, NULL);
- _exit(255);
+ sfree(cmd);
+
+ 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);
+ if (ret->to_cmd == -1) {
+ ret->error = dupprintf("/dev/null: %s", strerror(errno));
+ sfree(cmd);
+ return (Socket)ret;
+ }
+ ret->from_cmd = open(cmd, O_RDONLY);
+ if (ret->from_cmd == -1) {
+ ret->error = dupprintf("%s: %s", cmd, strerror(errno));
+ sfree(cmd);
+ return (Socket)ret;
+ }
+ sfree(cmd);
+ ret->cmd_err = -1;
}
- close(to_cmd_pipe[0]);
- close(from_cmd_pipe[1]);
-
- ret->to_cmd = to_cmd_pipe[1];
- ret->from_cmd = from_cmd_pipe[0];
-
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);
+ if (ret->cmd_err >= 0)
+ 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);