1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-21 14:18:38 -05:00
Simon Tatham b7bf2aec74 Reorganise proxy system into coroutines.
Previously, the proxy negotiation functions were written as explicit
state machines, with ps->state being manually set to a sequence of
positive integer values which would be tested by if statements in the
next call to the same negotiation function.

That's not how this code base likes to do things! We have a coroutine
system to allow those state machines to be implicit rather than
explicit, so that we can use ordinary control flow statements like
while loops. Reorganised each proxy negotiation function into a
coroutine-based system like that.

While I'm at it, I've also moved each proxy negotiator out into its
own source file, to make proxy.c less overcrowded and monolithic. And
_that_ gave me the opportunity to define each negotiator as an
implementation of a trait rather than as a single function - which
means now each one can define its own local variables and have its own
cleanup function, instead of all of them having to share the variables
inside the main ProxySocket struct.

In the new coroutine system, negotiators don't have to worry about the
mechanics of actually sending data down the underlying Socket any
more. The negotiator coroutine just appends to a bufchain (via a
provided bufchain_sink), and after every call to the coroutine,
central code in proxy.c transfers the data to the Socket itself. This
avoids a lot of intermediate allocations within the negotiators, which
previously kept having to make temporary strbufs or arrays in order to
have something to point an sk_write() at; now they can just put
formatted data directly into the output bufchain via the marshal.h
interface.

In this version of the code, I've also moved most of the SOCKS5 CHAP
implementation from cproxy.c into socks5.c, so that it can sit in the
same coroutine as the rest of the proxy negotiation control flow.
That's because calling a sub-coroutine (co-subroutine?) is awkward to
set up (though it is _possible_ - we do SSH-2 kex that way), and
there's no real need to bother in this case, since the only thing that
really needs to go in cproxy.c is the actual cryptography plus a flag
to tell socks5.c whether to offer CHAP authentication in the first
place.
2021-11-19 15:09:17 +00:00

97 lines
2.7 KiB
C

/*
* 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, SOCKS 4 & 5
*/
#ifndef PUTTY_PROXY_H
#define PUTTY_PROXY_H
typedef struct ProxySocket ProxySocket;
typedef struct ProxyNegotiator ProxyNegotiator;
typedef struct ProxyNegotiatorVT ProxyNegotiatorVT;
struct ProxySocket {
const char *error;
Socket *sub_socket;
Plug *plug;
SockAddr *remote_addr;
int remote_port;
bufchain pending_output_data;
bufchain pending_oob_output_data;
bufchain pending_input_data;
bool pending_eof;
bool 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.
*/
ProxyNegotiator *pn; /* non-NULL if still negotiating */
bufchain output_from_negotiator;
/* configuration, used to look up proxy settings */
Conf *conf;
Socket sock;
Plug plugimpl;
};
struct ProxyNegotiator {
const ProxyNegotiatorVT *vt;
/* Standard fields for any ProxyNegotiator. new() and free() don't
* have to set these up; that's done centrally, to save duplication. */
ProxySocket *ps;
bufchain *input;
bufchain_sink output[1];
/* Set to report success during proxy negotiation. */
bool done;
/* Set to report an error during proxy negotiation. The main
* ProxySocket will free it, and will then guarantee never to call
* process_queue again. */
char *error;
};
struct ProxyNegotiatorVT {
ProxyNegotiator *(*new)(const ProxyNegotiatorVT *);
void (*process_queue)(ProxyNegotiator *);
void (*free)(ProxyNegotiator *);
const char *type;
};
static inline ProxyNegotiator *proxy_negotiator_new(
const ProxyNegotiatorVT *vt)
{ return vt->new(vt); }
static inline void proxy_negotiator_process_queue(ProxyNegotiator *pn)
{ pn->vt->process_queue(pn); }
static inline void proxy_negotiator_free(ProxyNegotiator *pn)
{ pn->vt->free(pn); }
extern const ProxyNegotiatorVT http_proxy_negotiator_vt;
extern const ProxyNegotiatorVT socks4_proxy_negotiator_vt;
extern const ProxyNegotiatorVT socks5_proxy_negotiator_vt;
extern const ProxyNegotiatorVT telnet_proxy_negotiator_vt;
/*
* This may be reused by local-command proxies on individual
* platforms.
*/
char *format_telnet_command(SockAddr *addr, int port, Conf *conf);
/*
* These are implemented in cproxy.c or nocproxy.c, depending on
* whether encrypted proxy authentication is available.
*/
extern const bool socks5_chap_available;
strbuf *chap_response(ptrlen challenge, ptrlen password);
#endif