mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Pull out SOCKS protocol constants into a header.
This seemed like a worthwhile cleanup to do while I was working on this code anyway. Now all the magic numbers are defined in a header file by macro names indicating their meaning, and used by both the SOCKS client code in the proxy subdirectory and the SOCKS server code in portfwd.c.
This commit is contained in:
parent
05b5aa0bb9
commit
1bf93289c9
@ -13,6 +13,7 @@
|
|||||||
#include "ssh.h" /* For MD5 support */
|
#include "ssh.h" /* For MD5 support */
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
#include "socks.h"
|
||||||
#include "marshal.h"
|
#include "marshal.h"
|
||||||
|
|
||||||
static void hmacmd5_chap(const unsigned char *challenge, int challen,
|
static void hmacmd5_chap(const unsigned char *challenge, int challen,
|
||||||
@ -24,7 +25,7 @@ static void hmacmd5_chap(const unsigned char *challenge, int challen,
|
|||||||
|
|
||||||
void proxy_socks5_offerencryptedauth(BinarySink *bs)
|
void proxy_socks5_offerencryptedauth(BinarySink *bs)
|
||||||
{
|
{
|
||||||
put_byte(bs, 0x03); /* CHAP */
|
put_byte(bs, SOCKS5_AUTH_CHAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
int proxy_socks5_handlechap (ProxySocket *ps)
|
int proxy_socks5_handlechap (ProxySocket *ps)
|
||||||
@ -64,7 +65,7 @@ int proxy_socks5_handlechap (ProxySocket *ps)
|
|||||||
* with the server, where we negotiate version and
|
* with the server, where we negotiate version and
|
||||||
* number of attributes
|
* number of attributes
|
||||||
*/
|
*/
|
||||||
if (data[0] != 0x01) {
|
if (data[0] != SOCKS5_AUTH_CHAP_VERSION) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy wants "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy wants "
|
||||||
"a different CHAP version");
|
"a different CHAP version");
|
||||||
return 1;
|
return 1;
|
||||||
@ -96,7 +97,7 @@ int proxy_socks5_handlechap (ProxySocket *ps)
|
|||||||
ps->chap_current_datalen);
|
ps->chap_current_datalen);
|
||||||
|
|
||||||
switch (ps->chap_current_attribute) {
|
switch (ps->chap_current_attribute) {
|
||||||
case 0x00:
|
case SOCKS5_AUTH_CHAP_ATTR_STATUS:
|
||||||
/* Successful authentication */
|
/* Successful authentication */
|
||||||
if (data[0] == 0x00)
|
if (data[0] == 0x00)
|
||||||
ps->state = 2;
|
ps->state = 2;
|
||||||
@ -106,19 +107,19 @@ int proxy_socks5_handlechap (ProxySocket *ps)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x03:
|
case SOCKS5_AUTH_CHAP_ATTR_CHALLENGE:
|
||||||
outbuf[0] = 0x01; /* Version */
|
outbuf[0] = SOCKS5_AUTH_CHAP_VERSION;
|
||||||
outbuf[1] = 0x01; /* One attribute */
|
outbuf[1] = 0x01; /* One attribute */
|
||||||
outbuf[2] = 0x04; /* Response */
|
outbuf[2] = SOCKS5_AUTH_CHAP_ATTR_RESPONSE;
|
||||||
outbuf[3] = 0x10; /* Length */
|
outbuf[3] = 0x10; /* Length */
|
||||||
hmacmd5_chap(data, ps->chap_current_datalen,
|
hmacmd5_chap(data, ps->chap_current_datalen,
|
||||||
conf_get_str(ps->conf, CONF_proxy_password),
|
conf_get_str(ps->conf, CONF_proxy_password),
|
||||||
&outbuf[4]);
|
&outbuf[4]);
|
||||||
sk_write(ps->sub_socket, outbuf, 20);
|
sk_write(ps->sub_socket, outbuf, 20);
|
||||||
break;
|
break;
|
||||||
case 0x11:
|
case SOCKS5_AUTH_CHAP_ATTR_ALGLIST:
|
||||||
/* Chose a protocol */
|
/* Chose a protocol */
|
||||||
if (data[0] != 0x85) {
|
if (data[0] != SOCKS5_AUTH_CHAP_ALG_HMACMD5) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: Server chose "
|
plug_closing_error(ps->plug, "Proxy error: Server chose "
|
||||||
"CHAP of other than HMAC-MD5 but we "
|
"CHAP of other than HMAC-MD5 but we "
|
||||||
"didn't offer it!");
|
"didn't offer it!");
|
||||||
@ -146,12 +147,12 @@ int proxy_socks5_selectchap(ProxySocket *ps)
|
|||||||
if (username[0] || password[0]) {
|
if (username[0] || password[0]) {
|
||||||
char chapbuf[514];
|
char chapbuf[514];
|
||||||
int ulen;
|
int ulen;
|
||||||
chapbuf[0] = '\x01'; /* Version */
|
chapbuf[0] = SOCKS5_AUTH_CHAP_VERSION;
|
||||||
chapbuf[1] = '\x02'; /* Number of attributes sent */
|
chapbuf[1] = '\x02'; /* Number of attributes sent */
|
||||||
chapbuf[2] = '\x11'; /* First attribute - algorithms list */
|
chapbuf[2] = SOCKS5_AUTH_CHAP_ATTR_ALGLIST;
|
||||||
chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
|
chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
|
||||||
chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
|
chapbuf[4] = SOCKS5_AUTH_CHAP_ALG_HMACMD5;
|
||||||
chapbuf[5] = '\x02'; /* Second attribute - username */
|
chapbuf[5] = SOCKS5_AUTH_CHAP_ATTR_USERNAME;
|
||||||
|
|
||||||
ulen = strlen(username);
|
ulen = strlen(username);
|
||||||
if (ulen > 255) ulen = 255;
|
if (ulen > 255) ulen = 255;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "putty.h"
|
#include "putty.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
#include "socks.h"
|
||||||
|
|
||||||
#define do_proxy_dns(conf) \
|
#define do_proxy_dns(conf) \
|
||||||
(conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \
|
(conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \
|
||||||
@ -765,8 +766,8 @@ int proxy_socks4_negotiate (ProxySocket *ps, int change)
|
|||||||
char hostname[512];
|
char hostname[512];
|
||||||
bool write_hostname = false;
|
bool write_hostname = false;
|
||||||
|
|
||||||
put_byte(command, 4); /* SOCKS version 4 */
|
put_byte(command, SOCKS4_REQUEST_VERSION);
|
||||||
put_byte(command, 1); /* CONNECT command */
|
put_byte(command, SOCKS_CMD_CONNECT);
|
||||||
put_uint16(command, ps->remote_port);
|
put_uint16(command, ps->remote_port);
|
||||||
|
|
||||||
switch (sk_addrtype(ps->remote_addr)) {
|
switch (sk_addrtype(ps->remote_addr)) {
|
||||||
@ -778,7 +779,7 @@ int proxy_socks4_negotiate (ProxySocket *ps, int change)
|
|||||||
}
|
}
|
||||||
case ADDRTYPE_NAME:
|
case ADDRTYPE_NAME:
|
||||||
sk_getaddr(ps->remote_addr, hostname, lenof(hostname));
|
sk_getaddr(ps->remote_addr, hostname, lenof(hostname));
|
||||||
put_uint32(command, 1);
|
put_uint32(command, SOCKS4A_NAME_FOLLOWS_BASE);
|
||||||
write_hostname = true;
|
write_hostname = true;
|
||||||
break;
|
break;
|
||||||
case ADDRTYPE_IPV6:
|
case ADDRTYPE_IPV6:
|
||||||
@ -834,7 +835,7 @@ int proxy_socks4_negotiate (ProxySocket *ps, int change)
|
|||||||
|
|
||||||
if (ps->state == 1) {
|
if (ps->state == 1) {
|
||||||
/* response format:
|
/* response format:
|
||||||
* version number (1 byte) = 4
|
* version number (1 byte) = 0
|
||||||
* reply code (1 byte)
|
* reply code (1 byte)
|
||||||
* 90 = request granted
|
* 90 = request granted
|
||||||
* 91 = request rejected or failed
|
* 91 = request rejected or failed
|
||||||
@ -853,25 +854,25 @@ int proxy_socks4_negotiate (ProxySocket *ps, int change)
|
|||||||
/* get the response */
|
/* get the response */
|
||||||
bufchain_fetch(&ps->pending_input_data, data, 8);
|
bufchain_fetch(&ps->pending_input_data, data, 8);
|
||||||
|
|
||||||
if (data[0] != 0) {
|
if (data[0] != SOCKS4_REPLY_VERSION) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
||||||
"responded with unexpected "
|
"responded with unexpected "
|
||||||
"reply code version");
|
"reply code version");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[1] != 90) {
|
if (data[1] != SOCKS4_RESP_SUCCESS) {
|
||||||
|
|
||||||
switch (data[1]) {
|
switch (data[1]) {
|
||||||
case 92:
|
case SOCKS4_RESP_WANT_IDENTD:
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS server "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS server "
|
||||||
"wanted IDENTD on client");
|
"wanted IDENTD on client");
|
||||||
break;
|
break;
|
||||||
case 93:
|
case SOCKS4_RESP_IDENTD_MISMATCH:
|
||||||
plug_closing_error(ps->plug, "Proxy error: Username and "
|
plug_closing_error(ps->plug, "Proxy error: Username and "
|
||||||
"IDENTD on client don't agree");
|
"IDENTD on client don't agree");
|
||||||
break;
|
break;
|
||||||
case 91:
|
case SOCKS4_RESP_FAILURE:
|
||||||
default:
|
default:
|
||||||
plug_closing_error(ps->plug, "Proxy error: Error while "
|
plug_closing_error(ps->plug, "Proxy error: Error while "
|
||||||
"communicating with proxy");
|
"communicating with proxy");
|
||||||
@ -915,7 +916,7 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
int method_count_offset, methods_start;
|
int method_count_offset, methods_start;
|
||||||
|
|
||||||
command = strbuf_new();
|
command = strbuf_new();
|
||||||
put_byte(command, 5); /* SOCKS version 5 */
|
put_byte(command, SOCKS5_REQUEST_VERSION);
|
||||||
username = conf_get_str(ps->conf, CONF_proxy_username);
|
username = conf_get_str(ps->conf, CONF_proxy_username);
|
||||||
password = conf_get_str(ps->conf, CONF_proxy_password);
|
password = conf_get_str(ps->conf, CONF_proxy_password);
|
||||||
|
|
||||||
@ -923,11 +924,11 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
put_byte(command, 0);
|
put_byte(command, 0);
|
||||||
methods_start = command->len;
|
methods_start = command->len;
|
||||||
|
|
||||||
put_byte(command, 0x00); /* no authentication */
|
put_byte(command, SOCKS5_AUTH_NONE);
|
||||||
|
|
||||||
if (username[0] || password[0]) {
|
if (username[0] || password[0]) {
|
||||||
proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command));
|
proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command));
|
||||||
put_byte(command, 0x02); /* username/password */
|
put_byte(command, SOCKS5_AUTH_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
command->u[method_count_offset] = command->len - methods_start;
|
command->u[method_count_offset] = command->len - methods_start;
|
||||||
@ -994,16 +995,16 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
/* get the response */
|
/* get the response */
|
||||||
bufchain_fetch(&ps->pending_input_data, data, 2);
|
bufchain_fetch(&ps->pending_input_data, data, 2);
|
||||||
|
|
||||||
if (data[0] != 5) {
|
if (data[0] != SOCKS5_REPLY_VERSION) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
||||||
"returned unexpected version");
|
"returned unexpected version");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[1] == 0x00) ps->state = 2; /* no authentication needed */
|
if (data[1] == SOCKS5_AUTH_NONE) ps->state = 2;
|
||||||
else if (data[1] == 0x01) ps->state = 4; /* GSSAPI authentication */
|
else if (data[1] == SOCKS5_AUTH_GSSAPI) ps->state = 4;
|
||||||
else if (data[1] == 0x02) ps->state = 5; /* username/password authentication */
|
else if (data[1] == SOCKS5_AUTH_PASSWORD) ps->state = 5;
|
||||||
else if (data[1] == 0x03) ps->state = 6; /* CHAP authentication */
|
else if (data[1] == SOCKS5_AUTH_CHAP) ps->state = 6;
|
||||||
else {
|
else {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy did not "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy did not "
|
||||||
"accept our authentication");
|
"accept our authentication");
|
||||||
@ -1028,7 +1029,7 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
/* get the response */
|
/* get the response */
|
||||||
bufchain_fetch(&ps->pending_input_data, data, 2);
|
bufchain_fetch(&ps->pending_input_data, data, 2);
|
||||||
|
|
||||||
if (data[0] != 1) {
|
if (data[0] != SOCKS5_AUTH_PASSWORD_VERSION) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS password "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS password "
|
||||||
"subnegotiation contained wrong version "
|
"subnegotiation contained wrong version "
|
||||||
"number");
|
"number");
|
||||||
@ -1069,22 +1070,22 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
strbuf *command = strbuf_new();
|
strbuf *command = strbuf_new();
|
||||||
put_byte(command, 5); /* SOCKS version 5 */
|
put_byte(command, SOCKS5_REQUEST_VERSION);
|
||||||
put_byte(command, 1); /* CONNECT command */
|
put_byte(command, SOCKS_CMD_CONNECT);
|
||||||
put_byte(command, 0x00); /* reserved byte */
|
put_byte(command, 0x00); /* reserved byte */
|
||||||
|
|
||||||
switch (sk_addrtype(ps->remote_addr)) {
|
switch (sk_addrtype(ps->remote_addr)) {
|
||||||
case ADDRTYPE_IPV4:
|
case ADDRTYPE_IPV4:
|
||||||
put_byte(command, 1); /* IPv4 */
|
put_byte(command, SOCKS5_ADDR_IPV4);
|
||||||
sk_addrcopy(ps->remote_addr, strbuf_append(command, 4));
|
sk_addrcopy(ps->remote_addr, strbuf_append(command, 4));
|
||||||
break;
|
break;
|
||||||
case ADDRTYPE_IPV6:
|
case ADDRTYPE_IPV6:
|
||||||
put_byte(command, 4); /* IPv6 */
|
put_byte(command, SOCKS5_ADDR_IPV6);
|
||||||
sk_addrcopy(ps->remote_addr, strbuf_append(command, 16));
|
sk_addrcopy(ps->remote_addr, strbuf_append(command, 16));
|
||||||
break;
|
break;
|
||||||
case ADDRTYPE_NAME: {
|
case ADDRTYPE_NAME: {
|
||||||
char hostname[512];
|
char hostname[512];
|
||||||
put_byte(command, 3); /* domain name */
|
put_byte(command, SOCKS5_ADDR_HOSTNAME);
|
||||||
sk_getaddr(ps->remote_addr, hostname, lenof(hostname));
|
sk_getaddr(ps->remote_addr, hostname, lenof(hostname));
|
||||||
if (!put_pstring(command, hostname)) {
|
if (!put_pstring(command, hostname)) {
|
||||||
ps->error = "Proxy error: SOCKS 5 cannot "
|
ps->error = "Proxy error: SOCKS 5 cannot "
|
||||||
@ -1138,26 +1139,26 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
/* get the response */
|
/* get the response */
|
||||||
bufchain_fetch(&ps->pending_input_data, data, 5);
|
bufchain_fetch(&ps->pending_input_data, data, 5);
|
||||||
|
|
||||||
if (data[0] != 5) {
|
if (data[0] != SOCKS5_REPLY_VERSION) {
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
||||||
"returned wrong version number");
|
"returned wrong version number");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[1] != 0) {
|
if (data[1] != SOCKS5_RESP_SUCCESS) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
|
||||||
strcpy(buf, "Proxy error: ");
|
strcpy(buf, "Proxy error: ");
|
||||||
|
|
||||||
switch (data[1]) {
|
switch (data[1]) {
|
||||||
case 1: strcat(buf, "General SOCKS server failure"); break;
|
case SOCKS5_RESP_FAILURE: strcat(buf, "General SOCKS server failure"); break;
|
||||||
case 2: strcat(buf, "Connection not allowed by ruleset"); break;
|
case SOCKS5_RESP_CONNECTION_NOT_ALLOWED_BY_RULESET: strcat(buf, "Connection not allowed by ruleset"); break;
|
||||||
case 3: strcat(buf, "Network unreachable"); break;
|
case SOCKS5_RESP_NETWORK_UNREACHABLE: strcat(buf, "Network unreachable"); break;
|
||||||
case 4: strcat(buf, "Host unreachable"); break;
|
case SOCKS5_RESP_HOST_UNREACHABLE: strcat(buf, "Host unreachable"); break;
|
||||||
case 5: strcat(buf, "Connection refused"); break;
|
case SOCKS5_RESP_CONNECTION_REFUSED: strcat(buf, "Connection refused"); break;
|
||||||
case 6: strcat(buf, "TTL expired"); break;
|
case SOCKS5_RESP_TTL_EXPIRED: strcat(buf, "TTL expired"); break;
|
||||||
case 7: strcat(buf, "Command not supported"); break;
|
case SOCKS5_RESP_COMMAND_NOT_SUPPORTED: strcat(buf, "Command not supported"); break;
|
||||||
case 8: strcat(buf, "Address type not supported"); break;
|
case SOCKS5_RESP_ADDRTYPE_NOT_SUPPORTED: strcat(buf, "Address type not supported"); break;
|
||||||
default: sprintf(buf+strlen(buf),
|
default: sprintf(buf+strlen(buf),
|
||||||
"Unrecognised SOCKS error code %d",
|
"Unrecognised SOCKS error code %d",
|
||||||
data[1]);
|
data[1]);
|
||||||
@ -1173,9 +1174,9 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
*/
|
*/
|
||||||
len = 6; /* first 4 bytes, last 2 */
|
len = 6; /* first 4 bytes, last 2 */
|
||||||
switch (data[3]) {
|
switch (data[3]) {
|
||||||
case 1: len += 4; break; /* IPv4 address */
|
case SOCKS5_ADDR_IPV4: len += 4; break; /* IPv4 address */
|
||||||
case 4: len += 16; break;/* IPv6 address */
|
case SOCKS5_ADDR_IPV6: len += 16; break;/* IPv6 address */
|
||||||
case 3: len += 1+(unsigned char)data[4]; break; /* domain name */
|
case SOCKS5_ADDR_HOSTNAME: len += 1+(unsigned char)data[4]; break; /* domain name */
|
||||||
default:
|
default:
|
||||||
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
plug_closing_error(ps->plug, "Proxy error: SOCKS proxy "
|
||||||
"returned unrecognised address format");
|
"returned unrecognised address format");
|
||||||
@ -1202,7 +1203,7 @@ int proxy_socks5_negotiate (ProxySocket *ps, int change)
|
|||||||
const char *password = conf_get_str(ps->conf, CONF_proxy_password);
|
const char *password = conf_get_str(ps->conf, CONF_proxy_password);
|
||||||
if (username[0] || password[0]) {
|
if (username[0] || password[0]) {
|
||||||
strbuf *auth = strbuf_new_nm();
|
strbuf *auth = strbuf_new_nm();
|
||||||
put_byte(auth, 1); /* version number of subnegotiation */
|
put_byte(auth, SOCKS5_AUTH_PASSWORD_VERSION);
|
||||||
if (!put_pstring(auth, username)) {
|
if (!put_pstring(auth, username)) {
|
||||||
ps->error = "Proxy error: SOCKS 5 authentication cannot "
|
ps->error = "Proxy error: SOCKS 5 authentication cannot "
|
||||||
"support usernames longer than 255 chars";
|
"support usernames longer than 255 chars";
|
||||||
|
72
proxy/socks.h
Normal file
72
proxy/socks.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Constants used in the SOCKS protocols.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Command codes common to both versions */
|
||||||
|
#define SOCKS_CMD_CONNECT 1
|
||||||
|
#define SOCKS_CMD_BIND 2
|
||||||
|
|
||||||
|
/* SOCKS 4 definitions */
|
||||||
|
|
||||||
|
#define SOCKS4_REQUEST_VERSION 4
|
||||||
|
#define SOCKS4_REPLY_VERSION 0
|
||||||
|
|
||||||
|
#define SOCKS4_RESP_SUCCESS 90
|
||||||
|
#define SOCKS4_RESP_FAILURE 91
|
||||||
|
#define SOCKS4_RESP_WANT_IDENTD 92
|
||||||
|
#define SOCKS4_RESP_IDENTD_MISMATCH 93
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special nonsense IP address range, used as a signal to indicate
|
||||||
|
* that an ASCIZ hostname follows the user id field.
|
||||||
|
*
|
||||||
|
* Strictly speaking, the use of this extension indicates that we're
|
||||||
|
* speaking SOCKS 4A rather than vanilla SOCKS 4, although we don't
|
||||||
|
* bother to draw the distinction.
|
||||||
|
*/
|
||||||
|
#define SOCKS4A_NAME_FOLLOWS_BASE 0x00000001 /* inclusive */
|
||||||
|
#define SOCKS4A_NAME_FOLLOWS_LIMIT 0x00000100 /* exclusive */
|
||||||
|
|
||||||
|
/* SOCKS 5 definitions */
|
||||||
|
|
||||||
|
#define SOCKS5_REQUEST_VERSION 5
|
||||||
|
#define SOCKS5_REPLY_VERSION 5
|
||||||
|
|
||||||
|
/* Extra command codes extending the SOCKS_CMD_* list above */
|
||||||
|
#define SOCKS5_CMD_UDP_ASSOCIATE 3
|
||||||
|
|
||||||
|
#define SOCKS5_AUTH_NONE 0
|
||||||
|
#define SOCKS5_AUTH_GSSAPI 1
|
||||||
|
#define SOCKS5_AUTH_PASSWORD 2
|
||||||
|
#define SOCKS5_AUTH_CHAP 3
|
||||||
|
#define SOCKS5_AUTH_REJECTED 0xFF /* used in reply to indicate 'no
|
||||||
|
* acceptable method offered' */
|
||||||
|
|
||||||
|
#define SOCKS5_AUTH_PASSWORD_VERSION 1
|
||||||
|
|
||||||
|
#define SOCKS5_AUTH_CHAP_VERSION 1
|
||||||
|
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_STATUS 0x00
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_INFO 0x01
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_USERNAME 0x02
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_CHALLENGE 0x03
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_RESPONSE 0x04
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_CHARSET 0x05
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_IDENTIFIER 0x10
|
||||||
|
#define SOCKS5_AUTH_CHAP_ATTR_ALGLIST 0x11
|
||||||
|
|
||||||
|
#define SOCKS5_AUTH_CHAP_ALG_HMACMD5 0x85
|
||||||
|
|
||||||
|
#define SOCKS5_ADDR_IPV4 1
|
||||||
|
#define SOCKS5_ADDR_IPV6 4
|
||||||
|
#define SOCKS5_ADDR_HOSTNAME 3
|
||||||
|
|
||||||
|
#define SOCKS5_RESP_SUCCESS 0
|
||||||
|
#define SOCKS5_RESP_FAILURE 1
|
||||||
|
#define SOCKS5_RESP_CONNECTION_NOT_ALLOWED_BY_RULESET 2
|
||||||
|
#define SOCKS5_RESP_NETWORK_UNREACHABLE 3
|
||||||
|
#define SOCKS5_RESP_HOST_UNREACHABLE 4
|
||||||
|
#define SOCKS5_RESP_CONNECTION_REFUSED 5
|
||||||
|
#define SOCKS5_RESP_TTL_EXPIRED 6
|
||||||
|
#define SOCKS5_RESP_COMMAND_NOT_SUPPORTED 7
|
||||||
|
#define SOCKS5_RESP_ADDRTYPE_NOT_SUPPORTED 8
|
@ -9,6 +9,7 @@
|
|||||||
#include "putty.h"
|
#include "putty.h"
|
||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
#include "proxy/socks.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enumeration of values that live in the 'socks_state' field of
|
* Enumeration of values that live in the 'socks_state' field of
|
||||||
@ -216,10 +217,10 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
* _must_ have by now) to find out which SOCKS major
|
* _must_ have by now) to find out which SOCKS major
|
||||||
* version we're speaking. */
|
* version we're speaking. */
|
||||||
switch (pf->socksbuf->u[0]) {
|
switch (pf->socksbuf->u[0]) {
|
||||||
case 4:
|
case SOCKS4_REQUEST_VERSION:
|
||||||
pf->socks_state = SOCKS_4;
|
pf->socks_state = SOCKS_4;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case SOCKS5_REQUEST_VERSION:
|
||||||
pf->socks_state = SOCKS_5_INITIAL;
|
pf->socks_state = SOCKS_5_INITIAL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -250,13 +251,15 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
|
|
||||||
if (get_err(src) == BSE_OUT_OF_DATA)
|
if (get_err(src) == BSE_OUT_OF_DATA)
|
||||||
return;
|
return;
|
||||||
if (socks_version == 4 && message_type == 1) {
|
if (socks_version == SOCKS4_REQUEST_VERSION &&
|
||||||
|
message_type == SOCKS_CMD_CONNECT) {
|
||||||
/* CONNECT message */
|
/* CONNECT message */
|
||||||
bool name_based = false;
|
bool name_based = false;
|
||||||
|
|
||||||
port = get_uint16(src);
|
port = get_uint16(src);
|
||||||
ipv4 = get_uint32(src);
|
ipv4 = get_uint32(src);
|
||||||
if (ipv4 > 0x00000000 && ipv4 < 0x00000100) {
|
if (ipv4 >= SOCKS4A_NAME_FOLLOWS_BASE &&
|
||||||
|
ipv4 < SOCKS4A_NAME_FOLLOWS_LIMIT) {
|
||||||
/*
|
/*
|
||||||
* Addresses in this range indicate the SOCKS 4A
|
* Addresses in this range indicate the SOCKS 4A
|
||||||
* extension to specify a hostname, which comes
|
* extension to specify a hostname, which comes
|
||||||
@ -280,8 +283,8 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
output = strbuf_new();
|
output = strbuf_new();
|
||||||
put_byte(output, 0); /* reply version */
|
put_byte(output, SOCKS4_REPLY_VERSION);
|
||||||
put_byte(output, 90); /* SOCKS 4 'request granted' */
|
put_byte(output, SOCKS4_RESP_SUCCESS);
|
||||||
put_uint16(output, 0); /* null port field */
|
put_uint16(output, 0); /* null port field */
|
||||||
put_uint32(output, 0); /* null address field */
|
put_uint32(output, 0); /* null address field */
|
||||||
sk_write(pf->s, output->u, output->len);
|
sk_write(pf->s, output->u, output->len);
|
||||||
@ -294,8 +297,8 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
|
|
||||||
socks4_reject:
|
socks4_reject:
|
||||||
output = strbuf_new();
|
output = strbuf_new();
|
||||||
put_byte(output, 0); /* reply version */
|
put_byte(output, SOCKS4_REPLY_VERSION);
|
||||||
put_byte(output, 91); /* SOCKS 4 'request rejected' */
|
put_byte(output, SOCKS4_RESP_FAILURE);
|
||||||
put_uint16(output, 0); /* null port field */
|
put_uint16(output, 0); /* null port field */
|
||||||
put_uint32(output, 0); /* null address field */
|
put_uint32(output, 0); /* null address field */
|
||||||
sk_write(pf->s, output->u, output->len);
|
sk_write(pf->s, output->u, output->len);
|
||||||
@ -308,29 +311,31 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
socks_version = get_byte(src);
|
socks_version = get_byte(src);
|
||||||
methods = get_pstring(src);
|
methods = get_pstring(src);
|
||||||
|
|
||||||
method = 0xFF; /* means 'no usable method found' */
|
method = SOCKS5_AUTH_REJECTED;
|
||||||
{
|
|
||||||
int i;
|
/* Search the method list for AUTH_NONE, which is the
|
||||||
for (i = 0; i < methods.len; i++) {
|
* only one this client code can speak */
|
||||||
if (((const unsigned char *)methods.ptr)[i] == 0 ) {
|
for (size_t i = 0; i < methods.len; i++) {
|
||||||
method = 0; /* no auth */
|
unsigned char this_method =
|
||||||
break;
|
((const unsigned char *)methods.ptr)[i];
|
||||||
}
|
if (this_method == SOCKS5_AUTH_NONE) {
|
||||||
|
method = this_method;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_err(src) == BSE_OUT_OF_DATA)
|
if (get_err(src) == BSE_OUT_OF_DATA)
|
||||||
return;
|
return;
|
||||||
if (get_err(src))
|
if (get_err(src))
|
||||||
method = 0xFF;
|
method = SOCKS5_AUTH_REJECTED;
|
||||||
|
|
||||||
output = strbuf_new();
|
output = strbuf_new();
|
||||||
put_byte(output, 5); /* SOCKS version */
|
put_byte(output, SOCKS5_REPLY_VERSION);
|
||||||
put_byte(output, method); /* selected auth method */
|
put_byte(output, method);
|
||||||
sk_write(pf->s, output->u, output->len);
|
sk_write(pf->s, output->u, output->len);
|
||||||
strbuf_free(output);
|
strbuf_free(output);
|
||||||
|
|
||||||
if (method == 0xFF) {
|
if (method == SOCKS5_AUTH_REJECTED) {
|
||||||
pfd_close(pf);
|
pfd_close(pf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -345,48 +350,49 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
|
|||||||
message_type = get_byte(src);
|
message_type = get_byte(src);
|
||||||
reserved_byte = get_byte(src);
|
reserved_byte = get_byte(src);
|
||||||
|
|
||||||
if (socks_version == 5 && message_type == 1 &&
|
if (socks_version == SOCKS5_REQUEST_VERSION &&
|
||||||
|
message_type == SOCKS_CMD_CONNECT &&
|
||||||
reserved_byte == 0) {
|
reserved_byte == 0) {
|
||||||
|
|
||||||
reply_code = 0; /* success */
|
reply_code = SOCKS5_RESP_SUCCESS;
|
||||||
|
|
||||||
switch (get_byte(src)) {
|
switch (get_byte(src)) {
|
||||||
case 1: /* IPv4 */
|
case SOCKS5_ADDR_IPV4:
|
||||||
pf->hostname = ipv4_to_string(get_uint32(src));
|
pf->hostname = ipv4_to_string(get_uint32(src));
|
||||||
break;
|
break;
|
||||||
case 4: /* IPv6 */
|
case SOCKS5_ADDR_IPV6:
|
||||||
pf->hostname = ipv6_to_string(get_data(src, 16));
|
pf->hostname = ipv6_to_string(get_data(src, 16));
|
||||||
break;
|
break;
|
||||||
case 3: /* unresolved domain name */
|
case SOCKS5_ADDR_HOSTNAME:
|
||||||
pf->hostname = mkstr(get_pstring(src));
|
pf->hostname = mkstr(get_pstring(src));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pf->hostname = NULL;
|
pf->hostname = NULL;
|
||||||
reply_code = 8; /* address type not supported */
|
reply_code = SOCKS5_RESP_ADDRTYPE_NOT_SUPPORTED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pf->port = get_uint16(src);
|
pf->port = get_uint16(src);
|
||||||
} else {
|
} else {
|
||||||
reply_code = 7; /* command not supported */
|
reply_code = SOCKS5_RESP_COMMAND_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_err(src) == BSE_OUT_OF_DATA)
|
if (get_err(src) == BSE_OUT_OF_DATA)
|
||||||
return;
|
return;
|
||||||
if (get_err(src))
|
if (get_err(src))
|
||||||
reply_code = 1; /* general server failure */
|
reply_code = SOCKS5_RESP_FAILURE;
|
||||||
|
|
||||||
output = strbuf_new();
|
output = strbuf_new();
|
||||||
put_byte(output, 5); /* SOCKS version */
|
put_byte(output, SOCKS5_REPLY_VERSION);
|
||||||
put_byte(output, reply_code);
|
put_byte(output, reply_code);
|
||||||
put_byte(output, 0); /* reserved */
|
put_byte(output, 0); /* reserved */
|
||||||
put_byte(output, 1); /* IPv4 address follows */
|
put_byte(output, SOCKS5_ADDR_IPV4); /* IPv4 address follows */
|
||||||
put_uint32(output, 0); /* bound IPv4 address (unused) */
|
put_uint32(output, 0); /* bound IPv4 address (unused) */
|
||||||
put_uint16(output, 0); /* bound port number (unused) */
|
put_uint16(output, 0); /* bound port number (unused) */
|
||||||
sk_write(pf->s, output->u, output->len);
|
sk_write(pf->s, output->u, output->len);
|
||||||
strbuf_free(output);
|
strbuf_free(output);
|
||||||
|
|
||||||
if (reply_code != 0) {
|
if (reply_code != SOCKS5_RESP_SUCCESS) {
|
||||||
pfd_close(pf);
|
pfd_close(pf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user