1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Updates to proxy support, both from me and from Justin Bradford.

Removed unnecessary spin locks, added a few comments, added support
for Telnet-type proxies, and wrote some documentation.

[originally from svn r1607]
This commit is contained in:
Simon Tatham 2002-03-27 21:09:16 +00:00
parent edd1ab5701
commit ecd496a621
9 changed files with 308 additions and 69 deletions

View File

@ -2,7 +2,7 @@ PuTTY is copyright 1997-2001 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
and CORE SDI S.A.
Justin Bradford, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.29 2002/03/09 17:59:15 simon Exp $
\versionid $Id: config.but,v 1.30 2002/03/27 21:09:16 simon Exp $
\C{config} Configuring PuTTY
@ -1357,6 +1357,96 @@ types of server.
The Nagle algorithm is disabled by default.
\H{config-proxy} The Proxy panel
The Proxy panel allows you to configure PuTTY to use various types
of proxy in order to make its network connections. The settings in
this panel affect the primary network connection forming your PuTTY
session, but also any extra connections made as a result of SSH port
forwarding (see \k{using-port-forwarding}).
\S{config-proxy-type} Setting the proxy type
The \q{Proxy type} radio buttons allow you to configure what type of
proxy you want PuTTY to use for its network connections. The default
setting is \q{None}; in this mode no proxy is used for any
connection.
\b Selecting \q{HTTP} allows you to proxy your connections through a
web server supporting the HTTP \cw{CONNECT} command, as documented
in \W{http://www.ietf.org/rfc/rfc2817.txt}{RFC 2817}.
\b Selecting \q{SOCKS} allows you to proxy your connections through
a SOCKS server.
\b Many firewalls implement a less formal type of proxy in which a
user can make a Telnet connection directly to the firewall machine
and enter a command such as \c{connect myhost.com 22} to connect
through to an external host. Selecting \q{Telnet} allows you to tell
PuTTY to use this type of proxy.
Note [FIXME] that SOCKS is not yet supported, although it should be
by the time we make our next release.
\S{config-proxy-exclude} Excluding parts of the network from proxying
Typically you will only need to use a proxy to connect to non-local
parts of your network; for example, your proxy might be required for
connections outside your company's internal network. In the
\q{Exclude Hosts/IPs} box you can enter ranges of IP addresses, or
ranges of DNS names, for which PuTTY will avoid using the proxy and
make a direct connection instead.
The \q{Exclude Hosts/IPs} box may contain more than one exclusion
range, separated by commas. Each range can be an IP address or a DNS
name, with a \c{*} character allowing wildcards. For example:
\c *.example.com
This excludes any host with a name ending in \c{.example.com} from
proxying.
\c 192.168.88.*
This excludes any host with an IP address starting with 192.168.88
from proxying.
\c 192.168.88.*,*.example.com
This excludes both of the above ranges at once.
\S{config-proxy-auth} Username and password
If your proxy requires authentication, you can enter a username and
a password in the \q{Username} and \q{Password} boxes.
Currently these boxes have no effect ( [FIXME] presumably they're
for SOCKS only).
\S{config-proxy-command} Specifying the Telnet proxy command
If you are using the Telnet proxy type, the usual command required
by the firewall's Telnet server is \c{connect}, followed by a host
name and a port number. If your proxy needs a different command,
you can enter an alternative here.
In this string, you can use \c{\\n} to represent a new-line, \c{\\r}
to represent a carriage return, \c{\\t} to represent a tab
character, and \c{\\x} followed by two hex digits to represent any
other character. \c{\\\\} is used to encode the \c{\\} character
itself.
Also, the special strings \c{%host} and \c{%port} will be replaced
by the host name and port number you want to connect to. To get a
literal \c{%} sign, enter \c{%%}.
\S{config-proxy-socksver} Selecting the version of the SOCKS protocol
SOCKS servers exist in two versions: version 5
(\W{http://www.ietf.org/rfc/rfc1928.txt}{RFC 1928}) and the earlier
version 4. The \q{SOCKS Version} radio buttons allow you to select
which one to use, if you have selected the SOCKS proxy type.
\H{config-telnet} The Telnet panel
The Telnet panel allows you to configure options that only apply to

View File

@ -1,4 +1,4 @@
\versionid $Id: licence.but,v 1.3 2002/02/24 15:17:10 simon Exp $
\versionid $Id: licence.but,v 1.4 2002/03/27 21:09:16 simon Exp $
\A{licence} PuTTY Licence
@ -6,7 +6,7 @@ PuTTY is copyright 1997-2002 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
and CORE SDI S.A.
Justin Bradford, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files

View File

@ -66,7 +66,7 @@ BEGIN
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
LTEXT "Barry, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Barry, Justin Bradford, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Permission is hereby granted, free of charge, to any person", 1004, 10, 58, 206, 8
LTEXT "obtaining a copy of this software and associated documentation", 1005, 10, 66, 206, 8

253
proxy.c
View File

@ -21,21 +21,12 @@ void proxy_activate (Proxy_Socket p)
void *data;
int len;
p->lock_close =
p->lock_write =
p->lock_write_oob =
p->lock_receive =
p->lock_flush =
p->lock_closing =
p->lock_sent =
p->lock_accepting =
p->lock_freeze = 1;
p->state = PROXY_STATE_ACTIVE;
/* let's try to keep extra receive events from coming through */
sk_set_frozen(p->sub_socket, 1);
/* send buffered OOB writes */
while (bufchain_size(&p->pending_oob_output_data) > 0) {
bufchain_prefix(&p->pending_oob_output_data, &data, &len);
sk_write_oob(p->sub_socket, data, len);
@ -43,6 +34,7 @@ void proxy_activate (Proxy_Socket p)
}
bufchain_clear(&p->pending_oob_output_data);
/* send buffered normal writes */
while (bufchain_size(&p->pending_output_data) > 0) {
bufchain_prefix(&p->pending_output_data, &data, &len);
sk_write(p->sub_socket, data, len);
@ -50,28 +42,21 @@ void proxy_activate (Proxy_Socket p)
}
bufchain_clear(&p->pending_output_data);
p->lock_write_oob = 0;
p->lock_write = 0;
/* if we were asked to flush the output during
* the proxy negotiation process, do so now.
*/
if (p->pending_flush) sk_flush(p->sub_socket);
p->lock_flush = 0;
/* forward buffered recv data to the backend */
while (bufchain_size(&p->pending_input_data) > 0) {
bufchain_prefix(&p->pending_input_data, &data, &len);
plug_receive(p->plug, 0, data, len);
bufchain_consume(&p->pending_input_data, len);
}
bufchain_clear(&p->pending_input_data);
p->lock_receive = 0;
/* now set the underlying socket to whatever freeze state they wanted */
sk_set_frozen(p->sub_socket, p->freeze);
p->lock_freeze = 0;
p->lock_sent = 0;
p->lock_accepting = 0;
p->lock_closing = 0;
p->lock_close = 0;
}
/* basic proxy socket functions */
@ -89,7 +74,6 @@ static void sk_proxy_close (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_close) ;
sk_close(ps->sub_socket);
sfree(ps);
}
@ -98,7 +82,6 @@ static int sk_proxy_write (Socket s, char *data, int len)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_write) ;
if (ps->state != PROXY_STATE_ACTIVE) {
bufchain_add(&ps->pending_output_data, data, len);
return bufchain_size(&ps->pending_output_data);
@ -110,7 +93,6 @@ static int sk_proxy_write_oob (Socket s, char *data, int len)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_write_oob) ;
if (ps->state != PROXY_STATE_ACTIVE) {
bufchain_clear(&ps->pending_output_data);
bufchain_clear(&ps->pending_oob_output_data);
@ -124,7 +106,6 @@ static void sk_proxy_flush (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_flush) ;
if (ps->state != PROXY_STATE_ACTIVE) {
ps->pending_flush = 1;
return;
@ -148,7 +129,6 @@ static void sk_proxy_set_frozen (Socket s, int is_frozen)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_freeze) ;
if (ps->state != PROXY_STATE_ACTIVE) {
ps->freeze = is_frozen;
return;
@ -173,7 +153,6 @@ static int plug_proxy_closing (Plug p, char *error_msg,
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
while (ps->lock_closing) ;
if (ps->state != PROXY_STATE_ACTIVE) {
ps->closing_error_msg = error_msg;
ps->closing_error_code = error_code;
@ -189,7 +168,6 @@ static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
while (ps->lock_receive) ;
if (ps->state != PROXY_STATE_ACTIVE) {
/* we will lose the urgentness of this data, but since most,
* if not all, of this data will be consumed by the negotiation
@ -209,7 +187,6 @@ static void plug_proxy_sent (Plug p, int bufsize)
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
while (ps->lock_sent) ;
if (ps->state != PROXY_STATE_ACTIVE) {
ps->sent_bufsize = bufsize;
ps->negotiate(ps, PROXY_CHANGE_SENT);
@ -223,7 +200,6 @@ static int plug_proxy_accepting (Plug p, void *sock)
Proxy_Plug pp = (Proxy_Plug) p;
Proxy_Socket ps = pp->proxy_socket;
while (ps->lock_accepting) ;
if (ps->state != PROXY_STATE_ACTIVE) {
ps->accepting_sock = sock;
return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);
@ -340,15 +316,6 @@ Socket new_connection(SockAddr addr, char *hostname,
bufchain_init(&ret->pending_output_data);
bufchain_init(&ret->pending_oob_output_data);
ret->lock_close =
ret->lock_write =
ret->lock_write_oob =
ret->lock_receive =
ret->lock_flush =
ret->lock_closing =
ret->lock_sent =
ret->lock_accepting = 0;
ret->sub_socket = NULL;
ret->state = PROXY_STATE_NEW;
@ -451,9 +418,9 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
* for this proxy method, it's just a simple HTTP
* request
*/
char buf[1024], dest[21];
char buf[256], dest[64];
sk_getaddr(p->remote_addr, dest, 20);
sk_getaddr(p->remote_addr, dest, 64);
sprintf(buf, "CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n\r\n",
dest, p->remote_port, dest, p->remote_port);
@ -579,7 +546,7 @@ int proxy_socks_negotiate (Proxy_Socket p, int change)
}
/* ----------------------------------------------------------------------
* `Telnet' proxy type (as yet unimplemented).
* `Telnet' proxy type.
*
* (This is for ad-hoc proxies where you connect to the proxy's
* telnet port and send a command such as `connect host port'. The
@ -589,6 +556,206 @@ int proxy_socks_negotiate (Proxy_Socket p, int change)
int proxy_telnet_negotiate (Proxy_Socket p, int change)
{
p->error = "Network error: Telnet proxy implementation is incomplete";
if (p->state == PROXY_CHANGE_NEW) {
int so = 0, eo = 0;
/* we need to escape \\, \%, \r, \n, \t, \x??, \0???,
* %%, %host, and %port
*/
while (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++;
/* if we hit eol, break out of our escaping loop */
if (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,
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 (cfg.proxy_telnet_command[so] == '\\') {
/* we recognize \\, \%, \r, \n, \t, \x??.
* anything else, we just send unescaped (including the \).
*/
switch (cfg.proxy_telnet_command[eo]) {
case '\\':
sk_write(p->sub_socket, "\\", 1);
eo++;
break;
case '%':
sk_write(p->sub_socket, "%%", 1);
eo++;
break;
case 'r':
sk_write(p->sub_socket, "\r", 1);
eo++;
break;
case 'n':
sk_write(p->sub_socket, "\n", 1);
eo++;
break;
case 't':
sk_write(p->sub_socket, "\t", 1);
eo++;
break;
case 'x':
case 'X':
{
/* escaped hexadecimal value (ie. \xff) */
unsigned char v = 0;
int i = 0;
for (;;) {
eo++;
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);
eo = so + 1;
break;
}
/* we only extract two hex characters */
if (i == 1) {
sk_write(p->sub_socket, &v, 1);
eo++;
break;
}
i++;
v <<= 4;
}
}
break;
default:
sk_write(p->sub_socket,
cfg.proxy_telnet_command + so, 2);
eo++;
break;
}
} else {
/* % escape. we recognize %%, %host, %port. anything else,
* we just send unescaped (including the %). */
if (cfg.proxy_telnet_command[eo] == '%') {
sk_write(p->sub_socket, "%", 1);
eo++;
}
else if (strnicmp(cfg.proxy_telnet_command + eo,
"host", 4) == 0) {
char dest[64];
sk_getaddr(p->remote_addr, dest, 64);
sk_write(p->sub_socket, dest, strlen(dest));
eo += 4;
}
else if (strnicmp(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 {
/* 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);
}
}
/* 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) {
sk_write(p->sub_socket, cfg.proxy_telnet_command + so, eo - so);
}
p->state = 1;
return 0;
}
if (change == PROXY_CHANGE_CLOSING) {
/* if our proxy negotiation process involves closing and opening
* new sockets, then we would want to intercept this closing
* callback when we were expecting it. if we aren't anticipating
* a socket close, then some error must have occurred. we'll
* just pass those errors up to the backend.
*/
return plug_closing(p->plug, p->closing_error_msg,
p->closing_error_code,
p->closing_calling_back);
}
if (change == PROXY_CHANGE_SENT) {
/* some (or all) of what we wrote to the proxy was sent.
* we don't do anything new, however, until we receive the
* proxy's response. we might want to set a timer so we can
* timeout the proxy negotiation after a while...
*/
return 0;
}
if (change == PROXY_CHANGE_ACCEPTING) {
/* we should _never_ see this, as we are using our socket to
* connect to a proxy, not accepting inbound connections.
* what should we do? close the socket with an appropriate
* error message?
*/
return plug_accepting(p->plug, p->accepting_sock);
}
if (change == PROXY_CHANGE_RECEIVE) {
/* we have received data from the underlying socket, which
* we'll need to parse, process, and respond to appropriately.
*/
/* we're done */
proxy_activate(p);
/* proxy activate will have dealt with
* whatever is left of the buffer */
return 1;
}
plug_closing(p->plug, "Network error: Unexpected proxy error",
PROXY_ERROR_UNEXPECTED, 0);
return 0;
}

18
proxy.h
View File

@ -80,24 +80,6 @@ struct Socket_proxy_tag {
/* accepting */
void *accepting_sock;
/* spin locks, for the critical switch from negotiating
* to active state. we have to dump all of our pending
* buffers without new events (read, writes, etc) corrupting
* things. we should not have built up a large amount of
* pending data during negotiation, so hopefully this will
* not have a large effect on performance.
*/
char lock_close;
char lock_write;
char lock_write_oob;
char lock_receive;
char lock_flush;
char lock_closing;
char lock_sent;
char lock_accepting;
char lock_freeze;
};
typedef struct Plug_proxy_tag * Proxy_Plug;

View File

@ -59,7 +59,7 @@ BEGIN
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
LTEXT "Barry, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Barry, Justin Bradford, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Permission is hereby granted, free of charge, to any person", 1004, 10, 58, 206, 8
LTEXT "obtaining a copy of this software and associated documentation", 1005, 10, 66, 206, 8

View File

@ -366,7 +366,7 @@ void load_settings(char *section, int do_host, Config * cfg)
sizeof(cfg->proxy_username));
gpps(sesskey, "ProxyPassword", "", cfg->proxy_password,
sizeof(cfg->proxy_password));
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port",
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",
cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));
gppi(sesskey, "ProxySOCKSVersion", 5, &cfg->proxy_socks_version);

View File

@ -87,7 +87,7 @@ BEGIN
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
LTEXT "Barry, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Barry, Justin Bradford, and CORE SDI S.A.", 1003, 10, 42, 206, 8
LTEXT "Permission is hereby granted, free of charge, to any person", 1004, 10, 58, 206, 8
LTEXT "obtaining a copy of this software and associated documentation", 1005, 10, 66, 206, 8