1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Cleanups to proxy code: greater robustness in receiving proxy data,

better error reporting for SOCKS 5 and HTTP proxies.

[originally from svn r1973]
This commit is contained in:
Simon Tatham 2002-09-21 16:52:21 +00:00
parent d33c200de1
commit e7e92ac22a
3 changed files with 161 additions and 68 deletions

48
misc.c
View File

@ -88,6 +88,7 @@ void base64_encode_atom(unsigned char *data, int n, char *out)
* - return a (pointer,length) pair giving some initial data in
* the list, suitable for passing to a send or write system
* call
* - retrieve a larger amount of initial data from the list
* - return the current size of the buffer chain in bytes
*/
@ -155,16 +156,23 @@ void bufchain_add(bufchain *ch, void *data, int len)
void bufchain_consume(bufchain *ch, int len)
{
struct bufchain_granule *tmp;
assert(ch->buffersize >= len);
assert(ch->head != NULL && ch->head->bufpos + len <= ch->head->buflen);
ch->head->bufpos += len;
ch->buffersize -= len;
if (ch->head->bufpos >= ch->head->buflen) {
struct bufchain_granule *tmp = ch->head;
ch->head = tmp->next;
sfree(tmp);
if (!ch->head)
ch->tail = NULL;
while (len > 0) {
int remlen = len;
assert(ch->head != NULL);
if (remlen >= ch->head->buflen - ch->head->bufpos) {
remlen = ch->head->buflen - ch->head->bufpos;
tmp = ch->head;
ch->head = tmp->next;
sfree(tmp);
if (!ch->head)
ch->tail = NULL;
} else
ch->head->bufpos += remlen;
ch->buffersize -= remlen;
len -= remlen;
}
}
@ -174,6 +182,28 @@ void bufchain_prefix(bufchain *ch, void **data, int *len)
*data = ch->head->buf + ch->head->bufpos;
}
void bufchain_fetch(bufchain *ch, void *data, int len)
{
struct bufchain_granule *tmp;
char *data_c = (char *)data;
tmp = ch->head;
assert(ch->buffersize >= len);
while (len > 0) {
int remlen = len;
assert(tmp != NULL);
if (remlen >= tmp->buflen - tmp->bufpos)
remlen = tmp->buflen - tmp->bufpos;
memcpy(data_c, tmp->buf + tmp->bufpos, remlen);
tmp = tmp->next;
len -= remlen;
data_c += remlen;
}
}
/* ----------------------------------------------------------------------
* My own versions of malloc, realloc and free. Because I want
* malloc and realloc to bomb out and exit the program if they run

1
misc.h
View File

@ -27,6 +27,7 @@ int bufchain_size(bufchain *ch);
void bufchain_add(bufchain *ch, void *data, int len);
void bufchain_prefix(bufchain *ch, void **data, int *len);
void bufchain_consume(bufchain *ch, int len);
void bufchain_fetch(bufchain *ch, void *data, int len);
/*
* Debugging functions.

180
proxy.c
View File

@ -7,6 +7,8 @@
#include <windows.h>
#include <assert.h>
#define DEFINE_PLUG_METHOD_MACROS
#include "putty.h"
#include "network.h"
@ -329,7 +331,7 @@ Socket new_connection(SockAddr addr, char *hostname,
} else if (cfg.proxy_type == PROXY_TELNET) {
ret->negotiate = proxy_telnet_negotiate;
} else {
ret->error = "Network error: Unknown proxy method";
ret->error = "Proxy error: Unknown proxy method";
return (Socket) ret;
}
@ -484,7 +486,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
* we'll need to parse, process, and respond to appropriately.
*/
void *data;
char *data, *datap;
int len;
int eol;
@ -493,27 +495,45 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
int min_ver, maj_ver, status;
/* get the status line */
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
if (eol < 0) return 1;
len = bufchain_size(&p->pending_input_data);
assert(len > 0); /* or we wouldn't be here */
data = smalloc(len);
bufchain_fetch(&p->pending_input_data, data, len);
sscanf((char *)data, "HTTP/%i.%i %i", &maj_ver, &min_ver, &status);
eol = get_line_end(data, len);
if (eol < 0) {
sfree(data);
return 1;
}
status = -1;
/* We can't rely on whether the %n incremented the sscanf return */
if (sscanf((char *)data, "HTTP/%i.%i %n",
&maj_ver, &min_ver, &status) < 2 || status == -1) {
plug_closing(p->plug, "Proxy error: HTTP response was absent",
PROXY_ERROR_GENERAL, 0);
sfree(data);
return 1;
}
/* remove the status line from the input buffer. */
bufchain_consume(&p->pending_input_data, eol);
/* TODO: we need to support Proxy-Auth headers */
if (status < 200 || status > 299) {
if (data[status] != '2') {
/* error */
/* TODO: return a more specific error message,
* TODO: based on the status code.
*/
plug_closing(p->plug, "Network error: Error while communicating with proxy",
PROXY_ERROR_GENERAL, 0);
char buf[1024];
data[eol] = '\0';
while (eol > status &&
(data[eol-1] == '\r' || data[eol-1] == '\n'))
data[--eol] = '\0';
sprintf(buf, "Proxy error: %.900s",
data+status);
plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);
sfree(data);
return 1;
}
sfree(data);
p->state = 2;
}
@ -523,16 +543,22 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
* header of length 2, (ie. just "\r\n")
*/
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
len = bufchain_size(&p->pending_input_data);
assert(len > 0); /* or we wouldn't be here */
data = smalloc(len);
datap = data;
bufchain_fetch(&p->pending_input_data, data, len);
eol = get_line_end(datap, len);
if (eol < 0) {
sfree(data);
return 1;
}
while (eol > 2)
{
/* TODO: Proxy-Auth stuff. in some cases, we will
* TODO: need to extract information from headers.
*/
bufchain_consume(&p->pending_input_data, eol);
bufchain_prefix(&p->pending_input_data, &data, &len);
eol = get_line_end(data, len);
datap += eol;
eol = get_line_end(datap, len);
}
if (eol == 2) {
@ -541,14 +567,16 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
proxy_activate(p);
/* proxy activate will have dealt with
* whatever is left of the buffer */
sfree(data);
return 1;
}
sfree(data);
return 1;
}
}
plug_closing(p->plug, "Network error: Unexpected proxy error",
plug_closing(p->plug, "Proxy error: unexpected proxy error",
PROXY_ERROR_UNEXPECTED, 0);
return 1;
}
@ -576,8 +604,8 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
char * command;
if (sk_addrtype(p->remote_addr) != AF_INET) {
plug_closing(p->plug, "Network error: SOCKS version 4 does not support IPv6",
PROXY_ERROR_GENERAL, 0);
plug_closing(p->plug, "Proxy error: SOCKS version 4 does"
" not support IPv6", PROXY_ERROR_GENERAL, 0);
return 1;
}
@ -650,14 +678,16 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
* dest. address (4 bytes)
*/
char *data;
int len;
char data[8];
if (bufchain_size(&p->pending_input_data) < 8)
return 1; /* not got anything yet */
/* get the response */
bufchain_prefix(&p->pending_input_data, &data, &len);
bufchain_fetch(&p->pending_input_data, data, 8);
if (data[0] != 0) {
plug_closing(p->plug, "Network error: SOCKS proxy responded with "
plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "
"unexpected reply code version",
PROXY_ERROR_GENERAL, 0);
return 1;
@ -667,23 +697,23 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
switch (data[1]) {
case 92:
plug_closing(p->plug, "Network error: SOCKS server wanted IDENTD on client",
plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",
PROXY_ERROR_GENERAL, 0);
break;
case 93:
plug_closing(p->plug, "Network error: Username and IDENTD on client don't agree",
plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",
PROXY_ERROR_GENERAL, 0);
break;
case 91:
default:
plug_closing(p->plug, "Network error: Error while communicating with proxy",
plug_closing(p->plug, "Proxy error: Error while communicating with proxy",
PROXY_ERROR_GENERAL, 0);
break;
}
return 1;
}
bufchain_consume(&p->pending_input_data, 2);
bufchain_consume(&p->pending_input_data, 8);
/* we're done */
proxy_activate(p);
@ -693,7 +723,7 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
}
}
plug_closing(p->plug, "Network error: Unexpected proxy error",
plug_closing(p->plug, "Proxy error: unexpected proxy error",
PROXY_ERROR_UNEXPECTED, 0);
return 1;
}
@ -770,9 +800,6 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* we'll need to parse, process, and respond to appropriately.
*/
char *data;
int len;
if (p->state == 1) {
/* initial response:
@ -785,12 +812,16 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* 0x03 = CHAP
* 0xff = no acceptable methods
*/
char data[2];
if (bufchain_size(&p->pending_input_data) < 2)
return 1; /* not got anything yet */
/* get the response */
bufchain_prefix(&p->pending_input_data, &data, &len);
bufchain_fetch(&p->pending_input_data, data, 2);
if (data[0] != 5) {
plug_closing(p->plug, "Network error: Error while communicating with proxy",
plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",
PROXY_ERROR_GENERAL, 0);
return 1;
}
@ -800,8 +831,7 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
else if (data[1] == 0x02) p->state = 5; /* username/password authentication */
else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */
else {
plug_closing(p->plug, "Network error: We don't support any of the SOCKS "
"server's authentication methods",
plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",
PROXY_ERROR_GENERAL, 0);
return 1;
}
@ -816,19 +846,25 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* 0 = succeeded
* >0 = failed
*/
char data[2];
if (bufchain_size(&p->pending_input_data) < 2)
return 1; /* not got anything yet */
/* get the response */
bufchain_prefix(&p->pending_input_data, &data, &len);
bufchain_fetch(&p->pending_input_data, data, 2);
if (data[0] != 1) {
plug_closing(p->plug, "Network error: Error while communicating with proxy",
plug_closing(p->plug, "Proxy error: SOCKS password "
"subnegotiation contained wrong version number",
PROXY_ERROR_GENERAL, 0);
return 1;
}
if (data[1] != 0) {
plug_closing(p->plug, "Network error: Proxy refused authentication",
plug_closing(p->plug, "Proxy error: SOCKS proxy refused"
" password authentication",
PROXY_ERROR_GENERAL, 0);
return 1;
}
@ -904,40 +940,66 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* server bound address (variable)
* server bound port (2 bytes) [network order]
*/
char data[5];
int len;
/* First 5 bytes of packet are enough to tell its length. */
if (bufchain_size(&p->pending_input_data) < 5)
return 1; /* not got anything yet */
/* get the response */
bufchain_prefix(&p->pending_input_data, &data, &len);
bufchain_fetch(&p->pending_input_data, data, 5);
if (data[0] != 5) {
plug_closing(p->plug, "Network error: Error while communicating with proxy",
plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",
PROXY_ERROR_GENERAL, 0);
return 1;
}
if (data[1] != 0) {
char buf[256];
strcpy(buf, "Proxy error: ");
switch (data[1]) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
default:
plug_closing(p->plug, "Network error: Error while communicating with proxy",
PROXY_ERROR_GENERAL, 0);
case 1: strcat(buf, "General SOCKS server failure"); break;
case 2: strcat(buf, "Connection not allowed by ruleset"); break;
case 3: strcat(buf, "Network unreachable"); break;
case 4: strcat(buf, "Host unreachable"); break;
case 5: strcat(buf, "Connection refused"); break;
case 6: strcat(buf, "TTL expired"); break;
case 7: strcat(buf, "Command not supported"); break;
case 8: strcat(buf, "Address type not supported"); break;
default: sprintf(buf+strlen(buf),
"Unrecognised SOCKS error code %d",
data[1]);
break;
}
plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);
return 1;
}
/*
* Eat the rest of the reply packet.
*/
len = 6; /* first 4 bytes, last 2 */
switch (data[3]) {
case 1: len += 4; break; /* IPv4 address */
case 4: len += 16; break;/* IPv6 address */
case 3: len += (unsigned char)data[4]; break; /* domain name */
default:
plug_closing(p->plug, "Proxy error: SOCKS proxy returned "
"unrecognised address format",
PROXY_ERROR_GENERAL, 0);
return 1;
}
if (bufchain_size(&p->pending_input_data) < len)
return 1; /* not got whole reply yet */
bufchain_consume(&p->pending_input_data, len);
/* we're done */
proxy_activate(p);
/* proxy activate will have dealt with
* whatever is left of the buffer */
return 1;
}