Repository: mageddo/dns-proxy-server
Pull request #400 (2023-03-29 02:38:30)

SolverSystem: Support for Type AAAA Entries (IPv6)
Relates to #389 


Patch:
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 78b839f8..9191019c 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,5 +1,5 @@
 ## 3.15
-* Support for AAAA records on docker container solving.
+* Support for AAAA records on docker container and system solving.
 * Formalize Recursion Available on query responses as DPS supports this feature already, see #392
 * Defined a new process of releasing the stable versions, see "Getting Started -> Release Version Control" 
 for more details
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java b/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
index 1ad197af..ee4e0ce4 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
@@ -22,6 +22,7 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static com.mageddo.commons.lang.Objects.mapOrNull;
 import static com.mageddo.dnsproxyserver.docker.Labels.DEFAULT_NETWORK_LABEL;
 import static com.mageddo.dnsproxyserver.docker.domain.Network.BRIDGE;
 import static com.mageddo.dnsproxyserver.docker.domain.Network.DPS;
@@ -57,7 +58,12 @@ public class ContainerSolvingService {
   }
 
   public String findBestIpMatch(InspectContainerResponse inspect, IP.Version version) {
-    return this.findBestIpMatch(inspect, buildNetworks(inspect), this.dockerDAO::findHostMachineIpRaw, version);
+    return this.findBestIpMatch(
+      inspect,
+      buildNetworks(inspect),
+      () -> mapOrNull(this.dockerDAO.findHostMachineIp(version), IP::toText),
+      version
+    );
   }
 
   public String findBestIpMatch(
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java
index 9c9fddd0..e53c4767 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAO.java
@@ -8,14 +8,14 @@ import java.util.List;
 
 public interface DockerDAO {
 
-  IP findHostMachineIp();
-
   boolean isConnected();
 
   List<Container> findActiveContainers();
 
   InspectContainerResponse inspect(String id);
 
-  String findHostMachineIpRaw();
+  IP findHostMachineIp();
+
+  IP findHostMachineIp(IP.Version version);
 
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
index 9fa6ad4d..8b32e774 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
@@ -25,12 +25,12 @@ public class DockerDAODefault implements DockerDAO {
 
   @Override
   public IP findHostMachineIp() {
-    return IP.of(this.findHostMachineIpRaw());
+    return this.findHostMachineIp(IP.Version.IPV4);
   }
 
   @Override
-  public String findHostMachineIpRaw() {
-    return DockerNetworkService.findGatewayIp(this.findBestNetwork());
+  public IP findHostMachineIp(IP.Version version) {
+    return DockerNetworkService.findGatewayIp(this.findBestNetwork(version), version);
   }
 
   @Override
@@ -54,10 +54,11 @@ public class DockerDAODefault implements DockerDAO {
     return this.dockerClient.inspectContainerCmd(id).exec();
   }
 
-  Network findBestNetwork() {
+  Network findBestNetwork(IP.Version version) {
     final var network = this.dockerClient.listNetworksCmd()
       .exec()
       .stream()
+      .filter(it -> java.util.Objects.equals(it.getEnableIPv6(), version.isIpv6()))
       .min(NetworkComparator::compare)
       .orElse(null);
     log.debug(
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java
index 4ad99fa5..18c5bfe9 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAOMock.java
@@ -20,6 +20,11 @@ public class DockerDAOMock implements DockerDAO {
     return IP.of("127.0.0.1");
   }
 
+  @Override
+  public IP findHostMachineIp(IP.Version version) {
+    throw new UnsupportedOperationException();
+  }
+
   @Override
   public boolean isConnected() {
     return true;
@@ -35,9 +40,4 @@ public class DockerDAOMock implements DockerDAO {
     throw new UnsupportedOperationException();
   }
 
-  @Override
-  public String findHostMachineIpRaw() {
-    throw new UnsupportedOperationException();
-  }
-
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
index 25a9a5ae..8069f2e7 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
@@ -2,7 +2,7 @@ package com.mageddo.dnsproxyserver.docker;
 
 import com.github.dockerjava.api.model.Container;
 import com.github.dockerjava.api.model.Network;
-import com.mageddo.dnsproxyserver.utils.Ips;
+import com.mageddo.net.IP;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
@@ -23,7 +23,11 @@ public class DockerNetworkService {
   private final DockerNetworkDAO networkDAO;
   private final ContainerDAO containerDAO;
 
-  public static String findGatewayIp(Network network) {
+  public static IP findGatewayIp(Network network) {
+    return findGatewayIp(network, IP.Version.IPV4);
+  }
+
+  public static IP findGatewayIp(Network network, IP.Version version) {
     if (network == null) {
       return null;
     }
@@ -32,7 +36,8 @@ public class DockerNetworkService {
       .getConfig()
       .stream()
       .map(Network.Ipam.Config::getGateway)
-      .filter(Ips::isIpv4)
+      .map(IP::of)
+      .filter(it -> it.version() == version)
       .findFirst()
       .orElse(null)
       ;
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 d0f28ba3..4a1a6a66 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java
@@ -2,6 +2,7 @@ package com.mageddo.dnsproxyserver.server.dns;
 
 import com.mageddo.commons.lang.Objects;
 import com.mageddo.dnsproxyserver.config.Config.Entry;
+import com.mageddo.dnsproxyserver.server.dns.solver.HostnameQuery;
 import com.mageddo.dnsproxyserver.server.dns.solver.Response;
 import com.mageddo.dnsproxyserver.utils.Ips;
 import com.mageddo.net.IP;
@@ -272,4 +273,10 @@ public class Messages {
   public static boolean hasFlag(Message msg, int flag) {
     return msg.getHeader().getFlag(flag);
   }
+
+  public static HostnameQuery toHostnameQuery(Message query) {
+    final var host = Messages.findQuestionHostname(query);
+    final var version = Entry.Type.of(findQuestionTypeCode(query)).toVersion();
+    return HostnameQuery.of(host, version);
+  }
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
index fcc688d2..4223a8b0 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
@@ -30,6 +30,28 @@ public class HostnameQuery {
     return of(hostname, false, false);
   }
 
+  public static HostnameQuery of(Hostname hostname, IP.Version version) {
+    return HostnameQuery
+      .builder()
+      .hostname(hostname)
+      .version(version)
+      .build();
+  }
+
+  public static HostnameQuery of(Hostname hostname, boolean wildcards, boolean regex) {
+    return HostnameQuery
+      .builder()
+      .hostname(hostname)
+      .useWildcards(wildcards)
+      .useRegex(regex)
+      .version(IP.Version.IPV4)
+      .build();
+  }
+
+  public static HostnameQuery of(String hostname) {
+    return of(Hostname.of(hostname));
+  }
+
   public static HostnameQuery ofWildcard(String hostname) {
     return ofWildcard(hostname, IP.Version.IPV4);
   }
@@ -72,20 +94,6 @@ public class HostnameQuery {
       .build();
   }
 
-  public static HostnameQuery of(Hostname hostname, boolean wildcards, boolean regex) {
-    return HostnameQuery
-      .builder()
-      .hostname(hostname)
-      .useWildcards(wildcards)
-      .useRegex(regex)
-      .version(IP.Version.IPV4)
-      .build();
-  }
-
-  public static HostnameQuery of(String hostname) {
-    return of(Hostname.of(hostname));
-  }
-
   public boolean matches(Hostname hostname) {
     return matches(hostname.getCanonicalValue());
   }
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 f900746b..37514ab6 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
@@ -28,13 +28,16 @@ public class SolverSystem implements Solver {
     }
     final var config = Configs.getInstance();
     if (hostname.isEqualTo(config.getHostMachineHostname())) { // fixme fazer case com hostname + search domain
-      final var ip = this.machineService.findHostMachineIP();
+      final var ip = this.machineService.findHostMachineIP(questionType.toVersion());
       if (ip == null) {
         log.debug("status=hostMachineIpNotFound, host={}", hostname);
         return null;
       }
       log.debug("status=solvingHostMachineName, host={}, ip={}", hostname, ip);
-      return Response.of(Messages.aAnswer(query, ip.toText()), Messages.DEFAULT_TTL_DURATION);
+      return Response.of(
+        Messages.answer(query, ip.toText(), questionType.toVersion()),
+        Messages.DEFAULT_TTL_DURATION
+      );
     }
     return null;
   }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/usecase/HostMachineService.java b/src/main/java/com/mageddo/dnsproxyserver/usecase/HostMachineService.java
index 30b530ae..eab9c1b6 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/usecase/HostMachineService.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/usecase/HostMachineService.java
@@ -17,18 +17,25 @@ public class HostMachineService {
   private final DpsContainerManager dpsContainerManager;
 
   public IP findHostMachineIP() {
+    return this.findHostMachineIP(IP.Version.IPV4);
+  }
+
+  public IP findHostMachineIP(IP.Version version) {
     if (this.isDpsRunningInsideContainer()) {
-      return this.dockerDAO.findHostMachineIp();
+      return this.dockerDAO.findHostMachineIp(version);
     }
-    return this.findCurrentMachineIp();
+    return this.findCurrentMachineIp(version);
   }
 
-  boolean isDpsRunningInsideContainer() {
-    return this.dpsContainerManager.isDpsRunningInsideContainer();
+  IP findCurrentMachineIp() {
+    return this.findCurrentMachineIp(IP.Version.IPV4);
   }
 
-  IP findCurrentMachineIp() {
-    return Networks.findCurrentMachineIP();
+  IP findCurrentMachineIp(IP.Version version) {
+    return Networks.findCurrentMachineIP(version);
   }
 
+  boolean isDpsRunningInsideContainer() {
+    return this.dpsContainerManager.isDpsRunningInsideContainer();
+  }
 }
diff --git a/src/main/java/com/mageddo/net/IP.java b/src/main/java/com/mageddo/net/IP.java
index 7d8509d1..f720c558 100644
--- a/src/main/java/com/mageddo/net/IP.java
+++ b/src/main/java/com/mageddo/net/IP.java
@@ -36,5 +36,9 @@ public interface IP {
     public boolean isIpv6() {
       return this == IPV6;
     }
+
+    public boolean isIpv4() {
+      return this == IPV4;
+    }
   }
 }
diff --git a/src/main/java/com/mageddo/net/IpImpl.java b/src/main/java/com/mageddo/net/IpImpl.java
index 0cd1304e..6ba29e7d 100644
--- a/src/main/java/com/mageddo/net/IpImpl.java
+++ b/src/main/java/com/mageddo/net/IpImpl.java
@@ -2,11 +2,13 @@ package com.mageddo.net;
 
 import com.mageddo.dnsproxyserver.utils.Ips;
 import com.mageddo.utils.Bytes;
+import lombok.EqualsAndHashCode;
 import org.apache.commons.lang3.StringUtils;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
+@EqualsAndHashCode(of = "ip")
 class IpImpl implements IP {
 
   private final InetAddress ip;
diff --git a/src/main/java/com/mageddo/net/Networks.java b/src/main/java/com/mageddo/net/Networks.java
index a348043a..97a28c83 100644
--- a/src/main/java/com/mageddo/net/Networks.java
+++ b/src/main/java/com/mageddo/net/Networks.java
@@ -15,11 +15,18 @@ public class Networks {
 
   volatile static Network network = Network.getInstance();
 
-  @SneakyThrows
   public static IP findCurrentMachineIP() {
+    return findCurrentMachineIP(IP.Version.IPV4);
+  }
+
+  @SneakyThrows
+  public static IP findCurrentMachineIP(IP.Version version) {
     return findMachineIps()
       .stream()
-      .findFirst()
+      .filter(it -> it.version() == version) // todo needs a filter to exclude virtual network cards
+      .min(Comparator.comparing(it -> {
+        return it.isLoopback() ? Integer.MAX_VALUE : 0;
+      }))
       .orElse(null);
   }
 
@@ -29,21 +36,17 @@ public class Networks {
    *
    * @return Machine ips ordered by relevance.
    */
-  public static List<IP> findMachineIps() {
+  static List<IP> findMachineIps() {
     return findInterfaces()
       .stream()
       .sorted(Comparator.comparingInt(NetworkInterface::getIndex))
       .flatMap(NetworkInterface::inetAddresses)
-      .filter(it -> it.getAddress().length == IP.IPV4_BYTES) // todo needs a filter to exclude virtual network cards
       .map(it -> IP.of(it.getHostAddress()))
-      .sorted(Comparator.comparing(it -> {
-        return it.toText().startsWith("127") ? Integer.MAX_VALUE : 0;
-      }))
       .toList()
       ;
   }
 
-  public static List<NetworkInterface> findInterfaces() {
+  static List<NetworkInterface> findInterfaces() {
     return network.findNetworkInterfaces()
       .filter(it -> {
         try {


Test patch:
diff --git a/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java b/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
index a71e7ef7..28fd7c98 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
@@ -159,7 +159,7 @@ class ContainerSolvingServiceCompTest {
     // assert
     assertNotNull(ip);
     assertEquals("2001:db7:1::2", ip);
-    verify(this.dockerDAO, never()).findHostMachineIpRaw();
+    verify(this.dockerDAO, never()).findHostMachineIp();
   }
 
   @Test
diff --git a/src/test/java/com/mageddo/dnsproxyserver/docker/DockerDAODefaultTest.java b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerDAODefaultTest.java
new file mode 100644
index 00000000..ce799262
--- /dev/null
+++ b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerDAODefaultTest.java
@@ -0,0 +1,41 @@
+package com.mageddo.dnsproxyserver.docker;
+
+import com.mageddo.dnsproxyserver.templates.docker.NetworkTemplates;
+import com.mageddo.net.IP;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+@ExtendWith(MockitoExtension.class)
+class DockerDAODefaultTest {
+
+  @Spy
+  @InjectMocks
+  DockerDAODefault dockerDAO;
+
+  @Test
+  void mustSolveIpv6IP() {
+
+    // arrange
+    final var version = IP.Version.IPV6;
+    doReturn(NetworkTemplates.buildBridgeIpv4AndIpv6Network())
+      .when(this.dockerDAO)
+      .findBestNetwork(eq(version));
+
+    // act
+    final var ip = this.dockerDAO.findHostMachineIp(version);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals(IP.of("2001:db8:1::1"), ip);
+
+  }
+
+}
diff --git a/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java
index 418eb49d..ecc4e851 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java
@@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
 
 import static com.mageddo.dnsproxyserver.templates.docker.NetworkTemplates.buildBridgeIpv4AndIpv6Network;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 class DockerNetworkServiceTest {
 
@@ -17,6 +18,7 @@ class DockerNetworkServiceTest {
     final var ip = DockerNetworkService.findGatewayIp(network);
 
     // assert
-    assertEquals("172.21.0.1", ip);
+    assertNotNull(ip);
+    assertEquals("172.21.0.1", ip.toText());
   }
 }
diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
index 331ee9bb..dd604be3 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
@@ -16,6 +16,8 @@ import javax.inject.Inject;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
@@ -36,7 +38,7 @@ class SolverSystemCompTest {
 
     doReturn(IP.of("192.168.0.1"))
       .when(this.machineService)
-      .findHostMachineIP()
+      .findHostMachineIP(any())
     ;
 
     // act
@@ -49,7 +51,7 @@ class SolverSystemCompTest {
     assertTrue(Messages.hasFlag(msg, Flags.RA));
     assertEquals("host.docker.\t\t30\tIN\tA\t192.168.0.1", answer);
 
-    verify(this.machineService).findHostMachineIP();
+    verify(this.machineService).findHostMachineIP(eq(IP.Version.IPV4));
   }
 
 }
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/InetAddressTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/InetAddressTemplates.java
new file mode 100644
index 00000000..28ace952
--- /dev/null
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/InetAddressTemplates.java
@@ -0,0 +1,23 @@
+package com.mageddo.dnsproxyserver.templates;
+
+import lombok.SneakyThrows;
+
+import java.net.InetAddress;
+
+public class InetAddressTemplates {
+
+  @SneakyThrows
+  public static InetAddress loopback() {
+    return InetAddress.getByName("127.0.0.1");
+  }
+
+  @SneakyThrows
+  public static InetAddress local() {
+    return InetAddress.getByName(IpTemplates.LOCAL);
+  }
+
+  @SneakyThrows
+  public static InetAddress local192() {
+    return InetAddress.getByName(IpTemplates.LOCAL_192);
+  }
+}
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
index 9ebf63c7..9c47d47c 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
@@ -7,6 +7,7 @@ public class IpTemplates {
   public static final String LOCAL = "10.10.0.1";
   public static final String LOCAL_IPV6 = "2001:db8:1::2";
   public static final String LOCAL_EXTENDED_IPV6 = "2001:db8:1:0:0:0:0:2";
+  public static final String LOCAL_192 = "192.168.0.10";
 
   public static IP local(){
     return IP.of(LOCAL);
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/NetworkInterfaceTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/NetworkInterfaceTemplates.java
new file mode 100644
index 00000000..f181a46d
--- /dev/null
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/NetworkInterfaceTemplates.java
@@ -0,0 +1,55 @@
+package com.mageddo.dnsproxyserver.templates;
+
+import lombok.SneakyThrows;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.stream.Stream;
+
+import static org.mockito.Mockito.doReturn;
+
+public class NetworkInterfaceTemplates {
+
+  public static Stream<NetworkInterface> localAndLoopback() {
+    final var loopback = buildNetworkInterface(true, 0, InetAddressTemplates.loopback());
+    final var local192 = buildNetworkInterface(false, 1, InetAddressTemplates.local192());
+    final var local10 = buildNetworkInterface(false, 2, InetAddressTemplates.local());
+    return Stream.of(loopback, local10, local192);
+  }
+
+  @SneakyThrows
+  static NetworkInterface buildNetworkInterface(
+    final boolean isLoopback,
+    final int index,
+    final InetAddress address
+  ) {
+    final var loopback = Mockito.mock(
+      NetworkInterface.class,
+      Mockito
+        .withSettings()
+        .strictness(Strictness.LENIENT)
+    );
+    doReturn(isLoopback)
+      .when(loopback)
+      .isLoopback()
+    ;
+
+    doReturn(index)
+      .when(loopback)
+      .getIndex()
+    ;
+
+    doReturn(Stream.of(address))
+      .when(loopback)
+      .inetAddresses()
+    ;
+
+    doReturn(true)
+      .when(loopback)
+      .isUp()
+    ;
+    return loopback;
+  }
+}
diff --git a/src/test/java/com/mageddo/dnsproxyserver/usecase/HostMachineServiceTest.java b/src/test/java/com/mageddo/dnsproxyserver/usecase/HostMachineServiceTest.java
index e9cad3d4..dd393877 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/usecase/HostMachineServiceTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/usecase/HostMachineServiceTest.java
@@ -10,6 +10,7 @@ import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 
 @ExtendWith(MockitoExtension.class)
@@ -33,7 +34,7 @@ class HostMachineServiceTest {
     final var expectedIp = IpTemplates.local();
     doReturn(expectedIp)
       .when(this.machineService)
-      .findCurrentMachineIp()
+      .findCurrentMachineIp(any())
     ;
 
     // act
@@ -54,7 +55,7 @@ class HostMachineServiceTest {
     final var expectedIp = IpTemplates.local();
     doReturn(expectedIp)
       .when(this.dockerDAO)
-      .findHostMachineIp()
+      .findHostMachineIp(any())
     ;
 
     // act
diff --git a/src/test/java/com/mageddo/net/NetworksTest.java b/src/test/java/com/mageddo/net/NetworksTest.java
index b941cea3..9754f41f 100644
--- a/src/test/java/com/mageddo/net/NetworksTest.java
+++ b/src/test/java/com/mageddo/net/NetworksTest.java
@@ -1,15 +1,46 @@
 package com.mageddo.net;
 
+import com.mageddo.dnsproxyserver.templates.IpTemplates;
+import com.mageddo.dnsproxyserver.templates.NetworkInterfaceTemplates;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
+@ExtendWith(MockitoExtension.class)
 class NetworksTest {
 
+  static Network realNetwork;
+
+  @Spy
+  Network network;
+
+  @BeforeAll
+  static void beforeAll(){
+    realNetwork = Networks.network;
+  }
+
+  @AfterAll
+  static void afterAll(){
+    Networks.network = realNetwork;
+  }
+
+  @BeforeEach
+  void before(){
+    Networks.network = this.network;
+  }
 
   @Test
-  void mustFindCurrentMachineIpAddress(){
+  void mustFindCurrentMachineIpAddress() {
 
     // arrange
 
@@ -21,7 +52,7 @@ class NetworksTest {
   }
 
   @Test
-  void mustContainsLocalhostAddress(){
+  void mustContainsLocalhostAddress() {
     // arrange
 
     // act
@@ -31,4 +62,19 @@ class NetworksTest {
     assertTrue(ips.contains("127"), ips);
   }
 
+  @Test
+  void mustPreferNonLoopbackAddresses() {
+    // arrange
+    doReturn(NetworkInterfaceTemplates.localAndLoopback())
+      .when(this.network).findNetworkInterfaces();
+
+    // act
+    final var ip = Networks.findCurrentMachineIP();
+
+    // assert
+    assertNotNull(ip);
+    assertFalse(ip.isLoopback());
+    assertEquals(IpTemplates.LOCAL_192, ip.toText());
+  }
+
 }