From 9c9ed925fbb9d44496306647ba7826633f28121d Mon Sep 17 00:00:00 2001 From: Elvis Souza Date: Sat, 25 Feb 2023 21:42:35 -0300 Subject: [PATCH] Detect and configure systemd-resolved when available (#323) * moving code to the right place * created a minimal parser for resolved * generic config parser * refactoring to generify * basic resolved parser is working * adjustments and tests * process is tested * fixing tests * refactoring * refactoring * fixing * imports * integrating with configurator * creating more tests * restart service stuff * not able to test * fixed system restart * unused files * fixing permissiosn * Changed default dns server configs * won't restart service while testing * Fixing test * release notes --- RELEASE-NOTES.md | 9 ++ docker-compose-dev.yml | 6 +- gradle.properties | 2 +- .../mageddo/commons/exec/CommandLines.java | 87 +++++++++++ .../ExecutionValidationFailedException.java | 14 ++ .../com/mageddo/conf/parser/ConfParser.java | 91 ++++++++++++ .../java/com/mageddo/conf/parser/Entry.java | 17 +++ .../com/mageddo/conf/parser/EntryType.java | 10 ++ .../mageddo/conf/parser/EntryTypeDefault.java | 15 ++ .../com/mageddo/conf/parser/Transformer.java | 12 ++ .../dnsproxyserver/config/Configs.java | 2 +- .../config/entrypoint/ConfigEnv.java | 3 +- .../dnsconfigurator/linux/CleanerHandler.java | 19 +++ .../linux/ConfigureDPSHandler.java | 36 +++++ .../dnsconfigurator/linux/DpsTokens.java | 20 +++ .../dnsconfigurator/linux/EntryTypes.java | 22 +++ .../linux/LinuxDnsConfigurator.java | 68 +++++++-- .../dnsconfigurator/linux/ResolvFile.java | 19 ++- .../linux/ResolvconfConfigurator.java | 46 ++++++ .../linux/ResolvedConfigurator.java | 43 ++++++ .../resolvconf/DnsServerCleanerHandler.java | 22 --- .../SetMachineDNSServerHandler.java | 32 ---- .../dnsproxyserver/quarkus/Quarkus.java | 16 +- .../resolvconf/DnsEntryType.java | 11 -- .../resolvconf/ResolvConfParser.java | 95 ------------ .../systemd/ResolvedService.java | 15 ++ src/main/java/com/mageddo/utils/Files.java | 20 +++ src/main/java/com/mageddo/utils/Tests.java | 2 +- .../linux/LinuxDnsConfiguratorTest.java | 68 +++------ .../linux/ResolvconfConfiguratorTest.java | 104 +++++++++++++ .../ResolvedConfiguratorTest.java | 139 ++++++++++++++++++ .../systemd/ResolvedServiceTest.java | 28 ++++ .../dnsproxyserver/templates/IpTemplates.java | 13 ++ .../resolvconf/ResolvConfParserTest.java | 23 --- 34 files changed, 874 insertions(+), 255 deletions(-) create mode 100644 src/main/java/com/mageddo/commons/exec/CommandLines.java create mode 100644 src/main/java/com/mageddo/commons/exec/ExecutionValidationFailedException.java create mode 100644 src/main/java/com/mageddo/conf/parser/ConfParser.java create mode 100644 src/main/java/com/mageddo/conf/parser/Entry.java create mode 100644 src/main/java/com/mageddo/conf/parser/EntryType.java create mode 100644 src/main/java/com/mageddo/conf/parser/EntryTypeDefault.java create mode 100644 src/main/java/com/mageddo/conf/parser/Transformer.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/CleanerHandler.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ConfigureDPSHandler.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/DpsTokens.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/EntryTypes.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfigurator.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvedConfigurator.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/DnsServerCleanerHandler.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/SetMachineDNSServerHandler.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/resolvconf/DnsEntryType.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/resolvconf/ResolvConfParser.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/systemd/ResolvedService.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfiguratorTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/systemdresolved/ResolvedConfiguratorTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/systemd/ResolvedServiceTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java delete mode 100644 src/test/java/com/mageddo/resolvconf/ResolvConfParserTest.java diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 715a39399..95ea29a00 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,12 @@ +### 3.4.0-beta + +* DPS will detect and configure systemd-resolved when available +* The default value of MG_RESOLVCONF was changed to +``` +/host/etc/systemd/resolved.conf,/host/etc/resolv.conf,/etc/systemd/resolved.conf,/etc/resolv.conf +``` +See issue [#321](https://github.com/mageddo/dns-proxy-server/issues/321) for more details. + ### 3.3.0-beta #### MG_RESOLVCONF will now accept more than one value They will separate by comma , DPS will look for each value, try to use it and stops when finds a valid value, some path which is able to configure (an existing path, with the right read and write permissions and parseable by DPS) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 989e113fb..a03a5287d 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -5,15 +5,15 @@ services: environment: - MG_LOG_LEVEL=DEBUG - MG_REGISTER_CONTAINER_NAMES=1 - - MG_RESOLVCONF=/host/etc/resolv.conf +# - MG_RESOLVCONF=/host/etc/systemd/resolved.conf labels: - dps.container=true volumes: - ./build:/app - /var/run/docker.sock:/var/run/docker.sock - - /run/systemd/resolve/:/host/etc/ + - /etc/systemd/:/host/etc/systemd/ working_dir: /app - command: java -jar dns-proxy-server.jar --default-dns=false + command: java -jar dns-proxy-server.jar # ports: # - "172.17.0.1:53:53/udp" # - "192.168.0.128:53:53/udp" diff --git a/gradle.properties b/gradle.properties index 996e63816..2f8d941ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.3.0-beta +version=3.4.0-beta quarkusPluginId=io.quarkus quarkusPluginVersion=2.16.0.Final quarkusPlatformGroupId=io.quarkus.platform diff --git a/src/main/java/com/mageddo/commons/exec/CommandLines.java b/src/main/java/com/mageddo/commons/exec/CommandLines.java new file mode 100644 index 000000000..452d8622e --- /dev/null +++ b/src/main/java/com/mageddo/commons/exec/CommandLines.java @@ -0,0 +1,87 @@ +package com.mageddo.commons.exec; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DaemonExecutor; +import org.apache.commons.exec.ExecuteException; +import org.apache.commons.exec.ExecuteWatchdog; +import org.apache.commons.exec.Executor; +import org.apache.commons.exec.PumpStreamHandler; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; + +public class CommandLines { + + public static Result exec(String commandLine, Object... args) { + return exec(CommandLine.parse(String.format(commandLine, args)), + ExecuteWatchdog.INFINITE_TIMEOUT + ); + } + + public static Result exec(long timeout, String commandLine, Object... args) { + return exec(CommandLine.parse(String.format(commandLine, args)), timeout); + } + + public static Result exec(CommandLine commandLine) { + return exec(commandLine, ExecuteWatchdog.INFINITE_TIMEOUT); + } + + public static Result exec(CommandLine commandLine, long timeout) { + final var out = new ByteArrayOutputStream(); + final var executor = new DaemonExecutor(); + final var streamHandler = new PumpStreamHandler(out); + executor.setStreamHandler(streamHandler); + int exitCode; + try { + executor.setWatchdog(new ExecuteWatchdog(timeout)); + exitCode = executor.execute(commandLine); + } catch (ExecuteException e) { + exitCode = e.getExitValue(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return Result + .builder() + .executor(executor) + .out(out) + .exitCode(exitCode) + .build(); + } + + @Getter + @Builder + @ToString(of = {"exitCode"}) + public static class Result { + + @NonNull + private Executor executor; + + @NonNull + private ByteArrayOutputStream out; + + private int exitCode; + + public String getOutAsString() { + return this.out.toString(); + } + + public Result checkExecution() { + if (this.executor.isFailure(this.getExitCode())) { + throw new ExecutionValidationFailedException(this); + } + return this; + } + + public String toString(boolean printOut) { + return String.format( + "code=%d, out=%s", + this.exitCode, printOut ? this.getOutAsString() : null + ); + } + } +} diff --git a/src/main/java/com/mageddo/commons/exec/ExecutionValidationFailedException.java b/src/main/java/com/mageddo/commons/exec/ExecutionValidationFailedException.java new file mode 100644 index 000000000..618d8ae0e --- /dev/null +++ b/src/main/java/com/mageddo/commons/exec/ExecutionValidationFailedException.java @@ -0,0 +1,14 @@ +package com.mageddo.commons.exec; + +public class ExecutionValidationFailedException extends RuntimeException { + private final CommandLines.Result result; + + public ExecutionValidationFailedException(CommandLines.Result result) { + super(String.format("error, code=%d, error=%s", result.getExitCode(), result.getOutAsString())); + this.result = result; + } + + public CommandLines.Result result() { + return this.result; + } +} diff --git a/src/main/java/com/mageddo/conf/parser/ConfParser.java b/src/main/java/com/mageddo/conf/parser/ConfParser.java new file mode 100644 index 000000000..eb605a238 --- /dev/null +++ b/src/main/java/com/mageddo/conf/parser/ConfParser.java @@ -0,0 +1,91 @@ +package com.mageddo.conf.parser; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.mageddo.utils.Files.copyContent; + + +public class ConfParser { + + public static List parse(String in, Function parser) { + return parse(new BufferedReader(new StringReader(in)), parser); + } + + public static List parse(BufferedReader r, Function parser) { + try { + final var entries = new ArrayList(); + String line; + while ((line = r.readLine()) != null) { + entries.add(Entry + .builder() + .type(parser.apply(line)) + .line(line) + .build() + ); + } + return entries; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static void process(Path conf, Function parser, Transformer h) { + process(conf, conf, parser, h); + } + + public static void process(Path source, Path target, Function parser, Transformer t) { + try { + final var tmpFile = Files.createTempFile("dps", ".conf"); + try ( + var reader = Files.newBufferedReader(source); + var writer = Files.newBufferedWriter(tmpFile) + ) { + writeToOut(reader, writer, parser, t); + } + copyContent(tmpFile, target); + Files.delete(tmpFile); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + static void writeToOut( + BufferedReader reader, BufferedWriter writer, + Function parser, Transformer t + ) { + final var lines = parse(reader, parser); + lines + .stream() + .map(t::handle) + .filter(Objects::nonNull) + .forEach(line -> writeLine(writer, line)); + final var foundTokens = lines + .stream() + .map(it -> it.getType().name()) + .collect(Collectors.toSet()); + writeLine(writer, t.after(!lines.isEmpty(), foundTokens)); + } + + static void writeLine(BufferedWriter writer, String line) { + try { + if (line != null) { + writer.write(line); + writer.write('\n'); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + +} diff --git a/src/main/java/com/mageddo/conf/parser/Entry.java b/src/main/java/com/mageddo/conf/parser/Entry.java new file mode 100644 index 000000000..a9f99ecde --- /dev/null +++ b/src/main/java/com/mageddo/conf/parser/Entry.java @@ -0,0 +1,17 @@ +package com.mageddo.conf.parser; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +@Value +@Builder +public class Entry { + + @NonNull + EntryType type; + + @NonNull + String line; + +} diff --git a/src/main/java/com/mageddo/conf/parser/EntryType.java b/src/main/java/com/mageddo/conf/parser/EntryType.java new file mode 100644 index 000000000..d13d30b3d --- /dev/null +++ b/src/main/java/com/mageddo/conf/parser/EntryType.java @@ -0,0 +1,10 @@ +package com.mageddo.conf.parser; + +public interface EntryType { + + String name(); + + static EntryType of(String name) { + return new EntryTypeDefault(name); + } +} diff --git a/src/main/java/com/mageddo/conf/parser/EntryTypeDefault.java b/src/main/java/com/mageddo/conf/parser/EntryTypeDefault.java new file mode 100644 index 000000000..81171f5f1 --- /dev/null +++ b/src/main/java/com/mageddo/conf/parser/EntryTypeDefault.java @@ -0,0 +1,15 @@ +package com.mageddo.conf.parser; + +public class EntryTypeDefault implements EntryType { + + private final String name; + + public EntryTypeDefault(String name) { + this.name = name; + } + + @Override + public String name() { + return this.name; + } +} diff --git a/src/main/java/com/mageddo/conf/parser/Transformer.java b/src/main/java/com/mageddo/conf/parser/Transformer.java new file mode 100644 index 000000000..08c76c3d9 --- /dev/null +++ b/src/main/java/com/mageddo/conf/parser/Transformer.java @@ -0,0 +1,12 @@ +package com.mageddo.conf.parser; + +import java.util.Set; + +public interface Transformer { + + String handle(Entry entry); + + default String after(boolean fileHasContent, Set foundEntryTypes) { + return null; + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java b/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java index b6fbb3c1e..81a9b3794 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/Configs.java @@ -135,7 +135,7 @@ static Config build(ConfigFlag configFlag) { } static boolean runningInTestsAndNoCustomConfigPath(ConfigFlag configFlag) { - return !Arrays.toString(configFlag.getArgs()).contains("--conf-path") && Tests.runningOnJunit(); + return !Arrays.toString(configFlag.getArgs()).contains("--conf-path") && Tests.inTest(); } diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigEnv.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigEnv.java index dcd3541de..ecf8d9e9e 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigEnv.java +++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigEnv.java @@ -26,7 +26,8 @@ public class ConfigEnv { public static final String MG_DOMAIN = "MG_DOMAIN"; public static final String MG_DPS_NETWORK = "MG_DPS_NETWORK"; public static final String MG_DPS_NETWORK_AUTO_CONNECT = "MG_DPS_NETWORK_AUTO_CONNECT"; - public static final String DEFAULT_RESOLV_CONF_PATH = "/host/etc/resolv.conf,/etc/resolv.conf"; + public static final String DEFAULT_RESOLV_CONF_PATH = + "/host/etc/systemd/resolved.conf,/host/etc/resolv.conf,/etc/systemd/resolved.conf,/etc/resolv.conf"; private Path currentPath; private String resolvConfPath; diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/CleanerHandler.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/CleanerHandler.java new file mode 100644 index 000000000..d91e91118 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/CleanerHandler.java @@ -0,0 +1,19 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.conf.parser.Entry; +import com.mageddo.conf.parser.Transformer; +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.DpsTokens; +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.EntryTypes; + +public class CleanerHandler implements Transformer { + + @Override + public String handle(Entry entry) { + return switch (entry.getType().name()) { + case EntryTypes.DPS_SERVER -> null; + case EntryTypes.COMMENTED_SERVER -> DpsTokens.uncomment(entry.getLine()); + default -> entry.getLine(); + }; + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ConfigureDPSHandler.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ConfigureDPSHandler.java new file mode 100644 index 000000000..a448e3e6d --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ConfigureDPSHandler.java @@ -0,0 +1,36 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.conf.parser.Entry; +import com.mageddo.conf.parser.Transformer; +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.DpsTokens; +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.EntryTypes; + +import java.util.Set; +import java.util.function.Supplier; + +public class ConfigureDPSHandler implements Transformer { + + private final Supplier dpsDnsLineBuilder; + + public ConfigureDPSHandler(Supplier dpsDnsLineBuilder) { + this.dpsDnsLineBuilder = dpsDnsLineBuilder; + } + + @Override + public String handle(Entry entry) { + return switch (entry.getType().name()) { + case EntryTypes.DPS_SERVER -> this.dpsDnsLineBuilder.get(); + case EntryTypes.SERVER -> DpsTokens.comment(entry.getLine()); + default -> entry.getLine(); + }; + } + + @Override + public String after(boolean fileHasContent, Set foundEntryTypes) { + if (!fileHasContent || !foundEntryTypes.contains(EntryTypes.DPS_SERVER)) { + return this.dpsDnsLineBuilder.get(); + } + return null; + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/DpsTokens.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/DpsTokens.java new file mode 100644 index 000000000..7b21c24bb --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/DpsTokens.java @@ -0,0 +1,20 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +public class DpsTokens { + public static final String COMMENT_END = "# dps-comment"; + public static final String DPS_ENTRY_COMMENT = "# dps-entry"; + public static final String COMMENT = "#"; + + public static String comment(String line) { + return String.format("# %s # dps-comment", line); + } + + public static String uncomment(final String line) { + return line + .substring( + 2, + line + .indexOf(" " + COMMENT_END) + ); + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/EntryTypes.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/EntryTypes.java new file mode 100644 index 000000000..8db1c353e --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/EntryTypes.java @@ -0,0 +1,22 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.conf.parser.EntryType; + +public class EntryTypes { + + public static final String COMMENT = "COMMENT"; + public static final String COMMENTED_SERVER = "COMMENTED_SERVER"; + public static final String SERVER = "SERVER"; + public static final String DPS_SERVER = "DPS_SERVER"; + private static final String SEARCH = "SEARCH"; + public static final String OTHER = "OTHER"; + + + // parsed types + public static final EntryType COMMENT_TYPE = EntryType.of(COMMENT); + public static final EntryType COMMENTED_SERVER_TYPE = EntryType.of(COMMENTED_SERVER); + public static final EntryType SERVER_TYPE = EntryType.of(SERVER); + public static final EntryType DPS_SERVER_TYPE = EntryType.of(DPS_SERVER); + public static final EntryType OTHER_TYPE = EntryType.of(OTHER); + public static final EntryType SEARCH_TYPE = EntryType.of(SEARCH); +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfigurator.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfigurator.java index 0d6c6a70a..30bcc6a22 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfigurator.java +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfigurator.java @@ -3,10 +3,10 @@ import com.mageddo.commons.lang.Objects; import com.mageddo.dnsproxyserver.config.Configs; import com.mageddo.dnsproxyserver.dnsconfigurator.DnsConfigurator; -import com.mageddo.dnsproxyserver.dnsconfigurator.linux.resolvconf.DnsServerCleanerHandler; -import com.mageddo.dnsproxyserver.dnsconfigurator.linux.resolvconf.SetMachineDNSServerHandler; -import com.mageddo.dnsproxyserver.resolvconf.ResolvConfParser; +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.ResolvFile.Type; import com.mageddo.dnsproxyserver.server.dns.IP; +import com.mageddo.dnsproxyserver.systemd.ResolvedService; +import com.mageddo.utils.Tests; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,9 +17,11 @@ import java.nio.file.Path; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static com.mageddo.dnsproxyserver.utils.Splits.splitToPaths; +import static org.apache.commons.lang3.ObjectUtils.firstNonNull; @Slf4j @Default @@ -27,6 +29,8 @@ @RequiredArgsConstructor(onConstructor = @__({@Inject})) public class LinuxDnsConfigurator implements DnsConfigurator { + private final AtomicBoolean resolvedConfigured = new AtomicBoolean(); + private volatile AtomicReference confFile; @Override @@ -37,7 +41,15 @@ public void configure(IP ip) { return; } - ResolvConfParser.process(getConfFile(), new SetMachineDNSServerHandler(ip.raw())); + final var confFile = this.getConfFile(); + if (confFile.isResolvconf()) { + ResolvconfConfigurator.process(confFile.getPath(), ip); + } else if (confFile.isResolved()) { + this.configureResolved(ip, confFile); + } else { + throw newUnsupportedConfType(confFile); + } + log.debug("status=configured, path={}", this.getConfFile()); } @Override @@ -46,12 +58,21 @@ public void restore() { if (this.confFile.get() == null) { return; } - ResolvConfParser.process(getConfFile(), new DnsServerCleanerHandler()); - log.debug("status=restoredResolvConf, path={}", this.getConfFile()); + + final var confFile = this.getConfFile(); + if (confFile.isResolvconf()) { + ResolvconfConfigurator.restore(confFile.getPath()); + } else if (confFile.isResolved()) { + ResolvedConfigurator.restore(confFile.getPath()); + tryRestartResolved(); + } else { + throw newUnsupportedConfType(confFile); + } + log.debug("status=restored, path={}", this.getConfFile()); } - Path getConfFile() { - return this.confFile.get().getPath(); + ResolvFile getConfFile() { + return this.confFile.get(); } ResolvFile findBestConfFile() { @@ -87,14 +108,41 @@ String getConfigResolvPaths() { } ResolvFile toResolvFile(Path path) { - return ResolvFile.of(path, LinuxResolverConfDetector.detect(path)); + return ResolvFile.of(path, firstNonNull(LinuxResolverConfDetector.detect(path), Type.RESOLVCONF)); } void init() { if (this.confFile == null) { - this.confFile = new AtomicReference<>(findBestConfFile()); + this.confFile = new AtomicReference<>(this.findBestConfFile()); log.info("status=using, configFile={}", this.getConfFile()); } } + private RuntimeException newUnsupportedConfType(ResolvFile confFile) { + return new UnsupportedOperationException(String.format("conf file not supported: %s", confFile)); + } + + private void configureResolved(IP ip, ResolvFile confFile) { + if (this.resolvedConfigured.compareAndSet(false, true)) { + ResolvedConfigurator.configure(confFile.getPath(), ip); + tryRestartResolved(); + } + } + + static void tryRestartResolved() { + try { + if (Tests.inTest()) { + log.warn("status=wont-restart-service-while-testing"); + return; + } + ResolvedService.restart(); + } catch (Throwable e) { + log.warn( + "status=can't restart resolved service, please run: " + + "'service systemd-resolved restart' to apply DPS as default DNS.\n{}", + e.getMessage() + ); + } + } + } diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvFile.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvFile.java index 58c9584bf..878fd119b 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvFile.java +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvFile.java @@ -14,9 +14,26 @@ public static ResolvFile of(Path path, Type type) { return new ResolvFile(path, type); } + public boolean isResolvconf() { + return this.type.isResolvconf(); + } + + public boolean isResolved() { + return this.type.isResolved(); + } + public enum Type { + RESOLVCONF, - SYSTEMD_RESOLVED + SYSTEMD_RESOLVED; + + public boolean isResolvconf() { + return this == RESOLVCONF; + } + + public boolean isResolved() { + return this == SYSTEMD_RESOLVED; + } } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfigurator.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfigurator.java new file mode 100644 index 000000000..ccdc8d8fa --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfigurator.java @@ -0,0 +1,46 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.conf.parser.ConfParser; +import com.mageddo.conf.parser.EntryType; +import com.mageddo.dnsproxyserver.server.dns.IP; + +import java.nio.file.Path; +import java.util.function.Function; + +public class ResolvconfConfigurator { + + public static void process(Path confFile, IP ip) { + ConfParser.process( + confFile, + createParser(), + new ConfigureDPSHandler(() -> "nameserver " + ip.raw() + " # dps-entry") + ); + } + + public static void restore(Path confFile) { + ConfParser.process( + confFile, + createParser(), + new CleanerHandler() + ); + } + + private static Function createParser() { + return line -> { + if (line.endsWith(DpsTokens.DPS_ENTRY_COMMENT)) { + return EntryTypes.DPS_SERVER_TYPE; + } else if (line.startsWith("# nameserver ") && line.endsWith(DpsTokens.COMMENT_END)) { + return EntryTypes.COMMENTED_SERVER_TYPE; + } else if (line.startsWith(DpsTokens.COMMENT)) { + return EntryTypes.COMMENT_TYPE; + } else if (line.startsWith("nameserver")) { + return EntryTypes.SERVER_TYPE; + } else if (line.startsWith("search")) { + return EntryTypes.SEARCH_TYPE; + } else { + return EntryTypes.OTHER_TYPE; + } + }; + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvedConfigurator.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvedConfigurator.java new file mode 100644 index 000000000..9657593ce --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvedConfigurator.java @@ -0,0 +1,43 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.conf.parser.ConfParser; +import com.mageddo.conf.parser.EntryType; +import com.mageddo.dnsproxyserver.server.dns.IP; + +import java.nio.file.Path; +import java.util.function.Function; + +public class ResolvedConfigurator { + + public static void configure(Path confFile, IP ip) { + ConfParser.process( + confFile, + createParser(), + new ConfigureDPSHandler(() -> "DNS=" + ip.raw() + " # dps-entry") + ); + } + + public static void restore(Path confFile) { + ConfParser.process( + confFile, + createParser(), + new CleanerHandler() + ); + } + + private static Function createParser() { + return line -> { + if (line.endsWith(DpsTokens.DPS_ENTRY_COMMENT)) { + return EntryTypes.DPS_SERVER_TYPE; + } else if (line.startsWith("# DNS=") && line.endsWith(DpsTokens.COMMENT_END)) { + return EntryTypes.COMMENTED_SERVER_TYPE; + } else if (line.startsWith(DpsTokens.COMMENT)) { + return EntryTypes.COMMENT_TYPE; + } else if (line.startsWith("DNS=")) { + return EntryTypes.SERVER_TYPE; + } else { + return EntryTypes.OTHER_TYPE; + } + }; + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/DnsServerCleanerHandler.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/DnsServerCleanerHandler.java deleted file mode 100644 index d8dd1c660..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/DnsServerCleanerHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mageddo.dnsproxyserver.dnsconfigurator.linux.resolvconf; - -import com.mageddo.dnsproxyserver.resolvconf.DnsEntryType; -import com.mageddo.dnsproxyserver.resolvconf.ResolvConfParser; - -public class DnsServerCleanerHandler implements ResolvConfParser.Handler { - - @Override - public String handle(String line, DnsEntryType entryType) { - return switch (entryType) { - case PROXY -> null; - case COMMENTED_SERVER -> line.substring(2, line.indexOf(" # dps-comment")); - default -> line; - }; - } - - @Override - public String after(boolean hasContent, boolean foundDps) { - return null; - } - -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/SetMachineDNSServerHandler.java b/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/SetMachineDNSServerHandler.java deleted file mode 100644 index 85c890905..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/resolvconf/SetMachineDNSServerHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mageddo.dnsproxyserver.dnsconfigurator.linux.resolvconf; - -import com.mageddo.dnsproxyserver.resolvconf.DnsEntryType; -import com.mageddo.dnsproxyserver.resolvconf.ResolvConfParser; - -import static com.mageddo.dnsproxyserver.resolvconf.ResolvConfParser.buildDNSLine; - -public class SetMachineDNSServerHandler implements ResolvConfParser.Handler { - - private final String serverIP; - - public SetMachineDNSServerHandler(String serverIP) { - this.serverIP = serverIP; - } - - @Override - public String handle(String line, DnsEntryType entryType) { - return switch (entryType) { - case PROXY -> buildDNSLine(this.serverIP); - case SERVER -> String.format("# %s # dps-comment", line); - default -> line; - }; - } - - @Override - public String after(boolean hasContent, boolean foundDps) { - if (!hasContent || !foundDps) { - return buildDNSLine(this.serverIP); - } - return null; - } -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/quarkus/Quarkus.java b/src/main/java/com/mageddo/dnsproxyserver/quarkus/Quarkus.java index 7c83617fa..1ad90fa2a 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/quarkus/Quarkus.java +++ b/src/main/java/com/mageddo/dnsproxyserver/quarkus/Quarkus.java @@ -1,5 +1,6 @@ package com.mageddo.dnsproxyserver.quarkus; +import com.mageddo.utils.Tests; import io.quarkus.arc.Arc; import io.quarkus.runtime.configuration.ConfigUtils; @@ -9,16 +10,17 @@ public class Quarkus { public static boolean isTest() { return ConfigUtils - .getProfiles() - .contains("test") - ; + .getProfiles() + .contains("test") + || Tests.inTest() + ; } public static Set beansOf(Class clazz) { return Arc.container() - .select(clazz) - .stream() - .collect(Collectors.toSet()) - ; + .select(clazz) + .stream() + .collect(Collectors.toSet()) + ; } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/resolvconf/DnsEntryType.java b/src/main/java/com/mageddo/dnsproxyserver/resolvconf/DnsEntryType.java deleted file mode 100644 index b9b065ce9..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/resolvconf/DnsEntryType.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mageddo.dnsproxyserver.resolvconf; - -public enum DnsEntryType { - COMMENT, - COMMENTED_SERVER, - SERVER, - PROXY, - SEARCH, - ELSE, - ; -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/resolvconf/ResolvConfParser.java b/src/main/java/com/mageddo/dnsproxyserver/resolvconf/ResolvConfParser.java deleted file mode 100644 index f90495ab4..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/resolvconf/ResolvConfParser.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.mageddo.dnsproxyserver.resolvconf; - -import lombok.SneakyThrows; -import org.apache.commons.lang3.StringUtils; - -import java.io.BufferedReader; -import java.io.StringReader; -import java.nio.file.Files; -import java.nio.file.Path; - -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.COMMENT; -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.COMMENTED_SERVER; -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.ELSE; -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.PROXY; -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.SEARCH; -import static com.mageddo.dnsproxyserver.resolvconf.DnsEntryType.SERVER; - -public class ResolvConfParser { - - public static void process(Path resolvConfPath, Handler h) { - process(resolvConfPath, resolvConfPath, h); - } - - @SneakyThrows - public static void process(Path source, Path target, Handler h) { - String out; - try (var r = Files.newBufferedReader(source)) { - out = parse(r, h); - } - Files.writeString(target, out); - } - - public static String parse(String in, Handler h) { - return parse(new BufferedReader(new StringReader(in)), h); - } - - @SneakyThrows - public static String parse(BufferedReader r, Handler h) { - - final var sb = new StringBuilder(); - - boolean hasContent = false, foundDnsProxyEntry = false; - String line = null; - while ((line = r.readLine()) != null) { - hasContent = true; - - final var entryType = getDnsEntryType(line); - if (entryType == PROXY) { - foundDnsProxyEntry = true; - } - - final var res = h.handle(line, entryType); - if (StringUtils.isNotBlank(res)) { - sb.append(res); - sb.append('\n'); - } - - } - - final var res = h.after(hasContent, foundDnsProxyEntry); - if (StringUtils.isNotBlank(res)) { - sb.append(res); - sb.append('\n'); - } - return sb.toString(); - } - - static DnsEntryType getDnsEntryType(String line) { - if (line.endsWith("# dps-entry")) { - return PROXY; - } else if (line.startsWith("# nameserver ") && line.endsWith("# dps-comment")) { - return COMMENTED_SERVER; - } else if (line.startsWith("#")) { - return COMMENT; - } else if (line.startsWith("nameserver")) { - return SERVER; - } else if (line.startsWith("search")) { - return SEARCH; - } else { - return ELSE; - } - } - - public static String buildDNSLine(String serverIP) { - return "nameserver " + serverIP + " # dps-entry"; - } - - public interface Handler { - - String handle(String line, DnsEntryType entryType); - - String after(boolean hasContent, boolean foundDps); - } - -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/systemd/ResolvedService.java b/src/main/java/com/mageddo/dnsproxyserver/systemd/ResolvedService.java new file mode 100644 index 000000000..4df70e480 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/systemd/ResolvedService.java @@ -0,0 +1,15 @@ +package com.mageddo.dnsproxyserver.systemd; + +import com.mageddo.commons.exec.CommandLines; +import org.apache.commons.lang3.Validate; + +public class ResolvedService { + public static void restart() { + final var result = CommandLines.exec("service systemd-resolved restart"); + Validate.isTrue( + result.getExitCode() == 0, + "Not possible to restart resolved service: %d : %s", + result.getExitCode(), result.getOutAsString() + ); + } +} diff --git a/src/main/java/com/mageddo/utils/Files.java b/src/main/java/com/mageddo/utils/Files.java index 3c36c5ff6..4eb5a9484 100644 --- a/src/main/java/com/mageddo/utils/Files.java +++ b/src/main/java/com/mageddo/utils/Files.java @@ -1,9 +1,14 @@ package com.mageddo.utils; +import org.apache.commons.io.IOUtils; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; +import static java.nio.file.Files.newInputStream; +import static java.nio.file.Files.newOutputStream; + public class Files { public static Path createTempFileDeleteOnExit(final String prefix, final String suffix) { try { @@ -34,4 +39,19 @@ public static Path createIfNotExists(Path path) { public static String getPathName(Path path) { return path.getFileName().toString(); } + + /** + * Copy content but don't touch on the file permissions, java.nio.file.Files.copy() with REPLACE_EXISTING will change + * the file permissions. + * + * @param source + * @param target + */ + public static void copyContent(Path source, Path target) { + try (var in = newInputStream(source); var out = newOutputStream(target)) { + IOUtils.copy(in, out); + } 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 index 3992e4c6e..2e230ba80 100644 --- a/src/main/java/com/mageddo/utils/Tests.java +++ b/src/main/java/com/mageddo/utils/Tests.java @@ -1,7 +1,7 @@ package com.mageddo.utils; public class Tests { - public static boolean runningOnJunit() { + public static boolean inTest() { for (StackTraceElement element : Thread.currentThread().getStackTrace()) { if (element.getClassName().startsWith("org.junit.")) { return true; diff --git a/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfiguratorTest.java b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfiguratorTest.java index 25f0e93fe..f66fb48a6 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfiguratorTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/LinuxDnsConfiguratorTest.java @@ -2,7 +2,7 @@ import com.mageddo.dnsproxyserver.config.entrypoint.ConfigEnv; import com.mageddo.dnsproxyserver.dnsconfigurator.linux.ResolvFile.Type; -import com.mageddo.dnsproxyserver.server.dns.IP; +import com.mageddo.dnsproxyserver.templates.IpTemplates; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -21,46 +21,23 @@ class LinuxDnsConfiguratorTest { LinuxDnsConfigurator configurator = spy(new LinuxDnsConfigurator()); @Test - void mustConfigureDpsServerOnEmptyFile(@TempDir Path tmpDir) throws Exception { + void mustConfigureDpsServerOnEmptyFileAsResolvconf(@TempDir Path tmpDir) throws Exception { // arrrange final var resolvFile = Files.createTempFile(tmpDir, "resolv", ".conf"); - doReturn(Collections.singletonList(resolvFile)) - .when(this.configurator) - .buildConfPaths() - ; + final var ip = IpTemplates.local(); - // act - this.configurator.configure(IP.of("10.10.0.1")); - - // assert - assertEquals( - """ - nameserver 10.10.0.1 # dps-entry - """, - Files.readString(resolvFile) - ); - - } - - @Test - void mustCommentExistingServerAndSetupPassedConf(@TempDir Path tmpDir) throws Exception { - - // arrrange - final var resolvFile = tmpDir.resolve("resolv.conf"); - Files.writeString(resolvFile, "nameserver 8.8.8.8"); doReturn(Collections.singletonList(resolvFile)) .when(this.configurator) .buildConfPaths() ; // act - this.configurator.configure(IP.of("10.10.0.1")); + this.configurator.configure(ip); // assert assertEquals( """ - # nameserver 8.8.8.8 # dps-comment nameserver 10.10.0.1 # dps-entry """, Files.readString(resolvFile) @@ -68,26 +45,25 @@ void mustCommentExistingServerAndSetupPassedConf(@TempDir Path tmpDir) throws Ex } - @Test - void mustUseAlreadyExistentDpsServerLine(@TempDir Path tmpDir) throws Exception { + void mustConfigureDpsServerOnEmptyFileAsResolved(@TempDir Path tmpDir) throws Exception { // arrrange - final var resolvFile = tmpDir.resolve("resolv.conf"); - Files.writeString(resolvFile, "nameserver 8.8.8.8\nnameserver 4.4.4.4 # dps-entry"); + final var resolvFile = Files.createFile(tmpDir.resolve("resolved.conf")); + final var ip = IpTemplates.local(); + doReturn(Collections.singletonList(resolvFile)) .when(this.configurator) .buildConfPaths() ; // act - this.configurator.configure(IP.of("10.10.0.1")); + this.configurator.configure(ip); // assert assertEquals( """ - # nameserver 8.8.8.8 # dps-comment - nameserver 10.10.0.1 # dps-entry + DNS=10.10.0.1 # dps-entry """, Files.readString(resolvFile) ); @@ -95,30 +71,25 @@ void mustUseAlreadyExistentDpsServerLine(@TempDir Path tmpDir) throws Exception } @Test - void mustRestoreOriginalResolvconf(@TempDir Path tmpDir) throws Exception { + void shouldntConfigureResolvedTwice(@TempDir Path tmpDir) throws Exception { // arrrange - final var resolvFile = tmpDir.resolve("resolv.conf"); - Files.writeString(resolvFile, """ - # Provided by test - # nameserver 7.7.7.7 - # nameserver 8.8.8.8 # dps-comment - nameserver 9.9.9.9 # dps-entry - """); + final var resolvFile = Files.createFile(tmpDir.resolve("resolved.conf")); + final var ip = IpTemplates.local(); + doReturn(Collections.singletonList(resolvFile)) .when(this.configurator) .buildConfPaths() ; // act - this.configurator.restore(); + this.configurator.configure(ip); + this.configurator.configure(IpTemplates.loopback()); // assert assertEquals( """ - # Provided by test - # nameserver 7.7.7.7 - nameserver 8.8.8.8 + DNS=10.10.0.1 # dps-entry """, Files.readString(resolvFile) ); @@ -137,7 +108,10 @@ void mustSplitResolvPathConfigToMultiplePaths() { final var paths = this.configurator.buildConfPaths(); // assert - assertEquals("[/host/etc/resolv.conf, /etc/resolv.conf]", paths.toString()); + assertEquals( + "[/host/etc/systemd/resolved.conf, /host/etc/resolv.conf, /etc/systemd/resolved.conf, /etc/resolv.conf]", + paths.toString() + ); } @Test diff --git a/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfiguratorTest.java b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfiguratorTest.java new file mode 100644 index 000000000..655b0eb20 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/ResolvconfConfiguratorTest.java @@ -0,0 +1,104 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux; + +import com.mageddo.dnsproxyserver.server.dns.IP; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResolvconfConfiguratorTest { + + @Test + void mustConfigureDpsServerOnEmptyFile(@TempDir Path tmpDir) throws Exception { + + // arrrange + final var resolvFile = Files.createTempFile(tmpDir, "resolv", ".conf"); + + // act + ResolvconfConfigurator.process(resolvFile, IP.of("10.10.0.1")); + + // assert + assertEquals( + """ + nameserver 10.10.0.1 # dps-entry + """, + Files.readString(resolvFile) + ); + + } + + @Test + void mustCommentExistingServerAndSetupPassedConf(@TempDir Path tmpDir) throws Exception { + + // arrrange + final var resolvFile = tmpDir.resolve("resolv.conf"); + final var ip = IP.of("10.10.0.1"); + Files.writeString(resolvFile, "nameserver 8.8.8.8"); + + // act + ResolvconfConfigurator.process(resolvFile, ip); + + // assert + assertEquals( + """ + # nameserver 8.8.8.8 # dps-comment + nameserver 10.10.0.1 # dps-entry + """, + Files.readString(resolvFile) + ); + + } + + + @Test + void mustUseAlreadyExistentDpsServerLine(@TempDir Path tmpDir) throws Exception { + + // arrrange + final var resolvFile = tmpDir.resolve("resolv.conf"); + final var ip = IP.of("10.10.0.1"); + Files.writeString(resolvFile, "nameserver 8.8.8.8\nnameserver 4.4.4.4 # dps-entry"); + + // act + ResolvconfConfigurator.process(resolvFile, ip); + // assert + assertEquals( + """ + # nameserver 8.8.8.8 # dps-comment + nameserver 10.10.0.1 # dps-entry + """, + Files.readString(resolvFile) + ); + + } + + @Test + void mustRestoreOriginalResolvconf(@TempDir Path tmpDir) throws Exception { + + // arrrange + final var resolvFile = tmpDir.resolve("resolv.conf"); + + Files.writeString(resolvFile, """ + # Provided by test + # nameserver 7.7.7.7 + # nameserver 8.8.8.8 # dps-comment + nameserver 9.9.9.9 # dps-entry + """); + + // act + ResolvconfConfigurator.restore(resolvFile); + + // assert + assertEquals( + """ + # Provided by test + # nameserver 7.7.7.7 + nameserver 8.8.8.8 + """, + Files.readString(resolvFile) + ); + + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/systemdresolved/ResolvedConfiguratorTest.java b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/systemdresolved/ResolvedConfiguratorTest.java new file mode 100644 index 000000000..3e56939ae --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/dnsconfigurator/linux/systemdresolved/ResolvedConfiguratorTest.java @@ -0,0 +1,139 @@ +package com.mageddo.dnsproxyserver.dnsconfigurator.linux.systemdresolved; + +import com.mageddo.dnsproxyserver.dnsconfigurator.linux.ResolvedConfigurator; +import com.mageddo.dnsproxyserver.templates.IpTemplates; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResolvedConfiguratorTest { + + @Test + void mustConfigureDnsServerOnEmptyFile(@TempDir Path tmpDir) throws Exception { + // arrange + final var confFile = Files.createTempFile(tmpDir, "file", ".conf"); + final var localIp = IpTemplates.local(); + + // act + ResolvedConfigurator.configure(confFile, localIp); + + // assert + assertEquals(""" + DNS=10.10.0.1 # dps-entry + """, + Files.readString(confFile) + ); + } + + @Test + void mustConfigureDnsServerOnAlreadyExstingFile(@TempDir Path tmpDir) throws Exception { + // arrange + final var confFile = Files.writeString(tmpDir.resolve("file.conf"), """ + # This file is part of systemd. + # + [Resolve] + #DNS= + #DNS=172.157.5.1 + DNS=127.0.0.1 + #DNS=192.168.0.128 + #FallbackDNS= + #Domains= + """); + final var localIp = IpTemplates.local(); + + // act + ResolvedConfigurator.configure(confFile, localIp); + + // assert + assertEquals(""" + # This file is part of systemd. + # + [Resolve] + #DNS= + #DNS=172.157.5.1 + # DNS=127.0.0.1 # dps-comment + #DNS=192.168.0.128 + #FallbackDNS= + #Domains= + DNS=10.10.0.1 # dps-entry + """, Files.readString(confFile)); + } + + + @Test + void mustChangeActiveDNS(@TempDir Path tmpDir) throws Exception { + // arrange + final var confFile = Files.writeString(tmpDir.resolve("file.conf"), """ + [Resolve] + DNS=8.8.8.8 + FallbackDNS= + Domains= + """); + final var localIp = IpTemplates.local(); + + // act + ResolvedConfigurator.configure(confFile, localIp); + + // assert + assertEquals(""" + [Resolve] + # DNS=8.8.8.8 # dps-comment + FallbackDNS= + Domains= + DNS=10.10.0.1 # dps-entry + """, Files.readString(confFile)); + } + + @Test + void mustReuseDPSDNSLine(@TempDir Path tmpDir) throws Exception { + // arrange + final var confFile = Files.writeString(tmpDir.resolve("file.conf"), """ + [Resolve] + DNS=192.168.0.1 # dps-entry + """); + final var localIp = IpTemplates.local(); + + // act + ResolvedConfigurator.configure(confFile, localIp); + + // assert + assertEquals(""" + [Resolve] + DNS=10.10.0.1 # dps-entry + """, + Files.readString(confFile) + ); + } + + @Test + void mustRestore(@TempDir Path tmpDir) throws Exception { + // arrange + final var confFile = Files.writeString( + tmpDir.resolve("file.conf"), """ + [Resolve] + # DNS=8.8.8.8 # dps-comment + FallbackDNS= + Domains= + DNS=10.10.0.1 # dps-entry + """ + ); + final var localIp = IpTemplates.local(); + + // act + ResolvedConfigurator.restore(confFile); + + // assert + assertEquals(""" + [Resolve] + DNS=8.8.8.8 + FallbackDNS= + Domains= + """, + Files.readString(confFile) + ); + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/systemd/ResolvedServiceTest.java b/src/test/java/com/mageddo/dnsproxyserver/systemd/ResolvedServiceTest.java new file mode 100644 index 000000000..04b74d8ae --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/systemd/ResolvedServiceTest.java @@ -0,0 +1,28 @@ +package com.mageddo.dnsproxyserver.systemd; + +import org.apache.commons.exec.OS; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +class ResolvedServiceTest { + + @BeforeEach + void beforeEach(){ + assumeTrue(OS.isFamilyUnix() && !OS.isFamilyMac()); + } + + @Test + @Disabled + void mustRestartResolved(){ + + // arrange + + // act + ResolvedService.restart(); + + // assert + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java new file mode 100644 index 000000000..57f299e04 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java @@ -0,0 +1,13 @@ +package com.mageddo.dnsproxyserver.templates; + +import com.mageddo.dnsproxyserver.server.dns.IP; + +public class IpTemplates { + public static IP local(){ + return IP.of("10.10.0.1"); + } + + public static IP loopback(){ + return IP.of("127.0.0.1"); + } +} diff --git a/src/test/java/com/mageddo/resolvconf/ResolvConfParserTest.java b/src/test/java/com/mageddo/resolvconf/ResolvConfParserTest.java deleted file mode 100644 index 4922fa3a8..000000000 --- a/src/test/java/com/mageddo/resolvconf/ResolvConfParserTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mageddo.resolvconf; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.nio.file.Files; -import java.nio.file.Path; - -class ResolvConfParserTest { - - @Test - void mustConfigureDpsServer(@TempDir Path tmpDir) throws Exception { - - // arrrange - final var resolvFile = Files.createTempFile(tmpDir, "resolv", ".conf"); - - // act -// ResolvConfParser.parse(resolvFile, ) - - // assert - - } -}