mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-21 14:18:38 -05:00

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.
97 lines
2.7 KiB
C
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
|