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 10:26:41 +00:00
|
|
|
/*
|
|
|
|
* SOCKS 4 proxy negotiation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "putty.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "proxy.h"
|
|
|
|
#include "socks.h"
|
|
|
|
#include "sshcr.h"
|
|
|
|
|
|
|
|
typedef struct Socks4ProxyNegotiator {
|
|
|
|
int crLine;
|
|
|
|
ProxyNegotiator pn;
|
|
|
|
} Socks4ProxyNegotiator;
|
|
|
|
|
|
|
|
static ProxyNegotiator *proxy_socks4_new(const ProxyNegotiatorVT *vt)
|
|
|
|
{
|
|
|
|
Socks4ProxyNegotiator *s = snew(Socks4ProxyNegotiator);
|
|
|
|
s->pn.vt = vt;
|
|
|
|
s->crLine = 0;
|
|
|
|
return &s->pn;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void proxy_socks4_free(ProxyNegotiator *pn)
|
|
|
|
{
|
|
|
|
Socks4ProxyNegotiator *s = container_of(pn, Socks4ProxyNegotiator, pn);
|
|
|
|
sfree(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void proxy_socks4_process_queue(ProxyNegotiator *pn)
|
|
|
|
{
|
|
|
|
Socks4ProxyNegotiator *s = container_of(pn, Socks4ProxyNegotiator, pn);
|
|
|
|
|
|
|
|
crBegin(s->crLine);
|
|
|
|
|
|
|
|
{
|
|
|
|
char hostname[512];
|
|
|
|
bool write_hostname = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SOCKS 4 request packet:
|
|
|
|
*
|
|
|
|
* byte version
|
|
|
|
* byte command
|
|
|
|
* uint16 destination port number
|
|
|
|
* uint32 destination IPv4 address (or something in the
|
|
|
|
* SOCKS4A_NAME_FOLLOWS range)
|
|
|
|
* asciz username
|
|
|
|
* asciz destination hostname (if we sent SOCKS4A_NAME_FOLLOWS_*)
|
|
|
|
*/
|
|
|
|
|
|
|
|
put_byte(pn->output, SOCKS4_REQUEST_VERSION);
|
|
|
|
put_byte(pn->output, SOCKS_CMD_CONNECT);
|
|
|
|
put_uint16(pn->output, pn->ps->remote_port);
|
|
|
|
|
|
|
|
switch (sk_addrtype(pn->ps->remote_addr)) {
|
|
|
|
case ADDRTYPE_IPV4: {
|
|
|
|
char addr[4];
|
|
|
|
sk_addrcopy(pn->ps->remote_addr, addr);
|
|
|
|
put_data(pn->output, addr, 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ADDRTYPE_NAME:
|
|
|
|
put_uint32(pn->output, SOCKS4A_NAME_FOLLOWS_BASE);
|
|
|
|
sk_getaddr(pn->ps->remote_addr, hostname, lenof(hostname));
|
|
|
|
write_hostname = true;
|
|
|
|
break;
|
|
|
|
case ADDRTYPE_IPV6:
|
|
|
|
pn->error = dupstr("SOCKS version 4 does not support IPv6");
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
put_asciz(pn->output, conf_get_str(pn->ps->conf, CONF_proxy_username));
|
|
|
|
|
|
|
|
if (write_hostname)
|
|
|
|
put_asciz(pn->output, hostname);
|
|
|
|
}
|
|
|
|
|
|
|
|
crReturnV;
|
|
|
|
|
|
|
|
{
|
|
|
|
unsigned char data[8];
|
|
|
|
crMaybeWaitUntilV(bufchain_try_fetch_consume(pn->input, data, 8));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SOCKS 4 response packet:
|
|
|
|
*
|
|
|
|
* byte version
|
|
|
|
* byte status
|
|
|
|
* uint16 port number
|
|
|
|
* uint32 IPv4 address
|
|
|
|
*
|
|
|
|
* We don't need to worry about the port and destination address.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (data[0] != SOCKS4_REPLY_VERSION) {
|
|
|
|
pn->error = dupprintf("SOCKS proxy response contained reply "
|
2023-09-23 12:28:29 +00:00
|
|
|
"version number %d (expected %d)",
|
|
|
|
(int)data[0], SOCKS4_REPLY_VERSION);
|
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 10:26:41 +00:00
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (data[1]) {
|
|
|
|
case SOCKS4_RESP_SUCCESS:
|
|
|
|
pn->done = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCKS4_RESP_FAILURE:
|
|
|
|
pn->error = dupstr("SOCKS server reported failure to connect");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCKS4_RESP_WANT_IDENTD:
|
|
|
|
pn->error = dupstr("SOCKS server wanted IDENTD on client");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCKS4_RESP_IDENTD_MISMATCH:
|
|
|
|
pn->error = dupstr("Username and IDENTD on client don't agree");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
pn->error = dupprintf("SOCKS server sent unrecognised error "
|
|
|
|
"code %d", (int)data[1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
crFinishV;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct ProxyNegotiatorVT socks4_proxy_negotiator_vt = {
|
|
|
|
.new = proxy_socks4_new,
|
|
|
|
.free = proxy_socks4_free,
|
|
|
|
.process_queue = proxy_socks4_process_queue,
|
|
|
|
.type = "SOCKS 4",
|
|
|
|
};
|