From ef283bbd1b3cdbf719e5c1d827e58ff1a7e40f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?= Date: Wed, 5 Oct 2022 20:27:56 +0200 Subject: [PATCH] Fix InetAddress-wrapping of long AFSocketAddresses Long socket addresses, such as non-ASCII addresses in the Abstract Namespace close to the maximum length of 108 bytes may not be representable by our custom address wrapping representation used for InetAddress, resulting in an exception upon bind, etc. This is caused by URL-encoding any non-printable characters, resulting in three UTF-8 characters per byte (3*108+x > 255, an internal limit). Introduce a fixed-length hex-encoding, which is recognizable by a wrapped-hostname string starting with "[%%", followed by a sequence of 2-character hex values (2*108+2+x <= 255). --- .../org/newsclub/net/unix/AFInetAddress.java | 44 ++++++++++++++++--- .../unix/domain/AbstractNamespaceTest.java | 8 ++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java index c8078cfb2..17024a759 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java @@ -31,6 +31,7 @@ import java.net.URLEncoder; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; +import java.util.Locale; import java.util.Objects; /** @@ -87,6 +88,7 @@ class AFInetAddress { }; private static final char PREFIX = '['; + private static final String MARKER_HEX_ENCODING = "%%"; static final String INETADDR_SUFFIX = ".junixsocket"; /** @@ -111,6 +113,23 @@ static final String createUnresolvedHostname(byte[] socketAddress, AFAddressFami } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } + sb.append('.'); + sb.append(af.getJuxString()); + sb.append(INETADDR_SUFFIX); + + String str = sb.toString(); + + if (str.length() < 64 || str.getBytes(StandardCharsets.UTF_8).length <= 255) { + return str; + } + + sb.setLength(0); + sb.append(PREFIX); + sb.append(MARKER_HEX_ENCODING); + for (int i = 0, n = socketAddress.length; i < n; i++) { + sb.append(String.format(Locale.ENGLISH, "%02x", socketAddress[i])); + } + sb.append('.'); sb.append(af.getJuxString()); sb.append(INETADDR_SUFFIX); @@ -185,11 +204,26 @@ static final byte[] unwrapAddress(String hostname, AFAddressFamily af) throws } String encodedHostname = hostname.substring(1, domDot); - try { - return URLDecoder.decode(encodedHostname, StandardCharsets.ISO_8859_1.toString()).getBytes( - StandardCharsets.ISO_8859_1); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(e); + if (encodedHostname.startsWith(MARKER_HEX_ENCODING)) { + // Hex-only encoding + int len = encodedHostname.length(); + if ((len & 1) == 1) { + throw new IllegalStateException("Length of hex-encoded wrapping must be even"); + } + byte[] unwrapped = new byte[(len - 2) / 2]; + for (int i = 2, n = encodedHostname.length(), o = 0; i < n; i += 2, o++) { + int v = Integer.parseInt(encodedHostname.substring(i, i + 2), 16); + unwrapped[o] = (byte) v; + } + return unwrapped; + } else { + // URL-encoding + try { + return URLDecoder.decode(encodedHostname, StandardCharsets.ISO_8859_1.toString()).getBytes( + StandardCharsets.ISO_8859_1); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java index d754a62d8..151ba5215 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java @@ -82,4 +82,12 @@ public void testBindTrailingZeroes() throws Exception { // any sequence of 0's -> null testBind(AFUNIXSocketAddress.of(new byte[] {0, 0, 0}), null); } + + @Test + public void testBindLongAbstractAddress() throws Exception { + byte[] addr = new byte[108]; + addr[1] = '1'; + addr[79] = 'X'; + testBind(AFUNIXSocketAddress.of(addr)); + } }