Skip to content

Commit 5c2a5a7

Browse files
committed
[feature/nro-profile] Add reverse domain check (fixes #27).
1 parent 123b080 commit 5c2a5a7

File tree

5 files changed

+309
-1
lines changed

5 files changed

+309
-1
lines changed

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@
207207
<version>4.1</version>
208208
</dependency>
209209

210+
<dependency>
211+
<groupId>net.ripe.ipresource</groupId>
212+
<artifactId>ipresource</artifactId>
213+
<version>1.46</version>
214+
</dependency>
215+
210216
<!-- Testing -->
211217
<dependency>
212218
<groupId>org.testng</groupId>

src/main/java/net/apnic/rdap/conformance/Utils.java

+110
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.List;
77
import java.util.Set;
88
import java.util.regex.Pattern;
9+
import java.util.regex.Matcher;
910

1011
import com.google.gson.Gson;
1112
import com.google.common.util.concurrent.ListenableFuture;
@@ -528,4 +529,113 @@ public static boolean matchesSearch(final String strPattern,
528529
| Pattern.UNICODE_CASE);
529530
return pattern.matcher(value).matches();
530531
}
532+
533+
/**
534+
* <p>ipv4ArpaToPrefix</p>
535+
*
536+
* Converts an IPv4 reverse domain name into an address prefix.
537+
*
538+
* @param str The reverse domain name.
539+
* @return The address prefix for the domain name.
540+
*/
541+
private static String ipv4ArpaToPrefix(final String str) {
542+
Pattern pattern = Pattern.compile("([0-9\\.]+)\\.in-addr\\.arpa");
543+
Matcher matcher = pattern.matcher(str);
544+
if (matcher.find()) {
545+
String numbers = matcher.group(1);
546+
String[] numberList = numbers.split("\\.");
547+
int prefixLength = numberList.length * 8;
548+
int zeroes = (32 - prefixLength) / 8;
549+
if (zeroes < 0) {
550+
return "";
551+
}
552+
StringBuilder sb = new StringBuilder();
553+
for (int i = numberList.length - 1; i >= 0; i--) {
554+
sb.append(numberList[i]);
555+
if (!(numberList.length == 4 && i == 0)) {
556+
sb.append(".");
557+
}
558+
}
559+
while (zeroes-- > 0) {
560+
sb.append("0");
561+
if (zeroes > 0) {
562+
sb.append(".");
563+
}
564+
}
565+
sb.append("/");
566+
sb.append(prefixLength);
567+
return sb.toString();
568+
} else {
569+
return "";
570+
}
571+
}
572+
573+
/**
574+
* <p>ipv6ArpaToPrefix</p>
575+
*
576+
* Converts an IPv6 reverse domain name into an address prefix.
577+
*
578+
* @param str The reverse domain name.
579+
* @return The address prefix for the domain name.
580+
*/
581+
private static String ipv6ArpaToPrefix(final String str) {
582+
// my ($nums) = ($arpa =~ /^(.*)\.ip6\.arpa/i);
583+
// $nums =~ s/\.//g;
584+
// my $len = (length $nums);
585+
// my $prefix_len = $len * 4;
586+
// $nums = reverse $nums;
587+
// $nums .= '0' x (4 - (($len % 4) || 4));
588+
// my $addr = join ':', ($nums =~ /(.{4})/g);
589+
// if ((length $addr) < 39) {
590+
// $addr .= '::';
591+
// }
592+
// $addr .= '/'.$prefix_len;
593+
// return $addr;
594+
595+
Pattern pattern = Pattern.compile("([0-9A-Fa-f\\.]+)\\.ip6\\.arpa");
596+
Matcher matcher = pattern.matcher(str);
597+
if (matcher.find()) {
598+
String numbers = matcher.group(1);
599+
numbers = numbers.replaceAll("\\.", "");
600+
int len = numbers.length();
601+
int prefixLength = len * 4;
602+
StringBuilder numberSb =
603+
new StringBuilder(numbers).reverse();
604+
int subtract = len % 4;
605+
if (subtract == 0) {
606+
subtract = 4;
607+
}
608+
int zeroes = 4 - subtract;
609+
while (zeroes-- > 0) {
610+
numberSb.append("0");
611+
}
612+
String[] segments =
613+
numberSb.toString().split("(?<=\\G.{4})");
614+
String result = String.join(":", segments);
615+
if (result.length() < 39) {
616+
result = result + "::";
617+
}
618+
result = result + "/" + prefixLength;
619+
return result.toLowerCase();
620+
} else {
621+
return "";
622+
}
623+
}
624+
625+
/**
626+
* <p>arpaToPrefix</p>
627+
*
628+
* Converts a reverse domain name into an address prefix. If the
629+
* domain name is invalid, this will return an empty string.
630+
*
631+
* @param str The reverse domain name.
632+
* @return The address prefix for the domain name.
633+
*/
634+
public static String arpaToPrefix(final String str) {
635+
if (str.contains(".in-addr.arpa")) {
636+
return ipv4ArpaToPrefix(str);
637+
} else {
638+
return ipv6ArpaToPrefix(str);
639+
}
640+
}
531641
}

src/main/java/net/apnic/rdap/conformance/attributetest/Domain.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import net.apnic.rdap.conformance.Utils;
1212
import net.apnic.rdap.conformance.valuetest.Variant;
1313
import net.apnic.rdap.conformance.valuetest.StringTest;
14+
import net.apnic.rdap.conformance.valuetest.ReverseDomainMatch;
1415

1516
/**
1617
* <p>Domain class.</p>
@@ -48,7 +49,7 @@ public boolean run(final Context context, final Result proto,
4849
domainNames.setSearchDetails(key, pattern);
4950
}
5051

51-
return Utils.runTestList(
52+
boolean res = Utils.runTestList(
5253
context, proto, data, knownAttributes, checkUnknown,
5354
Arrays.asList(
5455
new ScalarAttribute("objectClassName",
@@ -63,6 +64,11 @@ public boolean run(final Context context, final Result proto,
6364
new StandardObject()
6465
)
6566
);
67+
68+
ReverseDomainMatch rdmTest = new ReverseDomainMatch();
69+
boolean res2 = rdmTest.run(context, proto, data);
70+
71+
return res && res2;
6672
}
6773

6874
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package net.apnic.rdap.conformance.valuetest;
2+
3+
import net.apnic.rdap.conformance.Result;
4+
import net.apnic.rdap.conformance.Context;
5+
import net.apnic.rdap.conformance.ValueTest;
6+
import net.apnic.rdap.conformance.Utils;
7+
8+
import net.ripe.ipresource.IpResource;
9+
10+
import java.util.*;
11+
12+
/**
13+
* <p>ReverseDomainMatch class.</p>
14+
*
15+
* See RFC 9083 [5.3].
16+
*
17+
* @author Tom Harrison <[email protected]>
18+
* @version 0.7-SNAPSHOT
19+
*/
20+
public final class ReverseDomainMatch implements ValueTest {
21+
/**
22+
* <p>Constructor for ReverseDomainMatch.</p>
23+
*/
24+
public ReverseDomainMatch() { }
25+
26+
/** {@inheritDoc} */
27+
public boolean run(final Context context, final Result proto,
28+
final Object argData) {
29+
Map<String, Object> object =
30+
Utils.castToMap(context, proto, argData);
31+
if (object == null) {
32+
return false;
33+
}
34+
35+
/* Get the domain name for this object. */
36+
String domainName = Utils.castToString(object.get("ldhName"));
37+
if (domainName == null) {
38+
return true;
39+
}
40+
if (!domainName.matches(".*\\.arpa\\.?")) {
41+
return true;
42+
}
43+
String prefix = Utils.arpaToPrefix(domainName);
44+
if (prefix.equals("")) {
45+
Result res = new Result(proto);
46+
res.setStatus(Result.Status.Failure);
47+
res.setInfo("invalid reverse domain name");
48+
context.addResult(res);
49+
return false;
50+
}
51+
52+
IpResource domainIp;
53+
try {
54+
domainIp = IpResource.parse(prefix);
55+
} catch (Exception e) {
56+
Result res = new Result(proto);
57+
res.setStatus(Result.Status.Failure);
58+
res.setInfo("invalid reverse domain name");
59+
context.addResult(res);
60+
return false;
61+
}
62+
63+
/* Get the network for this object (if present). */
64+
Map<String, Object> networkObject =
65+
Utils.castToMap(context, proto, object.get("network"));
66+
if (networkObject == null) {
67+
return true;
68+
}
69+
70+
String startAddress =
71+
Utils.castToString(networkObject.get("startAddress"));
72+
if (startAddress == null) {
73+
Result res = new Result(proto);
74+
res.setStatus(Result.Status.Failure);
75+
res.setInfo("network does not contain start address");
76+
context.addResult(res);
77+
return false;
78+
}
79+
String endAddress =
80+
Utils.castToString(networkObject.get("endAddress"));
81+
if (endAddress == null) {
82+
Result res = new Result(proto);
83+
res.setStatus(Result.Status.Failure);
84+
res.setInfo("network does not contain end address");
85+
context.addResult(res);
86+
return false;
87+
}
88+
89+
IpResource networkIp;
90+
try {
91+
networkIp = IpResource.parse(startAddress + "-" +
92+
endAddress);
93+
} catch (Exception e) {
94+
Result res = new Result(proto);
95+
res.setStatus(Result.Status.Failure);
96+
res.setInfo("invalid network start/end address");
97+
context.addResult(res);
98+
return false;
99+
}
100+
101+
/* Confirm that the domain range is contained within the
102+
* network. */
103+
Result res = new Result(proto);
104+
boolean contains = networkIp.contains(domainIp);
105+
if (contains) {
106+
res.setStatus(Result.Status.Success);
107+
} else {
108+
res.setStatus(Result.Status.Failure);
109+
}
110+
res.setInfo("network range contains domain range");
111+
context.addResult(res);
112+
return contains;
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package net.apnic.rdap.conformance;
2+
3+
import org.testng.annotations.Test;
4+
import static org.testng.Assert.assertEquals;
5+
import static org.testng.Assert.assertTrue;
6+
import static org.testng.Assert.assertNull;
7+
8+
import net.apnic.rdap.conformance.Utils;
9+
import java.io.*;
10+
11+
public class UtilsTest
12+
{
13+
public UtilsTest()
14+
{
15+
}
16+
17+
@Test
18+
public void testIpv4ArpaToPrefix() throws Exception
19+
{
20+
String s;
21+
22+
s = Utils.arpaToPrefix("in-addr.arpa");
23+
assertEquals(s, "",
24+
"Got empty string for invalid domain");
25+
26+
s = Utils.arpaToPrefix("1.in-addr.arpa");
27+
assertEquals(s, "1.0.0.0/8",
28+
"Got correct prefix for a /8");
29+
30+
s = Utils.arpaToPrefix("123.123.in-addr.arpa");
31+
assertEquals(s, "123.123.0.0/16",
32+
"Got correct prefix for a /16");
33+
34+
s = Utils.arpaToPrefix("1.2.3.in-addr.arpa");
35+
assertEquals(s, "3.2.1.0/24",
36+
"Got correct prefix for a /24");
37+
38+
s = Utils.arpaToPrefix("4.3.2.1.in-addr.arpa");
39+
assertEquals(s, "1.2.3.4/32",
40+
"Got correct prefix for a /32");
41+
42+
s = Utils.arpaToPrefix("5.4.3.2.1.in-addr.arpa");
43+
assertEquals(s, "",
44+
"Got empty string for invalid domain (too many segments)");
45+
46+
s = Utils.arpaToPrefix("a.b.c.in-addr.arpa");
47+
assertEquals(s, "",
48+
"Got empty string for invalid domain (contains letters)");
49+
}
50+
51+
@Test
52+
public void testIpv6ArpaToPrefix() throws Exception
53+
{
54+
String s;
55+
56+
s = Utils.arpaToPrefix("ip6.arpa");
57+
assertEquals(s, "",
58+
"Got empty string for invalid domain");
59+
60+
s = Utils.arpaToPrefix("a.ip6.arpa");
61+
assertEquals(s, "a000::/4",
62+
"Got correct prefix for a /4");
63+
64+
s = Utils.arpaToPrefix("a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.ip6.arpa");
65+
assertEquals(s, "dcba:dcba:dcba:dcba:dcba:dcba:dcba:dcba/128",
66+
"Got correct prefix for a /128");
67+
68+
s = Utils.arpaToPrefix("a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.d.a.b.c.ip6.arpa");
69+
assertEquals(s, "cbad:cbad:cbad:cbad:cbad:cbad:cbad:cba0/124",
70+
"Got correct prefix for a /124");
71+
}
72+
}

0 commit comments

Comments
 (0)