From 35f7d0fb62bcae6b877ad6125eac480260f0ff42 Mon Sep 17 00:00:00 2001 From: Elvis Souza Date: Thu, 2 Feb 2023 23:35:12 -0300 Subject: [PATCH] DPS 3 - Remote solver complete (#272) * Setup remote servers config * Testing default dns servers * Fixing tests * Fixing test * Fixing tests * Fixing test * Fixing tests * Fixing tests * Fixing all tests * Set default timeout * Some fixes * Adjusting version --- README.alpha.md | 4 +- build.gradle | 3 +- .../mageddo/dnsproxyserver/config/Config.java | 39 ++++-------- .../dnsproxyserver/config/ConfigDAO.java | 5 ++ .../dnsproxyserver/config/Configs.java | 37 +++++++++-- .../config/entrypoint/ConfigFlag.java | 7 ++- .../config/entrypoint/ConfigJson.java | 6 ++ .../config/entrypoint/ConfigJsonV1.java | 19 ++++++ .../config/entrypoint/ConfigJsonV2.java | 8 +++ .../config/entrypoint/JsonConfigs.java | 6 ++ .../{DockerRepository.java => DockerDAO.java} | 2 +- ...toryDefault.java => DockerDAODefault.java} | 2 +- ...RepositoryMock.java => DockerDAOMock.java} | 2 +- .../json/converter/IPConverter.java | 34 +++++++++++ .../dnsproxyserver/quarkus/QuarkusConfig.java | 24 +++++++- .../mageddo/dnsproxyserver/server/dns/IP.java | 30 +++++++++ .../dnsproxyserver/server/dns/IpAddr.java | 61 +++++++++++++++++++ .../dnsproxyserver/server/dns/Messages.java | 6 ++ .../dnsproxyserver/server/dns/UDPServer.java | 8 ++- .../server/dns/solver/DockerSolver.java | 6 +- .../server/dns/solver/RemoteResolvers.java | 28 +++++++++ .../server/dns/solver/RemoteSolver.java | 33 +++++++--- .../server/dns/solver/RemoteSolverConfig.java | 18 ------ .../server/dns/solver/SolverSystem.java | 6 +- .../dnsproxyserver/utils/InetAddresses.java | 16 +++++ src/main/java/com/mageddo/utils/Bytes.java | 19 ++++++ src/main/java/com/mageddo/utils/Files.java | 17 ++++++ src/main/java/com/mageddo/utils/Tests.java | 12 ++++ .../dnsproxyserver/config/ConfigsTest.java | 11 ++-- .../config/flags/ConfigFlagTest.java | 17 +++--- .../server/dns/solver/RemoteSolverTest.java | 31 ++++++++++ .../server/dns/solver/SolverSystemTest.java | 8 +-- .../resources/config-json-v1-test/002.json | 9 +++ src/test/resources/configs-test/001.json | 6 +- src/test/resources/configs-test/004.json | 6 +- src/test/resources/flags-test/002.txt | 4 +- 36 files changed, 454 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/mageddo/dnsproxyserver/config/ConfigDAO.java rename src/main/java/com/mageddo/dnsproxyserver/docker/{DockerRepository.java => DockerDAO.java} (86%) rename src/main/java/com/mageddo/dnsproxyserver/docker/{DockerRepositoryDefault.java => DockerDAODefault.java} (97%) rename src/main/java/com/mageddo/dnsproxyserver/docker/{DockerRepositoryMock.java => DockerDAOMock.java} (90%) create mode 100644 src/main/java/com/mageddo/dnsproxyserver/json/converter/IPConverter.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/server/dns/IpAddr.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteResolvers.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverConfig.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/utils/InetAddresses.java create mode 100644 src/main/java/com/mageddo/utils/Bytes.java create mode 100644 src/main/java/com/mageddo/utils/Files.java create mode 100644 src/main/java/com/mageddo/utils/Tests.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverTest.java diff --git a/README.alpha.md b/README.alpha.md index bcfea07ba..e6dc27f6a 100644 --- a/README.alpha.md +++ b/README.alpha.md @@ -28,7 +28,9 @@ $ ./gradlew shadowJar $ mkdir reflect &&\ $JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=./reflect -jar build/libs/dns-proxy-server*all.jar - /home/typer/Downloads/dns-proxy-server-linux-amd64-2.19.5/dns-proxy-server -default-dns=false -server-port=5481 --conf-path /tmp/xpto.json + /home/typer/Downloads/dns-proxy-server-linux-amd64-2.19.5/dns-proxy-server -default-dns=false -server-port=5481 -web-server-port=5381 + + --conf-path /tmp/xpto.json ``` diff --git a/build.gradle b/build.gradle index 56435e520..08f86a205 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,6 @@ dependencies { compileOnly group: 'com.mageddo.nativeimage', name: 'reflection-config-generator', version: '2.4.3' annotationProcessor group: 'com.mageddo.nativeimage', name: 'reflection-config-generator', version: '2.4.3' - implementation 'io.quarkus:quarkus-arc' implementation 'io.quarkus:quarkus-resteasy' implementation 'io.quarkus:quarkus-resteasy-jsonb' @@ -41,6 +40,8 @@ dependencies { implementation group: 'info.picocli', name: 'picocli', version: '4.7.1' + implementation 'com.mageddo.commons:commons-lang:0.1.3' + testCompileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.+' testAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.+' testImplementation 'io.quarkus:quarkus-junit5' diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/Config.java b/src/main/java/com/mageddo/dnsproxyserver/config/Config.java index bd21ea60e..91dccc7fa 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/Config.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/Config.java @@ -1,13 +1,14 @@ package com.mageddo.dnsproxyserver.config; import com.mageddo.dnsproxyserver.config.entrypoint.LogLevel; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; import com.mageddo.dnsproxyserver.server.dns.SimpleServer; -import com.mageddo.dnsproxyserver.server.dns.solver.RemoteSolverConfig; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NonNull; import lombok.Value; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; /** @@ -24,11 +25,12 @@ public class Config { @NonNull private String version; - // fixme isso nao precisa estar aqui, - // soh precisa ficar no json para ser respondido quando o solver da base local perguntar -// @NonNull -// @Builder.Default -// private List remoteDnsServers = new ArrayList<>(); + @NonNull + @Builder.Default + private List remoteDnsServers = new ArrayList<>(); + +// fixme isso nao precisa estar aqui, +// soh precisa ficar no json para ser respondido quando o solver da base local perguntar // // @NonNull // @Builder.Default @@ -67,30 +69,13 @@ public class Config { @NonNull private Boolean dpsNetworkAutoConnect; + @NonNull + private Path configPath; + public static SimpleServer.Protocol findDnsServerProtocol() { return SimpleServer.Protocol.BOTH; } - public static RemoteSolverConfig findRemoverSolverConfig() { - return new RemoteSolverConfig() - .setIp(new byte[]{8, 8, 8, 8}) - .setPort((short) 53); - } - - @Value - @AllArgsConstructor - public static class DNSServer { - @NonNull - private String ip; - - @NonNull - private Integer port; - - public static DNSServer of(String ip, int port) { - return new DNSServer(ip, port); - } - } - @Value public static class Env { diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/ConfigDAO.java b/src/main/java/com/mageddo/dnsproxyserver/config/ConfigDAO.java new file mode 100644 index 000000000..10e13f863 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/config/ConfigDAO.java @@ -0,0 +1,5 @@ +package com.mageddo.dnsproxyserver.config; + +public interface ConfigDAO { + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java b/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java index 3bb0b1219..8d8502f81 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java @@ -6,13 +6,20 @@ import com.mageddo.dnsproxyserver.config.entrypoint.ConfigProps; import com.mageddo.dnsproxyserver.config.entrypoint.JsonConfigs; import com.mageddo.dnsproxyserver.config.entrypoint.LogLevel; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; import com.mageddo.dnsproxyserver.utils.Numbers; +import com.mageddo.utils.Files; +import com.mageddo.utils.Tests; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import static com.mageddo.dnsproxyserver.utils.ObjectUtils.firstNonBlankRequiring; import static com.mageddo.dnsproxyserver.utils.ObjectUtils.firstNonNullRequiring; @@ -23,11 +30,13 @@ public class Configs { private static Config instance; public static Config build(ConfigFlag configFlag) { - final var jsonConfig = JsonConfigs.loadConfig(toAbsolutePath(configFlag)); - return build(configFlag, ConfigEnv.fromEnv(), jsonConfig); + final var configPath = toAbsolutePath(configFlag).toAbsolutePath(); + final var jsonConfig = JsonConfigs.loadConfig(configPath); + log.info("status=configuring, configFile={}", configPath); + return build(configFlag, ConfigEnv.fromEnv(), jsonConfig, configPath); } - public static Config build(ConfigFlag flag, ConfigEnv env, ConfigJson json) { + public static Config build(ConfigFlag flag, ConfigEnv env, ConfigJson json, Path configPath) { return Config.builder() .version(ConfigProps.getVersion()) .activeEnv(json.getActiveEnv()) @@ -51,9 +60,18 @@ public static Config build(ConfigFlag flag, ConfigEnv env, ConfigJson json) { .dpsNetworkAutoConnect(firstNonNullRequiring( env.getDpsNetworkAutoConnect(), json.getDpsNetworkAutoConnect(), flag.getDpsNetworkAutoConnect() )) + .remoteDnsServers(buildRemoteServers(json.getRemoteDnsServers())) + .configPath(configPath) .build(); } + static List buildRemoteServers(List servers) { + if (servers == null || servers.isEmpty()) { + return Collections.singletonList(IpAddr.of("8.8.8.8:53")); + } + return servers; + } + static LogLevel buildLogLevel(String logLevelName) { final var logLevel = EnumUtils.getEnumIgnoreCase(LogLevel.class, logLevelName); if (logLevel == null) { @@ -72,7 +90,11 @@ public static String parseLogFile(String v) { } public static Config buildAndRegister(String[] args) { - return buildAndRegister(ConfigFlag.parse(args)); + final var config = ConfigFlag.parse(args); + if (BooleanUtils.isTrue(config.getHelp()) || config.isVersion()) { + System.exit(0); + } + return buildAndRegister(config); } public static Config buildAndRegister(ConfigFlag flag) { @@ -84,7 +106,14 @@ public static Config getInstance() { } private static Path toAbsolutePath(ConfigFlag configFlag) { + if (runningInTestsAndNoCustomConfigPath(configFlag)) { + return Files.createTempFileExitOnExit("dns-proxy-server-junit", ".json"); + } return Paths.get(configFlag.getConfigPath()); // todo precisa converter para absolute path?! } + static boolean runningInTestsAndNoCustomConfigPath(ConfigFlag configFlag) { + return !Arrays.toString(configFlag.getArgs()).contains("--conf-path") && Tests.runningOnJunit(); + } + } diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigFlag.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigFlag.java index b5c1c435a..e731a2e3f 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigFlag.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigFlag.java @@ -132,6 +132,9 @@ this way you will probably not have resolution issues by acl (implies dps-networ ) private Boolean help; + @JsonIgnore + private String[] args; + @JsonIgnore private CommandLine commandLine; @@ -148,14 +151,14 @@ public static ConfigFlag parse(String[] args, PrintWriter writer) { commandLine.setUsageHelpWidth(120); final var flags = (ConfigFlag) commandLine.getCommand(); + flags.args = args; flags.commandLine = commandLine; Validate.isTrue(commandLine.execute(args) == 0, "Execution Failed"); final var shouldExit = (Boolean) flags.getCommandLine().getExecutionResult(); if (shouldExit == null || shouldExit) { flags.getCommandLine().getOut().flush(); - commandLine.getErr().write(String.format("%nexiting...%n")); - System.exit(0); + return flags; } return flags; diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJson.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJson.java index 3940379c2..fac386f25 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJson.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJson.java @@ -1,5 +1,9 @@ package com.mageddo.dnsproxyserver.config.entrypoint; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; + +import java.util.List; + public interface ConfigJson { String getActiveEnv(); @@ -23,4 +27,6 @@ public interface ConfigJson { Boolean getDpsNetwork(); Boolean getDpsNetworkAutoConnect(); + + List getRemoteDnsServers(); } diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1.java index e671ace59..38e180eb8 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1.java @@ -1,7 +1,12 @@ package com.mageddo.dnsproxyserver.config.entrypoint; +import com.mageddo.dnsproxyserver.server.dns.IP; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; +import com.mageddo.utils.Bytes; import lombok.Data; +import java.util.List; + @Data public class ConfigJsonV1 implements ConfigJson { @@ -17,6 +22,8 @@ public class ConfigJsonV1 implements ConfigJson { private Boolean registerContainerNames; + private List remoteDnsServers; + @Override public Boolean getDefaultDns() { @@ -42,4 +49,16 @@ public Boolean getDpsNetwork() { public Boolean getDpsNetworkAutoConnect() { return null; } + + @Override + public List getRemoteDnsServers() { + return this.remoteDnsServers + .stream() + .map(it -> toIpAddr(Bytes.toNative(it))) + .toList(); + } + + private IpAddr toIpAddr(byte[] ip) { + return IpAddr.of(IP.of(ip)); + } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java index 5ca671ea4..e28b06753 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java @@ -2,6 +2,7 @@ import com.mageddo.dnsproxyserver.config.Config; import com.mageddo.dnsproxyserver.config.EntryType; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; import lombok.Data; import lombok.experimental.Accessors; @@ -40,6 +41,13 @@ public class ConfigJsonV2 implements ConfigJson { private Boolean dpsNetworkAutoConnect; + public List getRemoteDnsServers(){ + return this.remoteDnsServers + .stream() + .map(IpAddr::of) + .toList(); + } + @Data @Accessors(chain = true) public static class Env { diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/JsonConfigs.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/JsonConfigs.java index c7340c9da..5703f2dc7 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/JsonConfigs.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/JsonConfigs.java @@ -3,10 +3,12 @@ import com.mageddo.dnsproxyserver.config.Config; import com.mageddo.json.JsonUtils; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import java.nio.file.Files; import java.nio.file.Path; +@Slf4j public class JsonConfigs { /** @@ -23,6 +25,10 @@ public static ConfigJson loadConfig(Path configPath) { final var objectMapper = JsonUtils.instance(); final var tree = objectMapper.readTree(configPath.toFile()); + if (tree.isEmpty()) { + log.info("status=emptyConfigFile, action=usingDefault, file={}", configPath); + return new ConfigJsonV2(); + } final var version = tree.at("/version").asInt(1); return switch (version) { diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepository.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java similarity index 86% rename from src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepository.java rename to src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java index d2cc0b28c..17688c9dc 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepository.java +++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java @@ -2,7 +2,7 @@ import com.mageddo.dnsproxyserver.server.dns.Hostname; -public interface DockerRepository { +public interface DockerDAO { /** * * @param host diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryDefault.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java similarity index 97% rename from src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryDefault.java rename to src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java index 2a5835ea8..396895e49 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryDefault.java +++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java @@ -21,7 +21,7 @@ @Singleton @Default @AllArgsConstructor(onConstructor = @__({@Inject})) -public class DockerRepositoryDefault implements DockerRepository { +public class DockerDAODefault implements DockerDAO { public static final String RUNNING_STATUS = "running"; private final DockerClient dockerClient; diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryMock.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java similarity index 90% rename from src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryMock.java rename to src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java index 033b6f4c6..fe54d6f23 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerRepositoryMock.java +++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java @@ -11,7 +11,7 @@ @Singleton @Alternative @AllArgsConstructor(onConstructor = @__({@Inject})) -public class DockerRepositoryMock implements DockerRepository { +public class DockerDAOMock implements DockerDAO { @Override public String findHostIp(Hostname host) { if (Objects.equals("acme.com", host)) { diff --git a/src/main/java/com/mageddo/dnsproxyserver/json/converter/IPConverter.java b/src/main/java/com/mageddo/dnsproxyserver/json/converter/IPConverter.java new file mode 100644 index 000000000..5cc2dd5de --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/json/converter/IPConverter.java @@ -0,0 +1,34 @@ +package com.mageddo.dnsproxyserver.json.converter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.mageddo.dnsproxyserver.server.dns.IP; + +import java.io.IOException; + +public class IPConverter { + public static class Serializer extends JsonSerializer { + @Override + public void serialize(IP value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null) { + gen.writeNull(); + } else { + gen.writeString(value.raw()); + } + } + } + + public static class Deserializer extends JsonDeserializer { + @Override + public IP deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (p.currentToken() == null) { + return null; + } + return IP.of(p.getValueAsString()); + } + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/quarkus/QuarkusConfig.java b/src/main/java/com/mageddo/dnsproxyserver/quarkus/QuarkusConfig.java index 92f6def96..a0df19d7d 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/quarkus/QuarkusConfig.java +++ b/src/main/java/com/mageddo/dnsproxyserver/quarkus/QuarkusConfig.java @@ -2,21 +2,39 @@ import com.mageddo.dnsproxyserver.config.Config; import com.mageddo.dnsproxyserver.config.Configs; +import com.mageddo.dnsproxyserver.server.dns.IpAddr; +import com.mageddo.dnsproxyserver.server.dns.solver.RemoteResolvers; +import com.mageddo.dnsproxyserver.utils.InetAddresses; import org.xbill.DNS.Resolver; import org.xbill.DNS.SimpleResolver; import javax.enterprise.inject.Produces; +import java.time.Duration; +import java.util.function.Function; + import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; public class QuarkusConfig { @Produces - public Resolver simpleResolver() { - return new SimpleResolver(Config.findRemoverSolverConfig().toSocketAddress()); + public RemoteResolvers remoteResolvers(Function resolverProvider) { + final var servers = Configs + .getInstance() + .getRemoteDnsServers(); + return RemoteResolvers.of(servers, resolverProvider); + } + + @Produces + public Function getResolverProvider() { + return it -> { + final var resolver = new SimpleResolver(InetAddresses.toSocketAddress(it.getRawIP(), it.getPortOrDef(53))); + resolver.setTimeout(Duration.ofMillis(300)); + return resolver; + }; } - public static void setup(Config config){ + public static void setup(Config config) { System.setProperty("quarkus.http.port", String.valueOf(config.getWebServerPort())); System.setProperty("quarkus.log.level", config.getLogLevel().name()); final var logFile = Configs.parseLogFile(config.getLogFile()); diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java new file mode 100644 index 000000000..c5a0273a8 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java @@ -0,0 +1,30 @@ +package com.mageddo.dnsproxyserver.server.dns; + +import org.apache.commons.lang3.Validate; + +public class IP { + + public static final int IP_BYTES = 4; + private final String ip; + + public IP(String ip) { + this.ip = ip; + } + + public static IP of(String ip) { + return new IP(ip); + } + + public static IP of(byte[] data) { + Validate.isTrue( + data.length == IP_BYTES, + "Array of bytes is not a valid IP representation, size must be %d", + IP_BYTES + ); + return of(String.format("%d.%d.%d.%d", data[0], data[1], data[2], data[3])); + } + + public String raw() { + return this.ip; + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/IpAddr.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IpAddr.java new file mode 100644 index 000000000..3a0c78502 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IpAddr.java @@ -0,0 +1,61 @@ +package com.mageddo.dnsproxyserver.server.dns; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mageddo.commons.lang.regex.Regexes; +import com.mageddo.dnsproxyserver.json.converter.IPConverter; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +import java.util.regex.Pattern; + +@Value +@Builder +public class IpAddr { + + public static final Pattern IP_ADDR_REGEX = + Pattern.compile("^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})((?::(\\d+)|))$"); + + @NonNull + @JsonDeserialize(using = IPConverter.Deserializer.class) + @JsonSerialize(using = IPConverter.Serializer.class) + private IP ip; + + private Integer port; + + public int getPortOrDef(int def) { + return this.port == null ? def : this.port; + } + + /*** + * + * @param addr something like 192.168.0.1 or 192.168.0.1:4411 + * @return parsed object. + */ + public static IpAddr of(String addr) { + Validate.isTrue( + Regexes.matcher(StringUtils.trimToEmpty(addr), IP_ADDR_REGEX).matches(), + "Need to pass a valid addr: actual=%s", addr + ); + final var groups = Regexes.groups(addr, IP_ADDR_REGEX); + return IpAddr + .builder() + .ip(IP.of(groups.get(1))) + .port(groups.get(3, s -> StringUtils.isBlank(s) ? null : Integer.parseInt(s))) + .build(); + } + + public static IpAddr of(IP ip) { + return IpAddr + .builder() + .ip(ip) + .build(); + } + + public String getRawIP() { + return this.ip.raw(); + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java index f0163e537..58d07c49b 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java @@ -4,6 +4,7 @@ import org.xbill.DNS.ARecord; import org.xbill.DNS.DClass; import org.xbill.DNS.Message; +import org.xbill.DNS.Rcode; import org.xbill.DNS.Section; import java.util.Optional; @@ -42,4 +43,9 @@ public static String findFirstAnswerRecord(Message msg) { } return section.get(0).toString(); } + + public static Message nxDomain(Message msg) { + msg.getHeader().setRcode(Rcode.NXDOMAIN); + return msg; + } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/UDPServer.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/UDPServer.java index c42dd2ee9..d33bc8184 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/UDPServer.java +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/UDPServer.java @@ -4,6 +4,7 @@ import com.mageddo.dnsproxyserver.threads.ThreadPool; import com.mageddo.dnsproxyserver.utils.Classes; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.Validate; import org.xbill.DNS.Message; @@ -73,10 +74,13 @@ Message solve(Message reqMsg) { log.debug("status=solved, solver={}, req={}, res={}", solverName, reqStr, simplePrint(res)); return res; } catch (Exception e) { - log.warn("status=solverFailed, solver={}, msg={}", solverName, e.getMessage(), e); + log.warn( + "status=solverFailed, solver={}, eClass={}, msg={}", + solverName, ClassUtils.getSimpleName(e), e.getMessage(), e + ); } } - return null; + return Messages.nxDomain(reqMsg); // if all failed and returned null, then return as can't find } void res(DatagramSocket server, Message handle, InetAddress address, int port) { diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/DockerSolver.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/DockerSolver.java index 550d97443..1b645c785 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/DockerSolver.java +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/DockerSolver.java @@ -1,6 +1,6 @@ package com.mageddo.dnsproxyserver.server.dns.solver; -import com.mageddo.dnsproxyserver.docker.DockerRepository; +import com.mageddo.dnsproxyserver.docker.DockerDAO; import com.mageddo.dnsproxyserver.server.dns.Messages; import com.mageddo.dnsproxyserver.server.dns.Wildcards; import lombok.AllArgsConstructor; @@ -15,14 +15,14 @@ @AllArgsConstructor(onConstructor = @__({@Inject})) public class DockerSolver implements Solver { - private final DockerRepository dockerRepository; + private final DockerDAO dockerDAO; @Override public Message handle(Message reqMsg) { final var askedHost = Messages.findQuestionHostname(reqMsg); for (final var host : Wildcards.buildHostAndWildcards(askedHost)) { - final var ip = this.dockerRepository.findHostIp(host); + final var ip = this.dockerDAO.findHostIp(host); if (ip == null) { return null; } diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteResolvers.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteResolvers.java new file mode 100644 index 000000000..26213de31 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteResolvers.java @@ -0,0 +1,28 @@ +package com.mageddo.dnsproxyserver.server.dns.solver; + +import com.mageddo.dnsproxyserver.server.dns.IpAddr; +import org.xbill.DNS.Resolver; + +import java.util.List; +import java.util.function.Function; + +public class RemoteResolvers { + + private final List resolvers; + + public RemoteResolvers(List resolvers) { + this.resolvers = resolvers; + } + + public static RemoteResolvers of(List servers, final Function resolverProvider) { + final var resolvers = servers + .stream() + .map(resolverProvider) + .toList(); + return new RemoteResolvers(resolvers); + } + + public List resolvers() { + return this.resolvers; + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolver.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolver.java index b4e093c7e..b78e5d2e0 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolver.java +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolver.java @@ -1,31 +1,46 @@ package com.mageddo.dnsproxyserver.server.dns.solver; -import com.mageddo.dnsproxyserver.server.dns.Messages; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ClassUtils; import org.xbill.DNS.Message; +import org.xbill.DNS.Rcode; import org.xbill.DNS.Resolver; import javax.inject.Inject; import javax.inject.Singleton; import java.io.IOException; -import java.io.UncheckedIOException; + +import static com.mageddo.dnsproxyserver.server.dns.Messages.simplePrint; @Slf4j @Singleton @AllArgsConstructor(onConstructor = @__({@Inject})) public class RemoteSolver implements Solver { - private final Resolver delegate; + private final RemoteResolvers delegate; @Override public Message handle(Message req) { - try { - final var res = this.delegate.send(req); - log.info("status=handled, req={}, res={}", Messages.simplePrint(req), Messages.simplePrint(res)); - return res; - } catch (IOException e) { - throw new UncheckedIOException(e); + Message lastErrorMsg = null; + for (int i = 0; i < this.delegate.resolvers().size(); i++) { + final Resolver resolver = this.delegate.resolvers().get(i); + try { + final var res = resolver.send(req); + if (res.getRcode() == Rcode.NOERROR) { + log.debug("status=found, i={}, req={}, res={}, server={}", i, simplePrint(req), simplePrint(res), resolver); + return res; + } else { + lastErrorMsg = res; + log.debug("status=notFound, i={}, req={}, res={}, server={}", i, simplePrint(req), simplePrint(res), resolver); + } + } catch (IOException e) { + log.warn( + "status=failed, i={}, req={}, server={}, errClass={}, msg={}", + i, simplePrint(req), resolver, ClassUtils.getSimpleName(e), e.getMessage(), e + ); + } } + return lastErrorMsg; } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverConfig.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverConfig.java deleted file mode 100644 index f5926b2ba..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.mageddo.dnsproxyserver.server.dns.solver; - -import com.mageddo.dnsproxyserver.utils.Ips; -import lombok.Data; -import lombok.experimental.Accessors; - -import java.net.InetSocketAddress; - -@Data -@Accessors(chain = true) -public class RemoteSolverConfig { - private byte[] ip; - private short port; - - public InetSocketAddress toSocketAddress() { - return new InetSocketAddress(Ips.toAddress(this.ip), this.port); - } -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystem.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystem.java index dbcf9692d..05806937d 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystem.java +++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystem.java @@ -1,7 +1,7 @@ package com.mageddo.dnsproxyserver.server.dns.solver; import com.mageddo.dnsproxyserver.config.Configs; -import com.mageddo.dnsproxyserver.docker.DockerRepository; +import com.mageddo.dnsproxyserver.docker.DockerDAO; import com.mageddo.dnsproxyserver.server.dns.Messages; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,14 +15,14 @@ @AllArgsConstructor(onConstructor = @__({@Inject})) public class SolverSystem implements Solver { - private final DockerRepository dockerRepository; + private final DockerDAO dockerDAO; @Override public Message handle(Message reqMsg) { final var hostname = Messages.findQuestionHostname(reqMsg); final var config = Configs.getInstance(); if (hostname.isEqualTo(config.getHostMachineHostname())) { - final var ip = this.dockerRepository.findHostMachineIp(); + final var ip = this.dockerDAO.findHostMachineIp(); if (ip == null) { log.debug("status=hostMachineIpNotFound, host={}", hostname); return null; diff --git a/src/main/java/com/mageddo/dnsproxyserver/utils/InetAddresses.java b/src/main/java/com/mageddo/dnsproxyserver/utils/InetAddresses.java new file mode 100644 index 000000000..d85f148f6 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/utils/InetAddresses.java @@ -0,0 +1,16 @@ +package com.mageddo.dnsproxyserver.utils; + +import com.mageddo.dnsproxyserver.server.dns.IpAddr; + +import java.net.InetSocketAddress; + +public class InetAddresses { + + public static InetSocketAddress toSocketAddress(String ip, int port) { + return new InetSocketAddress(Ips.toAddress(ip), port); + } + + public static InetSocketAddress toSocketAddress(IpAddr dns) { + return new InetSocketAddress(Ips.toAddress(dns.getIp().raw()), dns.getPort()); + } +} diff --git a/src/main/java/com/mageddo/utils/Bytes.java b/src/main/java/com/mageddo/utils/Bytes.java new file mode 100644 index 000000000..5f07a5dd1 --- /dev/null +++ b/src/main/java/com/mageddo/utils/Bytes.java @@ -0,0 +1,19 @@ +package com.mageddo.utils; + +public class Bytes { + public static byte[] toNative(Byte[] arr) { + final var newarr = new byte[arr.length]; + for (int i = 0; i < newarr.length; i++) { + newarr[i] = arr[i]; + } + return newarr; + } + + public static byte[] toNative(Integer[] arr) { + final var newarr = new byte[arr.length]; + for (int i = 0; i < newarr.length; i++) { + newarr[i] = arr[i].byteValue(); + } + return newarr; + } +} diff --git a/src/main/java/com/mageddo/utils/Files.java b/src/main/java/com/mageddo/utils/Files.java new file mode 100644 index 000000000..d3e475402 --- /dev/null +++ b/src/main/java/com/mageddo/utils/Files.java @@ -0,0 +1,17 @@ +package com.mageddo.utils; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; + +public class Files { + public static Path createTempFileExitOnExit(final String prefix, final String suffix) { + try { + final var p = java.nio.file.Files.createTempFile(prefix, suffix); + p.toFile().deleteOnExit(); + return p; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/src/main/java/com/mageddo/utils/Tests.java b/src/main/java/com/mageddo/utils/Tests.java new file mode 100644 index 000000000..3992e4c6e --- /dev/null +++ b/src/main/java/com/mageddo/utils/Tests.java @@ -0,0 +1,12 @@ +package com.mageddo.utils; + +public class Tests { + public static boolean runningOnJunit() { + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + if (element.getClassName().startsWith("org.junit.")) { + return true; + } + } + return false; + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/config/ConfigsTest.java b/src/test/java/com/mageddo/dnsproxyserver/config/ConfigsTest.java index 44ea0423d..468ef10fd 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/config/ConfigsTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/config/ConfigsTest.java @@ -19,6 +19,9 @@ class ConfigsTest { + static final String[] excludingFields = new String[]{"version", "configPath"}; + + @Test void mustParseDefaultConfigsAndCreateConfigFile(@TempDir Path tmpDir) { @@ -32,8 +35,8 @@ void mustParseDefaultConfigsAndCreateConfigFile(@TempDir Path tmpDir) { // assert assertEquals( - readAndSortJsonExcluding("/configs-test/001.json", "version"), - readAndSortJsonExcluding(config, "version") + readAndSortJsonExcluding("/configs-test/001.json", excludingFields), + readAndSortJsonExcluding(config, excludingFields) ); assertTrue(Files.exists(tmpConfigFile)); @@ -61,8 +64,8 @@ void mustRespectStoredConfig(@TempDir Path tmpDir) { // assert assertEquals( - readAndSortJsonExcluding("/configs-test/004.json", "version"), - sortJsonExcluding(config, "version") + readAndSortJsonExcluding("/configs-test/004.json", excludingFields), + sortJsonExcluding(config, excludingFields) ); } diff --git a/src/test/java/com/mageddo/dnsproxyserver/config/flags/ConfigFlagTest.java b/src/test/java/com/mageddo/dnsproxyserver/config/flags/ConfigFlagTest.java index 7fc6464cc..a8629a511 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/config/flags/ConfigFlagTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/config/flags/ConfigFlagTest.java @@ -1,15 +1,18 @@ package com.mageddo.dnsproxyserver.config.flags; +import com.mageddo.commons.lang.regex.Regexes; import com.mageddo.dnsproxyserver.config.entrypoint.ConfigFlag; import org.junit.jupiter.api.Test; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.regex.Pattern; import static com.mageddo.utils.TestUtils.readAndSortJson; import static com.mageddo.utils.TestUtils.readString; import static com.mageddo.utils.TestUtils.sortJson; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigFlagTest { @@ -30,7 +33,7 @@ void mustParseDefaultConfigs() throws Exception { } @Test - void mustPrintHelp() throws Exception { + void mustPrintHelp() { // arrange final var sw = new StringWriter(); @@ -44,11 +47,11 @@ void mustPrintHelp() throws Exception { readString("/flags-test/002.txt"), sw.toString() ); - + assertTrue(config.getHelp()); } @Test - void mustPrintVersion() throws Exception { + void mustPrintVersion() { // arrange final var sw = new StringWriter(); @@ -58,10 +61,8 @@ void mustPrintVersion() throws Exception { final var config = ConfigFlag.parse(args, new PrintWriter(sw)); // assert - assertEquals( - "${version}", - sw.toString() - ); - + final var validVersion = Regexes.matcher(sw.toString(), Pattern.compile("\\d+\\.\\d+.\\d+.*")).matches(); + assertTrue(validVersion, sw.toString()); + assertTrue(config.isVersion()); } } diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverTest.java new file mode 100644 index 000000000..1d917974b --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/RemoteSolverTest.java @@ -0,0 +1,31 @@ +package com.mageddo.dnsproxyserver.server.dns.solver; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import javax.inject.Inject; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +@QuarkusTest +class RemoteSolverTest { + + @Inject + RemoteResolvers remoteResolvers; + + @Test + void mustBuildWithDefaultRemoteServer(){ + + // arrange + + // act + final var resolvers = this.remoteResolvers.resolvers(); + + // assert + assertEquals(1, resolvers.size(), String.valueOf(resolvers)); + assertEquals("[SimpleResolver [/8.8.8.8:53]]", resolvers.toString()); + + } + +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemTest.java index 911ab57f0..ae6f3a5d9 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemTest.java @@ -1,6 +1,6 @@ package com.mageddo.dnsproxyserver.server.dns.solver; -import com.mageddo.dnsproxyserver.docker.DockerRepository; +import com.mageddo.dnsproxyserver.docker.DockerDAO; import com.mageddo.dnsproxyserver.server.dns.Messages; import com.mageddo.dnsproxyserver.templates.MessageTemplates; import io.quarkus.test.junit.QuarkusTest; @@ -19,7 +19,7 @@ class SolverSystemTest { @InjectMock(convertScopes = true) - DockerRepository dockerRepository; + DockerDAO dockerDAO; @Inject SolverSystem solver; @@ -31,7 +31,7 @@ void mustSolverHostMachineIp(){ final var query = MessageTemplates.buildAQuestionFor(hostname); doReturn( "192.168.0.1") - .when(this.dockerRepository) + .when(this.dockerDAO) .findHostMachineIp() ; @@ -43,7 +43,7 @@ void mustSolverHostMachineIp(){ assertThat(answer, CoreMatchers.containsString(hostname)); assertEquals("host.docker.\t\t30\tIN\tA\t192.168.0.1", answer); - verify(this.dockerRepository).findHostMachineIp(); + verify(this.dockerDAO).findHostMachineIp(); } } diff --git a/src/test/resources/config-json-v1-test/002.json b/src/test/resources/config-json-v1-test/002.json index 2c7d1ba33..2eb345535 100644 --- a/src/test/resources/config-json-v1-test/002.json +++ b/src/test/resources/config-json-v1-test/002.json @@ -9,5 +9,14 @@ "logFile" : "console", "logLevel" : "DEBUG", "registerContainerNames" : false, + "remoteDnsServers" : [ { + "ip" : "8.8.8.8", + "port" : null, + "rawIP" : "8.8.8.8" + }, { + "ip" : "4.4.4.4", + "port" : null, + "rawIP" : "4.4.4.4" + } ], "webServerPort" : 0 } diff --git a/src/test/resources/configs-test/001.json b/src/test/resources/configs-test/001.json index ac9b98bd5..60b077f41 100644 --- a/src/test/resources/configs-test/001.json +++ b/src/test/resources/configs-test/001.json @@ -9,6 +9,10 @@ "logFile" : "console", "logLevel" : "INFO", "registerContainerNames" : false, - "version" : "${version}", + "remoteDnsServers" : [ { + "ip" : "8.8.8.8", + "port" : 53, + "rawIP" : "8.8.8.8" + } ], "webServerPort" : 5380 } diff --git a/src/test/resources/configs-test/004.json b/src/test/resources/configs-test/004.json index e033217d5..a525862cb 100644 --- a/src/test/resources/configs-test/004.json +++ b/src/test/resources/configs-test/004.json @@ -9,6 +9,10 @@ "logFile" : "/var/log/dns-proxy-server.log", "logLevel" : "DEBUG", "registerContainerNames" : true, - "version" : "${version}", + "remoteDnsServers" : [ { + "ip" : "8.8.8.8", + "port" : 53, + "rawIP" : "8.8.8.8" + } ], "webServerPort" : 9393 } diff --git a/src/test/resources/flags-test/002.txt b/src/test/resources/flags-test/002.txt index da7f34dc6..954c0a2fa 100644 --- a/src/test/resources/flags-test/002.txt +++ b/src/test/resources/flags-test/002.txt @@ -1,7 +1,7 @@ Usage:
[-default-dns] [-dps-network] [-dps-network-auto-connect] [-help] [-register-container-names] [-service-publish-web-port] [-version] [-conf-path=] [-domain=] [-host-machine-hostname=] [-log-file=] [-log-level=] - [-server-port=] [-service=] [-web-server-port=] + [-server-port=] [-service=] [-web-server-port=] -conf-path, --conf-path= The config file path (default conf/config.json) -default-dns, --default-dns @@ -27,7 +27,7 @@ Usage:
[-default-dns] [-dps-network] [-dps-network-auto-connect] [- Log Level ERROR, WARNING, INFO, DEBUG (default INFO) -register-container-names, --register-container-names If must register container name / service name as host in DNS server (default false) - -server-port, --server-port= + -server-port, --server-port= The DNS server to start into (default 53) -service, --service= Setup as service, starting with machine at boot