/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.network;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasToString;
public class CidrsTests extends ESTestCase {
public void testNullCidr() {
try {
Cidrs.cidrMaskToMinMax(null);
fail("expected NullPointerException");
} catch (NullPointerException e) {
assertThat(e, hasToString(containsString("cidr")));
}
}
public void testSplittingSlash() {
List<String> cases = new ArrayList<>();
cases.add("1.2.3.4");
cases.add("1.2.3.4/32/32");
cases.add("1.2.3.4/");
cases.add("/");
for (String test : cases) {
try {
Cidrs.cidrMaskToMinMax(test);
fail("expected IllegalArgumentException after splitting");
} catch (IllegalArgumentException e) {
assertThat(e, hasToString(containsString("expected [a.b.c.d, e]")));
assertThat(e, hasToString(containsString("splitting on \"/\"")));
}
}
}
public void testSplittingDot() {
List<String> cases = new ArrayList<>();
cases.add("1.2.3/32");
cases.add("1/32");
cases.add("1./32");
cases.add("1../32");
cases.add("1.../32");
cases.add("1.2.3.4.5/32");
cases.add("/32");
for (String test : cases) {
try {
Cidrs.cidrMaskToMinMax(test);
fail("expected IllegalArgumentException after splitting");
} catch (IllegalArgumentException e) {
assertThat(e, hasToString(containsString("unable to parse")));
assertThat(e, hasToString(containsString("as an IP address literal")));
}
}
}
public void testValidSpecificCases() {
List<Tuple<String, long[]>> cases = new ArrayList<>();
cases.add(new Tuple<>("192.168.0.0/24", new long[]{(192L << 24) + (168 << 16), (192L << 24) + (168 << 16) + (1 << 8)}));
cases.add(new Tuple<>("192.168.128.0/17", new long[]{(192L << 24) + (168 << 16) + (128 << 8), (192L << 24) + (168 << 16) + (128 << 8) + (1 << 15)}));
cases.add(new Tuple<>("128.0.0.0/1", new long[]{128L << 24, (128L << 24) + (1L << 31)})); // edge case
cases.add(new Tuple<>("0.0.0.0/0", new long[]{0, 1L << 32})); // edge case
cases.add(new Tuple<>("0.0.0.0/1", new long[]{0, 1L << 31})); // edge case
cases.add(new Tuple<>(
"192.168.1.1/32",
new long[]{(192L << 24) + (168L << 16) + (1L << 8) + 1L, (192L << 24) + (168L << 16) + (1L << 8) + 1L + 1})
); // edge case
for (Tuple<String, long[]> test : cases) {
long[] actual = Cidrs.cidrMaskToMinMax(test.v1());
assertArrayEquals(test.v1(), test.v2(), actual);
}
}
public void testInvalidSpecificOctetCases() {
List<String> cases = new ArrayList<>();
cases.add("256.0.0.0/8"); // first octet out of range
cases.add("255.256.0.0/16"); // second octet out of range
cases.add("255.255.256.0/24"); // third octet out of range
cases.add("255.255.255.256/32"); // fourth octet out of range
cases.add("abc.0.0.0/8"); // octet that can not be parsed
cases.add("-1.0.0.0/8"); // first octet out of range
cases.add("128.-1.0.0/16"); // second octet out of range
cases.add("128.128.-1.0/24"); // third octet out of range
cases.add("128.128.128.-1/32"); // fourth octet out of range
for (String test : cases) {
try {
Cidrs.cidrMaskToMinMax(test);
fail("expected invalid address");
} catch (IllegalArgumentException e) {
assertThat(e, hasToString(containsString("unable to parse")));
assertThat(e, hasToString(containsString("as an IP address literal")));
}
}
}
public void testInvalidSpecificNetworkMaskCases() {
List<String> cases = new ArrayList<>();
cases.add("128.128.128.128/-1"); // network mask out of range
cases.add("128.128.128.128/33"); // network mask out of range
cases.add("128.128.128.128/abc"); // network mask that can not be parsed
for (String test : cases) {
try {
Cidrs.cidrMaskToMinMax(test);
fail("expected invalid network mask");
} catch (IllegalArgumentException e) {
assertThat(e, hasToString(containsString("network mask")));
}
}
}
public void testValidCombinations() {
for (long i = 0; i < (1 << 16); i++) {
String octetsString = Cidrs.octetsToString(Cidrs.longToOctets(i << 16));
for (int mask = 16; mask <= 32; mask++) {
String test = octetsString + "/" + mask;
long[] actual = Cidrs.cidrMaskToMinMax(test);
assertNotNull(test, actual);
assertEquals(test, 2, actual.length);
assertEquals(test, i << 16, actual[0]);
assertEquals(test, (i << 16) + (1L << (32 - mask)), actual[1]);
}
}
}
public void testInvalidCombinations() {
List<String> cases = new ArrayList<>();
cases.add("192.168.0.1/24"); // invalid because fourth octet is not zero
cases.add("192.168.1.0/16"); // invalid because third octet is not zero
cases.add("192.1.0.0/8"); // invalid because second octet is not zero
cases.add("128.0.0.0/0"); // invalid because first octet is not zero
// create cases that have a bit set outside of the network mask
int value = 1;
for (int i = 0; i < 31; i++) {
cases.add(Cidrs.octetsToCIDR(Cidrs.longToOctets(value), 32 - i - 1));
value <<= 1;
}
for (String test : cases) {
try {
Cidrs.cidrMaskToMinMax(test);
fail("expected invalid combination");
} catch (IllegalArgumentException e) {
assertThat(test, e, hasToString(containsString("invalid address/network mask combination")));
}
}
}
public void testRandomValidCombinations() {
List<Tuple<String, Integer>> cases = new ArrayList<>();
// random number of strings with valid octets and valid network masks
for (int i = 0; i < randomIntBetween(1, 1024); i++) {
int networkMask = randomIntBetween(0, 32);
long mask = (1L << (32 - networkMask)) - 1;
long address = randomLongInIPv4Range() & ~mask;
cases.add(new Tuple<>(Cidrs.octetsToCIDR(Cidrs.longToOctets(address), networkMask), networkMask));
}
for (Tuple<String, Integer> test : cases) {
long[] actual = Cidrs.cidrMaskToMinMax(test.v1());
assertNotNull(test.v1(), actual);
assertEquals(test.v1(), 2, actual.length);
// assert the resulting block has the right size
assertEquals(test.v1(), 1L << (32 - test.v2()), actual[1] - actual[0]);
}
}
private long randomLongInIPv4Range() {
return randomLong() & 0x00000000FFFFFFFFL;
}
}