Use dnsjava for dns lookup without jndi
Some checks failed
ci/woodpecker/push/build Pipeline failed

This commit is contained in:
Ryan Harg 2024-11-28 10:00:08 +01:00
parent 48be45de26
commit ba7c9d5fcc
10 changed files with 96 additions and 143 deletions

View file

@ -18,6 +18,7 @@
<surefire-plugin.version>3.5.0</surefire-plugin.version>
<mockito.version>5.14.1</mockito.version>
<assertj.version>3.26.3</assertj.version>
<dnsjava.version>3.6.2</dnsjava.version>
</properties>
<dependencyManagement>
@ -55,6 +56,11 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<version>${dnsjava.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>

View file

@ -0,0 +1,46 @@
package de.rpr.ddnsclient.lookup;
import de.rpr.ddnsclient.model.IPs;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.xbill.DNS.*;
import org.xbill.DNS.Record;
import java.util.Optional;
public class DnsJava implements DnsResolver {
private final String resolverAddress;
public DnsJava(@ConfigProperty(name = "", defaultValue = "9.9.9.9") String resolverAddress) {
this.resolverAddress = resolverAddress;
}
@Override
public IPs resolve(String hostname) {
String v4 = getIp(hostname, Type.A).orElseThrow(() -> new RuntimeException("Missing A Record"));
String v6 = getIp(hostname, Type.AAAA).orElseThrow(() -> new RuntimeException("Missing AAAA Record"));
return new IPs(v4, v6);
}
private Optional<String> getIp(String hostname, int type) {
try {
SimpleResolver resolver = new SimpleResolver(resolverAddress);
Lookup lookup = new Lookup(hostname, type);
lookup.setResolver(resolver);
Record[] records = lookup.run();
for (Record record : records) {
if (record instanceof ARecord) {
return Optional.of(((ARecord) record).getAddress().getHostAddress());
}
if (record instanceof AAAARecord) {
return Optional.of(((AAAARecord) record).getAddress().getHostAddress());
}
}
return Optional.empty();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,48 +1,7 @@
package de.rpr.ddnsclient.lookup;
import de.rpr.ddnsclient.model.IPs;
import jakarta.enterprise.context.ApplicationScoped;
import org.jboss.logging.Logger;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
@ApplicationScoped
public class DnsResolver {
private static final Logger log = Logger.getLogger(DnsResolver.class);
public static final int MAX_RETRIES = 5;
private final DirContext dirContext;
DnsResolver(DirContext dirContext) {
this.dirContext = dirContext;
}
public IPs resolve(String hostname) {
log.tracef("Trying to resolve %s", hostname);
int resolveCounter = 0;
try {
Attributes result;
do {
if (resolveCounter == MAX_RETRIES) {
throw new IllegalStateException("Couldn't resolve all attributes");
}
resolveCounter++;
log.tracef("Try #%d", resolveCounter);
result = dirContext.getAttributes(hostname, new String[]{"A", "AAAA"});
} while (result.size() != 2);
return new IPs(
result.get("A").get().toString(),
result.get("AAAA").get().toString()
);
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
public interface DnsResolver {
IPs resolve(String hostname);
}

View file

@ -1,7 +1,6 @@
package de.rpr.ddnsclient.lookup;
import de.rpr.ddnsclient.model.IPs;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
@ -81,33 +80,24 @@ public class PublicIpLookup {
try {
String provider = this.provider.map(it -> it.url).orElseGet(() -> Providers.random().url);
IPs.Builder ipsBuilder = new IPs.Builder();
String v4 = getIp(provider, CurlProcessFactory.IpClass.V4);
String v6 = getIp(provider, CurlProcessFactory.IpClass.V6);
return new IPs(v4, v6);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private String getIp(String provider, CurlProcessFactory.IpClass ipClass) {
try {
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V4).start();
Process curlProcess = curlProcessFactory.create(provider, ipClass).start();
curlProcess.waitFor();
InputStream inputStream = curlProcess.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String v4 = reader.readLine();
log.tracef("Retrieved ipv4: %s", v4);
ipsBuilder.v4(v4);
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V6).start();
curlProcess.waitFor();
InputStream inputStream = curlProcess.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String v6 = reader.readLine();
log.tracef("Retrieved ipv6: %s", v6);
ipsBuilder.v6(v6);
} catch (Exception e) {
throw new RuntimeException(e);
}
return ipsBuilder.build();
String ip = reader.readLine();
log.tracef("Retrieved ip: %s", ip);
return ip;
} catch (Exception e) {
throw new RuntimeException(e);
}

View file

@ -1,7 +1,11 @@
package de.rpr.ddnsclient.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.quarkus.runtime.annotations.RegisterForReflection;
import java.util.ArrayList;
@RegisterForReflection
public class Config extends ArrayList<Config.Value> {
public record Value(
@ -12,4 +16,9 @@ public class Config extends ArrayList<Config.Value> {
String token) {
}
@JsonCreator
public Config() {
// empty
}
}

View file

@ -2,23 +2,5 @@ package de.rpr.ddnsclient.model;
public record IPs(String v4, String v6) {
public static final class Builder {
String v4;
String v6;
public Builder v4(String v4) {
this.v4 = v4;
return this;
}
public Builder v6(String v6) {
this.v6 = v6;
return this;
}
public IPs build() {
return new IPs(v4, v6);
}
}
}

View file

@ -0,0 +1,16 @@
package de.rpr.ddnsclient.lookup;
import de.rpr.ddnsclient.model.IPs;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class DnsJavaTest {
@Test
void test() {
IPs result = new DnsJava("9.9.9.9").resolve("example.com");
assertThat(result.v4()).isNotBlank();
assertThat(result.v6()).isNotBlank();
}
}

View file

@ -1,53 +0,0 @@
package de.rpr.ddnsclient.lookup;
import de.rpr.ddnsclient.model.IPs;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.naming.NamingException;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class DnsResolverTest {
@Mock
DirContext dirContext;
@Test
void should_throw_exception_if_resolving_failes_repeatedly() throws NamingException {
when(dirContext.getAttributes(anyString(), any())).thenReturn(new BasicAttributes());
DnsResolver dnsResolver = new DnsResolver(dirContext);
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> dnsResolver.resolve("example.com"));
}
@Test
void should_return_ips() throws NamingException {
BasicAttributes attributes = new BasicAttributes() {{
put("A", "ipv4");
put("AAAA", "ipv6");
}};
when(dirContext.getAttributes(anyString(), any())).thenReturn(attributes);
DnsResolver dnsResolver = new DnsResolver(dirContext);
IPs result = dnsResolver.resolve("example.com");
assertThat(result.v4()).isEqualTo("ipv4");
assertThat(result.v6()).isEqualTo("ipv6");
}
}

View file

@ -8,9 +8,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import java.util.Optional;