Skip to content

Commit c2180d1

Browse files
sercherjaikiran
authored andcommitted
8315767: InetAddress: constructing objects from BSD literal addresses
Reviewed-by: dfuchs, aefimov, michaelm, jpai
1 parent 2a11e0d commit c2180d1

File tree

4 files changed

+301
-20
lines changed

4 files changed

+301
-20
lines changed

src/java.base/share/classes/java/net/Inet4Address.java

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -70,9 +70,9 @@
7070
* <p> When only one part is given, the value is stored directly in
7171
* the network address without any byte rearrangement.
7272
*
73-
* <p> These forms support parts specified in decimal format only.
74-
* For example, the following forms are supported by methods capable
75-
* of parsing textual representations of IPv4 addresses:
73+
* <p> For example, the following (decimal) forms are supported by the methods
74+
* {@link Inet4Address#ofLiteral(String)} and {@link InetAddress#getByName(String)}
75+
* which are capable of parsing textual representations of IPv4 addresses:
7676
* {@snippet :
7777
* // Dotted-decimal 'd.d.d.d' form with four part address literal
7878
* InetAddress.getByName("007.008.009.010"); // ==> /7.8.9.10
@@ -93,8 +93,16 @@
9393
* Inet4Address.ofLiteral("02130706689"); // ==> /127.0.1.1
9494
* }
9595
*
96+
* <p> The above forms adhere to "strict" decimal-only syntax.
97+
* Additionally, the {@link Inet4Address#ofPosixLiteral(String)}
98+
* method implements a POSIX {@code inet_addr} compatible "loose"
99+
* parsing algorithm, allowing octal and hexadecimal address segments.
100+
* Please refer to <a href="https://www.ietf.org/rfc/rfc6943.html#section-3.1.1">
101+
* <i>RFC&nbsp;6943: Issues in Identifier Comparison for Security
102+
* Purposes</i></a>. Aside from {@code Inet4Address.ofPosixLiteral(String)}, all methods only
103+
* support strict decimal parsing.
96104
* <p> For methods that return a textual representation as output
97-
* value, the first form, i.e. a dotted-quad string, is used.
105+
* value, the first form, i.e. a dotted-quad string in strict decimal notation, is used.
98106
*
99107
* <h3> The Scope of a Multicast Address </h3>
100108
*
@@ -112,6 +120,8 @@
112120
* RFC 2365: Administratively Scoped IP Multicast
113121
* @spec https://www.rfc-editor.org/info/rfc790
114122
* RFC 790: Assigned numbers
123+
* @spec https://www.rfc-editor.org/rfc/rfc6943.html#section-3.1.1
124+
* RFC 6943: Issues in Identifier Comparison for Security Purposes
115125
* @since 1.4
116126
*/
117127

@@ -180,6 +190,72 @@ public static Inet4Address ofLiteral(String ipv4AddressLiteral) {
180190
return parseAddressString(ipv4AddressLiteral, true);
181191
}
182192

193+
/**
194+
* Creates an {@code Inet4Address} based on the provided {@linkplain
195+
* Inet4Address##format-posix textual representation of an IPv4 address in
196+
* POSIX {@code inet_addr} compatible form}.
197+
* <p> <a id="format-posix"></a> The method {@code ofPosixLiteral}
198+
* implements <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_addr.html">
199+
* POSIX {@code inet_addr}</a> compatible parsing algorithm, allowing
200+
* octal and hexadecimal address segments. {@code "0"} is the prefix
201+
* for octal numbers, {@code "0x"} and {@code "0X"} are the prefixes
202+
* for hexadecimal numbers. Non-zero address segments that start from
203+
* non-zero digits are parsed as decimal numbers. The following
204+
* (non-decimal) forms are supported by this method:
205+
* {@snippet :
206+
* // Dotted-quad 'x.x.x.x' form with four part address literal
207+
* Inet4Address.ofPosixLiteral("0177.0.0.1"); // ==> /127.0.0.1
208+
* Inet4Address.ofPosixLiteral("0x7F.0.0.1"); // ==> /127.0.0.1
209+
*
210+
* // Dotted-triple 'x.x.x' form with three part address literal,
211+
* // the last part is placed in the rightmost two bytes
212+
* // of the constructed address
213+
* Inet4Address.ofPosixLiteral("0177.0.0402"); // ==> /127.0.1.2
214+
* Inet4Address.ofPosixLiteral("0x7F.0.0x102"); // ==> /127.0.1.2
215+
*
216+
* // Dotted-double 'x.x' form with two part address literal,
217+
* // the last part is placed in the rightmost three bytes
218+
* // of the constructed address
219+
* Inet4Address.ofPosixLiteral("0177.0201003"); // ==> /127.1.2.3
220+
* Inet4Address.ofPosixLiteral("0x7F.0x10203"); // ==> /127.1.2.3
221+
* Inet4Address.ofPosixLiteral("127.66051"); // ==> /127.1.2.3
222+
*
223+
* // Dotless 'x' form with one value that is stored directly in
224+
* // the constructed address bytes without any rearrangement
225+
* Inet4Address.ofPosixLiteral("0100401404"); // ==> /1.2.3.4
226+
* Inet4Address.ofPosixLiteral("0x1020304"); // ==> /1.2.3.4
227+
* Inet4Address.ofPosixLiteral("16909060"); // ==> /1.2.3.4
228+
* }
229+
* <p> If the provided IPv4 address literal cannot represent a
230+
* valid IPv4 address in {@linkplain Inet4Address##format-posix
231+
* POSIX form} an {@code IllegalArgumentException} is thrown.
232+
* <p> This method doesn't block, i.e. no hostname lookup is performed.
233+
*
234+
* @apiNote
235+
* This method produces different results compared to {@linkplain Inet4Address#ofLiteral}
236+
* when {@code posixIPAddressLiteral} parameter contains address segments with
237+
* leading zeroes. An address segment with a leading zero is always parsed as an octal
238+
* number by this method, therefore {@code 0255} (octal) will be parsed as
239+
* {@code 173} (decimal). On the other hand, {@link Inet4Address#ofLiteral
240+
* Inet4Address.ofLiteral} ignores leading zeros, parses all numbers as decimal and produces
241+
* {@code 255}. Where this method would parse {@code 0256.0256.0256.0256} (octal) and
242+
* produce {@code 174.174.174.174} (decimal) in four dotted quad notation,
243+
* {@link Inet4Address#ofLiteral Inet4Address.ofLiteral} will throw
244+
* {@code IllegalArgumentException}.
245+
*
246+
* @param posixIPAddressLiteral a textual representation of an IPv4 address.
247+
* @return an {@link Inet4Address} object with no hostname set, and constructed
248+
* from the provided IPv4 address literal.
249+
* @throws IllegalArgumentException if the {@code posixIPAddressLiteral} cannot be
250+
* parsed as an IPv4 address literal.
251+
* @throws NullPointerException if the {@code posixIPAddressLiteral} is {@code null}.
252+
* @since 23
253+
*/
254+
public static Inet4Address ofPosixLiteral(String posixIPAddressLiteral) {
255+
Objects.requireNonNull(posixIPAddressLiteral);
256+
return parseAddressStringPosix(posixIPAddressLiteral);
257+
}
258+
183259
/**
184260
* Parses the given string as an IPv4 address literal.
185261
* If the given {@code addressLiteral} string cannot be parsed as an IPv4 address literal
@@ -212,6 +288,45 @@ static Inet4Address parseAddressString(String addressLiteral, boolean throwIAE)
212288
return new Inet4Address(null, addrBytes);
213289
}
214290

291+
/**
292+
* Parses the given string as an IPv4 address literal in
293+
* {@linkplain Inet4Address##format-posix POSIX form.}
294+
*
295+
* <p> If the given {@code addressLiteral} string cannot be parsed as an IPv4 address literal
296+
* in POSIX form and {@code throwIAE} is {@code false}, {@code null} is returned.
297+
* If the given {@code addressLiteral} string cannot be parsed as an IPv4 address literal
298+
* and {@code throwIAE} is {@code true}, an {@code IllegalArgumentException}
299+
* is thrown.
300+
*
301+
* @apiNote
302+
* This method produces different results compared to {@linkplain Inet4Address#parseAddressString}
303+
* when {@code addressLiteral} parameter contains address segments with leading
304+
* zeroes. An address segment with a leading zero is always parsed as an octal
305+
* number by this method, therefore {@code 0255} (octal) will be parsed as
306+
* {@code 173} (decimal). On the other hand, {@link Inet4Address#parseAddressString}
307+
* ignores leading zeros, parses all numbers as decimal and produces {@code 255}.
308+
* Where this method would parse {@code 0256.0256.0256.0256} (octal) and produce
309+
* {@code 174.174.174.174} (decimal) in four dotted quad notation, {@linkplain
310+
* Inet4Address#parseAddressString} will either throw {@code IllegalArgumentException}
311+
* or return {@code null}, depending on the value of {@code throwIAE}.
312+
*
313+
* @param addressLiteral IPv4 address literal to parse
314+
* @param throwIAE whether to throw {@code IllegalArgumentException} if the
315+
* given {@code addressLiteral} string cannot be parsed as
316+
* an IPv4 address literal.
317+
* @return {@code Inet4Address} object constructed from the address literal;
318+
* or {@code null} if the literal cannot be parsed as an IPv4 address
319+
* @throws IllegalArgumentException if the given {@code addressLiteral} string
320+
* cannot be parsed as an IPv4 address literal and {@code throwIAE} is {@code true}.
321+
*/
322+
private static Inet4Address parseAddressStringPosix(String addressLiteral) {
323+
byte [] parsedBytes = IPAddressUtil.parseBsdLiteralV4(addressLiteral);
324+
if (parsedBytes == null) {
325+
throw IPAddressUtil.invalidIpAddressLiteral(addressLiteral);
326+
}
327+
return new Inet4Address(null, parsedBytes);
328+
}
329+
215330
/**
216331
* Replaces the object to be serialized with an InetAddress object.
217332
*

src/java.base/share/classes/java/net/InetAddress.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,7 @@ static InetAddress[] getAllByName0 (String host, boolean check)
17221722
* @throws NullPointerException if the {@code ipAddressLiteral} is {@code null}.
17231723
* @see Inet4Address#ofLiteral(String)
17241724
* @see Inet6Address#ofLiteral(String)
1725+
* @see Inet4Address#ofPosixLiteral(String)
17251726
* @since 22
17261727
*/
17271728
public static InetAddress ofLiteral(String ipAddressLiteral) {

src/java.base/share/classes/sun/net/util/IPAddressUtil.java

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -666,40 +666,81 @@ public static int digit(char ch, int radix) {
666666
* {@code false} otherwise.
667667
*/
668668
public static boolean isBsdParsableV4(String input) {
669+
return parseBsdLiteralV4(input) != null;
670+
}
671+
672+
/**
673+
* Parse String as IPv4 address literal by following
674+
* POSIX-style formatting rules.
675+
*
676+
* @param input a String representing an IPv4 address in POSIX format
677+
* @return a byte array representing the IPv4 numeric address
678+
* if input string is a parsable POSIX formatted IPv4 address literal,
679+
* {@code null} otherwise.
680+
*/
681+
public static byte[] parseBsdLiteralV4(String input) {
682+
683+
byte[] res = new byte[]{0,0,0,0};
684+
685+
int len = input.length();
686+
if (len == 0) {
687+
return null;
688+
}
669689
char firstSymbol = input.charAt(0);
670690
// Check if first digit is not a decimal digit
671691
if (parseAsciiDigit(firstSymbol, DECIMAL) == -1) {
672-
return false;
692+
return null;
673693
}
674694

675695
// Last character is dot OR is not a supported digit: [0-9,A-F,a-f]
676-
char lastSymbol = input.charAt(input.length() - 1);
696+
char lastSymbol = input.charAt(len - 1);
677697
if (lastSymbol == '.' || parseAsciiHexDigit(lastSymbol) == -1) {
678-
return false;
698+
return null;
679699
}
680700

681701
// Parse IP address fields
682702
CharBuffer charBuffer = CharBuffer.wrap(input);
683703
int fieldNumber = 0;
704+
long fieldValue = -1L;
684705
while (charBuffer.hasRemaining()) {
685-
long fieldValue = -1L;
706+
fieldValue = -1L;
686707
// Try to parse fields in all supported radixes
687708
for (int radix : SUPPORTED_RADIXES) {
688709
fieldValue = parseV4FieldBsd(radix, charBuffer, fieldNumber);
689710
if (fieldValue >= 0) {
711+
if (fieldValue < 256) {
712+
// Store the parsed field in the byte buffer.
713+
// If the field value is greater than 255, it can only be the last field.
714+
// If it is not the last one, parseV4FieldBsd enforces this limit
715+
// and returns TERMINAL_PARSE_ERROR.
716+
res[fieldNumber] = (byte) fieldValue;
717+
}
690718
fieldNumber++;
691719
break;
692720
} else if (fieldValue == TERMINAL_PARSE_ERROR) {
693-
return false;
721+
return null;
694722
}
695723
}
696724
// If field can't be parsed as one of supported radixes stop
697725
// parsing
698726
if (fieldValue < 0) {
699-
return false;
727+
return null;
700728
}
701729
}
702-
return true;
730+
// The last field value must be non-negative
731+
if (fieldValue < 0) {
732+
return null;
733+
}
734+
// If the last fieldValue is greater than 255 (fieldNumber < 4),
735+
// it is written to the last (4 - (fieldNumber - 1)) octets
736+
// in the network order
737+
if (fieldNumber < 4) {
738+
for (int i = 3; i >= fieldNumber - 1; --i) {
739+
res[i] = (byte) (fieldValue & 255);
740+
fieldValue >>= 8;
741+
}
742+
}
743+
return res;
703744
}
704745

705746
/**

0 commit comments

Comments
 (0)