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

Justin Bradford's proxy support patch. Currently supports only HTTP

CONNECT, but contains an extensible framework to allow other
proxies. Apparently SOCKS and ad-hoc-telnet-proxy are already
planned (the GUI mentions them already even though they don't work
yet). GUI includes full configurability and allows definition of
exclusion zones. Rock and roll.

[originally from svn r1598]
This commit is contained in:
Simon Tatham 2002-03-23 17:47:21 +00:00
parent 869989e7e6
commit eabd704d1e
15 changed files with 935 additions and 18 deletions

View File

@ -96,7 +96,7 @@ GOBJS1 = window.$(OBJ) windlg.$(OBJ) winctrls.$(OBJ) terminal.$(OBJ)
GOBJS2 = sizetip.$(OBJ) wcwidth.$(OBJ) unicode.$(OBJ) logging.$(OBJ)
GOBJS3 = printing.$(OBJ)
##-- objects putty puttytel plink
LOBJS1 = telnet.$(OBJ) raw.$(OBJ) rlogin.$(OBJ) ldisc.$(OBJ) winnet.$(OBJ)
LOBJS1 = telnet.$(OBJ) raw.$(OBJ) rlogin.$(OBJ) ldisc.$(OBJ) winnet.$(OBJ) proxy.$(OBJ)
##-- objects putty plink
POBJS = be_all.$(OBJ)
##-- objects puttytel
@ -104,9 +104,9 @@ TOBJS = be_nossh.$(OBJ)
##-- objects plink
PLOBJS = plink.$(OBJ) logging.$(OBJ)
##-- objects pscp
SOBJS = scp.$(OBJ) winnet.$(OBJ) be_none.$(OBJ) wildcard.$(OBJ)
SOBJS = scp.$(OBJ) winnet.$(OBJ) proxy.$(OBJ) be_none.$(OBJ) wildcard.$(OBJ)
##-- objects psftp
FOBJS = psftp.$(OBJ) winnet.$(OBJ) be_none.$(OBJ)
FOBJS = psftp.$(OBJ) winnet.$(OBJ) proxy.$(OBJ) be_none.$(OBJ)
##-- objects pscp psftp
SFOBJS = sftp.$(OBJ) int64.$(OBJ) logging.$(OBJ)
##-- objects putty puttytel pscp psftp plink
@ -303,7 +303,8 @@ pageantc.$(OBJ): pageantc.c puttymem.h
plink.$(OBJ): plink.c network.h misc.h puttymem.h storage.h putty.h tree234.h
portfwd.$(OBJ): portfwd.c network.h misc.h puttymem.h int64.h ssh.h putty.h
printing.$(OBJ): printing.c network.h misc.h puttymem.h putty.h
psftp.$(OBJ): psftp.c network.h misc.h sftp.h ssh.h storage.h int64.h puttymem.h putty.h
proxy.$(OBJ): proxy.c proxy.h network.h
psftp.$(OBJ): psftp.c network.h misc.h sftp.h ssh.h storage.h int64.h puttymem.h putty.h
puttygen.$(OBJ): puttygen.c network.h misc.h puttymem.h int64.h winstuff.h ssh.h putty.h
raw.$(OBJ): raw.c network.h misc.h puttymem.h putty.h
rlogin.$(OBJ): rlogin.c network.h misc.h puttymem.h putty.h

2
Recipe
View File

@ -105,7 +105,7 @@ SFTP = sftp int64 logging
# Miscellaneous objects appearing in all the network utilities (not
# Pageant or PuTTYgen).
MISC = misc version winstore settings tree234 winnet
MISC = misc version winstore settings tree234 winnet proxy
# Standard libraries, and the same with WinSocks 1 and 2.
LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib

View File

@ -27,6 +27,9 @@ struct socket_function_table {
int (*write) (Socket s, char *data, int len);
int (*write_oob) (Socket s, char *data, int len);
void (*flush) (Socket s);
void (*set_private_ptr) (Socket s, void *ptr);
void *(*get_private_ptr) (Socket s);
void (*set_frozen) (Socket s, int is_frozen);
/* ignored by tcp, but vital for ssl */
char *(*socket_error) (Socket s);
};
@ -60,6 +63,13 @@ struct plug_function_table {
*/
};
/* proxy indirection layer */
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug);
Socket new_listener(int port, Plug plug, int local_host_only);
/* socket functions */
void sk_init(void); /* called once at program startup */
void sk_cleanup(void); /* called just before program exit */
@ -95,8 +105,8 @@ Socket sk_register(void *sock, Plug plug);
* This is perhaps unnecessary now that we have the notion of a plug,
* but there is some existing code that uses it, so it stays.
*/
void sk_set_private_ptr(Socket s, void *ptr);
void *sk_get_private_ptr(Socket s);
#define sk_set_private_ptr(s, ptr) (((*s)->set_private_ptr) (s, ptr))
#define sk_get_private_ptr(s) (((*s)->get_private_ptr) (s))
/*
* Special error values are returned from sk_namelookup and sk_new
@ -123,7 +133,7 @@ char *sk_addr_error(SockAddr addr);
* associated local socket in order to avoid unbounded buffer
* growth.
*/
void sk_set_frozen(Socket sock, int is_frozen);
#define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen))
/*
* Call this after an operation that might have tried to send on a

View File

@ -138,7 +138,7 @@ char *pfd_newconnect(Socket *s, char *hostname, int port, void *c)
pr->ready = 1;
pr->c = c;
pr->s = *s = sk_new(addr, port, 0, 1, 0, (Plug) pr);
pr->s = *s = new_connection(addr, dummy_realhost, port, 0, 1, 0, (Plug) pr);
if ((err = sk_socket_error(*s))) {
sfree(pr);
return err;
@ -227,7 +227,7 @@ char *pfd_addforward(char *desthost, int destport, int port)
pr->ready = 0;
pr->waiting = NULL;
pr->s = s = sk_newlistener(port, (Plug) pr, !cfg.lport_acceptall);
pr->s = s = new_listener(port, (Plug) pr, !cfg.lport_acceptall);
if ((err = sk_socket_error(s))) {
sfree(pr);
return err;

594
proxy.c Normal file
View File

@ -0,0 +1,594 @@
/*
* Network proxy abstraction in PuTTY
*
* A proxy layer, if necessary, wedges itself between the network
* code and the higher level backend.
*/
#include <windows.h>
#define DEFINE_PLUG_METHOD_MACROS
#include "putty.h"
#include "network.h"
#include "proxy.h"
/*
* Call this when proxy negotiation is complete, so that this
* socket can begin working normally.
*/
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);
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);
bufchain_consume(&p->pending_oob_output_data, len);
}
bufchain_clear(&p->pending_oob_output_data);
while (bufchain_size(&p->pending_output_data) > 0) {
bufchain_prefix(&p->pending_output_data, &data, &len);
sk_write(p->sub_socket, data, len);
bufchain_consume(&p->pending_output_data, len);
}
bufchain_clear(&p->pending_output_data);
p->lock_write_oob = 0;
p->lock_write = 0;
if (p->pending_flush) sk_flush(p->sub_socket);
p->lock_flush = 0;
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 */
static Plug sk_proxy_plug (Socket s, Plug p)
{
Proxy_Socket ps = (Proxy_Socket) s;
Plug ret = ps->plug;
if (p)
ps->plug = p;
return ret;
}
static void sk_proxy_close (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
while (ps->lock_close) ;
sk_close(ps->sub_socket);
sfree(ps);
}
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);
}
return sk_write(ps->sub_socket, data, len);
}
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);
bufchain_add(&ps->pending_oob_output_data, data, len);
return len;
}
return sk_write_oob(ps->sub_socket, data, len);
}
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;
}
sk_flush(ps->sub_socket);
}
static void sk_proxy_set_private_ptr (Socket s, void *ptr)
{
Proxy_Socket ps = (Proxy_Socket) s;
sk_set_private_ptr(ps->sub_socket, ptr);
}
static void * sk_proxy_get_private_ptr (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
return sk_get_private_ptr(ps->sub_socket);
}
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;
}
sk_set_frozen(ps->sub_socket, is_frozen);
}
static char * sk_proxy_socket_error (Socket s)
{
Proxy_Socket ps = (Proxy_Socket) s;
if (ps->error != NULL || ps->sub_socket == NULL) {
return ps->error;
}
return sk_socket_error(ps->sub_socket);
}
/* basic proxy plug functions */
static int plug_proxy_closing (Plug p, char *error_msg,
int error_code, int calling_back)
{
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;
ps->closing_calling_back = calling_back;
return ps->negotiate(ps, PROXY_CHANGE_CLOSING);
}
return plug_closing(ps->plug, error_msg,
error_code, calling_back);
}
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
* process, hopefully it won't affect the protocol above us
*/
bufchain_add(&ps->pending_input_data, data, len);
ps->receive_urgent = urgent;
ps->receive_data = data;
ps->receive_len = len;
return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
}
return plug_receive(ps->plug, urgent, data, len);
}
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);
return;
}
plug_sent(ps->plug, bufsize);
}
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);
}
return plug_accepting(ps->plug, sock);
}
static int proxy_for_destination (SockAddr addr, char * hostname, int port)
{
int s = 0, e = 0;
char hostip[64];
int hostip_len, hostname_len;
char * exclude_list;
/* we want a string representation of the IP address for comparisons */
sk_getaddr(addr, hostip, 64);
hostip_len = strlen(hostip);
hostname_len = strlen(hostname);
exclude_list = cfg.proxy_exclude_list;
/* now parse the exclude list, and see if either our IP
* or hostname matches anything in it.
*/
while (exclude_list[s]) {
while (exclude_list[s] &&
(isspace(exclude_list[s]) ||
exclude_list[s] == ',')) s++;
if (!exclude_list[s]) break;
e = s;
while (exclude_list[e] &&
(isalnum(exclude_list[e]) ||
exclude_list[e] == '-' ||
exclude_list[e] == '.' ||
exclude_list[e] == '*')) e++;
if (exclude_list[s] == '*') {
/* wildcard at beginning of entry */
if (strnicmp(hostip + hostip_len - (e - s - 1),
exclude_list + s + 1, e - s - 1) == 0 ||
strnicmp(hostname + hostname_len - (e - s - 1),
exclude_list + s + 1, e - s - 1) == 0)
return 0; /* IP/hostname range excluded. do not use proxy. */
} else if (exclude_list[e-1] == '*') {
/* wildcard at end of entry */
if (strnicmp(hostip, exclude_list + s, e - s - 1) == 0 ||
strnicmp(hostname, exclude_list + s, e - s - 1) == 0)
return 0; /* IP/hostname range excluded. do not use proxy. */
} else {
/* no wildcard at either end, so let's try an absolute
* match (ie. a specific IP)
*/
if (stricmp(hostip, exclude_list + s) == 0)
return 0; /* IP/hostname excluded. do not use proxy. */
if (stricmp(hostname, exclude_list + s) == 0)
return 0; /* IP/hostname excluded. do not use proxy. */
}
s = e;
}
/* no matches in the exclude list, so use the proxy */
return 1;
}
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug)
{
static struct socket_function_table socket_fn_table = {
sk_proxy_plug,
sk_proxy_close,
sk_proxy_write,
sk_proxy_write_oob,
sk_proxy_flush,
sk_proxy_set_private_ptr,
sk_proxy_get_private_ptr,
sk_proxy_set_frozen,
sk_proxy_socket_error
};
static struct plug_function_table plug_fn_table = {
plug_proxy_closing,
plug_proxy_receive,
plug_proxy_sent,
plug_proxy_accepting
};
if (cfg.proxy_type != PROXY_NONE &&
proxy_for_destination(addr, hostname, port))
{
Proxy_Socket ret;
Proxy_Plug pplug;
SockAddr proxy_addr;
char * proxy_canonical_name;
ret = smalloc(sizeof(struct Socket_proxy_tag));
ret->fn = &socket_fn_table;
ret->plug = plug;
ret->remote_addr = addr;
ret->remote_port = port;
bufchain_init(&ret->pending_input_data);
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;
if (cfg.proxy_type == PROXY_HTTP) {
ret->negotiate = proxy_http_negotiate;
} else if (cfg.proxy_type == PROXY_SOCKS) {
ret->negotiate = proxy_socks_negotiate;
} else if (cfg.proxy_type == PROXY_TELNET) {
ret->negotiate = proxy_telnet_negotiate;
} else {
ret->error = "Network error: Unknown proxy method";
return (Socket) ret;
}
/* create the proxy plug to map calls from the actual
* socket into our proxy socket layer */
pplug = smalloc(sizeof(struct Plug_proxy_tag));
pplug->fn = &plug_fn_table;
pplug->proxy_socket = ret;
/* look-up proxy */
proxy_addr = sk_namelookup(cfg.proxy_host,
&proxy_canonical_name);
sfree(proxy_canonical_name);
/* create the actual socket we will be using,
* connected to our proxy server and port.
*/
ret->sub_socket = sk_new(proxy_addr, cfg.proxy_port,
privport, oobinline,
nodelay, (Plug) pplug);
if (sk_socket_error(ret->sub_socket) != NULL)
return (Socket) ret;
sk_addr_free(proxy_addr);
/* start the proxy negotiation process... */
sk_set_frozen(ret->sub_socket, 0);
ret->negotiate(ret, PROXY_CHANGE_NEW);
return (Socket) ret;
}
/* no proxy, so just return the direct socket */
return sk_new(addr, port, privport, oobinline, nodelay, plug);
}
Socket new_listener(int port, Plug plug, int local_host_only)
{
/* TODO: SOCKS (and potentially others) support inbound
* TODO: connections via the proxy. support them.
*/
return sk_newlistener(port, plug, local_host_only);
}
/* ----------------------------------------------------------------------
* HTTP CONNECT proxy type.
*/
static int get_line_end (char * data, int len)
{
int off = 0;
while (off < len)
{
if (data[off] == '\n') {
/* we have a newline */
off++;
/* is that the only thing on this line? */
if (off <= 2) return off;
/* if not, then there is the possibility that this header
* continues onto the next line, if it starts with a space
* or a tab.
*/
if (off + 1 < len &&
data[off+1] != ' ' &&
data[off+1] != '\t') return off;
/* the line does continue, so we have to keep going
* until we see an the header's "real" end of line.
*/
off++;
}
off++;
}
return -1;
}
int proxy_http_negotiate (Proxy_Socket p, int change)
{
if (p->state == PROXY_STATE_NEW) {
/* we are just beginning the proxy negotiate process,
* so we'll send off the initial bits of the request.
* for this proxy method, it's just a simple HTTP
* request
*/
char buf[1024], dest[21];
sk_getaddr(p->remote_addr, dest, 20);
sprintf(buf, "CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n\r\n",
dest, p->remote_port, dest, p->remote_port);
sk_write(p->sub_socket, buf, strlen(buf));
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.
*/
void *data;
int len;
int eol;
if (p->state == 1) {
int min_ver, maj_ver, status;
/* get the status line */
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
if (eol < 0) return 1;
sscanf((char *)data, "HTTP/%i.%i %i", &maj_ver, &min_ver, &status);
/* remove the status line from the input buffer. */
bufchain_consume(&p->pending_input_data, eol);
/* TODO: we need to support Proxy-Auth headers */
if (status < 200 || status > 299) {
/* error */
/* TODO: return a more specific error message,
* TODO: based on the status code.
*/
plug_closing(p->plug, "Network error: Error while communicating with proxy",
PROXY_ERROR_GENERAL, 0);
return 1;
}
p->state = 2;
}
if (p->state == 2) {
/* get headers. we're done when we get a
* header of length 2, (ie. just "\r\n")
*/
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
while (eol > 2)
{
/* TODO: Proxy-Auth stuff. in some cases, we will
* TODO: need to extract information from headers.
*/
bufchain_consume(&p->pending_input_data, eol);
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
}
if (eol == 2) {
/* we're done */
bufchain_consume(&p->pending_input_data, 2);
proxy_activate(p);
/* proxy activate will have dealt with
* whatever is left of the buffer */
return 1;
}
return 1;
}
}
plug_closing(p->plug, "Network error: Unexpected proxy error",
PROXY_ERROR_UNEXPECTED, 0);
return 0;
}
/* ----------------------------------------------------------------------
* SOCKS proxy type (as yet unimplemented).
*/
int proxy_socks_negotiate (Proxy_Socket p, int change)
{
p->error = "Network error: SOCKS proxy implementation is incomplete";
return 0;
}
/* ----------------------------------------------------------------------
* `Telnet' proxy type (as yet unimplemented).
*
* (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
* command is configurable, since this proxy type is typically not
* standardised or at all well-defined.)
*/
int proxy_telnet_negotiate (Proxy_Socket p, int change)
{
p->error = "Network error: Telnet proxy implementation is incomplete";
return 0;
}

119
proxy.h Normal file
View File

@ -0,0 +1,119 @@
/*
* Network proxy abstraction in PuTTY
*
* A proxy layer, if necessary, wedges itself between the
* network code and the higher level backend.
*
* Supported proxies: HTTP CONNECT, generic telnet
* In progress: SOCKS
*/
#ifndef PUTTY_PROXY_H
#define PUTTY_PROXY_H
#define PROXY_ERROR_GENERAL 8000
#define PROXY_ERROR_UNEXPECTED 8001
typedef struct Socket_proxy_tag * Proxy_Socket;
struct Socket_proxy_tag {
struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
char * error;
Socket sub_socket;
Plug plug;
SockAddr remote_addr;
int remote_port;
bufchain pending_output_data;
bufchain pending_oob_output_data;
int pending_flush;
bufchain pending_input_data;
#define PROXY_STATE_NEW -1
#define PROXY_STATE_ACTIVE 0
int state; /* proxy states greater than 0 are implementation
* dependent, but represent various stages/states
* of the initialization/setup/negotiation with the
* proxy server.
*/
int freeze; /* should we freeze the underlying socket when
* we are done with the proxy negotiation? this
* simply caches the value of sk_set_frozen calls.
*/
#define PROXY_CHANGE_NEW -1
#define PROXY_CHANGE_CLOSING 0
#define PROXY_CHANGE_SENT 1
#define PROXY_CHANGE_RECEIVE 2
#define PROXY_CHANGE_ACCEPTING 3
/* something has changed (a call from the sub socket
* layer into our Proxy Plug layer, or we were just
* created, etc), so the proxy layer needs to handle
* this change (the type of which is the second argument)
* and further the proxy negotiation process.
*/
int (*negotiate) (Proxy_Socket /* this */, int /* change type */);
/* current arguments of plug handlers
* (for use by proxy's negotiate function)
*/
/* closing */
char *closing_error_msg;
int closing_error_code;
int closing_calling_back;
/* receive */
int receive_urgent;
char *receive_data;
int receive_len;
/* sent */
int sent_bufsize;
/* 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;
struct Plug_proxy_tag {
struct plug_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
Proxy_Socket proxy_socket;
};
extern void proxy_activate (Proxy_Socket);
extern int proxy_http_negotiate (Proxy_Socket, int);
extern int proxy_telnet_negotiate (Proxy_Socket, int);
extern int proxy_socks_negotiate (Proxy_Socket, int);
#endif

View File

@ -242,6 +242,15 @@ typedef struct {
int warn_on_close;
int ping_interval; /* in seconds */
int tcp_nodelay;
/* Proxy options */
char proxy_exclude_list[512];
enum { PROXY_NONE, PROXY_HTTP, PROXY_SOCKS, PROXY_TELNET } proxy_type;
char proxy_host[512];
int proxy_port;
char proxy_username[32];
char proxy_password[32];
char proxy_telnet_command[512];
int proxy_socks_version;
/* SSH options */
char remote_cmd[512];
char remote_cmd2[512]; /* fallback if the first fails

2
raw.c
View File

@ -92,7 +92,7 @@ static char *raw_init(char *host, int port, char **realhost, int nodelay)
sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
logevent(buf);
}
s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
if ((err = sk_socket_error(s)))
return err;

View File

@ -122,7 +122,7 @@ static char *rlogin_init(char *host, int port, char **realhost, int nodelay)
sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
logevent(buf);
}
s = sk_new(addr, port, 1, 0, nodelay, &fn_table_ptr);
s = new_connection(addr, *realhost, port, 1, 0, nodelay, &fn_table_ptr);
if ((err = sk_socket_error(s)))
return err;

View File

@ -148,6 +148,17 @@ void save_settings(char *section, int do_host, Config * cfg)
write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);
write_setting_s(sesskey, "TerminalType", cfg->termtype);
write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);
/* proxy settings */
write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list);
write_setting_i(sesskey, "ProxyType", cfg->proxy_type);
write_setting_s(sesskey, "ProxyHost", cfg->proxy_host);
write_setting_i(sesskey, "ProxyPort", cfg->proxy_port);
write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username);
write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password);
write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command);
write_setting_i(sesskey, "ProxySOCKSVersion", cfg->proxy_socks_version);
{
char buf[2 * sizeof(cfg->environmt)], *p, *q;
p = buf;
@ -343,6 +354,22 @@ void load_settings(char *section, int do_host, Config * cfg)
sizeof(cfg->termtype));
gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed,
sizeof(cfg->termspeed));
/* proxy settings */
gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list,
sizeof(cfg->proxy_exclude_list));
gppi(sesskey, "ProxyType", PROXY_NONE, &cfg->proxy_type);
gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host,
sizeof(cfg->proxy_host));
gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port);
gpps(sesskey, "ProxyUsername", "", cfg->proxy_username,
sizeof(cfg->proxy_username));
gpps(sesskey, "ProxyPassword", "", cfg->proxy_password,
sizeof(cfg->proxy_password));
gpps(sesskey, "ProxyTelnetCommand", "connect %host %port",
cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));
gppi(sesskey, "ProxySOCKSVersion", 5, &cfg->proxy_socks_version);
{
char buf[2 * sizeof(cfg->environmt)], *p, *q;
gpps(sesskey, "Environment", "", buf, sizeof(buf));

2
ssh.c
View File

@ -1872,7 +1872,7 @@ static char *connect_to_host(char *host, int port, char **realhost, int nodelay)
sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
logevent(buf);
}
s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
if ((err = sk_socket_error(s))) {
s = NULL;
return err;

View File

@ -636,7 +636,7 @@ static char *telnet_init(char *host, int port, char **realhost, int nodelay)
sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
logevent(buf);
}
s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
if ((err = sk_socket_error(s)))
return err;

145
windlg.c
View File

@ -438,6 +438,32 @@ enum { IDCX_ABOUT =
IDC_NODELAY,
connectionpanelend,
proxypanelstart,
IDC_TITLE_PROXY,
IDC_BOX_PROXY1,
IDC_PROXYTYPESTATIC,
IDC_PROXYTYPENONE,
IDC_PROXYTYPEHTTP,
IDC_PROXYTYPESOCKS,
IDC_PROXYTYPETELNET,
IDC_PROXYHOSTSTATIC,
IDC_PROXYHOSTEDIT,
IDC_PROXYPORTSTATIC,
IDC_PROXYPORTEDIT,
IDC_PROXYEXCLUDESTATIC,
IDC_PROXYEXCLUDEEDIT,
IDC_PROXYUSERSTATIC,
IDC_PROXYUSEREDIT,
IDC_PROXYPASSSTATIC,
IDC_PROXYPASSEDIT,
IDC_BOX_PROXY2,
IDC_PROXYTELNETCMDSTATIC,
IDC_PROXYTELNETCMDEDIT,
IDC_PROXYSOCKSVERSTATIC,
IDC_PROXYSOCKSVER5,
IDC_PROXYSOCKSVER4,
proxypanelend,
telnetpanelstart,
IDC_TITLE_TELNET,
IDC_BOX_TELNET1,
@ -1243,6 +1269,20 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess)
CheckDlgButton(hwnd, IDC_LPORT_ALL, cfg.lport_acceptall);
CheckDlgButton(hwnd, IDC_RPORT_ALL, cfg.rport_acceptall);
CheckRadioButton(hwnd, IDC_PFWDLOCAL, IDC_PFWDREMOTE, IDC_PFWDLOCAL);
/* proxy config */
CheckRadioButton(hwnd, IDC_PROXYTYPENONE, IDC_PROXYTYPETELNET,
cfg.proxy_type == PROXY_HTTP ? IDC_PROXYTYPEHTTP :
cfg.proxy_type == PROXY_SOCKS ? IDC_PROXYTYPESOCKS :
cfg.proxy_type == PROXY_TELNET ? IDC_PROXYTYPETELNET : IDC_PROXYTYPENONE);
SetDlgItemText(hwnd, IDC_PROXYHOSTEDIT, cfg.proxy_host);
SetDlgItemInt(hwnd, IDC_PROXYPORTEDIT, cfg.proxy_port, FALSE);
SetDlgItemText(hwnd, IDC_PROXYEXCLUDEEDIT, cfg.proxy_exclude_list);
SetDlgItemText(hwnd, IDC_PROXYTELNETCMDEDIT, cfg.proxy_telnet_command);
SetDlgItemText(hwnd, IDC_PROXYUSEREDIT, cfg.proxy_username);
SetDlgItemText(hwnd, IDC_PROXYPASSEDIT, cfg.proxy_password);
CheckRadioButton(hwnd, IDC_PROXYSOCKSVER5, IDC_PROXYSOCKSVER4,
cfg.proxy_socks_version == 4 ? IDC_PROXYSOCKSVER4 : IDC_PROXYSOCKSVER5);
}
struct treeview_faff {
@ -1687,6 +1727,41 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
}
}
if (panel == proxypanelstart) {
/* The Proxy panel. Accelerators used: [acgoh] ntslypeuwmv */
struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13);
if (dlgtype == 0) {
bartitle(&cp, "Options controlling proxy usage",
IDC_TITLE_PROXY);
beginbox(&cp, "Proxy basics", IDC_BOX_PROXY1);
radioline(&cp, "Proxy type:", IDC_PROXYTYPESTATIC, 4,
"&None", IDC_PROXYTYPENONE,
"H&TTP", IDC_PROXYTYPEHTTP,
"&SOCKS", IDC_PROXYTYPESOCKS,
"Te&lnet", IDC_PROXYTYPETELNET, NULL);
multiedit(&cp,
"Prox&y Host", IDC_PROXYHOSTSTATIC, IDC_PROXYHOSTEDIT, 80,
"&Port", IDC_PROXYPORTSTATIC, IDC_PROXYPORTEDIT, 20, NULL);
multiedit(&cp,
"&Exclude Hosts/IPs", IDC_PROXYEXCLUDESTATIC,
IDC_PROXYEXCLUDEEDIT, 100, NULL);
staticedit(&cp, "&Username", IDC_PROXYUSERSTATIC,
IDC_PROXYUSEREDIT, 60);
staticedit(&cp, "Pass&word", IDC_PROXYPASSSTATIC,
IDC_PROXYPASSEDIT, 60);
endbox(&cp);
beginbox(&cp, "Misc. proxy settings", IDC_BOX_PROXY2);
multiedit(&cp,
"Telnet co&mmand", IDC_PROXYTELNETCMDSTATIC,
IDC_PROXYTELNETCMDEDIT, 100, NULL);
radioline(&cp, "SOCKS &Version", IDC_PROXYSOCKSVERSTATIC,
2, "Version 5", IDC_PROXYSOCKSVER5, "Version 4",
IDC_PROXYSOCKSVER4, NULL);
endbox(&cp);
}
}
if (panel == telnetpanelstart) {
/* The Telnet panel. Accelerators used: [acgoh] svldr bftk */
struct ctlpos cp;
@ -1957,6 +2032,7 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
treeview_insert(&tvfaff, 1, "Colours");
treeview_insert(&tvfaff, 0, "Connection");
if (dlgtype == 0) {
treeview_insert(&tvfaff, 1, "Proxy");
treeview_insert(&tvfaff, 1, "Telnet");
treeview_insert(&tvfaff, 1, "Rlogin");
if (backends[3].backend != NULL) {
@ -2040,6 +2116,8 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
create_controls(hwnd, dlgtype, tunnelspanelstart);
if (!strcmp(buffer, "Connection"))
create_controls(hwnd, dlgtype, connectionpanelstart);
if (!strcmp(buffer, "Proxy"))
create_controls(hwnd, dlgtype, proxypanelstart);
if (!strcmp(buffer, "Telnet"))
create_controls(hwnd, dlgtype, telnetpanelstart);
if (!strcmp(buffer, "Rlogin"))
@ -2741,6 +2819,73 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
GetDlgItemText(hwnd, IDC_TTEDIT, cfg.termtype,
sizeof(cfg.termtype) - 1);
break;
/* proxy config */
case IDC_PROXYHOSTEDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_PROXYHOSTEDIT, cfg.proxy_host,
sizeof(cfg.proxy_host) - 1);
break;
case IDC_PROXYPORTEDIT:
if (HIWORD(wParam) == EN_CHANGE) {
GetDlgItemText(hwnd, IDC_PROXYPORTEDIT, portname, 31);
if (isdigit(portname[0]))
MyGetDlgItemInt(hwnd, IDC_PROXYPORTEDIT, &cfg.proxy_port);
else {
service = getservbyname(portname, NULL);
if (service)
cfg.proxy_port = ntohs(service->s_port);
else
cfg.proxy_port = 0;
}
}
break;
case IDC_PROXYEXCLUDEEDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_PROXYEXCLUDEEDIT,
cfg.proxy_exclude_list,
sizeof(cfg.proxy_exclude_list) - 1);
break;
case IDC_PROXYUSEREDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_PROXYUSEREDIT,
cfg.proxy_username,
sizeof(cfg.proxy_username) - 1);
break;
case IDC_PROXYPASSEDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_PROXYPASSEDIT,
cfg.proxy_password,
sizeof(cfg.proxy_password) - 1);
break;
case IDC_PROXYTELNETCMDEDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_PROXYTELNETCMDEDIT,
cfg.proxy_telnet_command,
sizeof(cfg.proxy_telnet_command) - 1);
break;
case IDC_PROXYSOCKSVER5:
case IDC_PROXYSOCKSVER4:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
cfg.proxy_socks_version =
IsDlgButtonChecked(hwnd, IDC_PROXYSOCKSVER4) ? 4 : 5;
}
break;
case IDC_PROXYTYPENONE:
case IDC_PROXYTYPEHTTP:
case IDC_PROXYTYPESOCKS:
case IDC_PROXYTYPETELNET:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
cfg.proxy_type =
IsDlgButtonChecked(hwnd, IDC_PROXYTYPEHTTP) ? PROXY_HTTP :
IsDlgButtonChecked(hwnd, IDC_PROXYTYPESOCKS) ? PROXY_SOCKS :
IsDlgButtonChecked(hwnd, IDC_PROXYTYPETELNET) ? PROXY_TELNET :
PROXY_NONE;
}
break;
case IDC_LGFEDIT:
if (HIWORD(wParam) == EN_CHANGE)
GetDlgItemText(hwnd, IDC_LGFEDIT, cfg.logfilename,

View File

@ -393,6 +393,9 @@ static void sk_tcp_flush(Socket s)
static void sk_tcp_close(Socket s);
static int sk_tcp_write(Socket s, char *data, int len);
static int sk_tcp_write_oob(Socket s, char *data, int len);
static void sk_tcp_set_private_ptr(Socket s, void *ptr);
static void *sk_tcp_get_private_ptr(Socket s);
static void sk_tcp_set_frozen(Socket s, int is_frozen);
static char *sk_tcp_socket_error(Socket s);
extern char *do_select(SOCKET skt, int startup);
@ -405,6 +408,9 @@ Socket sk_register(void *sock, Plug plug)
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
sk_tcp_set_frozen,
sk_tcp_socket_error
};
@ -459,6 +465,9 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
sk_tcp_set_frozen,
sk_tcp_socket_error
};
@ -633,6 +642,9 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
sk_tcp_write,
sk_tcp_write_oob,
sk_tcp_flush,
sk_tcp_set_private_ptr,
sk_tcp_get_private_ptr,
sk_tcp_set_frozen,
sk_tcp_socket_error
};
@ -1043,13 +1055,13 @@ void net_pending_errors(void)
* Each socket abstraction contains a `void *' private field in
* which the client can keep state.
*/
void sk_set_private_ptr(Socket sock, void *ptr)
static void sk_tcp_set_private_ptr(Socket sock, void *ptr)
{
Actual_Socket s = (Actual_Socket) sock;
s->private_ptr = ptr;
}
void *sk_get_private_ptr(Socket sock)
static void *sk_tcp_get_private_ptr(Socket sock)
{
Actual_Socket s = (Actual_Socket) sock;
return s->private_ptr;
@ -1070,7 +1082,7 @@ static char *sk_tcp_socket_error(Socket sock)
return s->error;
}
void sk_set_frozen(Socket sock, int is_frozen)
static void sk_tcp_set_frozen(Socket sock, int is_frozen)
{
Actual_Socket s = (Actual_Socket) sock;
if (s->frozen == is_frozen)

View File

@ -188,7 +188,7 @@ char *x11_init(Socket * s, char *display, void *c)
pr->throttled = pr->throttle_override = 0;
pr->c = c;
pr->s = *s = sk_new(addr, port, 0, 1, 0, (Plug) pr);
pr->s = *s = new_connection(addr, dummy_realhost, port, 0, 1, 0, (Plug) pr);
if ((err = sk_socket_error(*s))) {
sfree(pr);
return err;