Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The <action> type attribute can be add,update,fix,remove.
<body>
<release version="3.13.1" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
<!-- FIX -->
<action type="fix" issue="NET-747" due-to="Aryan Ganjoo">Preserve IPv4-mapped IPv6 addresses in SubnetUtils6 parsing and formatting.</action>
<action type="fix" dev="ggregory" due-to="Javid Khan, Gary Gregory">Reject CR/LF in SimpleSMTPHeader and SimpleNNTPHeader fields #397.</action>
<action type="fix" dev="sebb" due-to="Javid Khan">Reject CR/LF in IMAP quoteMailboxName (#399).</action>
<action type="fix" dev="ggregory" due-to="Javid Khan, Gary Gregory">Reject CR/LF in FTP, POP3, NNTP and SMTP command senders (#400).</action>
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/org/apache/commons/net/util/SubnetUtils6.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.apache.commons.net.util;

import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
Expand Down Expand Up @@ -191,7 +192,8 @@ public String toString() {
private static String format(final BigInteger addr) {
final byte[] bytes = toByteArray16(addr);
try {
return InetAddress.getByAddress(bytes).getHostAddress();
// Inet6Address.getByAddress keeps IPv4-mapped addresses (::ffff:0:0/96) as IPv6; InetAddress.getByAddress would convert them to IPv4.
return Inet6Address.getByAddress(null, bytes, -1).getHostAddress();
} catch (final UnknownHostException e) {
// Should never happen with a valid 16-byte array
throw new IllegalStateException("Unexpected error formatting IPv6 address", e);
Expand Down Expand Up @@ -233,6 +235,14 @@ private static byte[] toBytes(final String address) {
if (inetAddr instanceof Inet6Address) {
return inetAddr.getAddress();
}
if (inetAddr instanceof Inet4Address && address.indexOf(':') >= 0) {
// The JDK parses IPv4-mapped IPv6 literals such as "::ffff:192.0.2.1" to an Inet4Address.
final byte[] mapped = new byte[16];
mapped[10] = (byte) 0xff;
mapped[11] = (byte) 0xff;
System.arraycopy(inetAddr.getAddress(), 0, mapped, 12, 4);
return mapped;
}
throw new IllegalArgumentException(String.format(PARSE_FAIL, address) + " - not an IPv6 address");
} catch (final UnknownHostException e) {
throw new IllegalArgumentException(String.format(PARSE_FAIL, address), e);
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/org/apache/commons/net/util/SubnetUtils6Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ void testInvalidIPv4Address() {
assertThrows(IllegalArgumentException.class, () -> new SubnetUtils6("192.168.1.1/24"));
}

@Test
void testIPv4MappedAddresses() {
final SubnetInfo info = new SubnetUtils6("::ffff:0:0/96").getInfo();
assertEquals("0:0:0:0:0:ffff:0:0", info.getNetworkAddress());
assertEquals("0:0:0:0:0:ffff:ffff:ffff", info.getHighAddress());
assertTrue(info.isInRange("::ffff:192.0.2.1"));
assertTrue(info.isInRange("::ffff:c000:201"));
assertFalse(info.isInRange("::192.0.2.1"));
assertEquals("0:0:0:0:0:ffff:c000:201/128", new SubnetUtils6("::ffff:192.0.2.1/128").getInfo().getCidrSignature());
// A high address that falls in the IPv4-mapped range must be formatted as IPv6 and round-trip.
final SubnetInfo low = new SubnetUtils6("::/80").getInfo();
assertEquals("0:0:0:0:0:ffff:ffff:ffff", low.getHighAddress());
assertTrue(low.isInRange(low.getHighAddress()));
// Plain IPv4 text is still rejected.
assertThrows(IllegalArgumentException.class, () -> info.isInRange("192.0.2.1"));
}

@Test
void testInvalidTwoArgConstructor() {
assertThrows(IllegalArgumentException.class, () -> new SubnetUtils6("2001:db8::1", 129));
Expand Down