mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
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'). [originally from svn r3164]
This commit is contained in:
parent
5e36ac8d67
commit
03fa61025b
6
Recipe
6
Recipe
@ -118,11 +118,11 @@ SFTP = sftp int64 logging
|
|||||||
# Miscellaneous objects appearing in all the network utilities (not
|
# Miscellaneous objects appearing in all the network utilities (not
|
||||||
# Pageant or PuTTYgen).
|
# Pageant or PuTTYgen).
|
||||||
WINMISC = misc version winstore settings tree234 winnet proxy cmdline
|
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 = misc version uxstore settings tree234 uxsel uxnet proxy cmdline
|
||||||
+ uxmisc
|
+ uxmisc uxproxy
|
||||||
MACMISC = misc version macstore settings tree234 macnet mtcpnet otnet proxy
|
MACMISC = misc version macstore settings tree234 macnet mtcpnet otnet proxy
|
||||||
+ macmisc macabout
|
+ macmisc macabout pproxy
|
||||||
|
|
||||||
# Character set library, for use in pterm.
|
# Character set library, for use in pterm.
|
||||||
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
|
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
|
||||||
|
10
config.c
10
config.c
@ -1296,14 +1296,14 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
|
|||||||
"Options controlling proxy usage");
|
"Options controlling proxy usage");
|
||||||
|
|
||||||
s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics");
|
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),
|
HELPCTX(proxy_type),
|
||||||
dlg_stdradiobutton_handler,
|
dlg_stdradiobutton_handler,
|
||||||
I(offsetof(Config, proxy_type)),
|
I(offsetof(Config, proxy_type)),
|
||||||
"None", 'n', I(PROXY_NONE),
|
"None", I(PROXY_NONE),
|
||||||
"HTTP", 't', I(PROXY_HTTP),
|
"HTTP", I(PROXY_HTTP),
|
||||||
"SOCKS", 's', I(PROXY_SOCKS),
|
"SOCKS", I(PROXY_SOCKS),
|
||||||
"Telnet", 'l', I(PROXY_TELNET),
|
"Telnet", I(PROXY_TELNET),
|
||||||
NULL);
|
NULL);
|
||||||
ctrl_columns(s, 2, 80, 20);
|
ctrl_columns(s, 2, 80, 20);
|
||||||
c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
|
c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
|
||||||
|
17
pproxy.c
Normal file
17
pproxy.c
Normal file
@ -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;
|
||||||
|
}
|
299
proxy.c
299
proxy.c
@ -347,6 +347,11 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
|
|||||||
return sk_namelookup(host, 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,
|
Socket new_connection(SockAddr addr, char *hostname,
|
||||||
int port, int privport,
|
int port, int privport,
|
||||||
int oobinline, int nodelay, Plug plug,
|
int oobinline, int nodelay, Plug plug,
|
||||||
@ -378,6 +383,11 @@ Socket new_connection(SockAddr addr, char *hostname,
|
|||||||
Proxy_Plug pplug;
|
Proxy_Plug pplug;
|
||||||
SockAddr proxy_addr;
|
SockAddr proxy_addr;
|
||||||
char *proxy_canonical_name;
|
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 = snew(struct Socket_proxy_tag);
|
||||||
ret->fn = &socket_fn_table;
|
ret->fn = &socket_fn_table;
|
||||||
@ -1161,103 +1171,117 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
|
|||||||
* standardised or at all well-defined.)
|
* 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) {
|
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)
|
||||||
|
|
||||||
int so = 0, eo = 0;
|
/* we need to escape \\, \%, \r, \n, \t, \x??, \0???,
|
||||||
|
* %%, %host, %port, %user, and %pass
|
||||||
|
*/
|
||||||
|
|
||||||
/* we need to escape \\, \%, \r, \n, \t, \x??, \0???,
|
while (cfg->proxy_telnet_command[eo] != 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 (cfg->proxy_telnet_command[eo] != 0 &&
|
||||||
|
cfg->proxy_telnet_command[eo] != '%' &&
|
||||||
|
cfg->proxy_telnet_command[eo] != '\\') eo++;
|
||||||
|
|
||||||
/* scan forward until we hit end-of-line,
|
/* if we hit eol, break out of our escaping loop */
|
||||||
* or an escape character (\ or %) */
|
if (cfg->proxy_telnet_command[eo] == 0) break;
|
||||||
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 there was any unescaped text before the escape
|
||||||
if (p->cfg.proxy_telnet_command[eo] == 0) break;
|
* character, send that now */
|
||||||
|
if (eo != so) {
|
||||||
|
ENSURE(eo - so);
|
||||||
|
memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);
|
||||||
|
retlen += eo - so;
|
||||||
|
}
|
||||||
|
|
||||||
/* if there was any unescaped text before the escape
|
so = eo++;
|
||||||
* 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 (cfg->proxy_telnet_command[eo] == 0) break;
|
||||||
|
|
||||||
/* if the escape character was the last character of
|
if (cfg->proxy_telnet_command[so] == '\\') {
|
||||||
* the line, we'll just stop and send it. */
|
|
||||||
if (p->cfg.proxy_telnet_command[eo] == 0) break;
|
|
||||||
|
|
||||||
if (p->cfg.proxy_telnet_command[so] == '\\') {
|
/* we recognize \\, \%, \r, \n, \t, \x??.
|
||||||
|
* anything else, we just send unescaped (including the \).
|
||||||
|
*/
|
||||||
|
|
||||||
/* we recognize \\, \%, \r, \n, \t, \x??.
|
switch (cfg->proxy_telnet_command[eo]) {
|
||||||
* anything else, we just send unescaped (including the \).
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (p->cfg.proxy_telnet_command[eo]) {
|
case '\\':
|
||||||
|
ENSURE(1);
|
||||||
|
ret[retlen++] = '\\';
|
||||||
|
eo++;
|
||||||
|
break;
|
||||||
|
|
||||||
case '\\':
|
case '%':
|
||||||
sk_write(p->sub_socket, "\\", 1);
|
ENSURE(1);
|
||||||
eo++;
|
ret[retlen++] = '%';
|
||||||
break;
|
eo++;
|
||||||
|
break;
|
||||||
|
|
||||||
case '%':
|
case 'r':
|
||||||
sk_write(p->sub_socket, "%%", 1);
|
ENSURE(1);
|
||||||
eo++;
|
ret[retlen++] = '\r';
|
||||||
break;
|
eo++;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'n':
|
||||||
sk_write(p->sub_socket, "\r", 1);
|
ENSURE(1);
|
||||||
eo++;
|
ret[retlen++] = '\n';
|
||||||
break;
|
eo++;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 't':
|
||||||
sk_write(p->sub_socket, "\n", 1);
|
ENSURE(1);
|
||||||
eo++;
|
ret[retlen++] = '\t';
|
||||||
break;
|
eo++;
|
||||||
|
break;
|
||||||
|
|
||||||
case 't':
|
case 'x':
|
||||||
sk_write(p->sub_socket, "\t", 1);
|
case 'X':
|
||||||
eo++;
|
{
|
||||||
break;
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
{
|
|
||||||
/* escaped hexadecimal value (ie. \xff) */
|
/* escaped hexadecimal value (ie. \xff) */
|
||||||
unsigned char v = 0;
|
unsigned char v = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
eo++;
|
eo++;
|
||||||
if (p->cfg.proxy_telnet_command[eo] >= '0' &&
|
if (cfg->proxy_telnet_command[eo] >= '0' &&
|
||||||
p->cfg.proxy_telnet_command[eo] <= '9')
|
cfg->proxy_telnet_command[eo] <= '9')
|
||||||
v += p->cfg.proxy_telnet_command[eo] - '0';
|
v += cfg->proxy_telnet_command[eo] - '0';
|
||||||
else if (p->cfg.proxy_telnet_command[eo] >= 'a' &&
|
else if (cfg->proxy_telnet_command[eo] >= 'a' &&
|
||||||
p->cfg.proxy_telnet_command[eo] <= 'f')
|
cfg->proxy_telnet_command[eo] <= 'f')
|
||||||
v += p->cfg.proxy_telnet_command[eo] - 'a' + 10;
|
v += cfg->proxy_telnet_command[eo] - 'a' + 10;
|
||||||
else if (p->cfg.proxy_telnet_command[eo] >= 'A' &&
|
else if (cfg->proxy_telnet_command[eo] >= 'A' &&
|
||||||
p->cfg.proxy_telnet_command[eo] <= 'F')
|
cfg->proxy_telnet_command[eo] <= 'F')
|
||||||
v += p->cfg.proxy_telnet_command[eo] - 'A' + 10;
|
v += cfg->proxy_telnet_command[eo] - 'A' + 10;
|
||||||
else {
|
else {
|
||||||
/* non hex character, so we abort and just
|
/* non hex character, so we abort and just
|
||||||
* send the whole thing unescaped (including \x)
|
* send the whole thing unescaped (including \x)
|
||||||
*/
|
*/
|
||||||
sk_write(p->sub_socket, "\\", 1);
|
ENSURE(1);
|
||||||
|
ret[retlen++] = '\\';
|
||||||
eo = so + 1;
|
eo = so + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we only extract two hex characters */
|
/* we only extract two hex characters */
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
sk_write(p->sub_socket, (char *)&v, 1);
|
ENSURE(1);
|
||||||
|
ret[retlen++] = v;
|
||||||
eo++;
|
eo++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1265,68 +1289,101 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
|
|||||||
i++;
|
i++;
|
||||||
v <<= 4;
|
v <<= 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
sk_write(p->sub_socket,
|
ENSURE(2);
|
||||||
p->cfg.proxy_telnet_command + so, 2);
|
memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2);
|
||||||
eo++;
|
retlen += 2;
|
||||||
break;
|
eo++;
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
|
|
||||||
/* % escape. we recognize %%, %host, %port, %user, %pass.
|
|
||||||
* anything else, we just send unescaped (including the %).
|
|
||||||
*/
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
/* resume scanning for additional escapes after this one. */
|
/* % escape. we recognize %%, %host, %port, %user, %pass.
|
||||||
so = eo;
|
* anything else, we just send unescaped (including the %).
|
||||||
|
*/
|
||||||
|
|
||||||
|
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++] = '%';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if there is any unescaped text at the end of the line, send it */
|
/* resume scanning for additional escapes after this one. */
|
||||||
if (eo != so) {
|
so = eo;
|
||||||
sk_write(p->sub_socket, p->cfg.proxy_telnet_command + so, eo - so);
|
}
|
||||||
}
|
|
||||||
|
/* 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;
|
p->state = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
6
proxy.h
6
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_socks4_negotiate (Proxy_Socket, int);
|
||||||
extern int proxy_socks5_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
|
#endif
|
||||||
|
2
putty.h
2
putty.h
@ -224,7 +224,7 @@ enum {
|
|||||||
/*
|
/*
|
||||||
* Proxy types.
|
* Proxy types.
|
||||||
*/
|
*/
|
||||||
PROXY_NONE, PROXY_HTTP, PROXY_SOCKS, PROXY_TELNET
|
PROXY_NONE, PROXY_HTTP, PROXY_SOCKS, PROXY_TELNET, PROXY_CMD
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
44
unix/uxcfg.c
44
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,
|
ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20,
|
||||||
HELPCTX(no_help), dlg_stdeditbox_handler,
|
HELPCTX(no_help), dlg_stdeditbox_handler,
|
||||||
I(offsetof(Config,shadowboldoffset)), I(-1));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
303
unix/uxproxy.c
Normal file
303
unix/uxproxy.c
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
* uxproxy.c: Unix implementation of platform_new_connection(),
|
||||||
|
* supporting an OpenSSH-like proxy command.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user