Patch for SOCKS4A in OpenSsh

Winton, William WWINTON at stratech.com
Sat May 11 08:10:04 EST 2002


I love SSH's ability to dynamically forward ports using SOCKS (either -D or
DynamicForward) (ie "ssh -D 1081 private.mine.net").  But the thing that has
caused me some pain, is that only SOCKS4 is supported.  The SOCKS4 proxy
specification does not permit hostnames, but only IP addresses.  This isn't
much of a problem if the target host is a public Internet host or otherwise
DNS resolvable target ...  but frequently SSH tunneling is used to get into
a private network, and the DNS which can resolve the hostname is on the
wrong side of the tunnel.  I don't think that SOCKS5 or anything that
complex would be usefully helpful, but there is an addition to SOCKS4
(called SOCKS4A) which does permit host names to be passed in in the SOCKS4
initiation packet.

See http://www.socks.nec.com/protocol/socks4a.protocol for details, and
here's a brief synopsis.

The SOCKS4 initiation packet looks like this:
            +----+----+----+----+----+----+----+----+----+----+....+----+
            | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
            +----+----+----+----+----+----+----+----+----+----+....+----+
# of bytes:   1    1      2              4           variable       1


>For version 4A, if the client cannot resolve the destination host's domain
name to find its IP address, it should set the first three bytes of DSTIP to
NULL and the last byte to a non-zero value. 
>Following the NULL byte terminating USERID, the client must sends the
destination domain name and terminates it with another NULL byte. 

The SOCKS4A initiation packet looks like this (where DSTIP is 0.0.0.X):
 
+----+----+----+----+----+----+----+----+----+----+....+----+----+----+....+
----+
            | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
HOSTNAME     |NULL|
 
+----+----+----+----+----+----+----+----+----+----+....+----+----+----+....+
----+
# of bytes:    1    1      2              4           variable       1
variable       1




I propose a change/addition to SSH (the client program only) which will
permit SOCKS4A proxy connections.  This only requires a relatively minor
change to the "channel_decode_socks4" function in "channels.c" .

Here's a DIFF which will apply this patch to openssh-3.1p1-1:

----------< cut >----------
--- openssh-3.1p1-1/channels.c	Mon Mar  4 20:57:45 2002
+++ openssh-3.1p1-1-socks4a/channels.c	Fri May 10 16:52:12 2002
@@ -908,11 +908,34 @@ channel_decode_socks4(Channel *c, fd_set
 	strlcpy(username, p, sizeof(username));
 	buffer_consume(&c->input, len);
 	buffer_consume(&c->input, 1);		/* trailing '\0' */
 
-	host = inet_ntoa(s4_req.dest_addr);
-	strlcpy(c->path, host, sizeof(c->path));
 	c->host_port = ntohs(s4_req.dest_port);
+	
+	/* check for socks4a vs socks4 */
+	if (0 == (s4_req.dest_addr.s_addr & htonl(IN_CLASSC_NET))) {
+		/*
+		 * is client using socks4a? if the first three octets of the
IP 
+		 * are zero, ie 0.0.0.1, then we get the host name from
after user
+		 */
+		have = buffer_len(&c->input);
+		p = buffer_ptr(&c->input);
+		len = strlen(p);
+		
+		debug2("channel %d: decode socks4a: host %s/%d", c->self, p,
len);
+		if (len > have)
+			fatal("channel %d: decode socks4a: len %d > have
%d",
+		    	c->self, len, have);
+				
+		strlcpy(c->path, p, sizeof(c->path));
+		buffer_consume(&c->input, len);
+		buffer_consume(&c->input, 1);		/* trailing '\0' */
+		
+	} else {
+		/* build the hostname from the IP, ie 44.33.22.11, socks4
style */
+		host = inet_ntoa(s4_req.dest_addr);
+		strlcpy(c->path, host, sizeof(c->path));
+	}
 
 	debug("channel %d: dynamic request: socks4 host %s port %u command
%u",
 	    c->self, host, c->host_port, s4_req.command);
----------< end cut >----------

I did a brief test of this under CYGWIN, but this patch should work
globally.

~ William Winton



More information about the openssh-unix-dev mailing list