From: simon Date: Thu, 11 Jul 2013 17:23:56 +0000 (+0000) Subject: Get rid of the fixed-size 'hostname' buffer in every port-forwarded X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/commitdiff_plain/4fba1c1f510d22a19b075195c8943cd933419f4e 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. git-svn-id: svn://svn.tartarus.org/sgt/putty@9903 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/portfwd.c b/portfwd.c index 39ff5972..70b89100 100644 --- a/portfwd.c +++ b/portfwd.c @@ -33,14 +33,16 @@ struct PFwdPrivate { int dynamic; /* * `hostname' and `port' are the real hostname and port, once - * we know what we're connecting to; they're unused for this - * 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. + * we know what we're connecting to. */ - char hostname[256+8]; + char *hostname; 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 * connection data before we are actually able to send it; so * we may have to temporarily hold some in a dynamically @@ -50,6 +52,26 @@ struct PFwdPrivate { 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, 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; if (pr->dynamic) { while (len--) { - /* - * Throughout SOCKS negotiation, "hostname" is re-used as a - * random protocol buffer with "port" storing the length. - */ - 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); - return 1; + if (pr->sockslen >= pr->sockssize) { + pr->sockssize = pr->sockslen * 5 / 4 + 256; + pr->socksbuf = sresize(pr->socksbuf, pr->sockssize, char); } - pr->hostname[pr->port++] = *data++; + pr->socksbuf[pr->sockslen++] = *data++; /* * Now check what's in the buffer to see if it's a * valid and complete message in the SOCKS exchange. */ if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) && - pr->hostname[0] == 4) { + pr->socksbuf[0] == 4) { /* * SOCKS 4. */ if (pr->dynamic == 1) pr->dynamic = 0x4000; - if (pr->port < 2) continue;/* don't have command code yet */ - if (pr->hostname[1] != 1) { + if (pr->sockslen < 2) + continue; /* don't have command code yet */ + if (pr->socksbuf[1] != 1) { /* Not CONNECT. */ /* Send back a SOCKS 4 error before closing. */ char data[8]; @@ -123,15 +134,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) pfd_close(pr->s); return 1; } - if (pr->port <= 8) continue; /* haven't started user/hostname */ - if (pr->hostname[pr->port-1] != 0) + if (pr->sockslen <= 8) + continue; /* haven't started user/hostname */ + if (pr->socksbuf[pr->sockslen-1] != 0) continue; /* haven't _finished_ user/hostname */ /* * Now we have a full SOCKS 4 request. Check it to * see if it's a SOCKS 4A request. */ - if (pr->hostname[4] == 0 && pr->hostname[5] == 0 && - pr->hostname[6] == 0 && pr->hostname[7] != 0) { + if (pr->socksbuf[4] == 0 && pr->socksbuf[5] == 0 && + pr->socksbuf[6] == 0 && pr->socksbuf[7] != 0) { /* * It's SOCKS 4A. So if we haven't yet * 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; if (pr->dynamic == 0x4000) { pr->dynamic = 0x4001; - pr->port = 8; /* reset buffer to overwrite name */ + pr->sockslen = 8; /* reset buffer to overwrite name */ continue; } - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - len= pr->port - 8; - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - memmove(pr->hostname, pr->hostname + 8, len); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + len = pr->sockslen - 8; + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = snewn(len+1, char); + pr->hostname[len] = '\0'; + memcpy(pr->hostname, pr->socksbuf + 8, len); goto connect; } 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 * then just go. */ - pr->hostname[0] = 0; /* reply version code */ - pr->hostname[1] = 90; /* request granted */ - sk_write(pr->s, pr->hostname, 8); - pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->socksbuf[0] = 0; /* reply version code */ + pr->socksbuf[1] = 90; /* request granted */ + sk_write(pr->s, pr->socksbuf, 8); + pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } } if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) && - pr->hostname[0] == 5) { + pr->socksbuf[0] == 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. */ - if (pr->port < 2) continue;/* no method count yet */ - if (pr->port < 2 + (unsigned char)pr->hostname[1]) + if (pr->sockslen < 2) + continue; /* no method count yet */ + if (pr->sockslen < 2 + (unsigned char)pr->socksbuf[1]) continue; /* no methods yet */ method = 0xFF; /* invalid */ - for (i = 0; i < (unsigned char)pr->hostname[1]; i++) - if (pr->hostname[2+i] == 0) { + for (i = 0; i < (unsigned char)pr->socksbuf[1]; i++) + if (pr->socksbuf[2+i] == 0) { method = 0;/* no auth */ break; } @@ -197,7 +212,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) data[1] = method; sk_write(pr->s, data, 2); pr->dynamic = 0x5001; - pr->port = 0; /* re-empty the buffer */ + pr->sockslen = 0; /* re-empty the buffer */ continue; } @@ -219,16 +234,16 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) reply[0] = 5; /* VER */ reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */ - if (pr->port < 6) continue; - atype = (unsigned char)pr->hostname[3]; + if (pr->sockslen < 6) continue; + atype = (unsigned char)pr->socksbuf[3]; if (atype == 1) /* IPv4 address */ alen = 4; if (atype == 4) /* IPv6 address */ alen = 16; if (atype == 3) /* domain name has leading length */ - alen = 1 + (unsigned char)pr->hostname[4]; - if (pr->port < 6 + alen) continue; - if (pr->hostname[1] != 1 || pr->hostname[2] != 0) { + alen = 1 + (unsigned char)pr->socksbuf[4]; + if (pr->sockslen < 6 + alen) continue; + if (pr->socksbuf[1] != 1 || pr->socksbuf[2] != 0) { /* Not CONNECT or reserved field nonzero - error */ reply[1] = 1; /* generic failure */ 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 * 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) { /* REP=0 (success) already */ sk_write(pr->s, (char *) reply, lenof(reply)); - sprintf(pr->hostname, "%d.%d.%d.%d", - (unsigned char)pr->hostname[4], - (unsigned char)pr->hostname[5], - (unsigned char)pr->hostname[6], - (unsigned char)pr->hostname[7]); + pr->hostname = dupprintf("%d.%d.%d.%d", + (unsigned char)pr->socksbuf[4], + (unsigned char)pr->socksbuf[5], + (unsigned char)pr->socksbuf[6], + (unsigned char)pr->socksbuf[7]); goto connect; } else if (atype == 3) { /* REP=0 (success) already */ 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'; + memcpy(pr->hostname, pr->socksbuf + 5, alen-1); goto connect; } else { /* @@ -283,6 +299,8 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len) * connection. */ connect: + sfree(pr->socksbuf); + pr->socksbuf = NULL; /* * 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. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->throttled = pr->throttle_override = 0; 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, 0, 1, 0, 0, (Plug) pr, conf); if ((err = sk_socket_error(*s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -396,8 +413,7 @@ static int pfd_accepting(Plug p, OSSocket sock) const char *err; org = (struct PFwdPrivate *)p; - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; @@ -405,7 +421,7 @@ static int pfd_accepting(Plug p, OSSocket sock) pr->s = s = sk_register(sock, (Plug) pr); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); 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_! */ } else { pr->dynamic = 0; - strcpy(pr->hostname, org->hostname); + pr->hostname = dupstr(org->hostname); pr->port = org->port; pr->c = new_sock_channel(org->backhandle, s); if (pr->c == NULL) { - sfree(pr); + free_portfwd_private(pr); return 1; } else { /* 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. */ - pr = snew(struct PFwdPrivate); - pr->buffer = NULL; + pr = new_portfwd_private(); pr->fn = &fn_table; pr->c = NULL; if (desthost) { - strcpy(pr->hostname, desthost); + pr->hostname = dupstr(desthost); pr->port = destport; pr->dynamic = 0; } else @@ -477,7 +492,7 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr, !conf_get_int(conf, CONF_lport_acceptall), conf, address_family); if ((err = sk_socket_error(s)) != NULL) { - sfree(pr); + free_portfwd_private(pr); return err; } @@ -497,8 +512,7 @@ void pfd_close(Socket s) pr = (struct PFwdPrivate *) sk_get_private_ptr(s); - sfree(pr->buffer); - sfree(pr); + free_portfwd_private(pr); sk_close(s); }