From 0f0a25075f6e788462506686cf538bc13a5beedb Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 6 May 2003 19:52:31 +0000 Subject: [PATCH] Support, on Unix only (so far), for OpenSSH-style generic proxying (running a local command in a pair of pipes and proxying through that, for example `ssh proxyhost nc -q0 %host %port'). git-svn-id: svn://svn.tartarus.org/sgt/putty@3164 cda61777-01e9-0310-a592-d414129be87e --- Recipe | 6 +- config.c | 10 +- pproxy.c | 17 ++++ proxy.c | 311 ++++++++++++++++++++++++++++++++++----------------------- proxy.h | 6 ++ putty.h | 2 +- unix/uxcfg.c | 44 ++++++++ unix/uxproxy.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 563 insertions(+), 136 deletions(-) create mode 100644 pproxy.c create mode 100644 unix/uxproxy.c diff --git a/Recipe b/Recipe index 5d6f740f..255c4cb1 100644 --- a/Recipe +++ b/Recipe @@ -118,11 +118,11 @@ SFTP = sftp int64 logging # Miscellaneous objects appearing in all the network utilities (not # Pageant or PuTTYgen). WINMISC = misc version winstore settings tree234 winnet proxy cmdline - + windefs winmisc + + windefs winmisc pproxy UXMISC = misc version uxstore settings tree234 uxsel uxnet proxy cmdline - + uxmisc + + uxmisc uxproxy MACMISC = misc version macstore settings tree234 macnet mtcpnet otnet proxy - + macmisc macabout + + macmisc macabout pproxy # Character set library, for use in pterm. CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc diff --git a/config.c b/config.c index fd5cb958..e462ec25 100644 --- a/config.c +++ b/config.c @@ -1296,14 +1296,14 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist, "Options controlling proxy usage"); s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics"); - ctrl_radiobuttons(s, "Proxy type:", NO_SHORTCUT, 4, + ctrl_radiobuttons(s, "Proxy type:", 't', 4, HELPCTX(proxy_type), dlg_stdradiobutton_handler, I(offsetof(Config, proxy_type)), - "None", 'n', I(PROXY_NONE), - "HTTP", 't', I(PROXY_HTTP), - "SOCKS", 's', I(PROXY_SOCKS), - "Telnet", 'l', I(PROXY_TELNET), + "None", I(PROXY_NONE), + "HTTP", I(PROXY_HTTP), + "SOCKS", I(PROXY_SOCKS), + "Telnet", I(PROXY_TELNET), NULL); ctrl_columns(s, 2, 80, 20); c = ctrl_editbox(s, "Proxy hostname", 'y', 100, diff --git a/pproxy.c b/pproxy.c new file mode 100644 index 00000000..6a870ed1 --- /dev/null +++ b/pproxy.c @@ -0,0 +1,17 @@ +/* + * pproxy.c: dummy implementation of platform_new_connection(), to + * be supplanted on any platform which has its own local proxy + * method. + */ + +#include "putty.h" +#include "network.h" +#include "proxy.h" + +Socket new_connection(SockAddr addr, char *hostname, + int port, int privport, + int oobinline, int nodelay, Plug plug, + const Config *cfg) +{ + return NULL; +} diff --git a/proxy.c b/proxy.c index a15b3ba1..59f2abb7 100644 --- a/proxy.c +++ b/proxy.c @@ -347,6 +347,11 @@ SockAddr name_lookup(char *host, int port, char **canonicalname, return sk_namelookup(host, canonicalname); } +Socket platform_new_connection(SockAddr addr, char *hostname, + int port, int privport, + int oobinline, int nodelay, Plug plug, + const Config *cfg); + Socket new_connection(SockAddr addr, char *hostname, int port, int privport, int oobinline, int nodelay, Plug plug, @@ -378,6 +383,11 @@ Socket new_connection(SockAddr addr, char *hostname, Proxy_Plug pplug; SockAddr proxy_addr; char *proxy_canonical_name; + Socket sret; + + if ( (sret = platform_new_connection(addr, hostname, port, privport, + oobinline, nodelay, plug, cfg)) ) + return sret; ret = snew(struct Socket_proxy_tag); ret->fn = &socket_fn_table; @@ -1161,103 +1171,117 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) * standardised or at all well-defined.) */ -int proxy_telnet_negotiate (Proxy_Socket p, int change) +char *format_telnet_command(SockAddr addr, int port, const Config *cfg) { - if (p->state == PROXY_CHANGE_NEW) { - - int so = 0, eo = 0; - - /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, - * %%, %host, %port, %user, and %pass - */ - - while (p->cfg.proxy_telnet_command[eo] != 0) { - - /* scan forward until we hit end-of-line, - * or an escape character (\ or %) */ - while (p->cfg.proxy_telnet_command[eo] != 0 && - p->cfg.proxy_telnet_command[eo] != '%' && - p->cfg.proxy_telnet_command[eo] != '\\') eo++; - - /* if we hit eol, break out of our escaping loop */ - if (p->cfg.proxy_telnet_command[eo] == 0) break; - - /* if there was any unescaped text before the escape - * character, send that now */ - if (eo != so) { - sk_write(p->sub_socket, - p->cfg.proxy_telnet_command + so, eo - so); - } - - so = eo++; - - /* if the escape character was the last character of - * the line, we'll just stop and send it. */ - if (p->cfg.proxy_telnet_command[eo] == 0) break; + char *ret = NULL; + int retlen = 0, retsize = 0; + int so = 0, eo = 0; +#define ENSURE(n) do { \ + if (retsize < retlen + n) { \ + retsize = retlen + n + 512; \ + ret = sresize(ret, retsize, char); \ + } \ +} while (0) + + /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, + * %%, %host, %port, %user, and %pass + */ - if (p->cfg.proxy_telnet_command[so] == '\\') { + while (cfg->proxy_telnet_command[eo] != 0) { - /* we recognize \\, \%, \r, \n, \t, \x??. - * anything else, we just send unescaped (including the \). - */ + /* scan forward until we hit end-of-line, + * or an escape character (\ or %) */ + while (cfg->proxy_telnet_command[eo] != 0 && + cfg->proxy_telnet_command[eo] != '%' && + cfg->proxy_telnet_command[eo] != '\\') eo++; - switch (p->cfg.proxy_telnet_command[eo]) { + /* if we hit eol, break out of our escaping loop */ + if (cfg->proxy_telnet_command[eo] == 0) break; - case '\\': - sk_write(p->sub_socket, "\\", 1); - eo++; - break; + /* if there was any unescaped text before the escape + * character, send that now */ + if (eo != so) { + ENSURE(eo - so); + memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + retlen += eo - so; + } - case '%': - sk_write(p->sub_socket, "%%", 1); - eo++; - break; + so = eo++; - case 'r': - sk_write(p->sub_socket, "\r", 1); - eo++; - break; + /* if the escape character was the last character of + * the line, we'll just stop and send it. */ + if (cfg->proxy_telnet_command[eo] == 0) break; - case 'n': - sk_write(p->sub_socket, "\n", 1); - eo++; - break; + if (cfg->proxy_telnet_command[so] == '\\') { - case 't': - sk_write(p->sub_socket, "\t", 1); - eo++; - break; + /* we recognize \\, \%, \r, \n, \t, \x??. + * anything else, we just send unescaped (including the \). + */ - case 'x': - case 'X': - { + switch (cfg->proxy_telnet_command[eo]) { + + case '\\': + ENSURE(1); + ret[retlen++] = '\\'; + eo++; + break; + + case '%': + ENSURE(1); + ret[retlen++] = '%'; + eo++; + break; + + case 'r': + ENSURE(1); + ret[retlen++] = '\r'; + eo++; + break; + + case 'n': + ENSURE(1); + ret[retlen++] = '\n'; + eo++; + break; + + case 't': + ENSURE(1); + ret[retlen++] = '\t'; + eo++; + break; + + case 'x': + case 'X': + { /* escaped hexadecimal value (ie. \xff) */ unsigned char v = 0; int i = 0; for (;;) { eo++; - if (p->cfg.proxy_telnet_command[eo] >= '0' && - p->cfg.proxy_telnet_command[eo] <= '9') - v += p->cfg.proxy_telnet_command[eo] - '0'; - else if (p->cfg.proxy_telnet_command[eo] >= 'a' && - p->cfg.proxy_telnet_command[eo] <= 'f') - v += p->cfg.proxy_telnet_command[eo] - 'a' + 10; - else if (p->cfg.proxy_telnet_command[eo] >= 'A' && - p->cfg.proxy_telnet_command[eo] <= 'F') - v += p->cfg.proxy_telnet_command[eo] - 'A' + 10; + if (cfg->proxy_telnet_command[eo] >= '0' && + cfg->proxy_telnet_command[eo] <= '9') + v += cfg->proxy_telnet_command[eo] - '0'; + else if (cfg->proxy_telnet_command[eo] >= 'a' && + cfg->proxy_telnet_command[eo] <= 'f') + v += cfg->proxy_telnet_command[eo] - 'a' + 10; + else if (cfg->proxy_telnet_command[eo] >= 'A' && + cfg->proxy_telnet_command[eo] <= 'F') + v += cfg->proxy_telnet_command[eo] - 'A' + 10; else { /* non hex character, so we abort and just * send the whole thing unescaped (including \x) */ - sk_write(p->sub_socket, "\\", 1); + ENSURE(1); + ret[retlen++] = '\\'; eo = so + 1; break; } /* we only extract two hex characters */ if (i == 1) { - sk_write(p->sub_socket, (char *)&v, 1); + ENSURE(1); + ret[retlen++] = v; eo++; break; } @@ -1265,69 +1289,102 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change) i++; v <<= 4; } - } - break; - - default: - sk_write(p->sub_socket, - p->cfg.proxy_telnet_command + so, 2); - eo++; - break; } - } else { - - /* % escape. we recognize %%, %host, %port, %user, %pass. - * anything else, we just send unescaped (including the %). - */ + break; - if (p->cfg.proxy_telnet_command[eo] == '%') { - sk_write(p->sub_socket, "%", 1); - eo++; - } - else if (strnicmp(p->cfg.proxy_telnet_command + eo, - "host", 4) == 0) { - char dest[512]; - sk_getaddr(p->remote_addr, dest, lenof(dest)); - sk_write(p->sub_socket, dest, strlen(dest)); - eo += 4; - } - else if (strnicmp(p->cfg.proxy_telnet_command + eo, - "port", 4) == 0) { - char port[8]; - sprintf(port, "%i", p->remote_port); - sk_write(p->sub_socket, port, strlen(port)); - eo += 4; - } - else if (strnicmp(p->cfg.proxy_telnet_command + eo, - "user", 4) == 0) { - sk_write(p->sub_socket, p->cfg.proxy_username, - strlen(p->cfg.proxy_username)); - eo += 4; - } - else if (strnicmp(p->cfg.proxy_telnet_command + eo, - "pass", 4) == 0) { - sk_write(p->sub_socket, p->cfg.proxy_password, - strlen(p->cfg.proxy_password)); - eo += 4; - } - else { - /* we don't escape this, so send the % now, and - * don't advance eo, so that we'll consider the - * text immediately following the % as unescaped. - */ - sk_write(p->sub_socket, "%", 1); - } + default: + ENSURE(2); + memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2); + retlen += 2; + eo++; + break; } + } else { - /* resume scanning for additional escapes after this one. */ - so = eo; - } + /* % escape. we recognize %%, %host, %port, %user, %pass. + * anything else, we just send unescaped (including the %). + */ - /* if there is any unescaped text at the end of the line, send it */ - if (eo != so) { - sk_write(p->sub_socket, p->cfg.proxy_telnet_command + so, eo - so); + if (cfg->proxy_telnet_command[eo] == '%') { + ENSURE(1); + ret[retlen++] = '%'; + eo++; + } + else if (strnicmp(cfg->proxy_telnet_command + eo, + "host", 4) == 0) { + char dest[512]; + int destlen; + sk_getaddr(addr, dest, lenof(dest)); + destlen = strlen(dest); + ENSURE(destlen); + memcpy(ret+retlen, dest, destlen); + retlen += destlen; + eo += 4; + } + else if (strnicmp(cfg->proxy_telnet_command + eo, + "port", 4) == 0) { + char portstr[8], portlen; + portlen = sprintf(portstr, "%i", port); + ENSURE(portlen); + memcpy(ret + retlen, portstr, portlen); + retlen += portlen; + eo += 4; + } + else if (strnicmp(cfg->proxy_telnet_command + eo, + "user", 4) == 0) { + int userlen = strlen(cfg->proxy_username); + ENSURE(userlen); + memcpy(ret+retlen, cfg->proxy_username, userlen); + retlen += userlen; + eo += 4; + } + else if (strnicmp(cfg->proxy_telnet_command + eo, + "pass", 4) == 0) { + int passlen = strlen(cfg->proxy_password); + ENSURE(passlen); + memcpy(ret+retlen, cfg->proxy_password, passlen); + retlen += passlen; + eo += 4; + } + else { + /* we don't escape this, so send the % now, and + * don't advance eo, so that we'll consider the + * text immediately following the % as unescaped. + */ + ENSURE(1); + ret[retlen++] = '%'; + } } + /* resume scanning for additional escapes after this one. */ + so = eo; + } + + /* if there is any unescaped text at the end of the line, send it */ + if (eo != so) { + ENSURE(eo - so); + memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so); + retlen += eo - so; + } + + ENSURE(1); + ret[retlen] = '\0'; + return ret; + +#undef ENSURE +} + +int proxy_telnet_negotiate (Proxy_Socket p, int change) +{ + if (p->state == PROXY_CHANGE_NEW) { + char *formatted_cmd; + + formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port, + &p->cfg); + + sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd)); + sfree(formatted_cmd); + p->state = 1; return 0; } diff --git a/proxy.h b/proxy.h index 2bd52203..d4dec6a1 100644 --- a/proxy.h +++ b/proxy.h @@ -100,4 +100,10 @@ extern int proxy_telnet_negotiate (Proxy_Socket, int); extern int proxy_socks4_negotiate (Proxy_Socket, int); extern int proxy_socks5_negotiate (Proxy_Socket, int); +/* + * This may be reused by local-command proxies on individual + * platforms. + */ +char *format_telnet_command(SockAddr addr, int port, const Config *cfg); + #endif diff --git a/putty.h b/putty.h index 93a10c72..1a02dbca 100644 --- a/putty.h +++ b/putty.h @@ -224,7 +224,7 @@ enum { /* * Proxy types. */ - PROXY_NONE, PROXY_HTTP, PROXY_SOCKS, PROXY_TELNET + PROXY_NONE, PROXY_HTTP, PROXY_SOCKS, PROXY_TELNET, PROXY_CMD }; enum { diff --git a/unix/uxcfg.c b/unix/uxcfg.c index 9b13348d..1bf58b79 100644 --- a/unix/uxcfg.c +++ b/unix/uxcfg.c @@ -125,4 +125,48 @@ void unix_setup_config_box(struct controlbox *b, int midsession, void *win) ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20, HELPCTX(no_help), dlg_stdeditbox_handler, I(offsetof(Config,shadowboldoffset)), I(-1)); + + /* + * Unix supports a local-command proxy. This also means we must + * adjust the text on the `Telnet command' control. + */ + s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics"); + { + int i; + for (i = 0; i < s->ncontrols; i++) { + c = s->ctrls[i]; + if (c->generic.type == CTRL_RADIO && + c->generic.context.i == offsetof(Config, proxy_type)) { + assert(c->generic.handler == dlg_stdradiobutton_handler); + c->radio.nbuttons++; + c->radio.ncolumns++; + c->radio.buttons = + sresize(c->radio.buttons, c->radio.nbuttons, char *); + c->radio.buttons[c->radio.nbuttons-1] = + dupstr("Local"); + c->radio.buttondata = + sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); + c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD); + break; + } + } + } + s = ctrl_getset(b, "Connection/Proxy", "misc", + "Miscellaneous proxy settings"); + { + int i; + for (i = 0; i < s->ncontrols; i++) { + c = s->ctrls[i]; + if (c->generic.type == CTRL_EDITBOX && + c->generic.context.i == + offsetof(Config, proxy_telnet_command)) { + assert(c->generic.handler == dlg_stdeditbox_handler); + sfree(c->generic.label); + c->generic.label = dupstr("Telnet command, or local" + " proxy command"); + break; + } + } + } + } diff --git a/unix/uxproxy.c b/unix/uxproxy.c new file mode 100644 index 00000000..cd256fdb --- /dev/null +++ b/unix/uxproxy.c @@ -0,0 +1,303 @@ +/* + * uxproxy.c: Unix implementation of platform_new_connection(), + * supporting an OpenSSH-like proxy command. + */ + +#include +#include +#include +#include +#include + +#define DEFINE_PLUG_METHOD_MACROS +#include "tree234.h" +#include "putty.h" +#include "network.h" +#include "proxy.h" + +typedef struct Socket_localproxy_tag * Local_Proxy_Socket; + +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 */ + + char *error; + + Plug plug; + + bufchain pending_output_data; + bufchain pending_input_data; + + void *privptr; +}; + +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 int localproxy_fromfd_cmp(void *av, void *bv) +{ + Local_Proxy_Socket a = (Local_Proxy_Socket)av; + Local_Proxy_Socket b = (Local_Proxy_Socket)bv; + if (a->from_cmd < b->from_cmd) + return -1; + if (a->from_cmd > b->from_cmd) + return +1; + return 0; +} +static int localproxy_fromfd_find(void *av, void *bv) +{ + int a = *(int *)av; + Local_Proxy_Socket b = (Local_Proxy_Socket)bv; + if (a < b->from_cmd) + return -1; + if (a > b->from_cmd) + return +1; + return 0; +} +static int localproxy_tofd_cmp(void *av, void *bv) +{ + Local_Proxy_Socket a = (Local_Proxy_Socket)av; + Local_Proxy_Socket b = (Local_Proxy_Socket)bv; + if (a->to_cmd < b->to_cmd) + return -1; + if (a->to_cmd > b->to_cmd) + return +1; + return 0; +} +static int localproxy_tofd_find(void *av, void *bv) +{ + int a = *(int *)av; + Local_Proxy_Socket b = (Local_Proxy_Socket)bv; + if (a < b->to_cmd) + return -1; + if (a > b->to_cmd) + return +1; + return 0; +} + +/* basic proxy socket functions */ + +static Plug sk_localproxy_plug (Socket s, Plug p) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + Plug ret = ps->plug; + if (p) + ps->plug = p; + return ret; +} + +static void sk_localproxy_close (Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + del234(localproxy_by_fromfd, ps); + del234(localproxy_by_tofd, ps); + + close(ps->to_cmd); + close(ps->from_cmd); + + sfree(ps); +} + +static int localproxy_try_send(Local_Proxy_Socket ps) +{ + int sent = 0; + + while (bufchain_size(&ps->pending_output_data) > 0) { + void *data; + int len, ret; + + bufchain_prefix(&ps->pending_output_data, &data, &len); + ret = write(ps->to_cmd, data, len); + if (ret < 0 && errno != EWOULDBLOCK) { + /* We're inside the Unix frontend here, so we know + * that the frontend handle is unnecessary. */ + logevent(NULL, strerror(errno)); + fatalbox("%s", strerror(errno)); + } else if (ret <= 0) { + break; + } else { + bufchain_consume(&ps->pending_output_data, ret); + sent += ret; + } + } + + if (bufchain_size(&ps->pending_output_data) == 0) + uxsel_del(ps->to_cmd); + else + uxsel_set(ps->to_cmd, 2, localproxy_select_result); + + return sent; +} + +static int sk_localproxy_write (Socket s, const char *data, int len) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + bufchain_add(&ps->pending_output_data, data, len); + + localproxy_try_send(ps); + + return bufchain_size(&ps->pending_output_data); +} + +static int sk_localproxy_write_oob (Socket s, const char *data, int len) +{ + /* + * oob data is treated as inband; nasty, but nothing really + * better we can do + */ + 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) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + ps->privptr = ptr; +} + +static void * sk_localproxy_get_private_ptr (Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + return ps->privptr; +} + +static void sk_localproxy_set_frozen (Socket s, int is_frozen) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + + if (is_frozen) + uxsel_del(ps->from_cmd); + else + uxsel_set(ps->from_cmd, 1, localproxy_select_result); +} + +static const char * sk_localproxy_socket_error (Socket s) +{ + Local_Proxy_Socket ps = (Local_Proxy_Socket) s; + return ps->error; +} + +static int localproxy_select_result(int fd, int event) +{ + Local_Proxy_Socket s; + char buf[20480]; + int ret; + + if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_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); + } + } else if (event == 2) { + assert(fd == s->to_cmd); + if (localproxy_try_send(s)) + plug_sent(s->plug, bufchain_size(&s->pending_output_data)); + return 1; + } + + return 1; +} + +Socket platform_new_connection(SockAddr addr, char *hostname, + int port, int privport, + int oobinline, int nodelay, Plug plug, + const Config *cfg) +{ + char *cmd; + + static const struct socket_function_table socket_fn_table = { + sk_localproxy_plug, + sk_localproxy_close, + sk_localproxy_write, + sk_localproxy_write_oob, + sk_localproxy_flush, + sk_localproxy_set_private_ptr, + sk_localproxy_get_private_ptr, + sk_localproxy_set_frozen, + sk_localproxy_socket_error + }; + + Local_Proxy_Socket ret; + int to_cmd_pipe[2], from_cmd_pipe[2], pid; + + if (cfg->proxy_type != PROXY_CMD) + 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; + + bufchain_init(&ret->pending_input_data); + bufchain_init(&ret->pending_output_data); + + /* + * 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); + } + + 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); + + add234(localproxy_by_fromfd, ret); + add234(localproxy_by_tofd, ret); + + uxsel_set(ret->from_cmd, 1, localproxy_select_result); + + return (Socket) ret; +} -- 2.11.0