diff --git a/src/java.base/share/classes/java/net/HostPortrange.java b/src/java.base/share/classes/java/net/HostPortrange.java index 1b97f6ed7ac..5e0696309d0 100644 --- a/src/java.base/share/classes/java/net/HostPortrange.java +++ b/src/java.base/share/classes/java/net/HostPortrange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -137,7 +137,7 @@ class HostPortrange { } this.ipv4 = this.literal = ipv4; if (ipv4) { - byte[] ip = IPAddressUtil.textToNumericFormatV4(hoststr); + byte[] ip = IPAddressUtil.validateNumericFormatV4(hoststr); if (ip == null) { throw new IllegalArgumentException("illegal IPv4 address"); } diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index 659b8892951..3b450ba6736 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -1254,7 +1254,11 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In private byte [] createAddressByteArray(String addrStr) { byte[] addrArray; // check if IPV4 address - most likely - addrArray = IPAddressUtil.textToNumericFormatV4(addrStr); + try { + addrArray = IPAddressUtil.validateNumericFormatV4(addrStr); + } catch (IllegalArgumentException iae) { + return null; + } if (addrArray == null) { addrArray = IPAddressUtil.textToNumericFormatV6(addrStr); } @@ -1470,13 +1474,19 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In } // if host is an IP address, we won't do further lookup - if (Character.digit(host.charAt(0), 16) != -1 + if (IPAddressUtil.digit(host.charAt(0), 16) != -1 || (host.charAt(0) == ':')) { - byte[] addr = null; + byte[] addr; int numericZone = -1; String ifname = null; // see if it is IPv4 address - addr = IPAddressUtil.textToNumericFormatV4(host); + try { + addr = IPAddressUtil.validateNumericFormatV4(host); + } catch (IllegalArgumentException iae) { + var uhe = new UnknownHostException(host); + uhe.initCause(iae); + throw uhe; + } if (addr == null) { // This is supposed to be an IPv6 literal // Check if a numeric or string zone id is present diff --git a/src/java.base/share/classes/java/net/SocketPermission.java b/src/java.base/share/classes/java/net/SocketPermission.java index aca7642931f..07c367b6ba7 100644 --- a/src/java.base/share/classes/java/net/SocketPermission.java +++ b/src/java.base/share/classes/java/net/SocketPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -468,7 +468,7 @@ public final class SocketPermission extends Permission if (!host.isEmpty()) { // see if we are being initialized with an IP address. char ch = host.charAt(0); - if (ch == ':' || Character.digit(ch, 16) != -1) { + if (ch == ':' || IPAddressUtil.digit(ch, 16) != -1) { byte ip[] = IPAddressUtil.textToNumericFormatV4(host); if (ip == null) { ip = IPAddressUtil.textToNumericFormatV6(host); diff --git a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java index 74882e4a7cc..efa68069f13 100644 --- a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java +++ b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,8 @@ package sun.net.util; -import java.io.IOException; +import sun.security.action.GetPropertyAction; + import java.io.UncheckedIOException; import java.net.Inet6Address; import java.net.InetAddress; @@ -33,6 +34,7 @@ import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URL; +import java.nio.CharBuffer; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; @@ -100,7 +102,7 @@ public class IPAddressUtil { tmpValue = 0; newOctet = true; } else { - int digit = Character.digit(c, 10); + int digit = digit(c, 10); if (digit < 0) { return null; } @@ -125,6 +127,29 @@ public class IPAddressUtil { return res; } + /** + * Validates if input string is a valid IPv4 address literal. + * If the "jdk.net.allowAmbiguousIPAddressLiterals" system property is set + * to {@code false}, or is not set then validation of the address string is performed as follows: + * If string can't be parsed by following IETF IPv4 address string literals + * formatting style rules (default one), but can be parsed by following BSD formatting + * style rules, the IPv4 address string content is treated as ambiguous and + * {@code IllegalArgumentException} is thrown. + * + * @param src input string + * @return bytes array if string is a valid IPv4 address string + * @throws IllegalArgumentException if "jdk.net.allowAmbiguousIPAddressLiterals" SP is set to + * "false" and IPv4 address string {@code "src"} is ambiguous + */ + public static byte[] validateNumericFormatV4(String src) { + byte[] parsedBytes = textToNumericFormatV4(src); + if (!ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE + && parsedBytes == null && isBsdParsableV4(src)) { + throw new IllegalArgumentException("Invalid IP address literal: " + src); + } + return parsedBytes; + } + /* * Convert IPv6 presentation level address to network order binary form. * credit: @@ -170,7 +195,7 @@ public class IPAddressUtil { val = 0; while (i < srcb_length) { ch = srcb[i++]; - int chval = Character.digit(ch, 16); + int chval = digit(ch, 16); if (chval != -1) { val <<= 4; val |= chval; @@ -551,4 +576,245 @@ public class IPAddressUtil { } return null; } + + /** + * Returns the numeric value of the character {@code ch} in the + * specified radix. + * + * @param ch the character to be converted. + * @param radix the radix. + * @return the numeric value represented by the character in the + * specified radix. + */ + public static int digit(char ch, int radix) { + if (ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE) { + return Character.digit(ch, radix); + } else { + return parseAsciiDigit(ch, radix); + } + } + + /** + * Try to parse String as IPv4 address literal by following + * BSD-style formatting rules. + * + * @param input input string + * @return {@code true} if input string is parsable as IPv4 address literal, + * {@code false} otherwise. + */ + public static boolean isBsdParsableV4(String input) { + char firstSymbol = input.charAt(0); + // Check if first digit is not a decimal digit + if (parseAsciiDigit(firstSymbol, DECIMAL) == -1) { + return false; + } + + // Last character is dot OR is not a supported digit: [0-9,A-F,a-f] + char lastSymbol = input.charAt(input.length() - 1); + if (lastSymbol == '.' || parseAsciiHexDigit(lastSymbol) == -1) { + return false; + } + + // Parse IP address fields + CharBuffer charBuffer = CharBuffer.wrap(input); + int fieldNumber = 0; + while (charBuffer.hasRemaining()) { + long fieldValue = -1L; + // Try to parse fields in all supported radixes + for (int radix : SUPPORTED_RADIXES) { + fieldValue = parseV4FieldBsd(radix, charBuffer, fieldNumber); + if (fieldValue >= 0) { + fieldNumber++; + break; + } else if (fieldValue == TERMINAL_PARSE_ERROR) { + return false; + } + } + // If field can't be parsed as one of supported radixes stop + // parsing + if (fieldValue < 0) { + return false; + } + } + return true; + } + + /** + * Method tries to parse IP address field that starts from {@linkplain CharBuffer#position() + * current position} of the provided character buffer. + *
+ * This method supports three {@code "radix"} values to decode field values in + * {@code "HEXADECIMAL (radix=16)"}, {@code "DECIMAL (radix=10)"} and + * {@code "OCTAL (radix=8)"} radixes. + *
+ * If {@code -1} value is returned the char buffer position is reset to the value + * it was before it was called. + *
+ * Method returns {@code -2} if formatting illegal for all supported {@code radix} + * values is observed, and there is no point in checking other radix values. + * That includes the following cases: