This commit is contained in:
parent
48be45de26
commit
ba7c9d5fcc
10 changed files with 96 additions and 143 deletions
6
pom.xml
6
pom.xml
|
@ -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>
|
||||
|
|
|
@ -47,7 +47,7 @@ public class Updater {
|
|||
void run() {
|
||||
log.trace("Updater running.");
|
||||
|
||||
if(config.isEmpty()) {
|
||||
if (config.isEmpty()) {
|
||||
throw new IllegalStateException("Missing configuration");
|
||||
}
|
||||
|
||||
|
|
46
src/main/java/de/rpr/ddnsclient/lookup/DnsJava.java
Normal file
46
src/main/java/de/rpr/ddnsclient/lookup/DnsJava.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
try {
|
||||
Process curlProcess = curlProcessFactory.create(provider, CurlProcessFactory.IpClass.V4).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);
|
||||
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.V6).start();
|
||||
Process curlProcess = curlProcessFactory.create(provider, ipClass).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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
src/test/java/de/rpr/ddnsclient/lookup/DnsJavaTest.java
Normal file
16
src/test/java/de/rpr/ddnsclient/lookup/DnsJavaTest.java
Normal 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();
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue