diff options
Diffstat (limited to 'proxy/socks4.c')
-rw-r--r-- | proxy/socks4.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/proxy/socks4.c b/proxy/socks4.c new file mode 100644 index 00000000..ac85ec05 --- /dev/null +++ b/proxy/socks4.c @@ -0,0 +1,136 @@ +/* + * 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 " + "version number %d (expected 0)", + (int)data[0]); + 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", +}; |