Reject IPv6-mapped IPv4 addresses when using the CIDR notation. (#26254)

It introduces ambiguity as to whether the prefix length should be interpreted as
a v4 prefix length or a v6 prefix length.

See https://issues.apache.org/jira/browse/LUCENE-7920.

Closes #26078
This commit is contained in:
Adrien Grand 2017-08-28 10:04:05 +02:00 committed by GitHub
parent 262ea9534f
commit d692ccf261
4 changed files with 71 additions and 22 deletions

View File

@ -16,6 +16,8 @@
package org.elasticsearch.common.network;
import org.elasticsearch.common.collect.Tuple;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@ -354,4 +356,32 @@ public class InetAddresses {
throw new AssertionError(e);
}
}
/**
* Parse an IP address and its prefix length using the CIDR notation.
* @throws IllegalArgumentException if the string is not formatted as {@code ip_address/prefix_length}
* @throws IllegalArgumentException if the IP address is an IPv6-mapped ipv4 address
* @throws IllegalArgumentException if the prefix length is not in 0-32 for IPv4 addresses and 0-128 for IPv6 addresses
* @throws NumberFormatException if the prefix length is not an integer
*/
public static Tuple<InetAddress, Integer> parseCidr(String maskedAddress) {
String[] fields = maskedAddress.split("/");
if (fields.length == 2) {
final String addressString = fields[0];
final InetAddress address = forString(addressString);
if (addressString.contains(":") && address.getAddress().length == 4) {
throw new IllegalArgumentException("CIDR notation is not allowed with IPv6-mapped IPv4 address [" + addressString +
" as it introduces ambiguity as to whether the prefix length should be interpreted as a v4 prefix length or a" +
" v6 prefix length");
}
final int prefixLength = Integer.parseInt(fields[1]);
if (prefixLength < 0 || prefixLength > 8 * address.getAddress().length) {
throw new IllegalArgumentException("Illegal prefix length [" + prefixLength + "] in [" + maskedAddress +
"]. Must be 0-32 for IPv4 ranges, 0-128 for IPv6 ranges");
}
return new Tuple<>(address, prefixLength);
} else {
throw new IllegalArgumentException("Expected [ip/prefix] but was [" + maskedAddress + "]");
}
}
}

View File

@ -31,6 +31,7 @@ import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -163,14 +164,8 @@ public class IpFieldMapper extends FieldMapper {
}
String term = value.toString();
if (term.contains("/")) {
String[] fields = term.split("/");
if (fields.length == 2) {
InetAddress address = InetAddresses.forString(fields[0]);
int prefixLength = Integer.parseInt(fields[1]);
return InetAddressPoint.newPrefixQuery(name(), address, prefixLength);
} else {
throw new IllegalArgumentException("Expected [ip/prefix] but was [" + term + "]");
}
final Tuple<InetAddress, Integer> cidr = InetAddresses.parseCidr(term);
return InetAddressPoint.newPrefixQuery(name(), cidr.v1(), cidr.v2());
}
InetAddress address = InetAddresses.forString(term);
return InetAddressPoint.newExactQuery(name(), address);

View File

@ -22,6 +22,7 @@ import org.apache.lucene.document.InetAddressPoint;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.network.InetAddresses;
@ -127,21 +128,12 @@ public final class IpRangeAggregationBuilder
}
Range(String key, String mask) {
String[] splits = mask.split("/");
if (splits.length != 2) {
throw new IllegalArgumentException("Expected [ip/prefix_length] but got [" + mask
+ "], which contains zero or more than one [/]");
}
InetAddress value = InetAddresses.forString(splits[0]);
int prefixLength = Integer.parseInt(splits[1]);
// copied from InetAddressPoint.newPrefixQuery
if (prefixLength < 0 || prefixLength > 8 * value.getAddress().length) {
throw new IllegalArgumentException("illegal prefixLength [" + prefixLength
+ "] in [" + mask + "]. Must be 0-32 for IPv4 ranges, 0-128 for IPv6 ranges");
}
final Tuple<InetAddress, Integer> cidr = InetAddresses.parseCidr(mask);
final InetAddress address = cidr.v1();
final int prefixLength = cidr.v2();
// create the lower value by zeroing out the host portion, upper value by filling it with all ones.
byte lower[] = value.getAddress();
byte upper[] = value.getAddress();
byte lower[] = address.getAddress();
byte upper[] = address.getAddress();
for (int i = prefixLength; i < 8 * lower.length; i++) {
int m = 1 << (7 - (i & 7));
lower[i >> 3] &= ~m;

View File

@ -16,7 +16,9 @@
package org.elasticsearch.common.network;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;
import java.net.InetAddress;
import java.net.UnknownHostException;
@ -214,4 +216,34 @@ public class InetAddressesTests extends ESTestCase {
InetAddress ip = InetAddresses.forString(ipStr);
assertEquals("[3ffe::1]", InetAddresses.toUriString(ip));
}
public void testParseCidr() {
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> InetAddresses.parseCidr(""));
assertThat(e.getMessage(), Matchers.containsString("Expected [ip/prefix] but was []"));
e = expectThrows(IllegalArgumentException.class, () -> InetAddresses.parseCidr("192.168.1.42/33"));
assertThat(e.getMessage(), Matchers.containsString("Illegal prefix length"));
e = expectThrows(IllegalArgumentException.class, () -> InetAddresses.parseCidr("::1/129"));
assertThat(e.getMessage(), Matchers.containsString("Illegal prefix length"));
e = expectThrows(IllegalArgumentException.class, () -> InetAddresses.parseCidr("::ffff:0:0/96"));
assertThat(e.getMessage(), Matchers.containsString("CIDR notation is not allowed with IPv6-mapped IPv4 address"));
Tuple<InetAddress, Integer> cidr = InetAddresses.parseCidr("192.168.0.0/24");
assertEquals(InetAddresses.forString("192.168.0.0"), cidr.v1());
assertEquals(Integer.valueOf(24), cidr.v2());
cidr = InetAddresses.parseCidr("::fffe:0:0/95");
assertEquals(InetAddresses.forString("::fffe:0:0"), cidr.v1());
assertEquals(Integer.valueOf(95), cidr.v2());
cidr = InetAddresses.parseCidr("192.168.0.0/32");
assertEquals(InetAddresses.forString("192.168.0.0"), cidr.v1());
assertEquals(Integer.valueOf(32), cidr.v2());
cidr = InetAddresses.parseCidr("::fffe:0:0/128");
assertEquals(InetAddresses.forString("::fffe:0:0"), cidr.v1());
assertEquals(Integer.valueOf(128), cidr.v2());
}
}