mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8277608: Address IP Addressing
Reviewed-by: dfuchs, rhalade, michaelm
This commit is contained in:
parent
ddb106be7a
commit
243c76f59f
4 changed files with 288 additions and 12 deletions
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -137,7 +137,7 @@ class HostPortrange {
|
||||||
}
|
}
|
||||||
this.ipv4 = this.literal = ipv4;
|
this.ipv4 = this.literal = ipv4;
|
||||||
if (ipv4) {
|
if (ipv4) {
|
||||||
byte[] ip = IPAddressUtil.textToNumericFormatV4(hoststr);
|
byte[] ip = IPAddressUtil.validateNumericFormatV4(hoststr);
|
||||||
if (ip == null) {
|
if (ip == null) {
|
||||||
throw new IllegalArgumentException("illegal IPv4 address");
|
throw new IllegalArgumentException("illegal IPv4 address");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1254,7 +1254,11 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||||
private byte [] createAddressByteArray(String addrStr) {
|
private byte [] createAddressByteArray(String addrStr) {
|
||||||
byte[] addrArray;
|
byte[] addrArray;
|
||||||
// check if IPV4 address - most likely
|
// check if IPV4 address - most likely
|
||||||
addrArray = IPAddressUtil.textToNumericFormatV4(addrStr);
|
try {
|
||||||
|
addrArray = IPAddressUtil.validateNumericFormatV4(addrStr);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (addrArray == null) {
|
if (addrArray == null) {
|
||||||
addrArray = IPAddressUtil.textToNumericFormatV6(addrStr);
|
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 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) == ':')) {
|
|| (host.charAt(0) == ':')) {
|
||||||
byte[] addr = null;
|
byte[] addr;
|
||||||
int numericZone = -1;
|
int numericZone = -1;
|
||||||
String ifname = null;
|
String ifname = null;
|
||||||
// see if it is IPv4 address
|
// 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) {
|
if (addr == null) {
|
||||||
// This is supposed to be an IPv6 literal
|
// This is supposed to be an IPv6 literal
|
||||||
// Check if a numeric or string zone id is present
|
// Check if a numeric or string zone id is present
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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()) {
|
if (!host.isEmpty()) {
|
||||||
// see if we are being initialized with an IP address.
|
// see if we are being initialized with an IP address.
|
||||||
char ch = host.charAt(0);
|
char ch = host.charAt(0);
|
||||||
if (ch == ':' || Character.digit(ch, 16) != -1) {
|
if (ch == ':' || IPAddressUtil.digit(ch, 16) != -1) {
|
||||||
byte ip[] = IPAddressUtil.textToNumericFormatV4(host);
|
byte ip[] = IPAddressUtil.textToNumericFormatV4(host);
|
||||||
if (ip == null) {
|
if (ip == null) {
|
||||||
ip = IPAddressUtil.textToNumericFormatV6(host);
|
ip = IPAddressUtil.textToNumericFormatV6(host);
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,7 +25,8 @@
|
||||||
|
|
||||||
package sun.net.util;
|
package sun.net.util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import sun.security.action.GetPropertyAction;
|
||||||
|
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -33,6 +34,7 @@ import java.net.InetSocketAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.security.PrivilegedActionException;
|
import java.security.PrivilegedActionException;
|
||||||
|
@ -100,7 +102,7 @@ public class IPAddressUtil {
|
||||||
tmpValue = 0;
|
tmpValue = 0;
|
||||||
newOctet = true;
|
newOctet = true;
|
||||||
} else {
|
} else {
|
||||||
int digit = Character.digit(c, 10);
|
int digit = digit(c, 10);
|
||||||
if (digit < 0) {
|
if (digit < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -125,6 +127,29 @@ public class IPAddressUtil {
|
||||||
return res;
|
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.
|
* Convert IPv6 presentation level address to network order binary form.
|
||||||
* credit:
|
* credit:
|
||||||
|
@ -170,7 +195,7 @@ public class IPAddressUtil {
|
||||||
val = 0;
|
val = 0;
|
||||||
while (i < srcb_length) {
|
while (i < srcb_length) {
|
||||||
ch = srcb[i++];
|
ch = srcb[i++];
|
||||||
int chval = Character.digit(ch, 16);
|
int chval = digit(ch, 16);
|
||||||
if (chval != -1) {
|
if (chval != -1) {
|
||||||
val <<= 4;
|
val <<= 4;
|
||||||
val |= chval;
|
val |= chval;
|
||||||
|
@ -551,4 +576,245 @@ public class IPAddressUtil {
|
||||||
}
|
}
|
||||||
return null;
|
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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* If {@code -1} value is returned the char buffer position is reset to the value
|
||||||
|
* it was before it was called.
|
||||||
|
* <p>
|
||||||
|
* 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:<ul>
|
||||||
|
* <li>Two subsequent dots are observer
|
||||||
|
* <li>Number of dots more than 3
|
||||||
|
* <li>Field value exceeds max allowed
|
||||||
|
* <li>Character is not a valid digit for the requested {@code radix} value, given
|
||||||
|
* that a field has the radix specific prefix
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param radix digits encoding radix to use for parsing. Valid values: 8, 10, 16.
|
||||||
|
* @param buffer {@code CharBuffer} with position set to the field's fist character
|
||||||
|
* @param fieldNumber parsed field number
|
||||||
|
* @return {@code CANT_PARSE_IN_RADIX} if field can not be parsed in requested {@code radix}.
|
||||||
|
* {@code TERMINAL_PARSE_ERROR} if field can't be parsed and the whole parse process should be terminated.
|
||||||
|
* Parsed field value otherwise.
|
||||||
|
*/
|
||||||
|
private static long parseV4FieldBsd(int radix, CharBuffer buffer, int fieldNumber) {
|
||||||
|
int initialPos = buffer.position();
|
||||||
|
long val = 0;
|
||||||
|
int digitsCount = 0;
|
||||||
|
if (!checkPrefix(buffer, radix)) {
|
||||||
|
val = CANT_PARSE_IN_RADIX;
|
||||||
|
}
|
||||||
|
boolean dotSeen = false;
|
||||||
|
while (buffer.hasRemaining() && val != CANT_PARSE_IN_RADIX && !dotSeen) {
|
||||||
|
char c = buffer.get();
|
||||||
|
if (c == '.') {
|
||||||
|
dotSeen = true;
|
||||||
|
// Fail if 4 dots in IP address string.
|
||||||
|
// fieldNumber counter starts from 0, therefore 3
|
||||||
|
if (fieldNumber == 3) {
|
||||||
|
// Terminal state, can stop parsing: too many fields
|
||||||
|
return TERMINAL_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
// Check for literals with two dots, like '1.2..3', '1.2.3..'
|
||||||
|
if (digitsCount == 0) {
|
||||||
|
// Terminal state, can stop parsing: dot with no digits
|
||||||
|
return TERMINAL_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
if (val > 255) {
|
||||||
|
// Terminal state, can stop parsing: too big value for an octet
|
||||||
|
return TERMINAL_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int dv = parseAsciiDigit(c, radix);
|
||||||
|
if (dv >= 0) {
|
||||||
|
digitsCount++;
|
||||||
|
val *= radix;
|
||||||
|
val += dv;
|
||||||
|
} else {
|
||||||
|
// Spotted digit can't be parsed in the requested 'radix'.
|
||||||
|
// The order in which radixes are checked - hex, octal, decimal:
|
||||||
|
// - if symbol is not a valid digit in hex radix - terminal
|
||||||
|
// - if symbol is not a valid digit in octal radix, and given
|
||||||
|
// that octal prefix was observed before - terminal
|
||||||
|
// - if symbol is not a valid digit in decimal radix - terminal
|
||||||
|
return TERMINAL_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (val == CANT_PARSE_IN_RADIX) {
|
||||||
|
buffer.position(initialPos);
|
||||||
|
} else if (!dotSeen) {
|
||||||
|
// It is the last field - check its value
|
||||||
|
// This check will ensure that address strings with less
|
||||||
|
// than 4 fields, i.e. A, A.B and A.B.C address types
|
||||||
|
// contain value less then the allowed maximum for the last field.
|
||||||
|
long maxValue = (1L << ((4 - fieldNumber) * 8)) - 1;
|
||||||
|
if (val > maxValue) {
|
||||||
|
// Terminal state, can stop parsing: last field value exceeds its
|
||||||
|
// allowed value
|
||||||
|
return TERMINAL_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method moves the position of the supplied CharBuffer by analysing the digit prefix
|
||||||
|
// symbols if any.
|
||||||
|
// The caller should reset the position when method returns false.
|
||||||
|
private static boolean checkPrefix(CharBuffer buffer, int radix) {
|
||||||
|
return switch (radix) {
|
||||||
|
case OCTAL -> isOctalFieldStart(buffer);
|
||||||
|
case DECIMAL -> isDecimalFieldStart(buffer);
|
||||||
|
case HEXADECIMAL -> isHexFieldStart(buffer);
|
||||||
|
default -> throw new AssertionError("Not supported radix");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method always moves the position of the supplied CharBuffer
|
||||||
|
// removing the octal prefix symbols '0'.
|
||||||
|
// The caller should reset the position when method returns false.
|
||||||
|
private static boolean isOctalFieldStart(CharBuffer cb) {
|
||||||
|
// .0<EOS> is not treated as octal field
|
||||||
|
if (cb.remaining() < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch two first characters
|
||||||
|
int position = cb.position();
|
||||||
|
char first = cb.get();
|
||||||
|
char second = cb.get();
|
||||||
|
|
||||||
|
// Return false if the first char is not octal prefix '0' or second is a
|
||||||
|
// field separator - parseV4FieldBsd will reset position to start of the field.
|
||||||
|
// '.0.' fields will be successfully parsed in decimal radix.
|
||||||
|
boolean isOctalPrefix = first == '0' && second != '.';
|
||||||
|
|
||||||
|
// If the prefix looks like octal - consume '0', otherwise 'false' is returned
|
||||||
|
// and caller will reset the buffer position.
|
||||||
|
if (isOctalPrefix) {
|
||||||
|
cb.position(position + 1);
|
||||||
|
}
|
||||||
|
return isOctalPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method doesn't move the position of the supplied CharBuffer
|
||||||
|
private static boolean isDecimalFieldStart(CharBuffer cb) {
|
||||||
|
return cb.hasRemaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method always moves the position of the supplied CharBuffer
|
||||||
|
// removing the hexadecimal prefix symbols '0x'.
|
||||||
|
// The caller should reset the position when method returns false.
|
||||||
|
private static boolean isHexFieldStart(CharBuffer cb) {
|
||||||
|
if (cb.remaining() < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char first = cb.get();
|
||||||
|
char second = cb.get();
|
||||||
|
return first == '0' && (second == 'x' || second == 'X');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse ASCII digit in given radix
|
||||||
|
private static int parseAsciiDigit(char c, int radix) {
|
||||||
|
assert radix == OCTAL || radix == DECIMAL || radix == HEXADECIMAL;
|
||||||
|
if (radix == HEXADECIMAL) {
|
||||||
|
return parseAsciiHexDigit(c);
|
||||||
|
}
|
||||||
|
int val = c - '0';
|
||||||
|
return (val < 0 || val >= radix) ? -1 : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse ASCII digit in hexadecimal radix
|
||||||
|
private static int parseAsciiHexDigit(char digit) {
|
||||||
|
char c = Character.toLowerCase(digit);
|
||||||
|
if (c >= 'a' && c <= 'f') {
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
return parseAsciiDigit(c, DECIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supported radixes
|
||||||
|
private static final int HEXADECIMAL = 16;
|
||||||
|
private static final int DECIMAL = 10;
|
||||||
|
private static final int OCTAL = 8;
|
||||||
|
// Order in which field formats are exercised to parse one IP address textual field
|
||||||
|
private static final int[] SUPPORTED_RADIXES = new int[]{HEXADECIMAL, OCTAL, DECIMAL};
|
||||||
|
|
||||||
|
// BSD parser's return values
|
||||||
|
private final static long CANT_PARSE_IN_RADIX = -1L;
|
||||||
|
private final static long TERMINAL_PARSE_ERROR = -2L;
|
||||||
|
|
||||||
|
private static final String ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP = "jdk.net.allowAmbiguousIPAddressLiterals";
|
||||||
|
private static final boolean ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP_VALUE = Boolean.valueOf(
|
||||||
|
GetPropertyAction.privilegedGetProperty(ALLOW_AMBIGUOUS_IPADDRESS_LITERALS_SP, "false"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue