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

Get rid of the fixed-size 'hostname' buffer in every port-forwarded

connection, and replace it with sensible dynamically allocated
storage. While I'm at it, get rid of the disgusting dual use between
storing an actual hostname and storing an incoming SOCKS request; we
now have a separate pointer variable for each.

[originally from svn r9903]
This commit is contained in:
Simon Tatham 2013-07-11 17:23:56 +00:00
parent 8b6a8b617f
commit bc2076185e

166
portfwd.c
View File

@ -33,13 +33,15 @@ struct PFwdPrivate {
int dynamic; int dynamic;
/* /*
* `hostname' and `port' are the real hostname and port, once * `hostname' and `port' are the real hostname and port, once
* we know what we're connecting to; they're unused for this * we know what we're connecting to.
* purpose while conducting a local SOCKS exchange, which means
* we can also use them as a buffer and pointer for reading
* data from the SOCKS client.
*/ */
char hostname[256+8]; char *hostname;
int port; int port;
/*
* `socksbuf' is the buffer we use to accumulate a SOCKS request.
*/
char *socksbuf;
int sockslen, sockssize;
/* /*
* When doing dynamic port forwarding, we can receive * When doing dynamic port forwarding, we can receive
* connection data before we are actually able to send it; so * connection data before we are actually able to send it; so
@ -50,6 +52,26 @@ struct PFwdPrivate {
int buflen; int buflen;
}; };
static struct PFwdPrivate *new_portfwd_private(void)
{
struct PFwdPrivate *pr = snew(struct PFwdPrivate);
pr->hostname = NULL;
pr->socksbuf = NULL;
pr->sockslen = pr->sockssize = 0;
pr->buffer = NULL;
return pr;
}
static void free_portfwd_private(struct PFwdPrivate *pr)
{
if (!pr)
return;
sfree(pr->hostname);
sfree(pr->socksbuf);
sfree(pr->buffer);
sfree(pr);
}
static void pfd_log(Plug plug, int type, SockAddr addr, int port, static void pfd_log(Plug plug, int type, SockAddr addr, int port,
const char *error_msg, int error_code) const char *error_msg, int error_code)
{ {
@ -83,37 +105,26 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
if (pr->dynamic) { if (pr->dynamic) {
while (len--) { while (len--) {
/* if (pr->sockslen >= pr->sockssize) {
* Throughout SOCKS negotiation, "hostname" is re-used as a pr->sockssize = pr->sockslen * 5 / 4 + 256;
* random protocol buffer with "port" storing the length. pr->socksbuf = sresize(pr->socksbuf, pr->sockssize, char);
*/
if (pr->port >= lenof(pr->hostname)) {
/* Request too long. */
if ((pr->dynamic >> 12) == 4) {
/* Send back a SOCKS 4 error before closing. */
char data[8];
memset(data, 0, sizeof(data));
data[1] = 91; /* generic `request rejected' */
sk_write(pr->s, data, 8);
} }
pfd_close(pr->s); pr->socksbuf[pr->sockslen++] = *data++;
return 1;
}
pr->hostname[pr->port++] = *data++;
/* /*
* Now check what's in the buffer to see if it's a * Now check what's in the buffer to see if it's a
* valid and complete message in the SOCKS exchange. * valid and complete message in the SOCKS exchange.
*/ */
if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) && if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) &&
pr->hostname[0] == 4) { pr->socksbuf[0] == 4) {
/* /*
* SOCKS 4. * SOCKS 4.
*/ */
if (pr->dynamic == 1) if (pr->dynamic == 1)
pr->dynamic = 0x4000; pr->dynamic = 0x4000;
if (pr->port < 2) continue;/* don't have command code yet */ if (pr->sockslen < 2)
if (pr->hostname[1] != 1) { continue; /* don't have command code yet */
if (pr->socksbuf[1] != 1) {
/* Not CONNECT. */ /* Not CONNECT. */
/* Send back a SOCKS 4 error before closing. */ /* Send back a SOCKS 4 error before closing. */
char data[8]; char data[8];
@ -123,15 +134,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
pfd_close(pr->s); pfd_close(pr->s);
return 1; return 1;
} }
if (pr->port <= 8) continue; /* haven't started user/hostname */ if (pr->sockslen <= 8)
if (pr->hostname[pr->port-1] != 0) continue; /* haven't started user/hostname */
if (pr->socksbuf[pr->sockslen-1] != 0)
continue; /* haven't _finished_ user/hostname */ continue; /* haven't _finished_ user/hostname */
/* /*
* Now we have a full SOCKS 4 request. Check it to * Now we have a full SOCKS 4 request. Check it to
* see if it's a SOCKS 4A request. * see if it's a SOCKS 4A request.
*/ */
if (pr->hostname[4] == 0 && pr->hostname[5] == 0 && if (pr->socksbuf[4] == 0 && pr->socksbuf[5] == 0 &&
pr->hostname[6] == 0 && pr->hostname[7] != 0) { pr->socksbuf[6] == 0 && pr->socksbuf[7] != 0) {
/* /*
* It's SOCKS 4A. So if we haven't yet * It's SOCKS 4A. So if we haven't yet
* collected the host name, we should continue * collected the host name, we should continue
@ -141,15 +153,17 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
int len; int len;
if (pr->dynamic == 0x4000) { if (pr->dynamic == 0x4000) {
pr->dynamic = 0x4001; pr->dynamic = 0x4001;
pr->port = 8; /* reset buffer to overwrite name */ pr->sockslen = 8; /* reset buffer to overwrite name */
continue; continue;
} }
pr->hostname[0] = 0; /* reply version code */ pr->socksbuf[0] = 0; /* reply version code */
pr->hostname[1] = 90; /* request granted */ pr->socksbuf[1] = 90; /* request granted */
sk_write(pr->s, pr->hostname, 8); sk_write(pr->s, pr->socksbuf, 8);
len= pr->port - 8; len = pr->sockslen - 8;
pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2);
memmove(pr->hostname, pr->hostname + 8, len); pr->hostname = snewn(len+1, char);
pr->hostname[len] = '\0';
memcpy(pr->hostname, pr->socksbuf + 8, len);
goto connect; goto connect;
} else { } else {
/* /*
@ -157,21 +171,21 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
* the IP address into the hostname string and * the IP address into the hostname string and
* then just go. * then just go.
*/ */
pr->hostname[0] = 0; /* reply version code */ pr->socksbuf[0] = 0; /* reply version code */
pr->hostname[1] = 90; /* request granted */ pr->socksbuf[1] = 90; /* request granted */
sk_write(pr->s, pr->hostname, 8); sk_write(pr->s, pr->socksbuf, 8);
pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2);
sprintf(pr->hostname, "%d.%d.%d.%d", pr->hostname = dupprintf("%d.%d.%d.%d",
(unsigned char)pr->hostname[4], (unsigned char)pr->socksbuf[4],
(unsigned char)pr->hostname[5], (unsigned char)pr->socksbuf[5],
(unsigned char)pr->hostname[6], (unsigned char)pr->socksbuf[6],
(unsigned char)pr->hostname[7]); (unsigned char)pr->socksbuf[7]);
goto connect; goto connect;
} }
} }
if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) && if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) &&
pr->hostname[0] == 5) { pr->socksbuf[0] == 5) {
/* /*
* SOCKS 5. * SOCKS 5.
*/ */
@ -184,12 +198,13 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
/* /*
* We're receiving a set of method identifiers. * We're receiving a set of method identifiers.
*/ */
if (pr->port < 2) continue;/* no method count yet */ if (pr->sockslen < 2)
if (pr->port < 2 + (unsigned char)pr->hostname[1]) continue; /* no method count yet */
if (pr->sockslen < 2 + (unsigned char)pr->socksbuf[1])
continue; /* no methods yet */ continue; /* no methods yet */
method = 0xFF; /* invalid */ method = 0xFF; /* invalid */
for (i = 0; i < (unsigned char)pr->hostname[1]; i++) for (i = 0; i < (unsigned char)pr->socksbuf[1]; i++)
if (pr->hostname[2+i] == 0) { if (pr->socksbuf[2+i] == 0) {
method = 0;/* no auth */ method = 0;/* no auth */
break; break;
} }
@ -197,7 +212,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
data[1] = method; data[1] = method;
sk_write(pr->s, data, 2); sk_write(pr->s, data, 2);
pr->dynamic = 0x5001; pr->dynamic = 0x5001;
pr->port = 0; /* re-empty the buffer */ pr->sockslen = 0; /* re-empty the buffer */
continue; continue;
} }
@ -219,16 +234,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
reply[0] = 5; /* VER */ reply[0] = 5; /* VER */
reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */ reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */
if (pr->port < 6) continue; if (pr->sockslen < 6) continue;
atype = (unsigned char)pr->hostname[3]; atype = (unsigned char)pr->socksbuf[3];
if (atype == 1) /* IPv4 address */ if (atype == 1) /* IPv4 address */
alen = 4; alen = 4;
if (atype == 4) /* IPv6 address */ if (atype == 4) /* IPv6 address */
alen = 16; alen = 16;
if (atype == 3) /* domain name has leading length */ if (atype == 3) /* domain name has leading length */
alen = 1 + (unsigned char)pr->hostname[4]; alen = 1 + (unsigned char)pr->socksbuf[4];
if (pr->port < 6 + alen) continue; if (pr->sockslen < 6 + alen) continue;
if (pr->hostname[1] != 1 || pr->hostname[2] != 0) { if (pr->socksbuf[1] != 1 || pr->socksbuf[2] != 0) {
/* Not CONNECT or reserved field nonzero - error */ /* Not CONNECT or reserved field nonzero - error */
reply[1] = 1; /* generic failure */ reply[1] = 1; /* generic failure */
sk_write(pr->s, (char *) reply, lenof(reply)); sk_write(pr->s, (char *) reply, lenof(reply));
@ -239,21 +254,22 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
* Now we have a viable connect request. Switch * Now we have a viable connect request. Switch
* on atype. * on atype.
*/ */
pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen); pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+4+alen);
if (atype == 1) { if (atype == 1) {
/* REP=0 (success) already */ /* REP=0 (success) already */
sk_write(pr->s, (char *) reply, lenof(reply)); sk_write(pr->s, (char *) reply, lenof(reply));
sprintf(pr->hostname, "%d.%d.%d.%d", pr->hostname = dupprintf("%d.%d.%d.%d",
(unsigned char)pr->hostname[4], (unsigned char)pr->socksbuf[4],
(unsigned char)pr->hostname[5], (unsigned char)pr->socksbuf[5],
(unsigned char)pr->hostname[6], (unsigned char)pr->socksbuf[6],
(unsigned char)pr->hostname[7]); (unsigned char)pr->socksbuf[7]);
goto connect; goto connect;
} else if (atype == 3) { } else if (atype == 3) {
/* REP=0 (success) already */ /* REP=0 (success) already */
sk_write(pr->s, (char *) reply, lenof(reply)); sk_write(pr->s, (char *) reply, lenof(reply));
memmove(pr->hostname, pr->hostname + 5, alen-1); pr->hostname = snewn(alen, char);
pr->hostname[alen-1] = '\0'; pr->hostname[alen-1] = '\0';
memcpy(pr->hostname, pr->socksbuf + 5, alen-1);
goto connect; goto connect;
} else { } else {
/* /*
@ -283,6 +299,8 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
* connection. * connection.
*/ */
connect: connect:
sfree(pr->socksbuf);
pr->socksbuf = NULL;
/* /*
* Freeze the socket until the SSH server confirms the * Freeze the socket until the SSH server confirms the
@ -358,8 +376,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
/* /*
* Open socket. * Open socket.
*/ */
pr = snew(struct PFwdPrivate); pr = new_portfwd_private();
pr->buffer = NULL;
pr->fn = &fn_table; pr->fn = &fn_table;
pr->throttled = pr->throttle_override = 0; pr->throttled = pr->throttle_override = 0;
pr->ready = 1; pr->ready = 1;
@ -370,7 +387,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
pr->s = *s = new_connection(addr, dummy_realhost, port, pr->s = *s = new_connection(addr, dummy_realhost, port,
0, 1, 0, 0, (Plug) pr, conf); 0, 1, 0, 0, (Plug) pr, conf);
if ((err = sk_socket_error(*s)) != NULL) { if ((err = sk_socket_error(*s)) != NULL) {
sfree(pr); free_portfwd_private(pr);
return err; return err;
} }
@ -396,8 +413,7 @@ static int pfd_accepting(Plug p, OSSocket sock)
const char *err; const char *err;
org = (struct PFwdPrivate *)p; org = (struct PFwdPrivate *)p;
pr = snew(struct PFwdPrivate); pr = new_portfwd_private();
pr->buffer = NULL;
pr->fn = &fn_table; pr->fn = &fn_table;
pr->c = NULL; pr->c = NULL;
@ -405,7 +421,7 @@ static int pfd_accepting(Plug p, OSSocket sock)
pr->s = s = sk_register(sock, (Plug) pr); pr->s = s = sk_register(sock, (Plug) pr);
if ((err = sk_socket_error(s)) != NULL) { if ((err = sk_socket_error(s)) != NULL) {
sfree(pr); free_portfwd_private(pr);
return err != NULL; return err != NULL;
} }
@ -420,12 +436,12 @@ static int pfd_accepting(Plug p, OSSocket sock)
sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */
} else { } else {
pr->dynamic = 0; pr->dynamic = 0;
strcpy(pr->hostname, org->hostname); pr->hostname = dupstr(org->hostname);
pr->port = org->port; pr->port = org->port;
pr->c = new_sock_channel(org->backhandle, s); pr->c = new_sock_channel(org->backhandle, s);
if (pr->c == NULL) { if (pr->c == NULL) {
sfree(pr); free_portfwd_private(pr);
return 1; return 1;
} else { } else {
/* asks to forward to the specified host/port for this */ /* asks to forward to the specified host/port for this */
@ -459,12 +475,11 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
/* /*
* Open socket. * Open socket.
*/ */
pr = snew(struct PFwdPrivate); pr = new_portfwd_private();
pr->buffer = NULL;
pr->fn = &fn_table; pr->fn = &fn_table;
pr->c = NULL; pr->c = NULL;
if (desthost) { if (desthost) {
strcpy(pr->hostname, desthost); pr->hostname = dupstr(desthost);
pr->port = destport; pr->port = destport;
pr->dynamic = 0; pr->dynamic = 0;
} else } else
@ -477,7 +492,7 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
!conf_get_int(conf, CONF_lport_acceptall), !conf_get_int(conf, CONF_lport_acceptall),
conf, address_family); conf, address_family);
if ((err = sk_socket_error(s)) != NULL) { if ((err = sk_socket_error(s)) != NULL) {
sfree(pr); free_portfwd_private(pr);
return err; return err;
} }
@ -497,8 +512,7 @@ void pfd_close(Socket s)
pr = (struct PFwdPrivate *) sk_get_private_ptr(s); pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
sfree(pr->buffer); free_portfwd_private(pr);
sfree(pr);
sk_close(s); sk_close(s);
} }