mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
137 lines
3.7 KiB
C
137 lines
3.7 KiB
C
|
/*
|
||
|
* 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",
|
||
|
};
|