From: Damien Miller Subject: nc(1): SOCKS4A support To: tech@openbsd.org Date: Wed, 21 May 2025 18:29:56 +1000 Hi, OpenSSH supports the SOCKS protocol via ssh(1)'s -D option. This supports SOCKS4, SOCKS4A and SOCKS5. nc(1) supports SOCKS4 and SOCKS5 (and HTTP CONNECT) but not SOCKS4A, but I'd like to ensure that SOCKS4A is tested while I refactor some code in ssh(1). So this adds SOCKS4A support for nc(1) and adds use of it to the ssh(1) regression tests. FYI SOCKS4A is a simple extension to SOCKS4 that allows passing the destination host as a hostname (SOCKS4 only allows passing IPv4 addresses). ok? Index: regress/usr.bin/ssh/dynamic-forward.sh =================================================================== RCS file: /cvs/src/regress/usr.bin/ssh/dynamic-forward.sh,v diff -u -p -r1.17 dynamic-forward.sh --- regress/usr.bin/ssh/dynamic-forward.sh 8 Mar 2024 11:34:10 -0000 1.17 +++ regress/usr.bin/ssh/dynamic-forward.sh 21 May 2025 08:26:04 -0000 @@ -48,7 +48,7 @@ stop_ssh() { check_socks() { direction=$1 expect_success=$2 - for s in 4 5; do + for s in 4A 4 5; do for h in 127.0.0.1 localhost; do trace "testing ssh socks version $s host $h (-$direction)" ${REAL_SSH} -q -F $OBJ/ssh_config -o \ Index: usr.bin/nc/nc.1 =================================================================== RCS file: /cvs/src/usr.bin/nc/nc.1,v diff -u -p -r1.98 nc.1 --- usr.bin/nc/nc.1 1 Apr 2024 12:40:18 -0000 1.98 +++ usr.bin/nc/nc.1 21 May 2025 08:26:04 -0000 @@ -338,12 +338,18 @@ when talking to the proxy server. Supported protocols are .Cm 4 (SOCKS v.4), +.Cm 4A +(SOCKS v.4A), .Cm 5 (SOCKS v.5) and .Cm connect (HTTPS proxy). If the protocol is not specified, SOCKS version 5 is used. +Note that the SOCKS v.4 protocol is very limited and can only be used when +the destination host can be resolved to an IPv4 address. +The other protocols pass the destination as a string to be interpreted +by the remote proxy and do not have this limitiation. .It Fl x Ar proxy_address Ns Op : Ns Ar port Connect to .Ar destination Index: usr.bin/nc/netcat.c =================================================================== RCS file: /cvs/src/usr.bin/nc/netcat.c,v diff -u -p -r1.229 netcat.c --- usr.bin/nc/netcat.c 2 Nov 2024 17:19:27 -0000 1.229 +++ usr.bin/nc/netcat.c 21 May 2025 08:26:04 -0000 @@ -190,6 +190,8 @@ main(int argc, char *argv[]) socksv = -1; /* HTTP proxy CONNECT */ else if (strcmp(optarg, "4") == 0) socksv = 4; /* SOCKS v.4 */ + else if (strcasecmp(optarg, "4A") == 0) + socksv = 44; /* SOCKS v.4A */ else if (strcmp(optarg, "5") == 0) socksv = 5; /* SOCKS v.5 */ else Index: usr.bin/nc/socks.c =================================================================== RCS file: /cvs/src/usr.bin/nc/socks.c,v diff -u -p -r1.31 socks.c --- usr.bin/nc/socks.c 8 Jun 2022 20:20:26 -0000 1.31 +++ usr.bin/nc/socks.c 21 May 2025 08:26:04 -0000 @@ -293,7 +293,7 @@ socks_connect(const char *host, const ch default: errx(1, "connection failed, unsupported address type"); } - } else if (socksv == 4) { + } else if (socksv == 4 || socksv == 44) { /* This will exit on lookup failure */ decode_addrport(host, port, (struct sockaddr *)&addr, sizeof(addr), 1, 0); @@ -302,10 +302,22 @@ socks_connect(const char *host, const ch buf[0] = SOCKS_V4; buf[1] = SOCKS_CONNECT; /* connect */ memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port); - memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); + if (socksv == 4) { + memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); + } else { + /* SOCKS4A uses addr of 0.0.0.x, and hostname later */ + buf[4] = buf[5] = buf[6] = 0; + buf[7] = 1; + } buf[8] = 0; /* empty username */ wlen = 9; - + if (socksv == 44) { + /* SOCKS4A has nul-terminated hostname after user */ + if (strlcpy(buf + 9, host, + sizeof(buf) - 9) >= sizeof(buf) - 9) + errx(1, "hostname too big"); + wlen = 9 + strlen(host) + 1; + } cnt = atomicio(vwrite, proxyfd, buf, wlen); if (cnt != wlen) err(1, "write failed (%zu/%zu)", cnt, wlen);