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
##-- 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
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
# 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
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);
};
*/
};
+/* 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 */
* 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
* 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
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;
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;
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
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
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;
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;
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;
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));
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;
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;
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,
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 {
}
}
+ 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;
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) {
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"))
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,
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);
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
};
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
};
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
};
* 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;
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)
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;