From 9ac46dd031ec7d2611089284fc0e6217496bd369 Mon Sep 17 00:00:00 2001 From: Elvis Souza Date: Thu, 7 Nov 2024 14:58:42 -0300 Subject: [PATCH] Implementing Stub Solver but not exposing (#598) * stub core implementing * validating structures * address mapper tested * testing and refactoring * refactoring * testing ipv4 validator * testing * new test * refactoring * clean code * docs * clean code * release notes * [Gradle Release Plugin] - new version commit: '3.32.0-snapshot'. * clearing todos * clean code * adjusting version --- RELEASE-NOTES.md | 3 + build.gradle | 3 +- gradle.properties | 2 +- .../solver/stub/HostnameIpExtractor.java | 35 ++++++ .../solver/stub/SolverStub.java | 16 +++ .../addressexpression/AddressExpressions.java | 29 +++++ .../addressexpression/HexadecimalParser.java | 16 +++ .../stub/addressexpression/Ipv4Parser.java | 16 +++ .../stub/addressexpression/Ipv6Parser.java | 27 ++++ .../addressexpression/ParseException.java | 12 ++ .../solver/stub/addressexpression/Parser.java | 7 ++ .../com/mageddo/dnsproxyserver/utils/Ips.java | 8 +- .../solver/stub/AddressExpressionsTest.java | 55 +++++++++ .../solver/stub/HostnameIpExtractorTest.java | 115 ++++++++++++++++++ .../mageddo/dnsproxyserver/utils/IpsTest.java | 17 +++ .../testing/templates/HostnameTemplates.java | 9 ++ 16 files changed, 367 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractor.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/SolverStub.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/AddressExpressions.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/HexadecimalParser.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv4Parser.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv6Parser.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/ParseException.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Parser.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/solver/stub/AddressExpressionsTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractorTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/utils/IpsTest.java diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 6eb319043..bb5b700fc 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,6 @@ +## 3.32.0 +* Implementing Stub Solver but not exposing. #545 + ## 3.31.1 * Adjusting Canary Rate Threshold Circuit Breaker to mark all circuits as open at the start. #533 diff --git a/build.gradle b/build.gradle index e1981e991..14352e17c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ buildscript { mavenLocal() mavenCentral() gradlePluginPortal() + maven { url "https://oss.sonatype.org/service/local/repositories/releases/content" } } } @@ -56,7 +57,7 @@ dependencies { implementation('jakarta.enterprise:jakarta.enterprise.cdi-api:2.0.2') implementation('jakarta.ws.rs:jakarta.ws.rs-api:2.1.6') - implementation('com.mageddo.commons:commons-lang:0.1.16') + implementation('com.mageddo.commons:commons-lang:0.1.21') implementation('org.apache.commons:commons-exec:1.3') implementation('ch.qos.logback:logback-classic:1.5.6') diff --git a/gradle.properties b/gradle.properties index 84a45b516..59e3a9861 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=3.31.1-snapshot +version=3.32.0-snapshot diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractor.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractor.java new file mode 100644 index 000000000..1a5440e75 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractor.java @@ -0,0 +1,35 @@ +package com.mageddo.dnsproxyserver.solver.stub; + +import com.mageddo.dnsproxyserver.solver.stub.addressexpression.AddressExpressions; +import com.mageddo.dnsproxyserver.solver.stub.addressexpression.ParseException; +import com.mageddo.net.IP; +import org.apache.commons.lang3.Validate; + +public class HostnameIpExtractor { + + public static IP extract(String hostname, String domain) { + + hostname = removeDomainFrom(hostname, domain); + Validate.notBlank(hostname, "Hostname is empty"); + + RuntimeException lastException = null; + for (int i = 0; i < hostname.length(); i++) { + try { + return AddressExpressions.toIp(hostname.substring(i)); + } catch (ParseException e) { + lastException = e; + } + } + + throw lastException; + } + + static String removeDomainFrom(String hostname, String domain) { + final var idx = hostname.indexOf(domain); + if (idx < 0) { + return hostname; + } + return hostname.substring(0, idx - 1); + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/SolverStub.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/SolverStub.java new file mode 100644 index 000000000..222fab939 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/SolverStub.java @@ -0,0 +1,16 @@ +package com.mageddo.dnsproxyserver.solver.stub; + +import com.mageddo.dnsproxyserver.solver.Response; +import com.mageddo.dnsproxyserver.solver.Solver; +import org.xbill.DNS.Message; + +/** + * Extract the address from the hostname then answer. + * Inspired at nip.io and sslip.io, see #545. + */ +public class SolverStub implements Solver { + @Override + public Response handle(Message query) { + return null; + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/AddressExpressions.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/AddressExpressions.java new file mode 100644 index 000000000..385be8b06 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/AddressExpressions.java @@ -0,0 +1,29 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +import com.mageddo.net.IP; + +import java.util.List; + +public class AddressExpressions { + + public static IP toIp(String addressExpression) { + RuntimeException lastException = null; + for (final var parser : buildParsers()) { + try { + return parser.parse(addressExpression); + } catch (ParseException e) { + lastException = e; + } + } + throw lastException; + } + + static List buildParsers() { + return List.of( + new Ipv6Parser(), + new Ipv4Parser(), + new HexadecimalParser() + ); + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/HexadecimalParser.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/HexadecimalParser.java new file mode 100644 index 000000000..e653a3d8a --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/HexadecimalParser.java @@ -0,0 +1,16 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +import com.mageddo.net.IP; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; + +public class HexadecimalParser implements Parser { + @Override + public IP parse(String addressExpression) { + try { + return IP.of(Hex.decodeHex(addressExpression)); + } catch (DecoderException e) { + throw new ParseException("not a hexadecimal address: " + addressExpression, e); + } + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv4Parser.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv4Parser.java new file mode 100644 index 000000000..e8e7df4c8 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv4Parser.java @@ -0,0 +1,16 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +import com.mageddo.dnsproxyserver.utils.Ips; +import com.mageddo.net.IP; + +public class Ipv4Parser implements Parser { + @Override + public IP parse(String addressExpression) { + final var normalizedStr = addressExpression.replaceAll("-", "."); + if (Ips.isIpv4(normalizedStr)) { + return IP.of(normalizedStr); + } + throw new ParseException("invalid ipv4 address expression: " + addressExpression); + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv6Parser.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv6Parser.java new file mode 100644 index 000000000..5d197c8d8 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Ipv6Parser.java @@ -0,0 +1,27 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +import com.mageddo.net.IP; +import org.apache.commons.lang3.StringUtils; + +public class Ipv6Parser implements Parser { + @Override + public IP parse(String addressExpression) { + if (isIpv6(addressExpression)) { + try { + return IP.of(addressExpression.replaceAll("-", ":")); + } catch (RuntimeException e){ + throw throwError(addressExpression); + } + } + throw throwError(addressExpression); + } + + RuntimeException throwError(String addressExpression) { + throw new ParseException("Not ipv6 address: " + addressExpression); + } + + static boolean isIpv6(String addressExpression) { + return (addressExpression.contains("--") || StringUtils.countMatches(addressExpression, "-") >= IP.IPV4_BYTES) + && !addressExpression.contains("."); + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/ParseException.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/ParseException.java new file mode 100644 index 000000000..086d71ce6 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/ParseException.java @@ -0,0 +1,12 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +public class ParseException extends RuntimeException { + + public ParseException(String message) { + super(message); + } + + public ParseException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Parser.java b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Parser.java new file mode 100644 index 000000000..90ccb7829 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/stub/addressexpression/Parser.java @@ -0,0 +1,7 @@ +package com.mageddo.dnsproxyserver.solver.stub.addressexpression; + +import com.mageddo.net.IP; + +public interface Parser { + IP parse(String addressExpression); +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java b/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java index 3208774b5..4dc6cabf0 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java +++ b/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java @@ -1,5 +1,6 @@ package com.mageddo.dnsproxyserver.utils; +import com.mageddo.commons.regex.Regexes; import com.mageddo.net.IP; import com.mageddo.utils.Bytes; import org.apache.commons.lang3.StringUtils; @@ -10,9 +11,14 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; +import java.util.regex.Pattern; public class Ips { + private static final Pattern IPV4_REGEX = Pattern.compile( + "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$" + ); + private Ips() { } @@ -73,7 +79,7 @@ public static boolean isIpv6(String v) { } public static boolean isIpv4(String v) { - return !isIpv6(v); + return Regexes.matches(v, IPV4_REGEX); } public static Short[] toShortArray(String ip) { diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/stub/AddressExpressionsTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/stub/AddressExpressionsTest.java new file mode 100644 index 000000000..59e351f55 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/stub/AddressExpressionsTest.java @@ -0,0 +1,55 @@ +package com.mageddo.dnsproxyserver.solver.stub; + +import com.mageddo.dnsproxyserver.solver.stub.addressexpression.AddressExpressions; +import com.mageddo.net.IP; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AddressExpressionsTest { + + @Test + void mustConvertIpv4ExpressionSplitByDots() { + final var exp = "10.0.0.1"; + + final var addr = AddressExpressions.toIp(exp); + + assertEquals(IP.of(exp), addr); + } + + @Test + void mustConvertIpv4ExpressionSplitByDash() { + final var exp = "10-0-0-1"; + + final var addr = AddressExpressions.toIp(exp); + + assertEquals(IP.of("10.0.0.1"), addr); + } + + @Test + void mustConvertIpv6ExpressionSplitByDash() { + final var exp = "a--1"; + + final var addr = AddressExpressions.toIp(exp); + + assertEquals(IP.of("a::1"), addr); + } + + @Test + void mustConvertExpandedIpv6ExpressionSplitByDash() { + final var exp = "000a-0-0-0-0-0-0-0001"; + + final var addr = AddressExpressions.toIp(exp); + + assertEquals(IP.of("a::1"), addr); + } + + @Test + void mustConvertHexadecimal() { + final var exp = "0a000803"; + + final var addr = AddressExpressions.toIp(exp); + + assertEquals(IP.of("10.0.8.3"), addr); + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractorTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractorTest.java new file mode 100644 index 000000000..db178cf21 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/stub/HostnameIpExtractorTest.java @@ -0,0 +1,115 @@ +package com.mageddo.dnsproxyserver.solver.stub; + +import com.mageddo.net.IP; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import testing.templates.HostnameTemplates; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HostnameIpExtractorTest { + + static final String SSLIP_IO = "sslip.io"; + static final String NIP_IO = "nip.io"; + + @Test + @DisplayName("Must extract ipv4 from hostname starting with name, using dot-decimal notation") + void mustExtractIpv4IpFromHostnameStartingWithNameUsingDotDecimalNotation() { + final var hostname = HostnameTemplates.startingWithNameDotDecimalNotation(); + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("192.168.0.1"), addr); + } + + @Test + @DisplayName("Must extract ipv4 from hostname starting with name, separated by dash, using dot-decimal notation") + void mustExtractIpv4IpFromHostnameStartingWithNameSeparatedByDashUsingDotDecimalNotation() { + final var hostname = HostnameTemplates.startingWithNameDashSeparationDotDecimalNotation(); + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("192.168.0.2"), addr); + } + + @Test + void mustExtractIpWhenAllUsingDots() { + final var hostname = "customer1.app.10.0.0.1.nip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, NIP_IO); + + assertEquals(IP.of("10.0.0.1"), addr); + } + + @Test + void mustExtractIpWhenAllUsingShortIpv6() { + final var hostname = "customer1.app.a--1.sslip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("a:0:0:0:0:0:0:1"), addr); + } + + @Test + void mustExtractIpv4FromTheEnd() { + final var hostname = "116-116-203-255-68.nip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, NIP_IO); + + assertEquals(IP.of("116.203.255.68"), addr); + } + + @Test + void mustExtractIpWhenAllUsingShortIpv6EvenWhenNameIsAValidHexadecimal() { + final var hostname = "bb.a--1.sslip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("a:0:0:0:0:0:0:1"), addr); + } + + @Test + void mustExtractIpv4IpFromLabeledHexadecimal() { + final var hostname = "acme.c0a801fc.sslip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("192.168.1.252"), addr); + } + + @Test + void mustExtractIpv4IpFromHexadecimal() { + final var hostname = "c0a801fc.sslip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("192.168.1.252"), addr); + } + + @Test + void mustExtractIpv6IpFromLabeledHexadecimal() { + final var hostname = "acme.20010DB8000000000000000000000001.sslip.io"; + + final var addr = HostnameIpExtractor.extract(hostname, SSLIP_IO); + + assertEquals(IP.of("2001:db8::1"), addr); + } + + @Test + void mustRemoveDomainFromHostname() { + final var hostname = HostnameTemplates.startingWithNameDotDecimalNotation(); + + final var subdomain = HostnameIpExtractor.removeDomainFrom(hostname, SSLIP_IO); + + assertEquals("www.192.168.0.1", subdomain); + } + + @Test + void mustKeepTheHostnameWhenDomainIsNotPresent() { + final var hostname = "example.com"; + + final var subdomain = HostnameIpExtractor.removeDomainFrom(hostname, SSLIP_IO); + + assertEquals(hostname, subdomain); + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/utils/IpsTest.java b/src/test/java/com/mageddo/dnsproxyserver/utils/IpsTest.java new file mode 100644 index 000000000..8748ea557 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/utils/IpsTest.java @@ -0,0 +1,17 @@ +package com.mageddo.dnsproxyserver.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class IpsTest { + @Test + void mustBeIpv4(){ + assertTrue(Ips.isIpv4("192.168.1.1")); + } + + @Test + void mustNotBeIpv4(){ + assertFalse(Ips.isIpv4("a.a.a.a")); + } +} diff --git a/src/test/java/testing/templates/HostnameTemplates.java b/src/test/java/testing/templates/HostnameTemplates.java index 053801757..8946e47e8 100644 --- a/src/test/java/testing/templates/HostnameTemplates.java +++ b/src/test/java/testing/templates/HostnameTemplates.java @@ -6,4 +6,13 @@ public class HostnameTemplates { public static final String COM_WILDCARD = ".com"; public static final String NGINX_COM_BR = "nginx.com.br"; public static final String HOST_DOCKER = "host.docker"; + + public static String startingWithNameDotDecimalNotation() { + return "www.192.168.0.1.sslip.io"; + } + + public static String startingWithNameDashSeparationDotDecimalNotation() { + return "meusite-192.168.0.2.sslip.io"; + } + }