/* * 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 %d)", (int)data[0], SOCKS4_REPLY_VERSION); 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", };