diff --git a/build-logic/tools/build.gradle b/build-logic/tools/build.gradle index 3af03ef10..4d2153a1d 100644 --- a/build-logic/tools/build.gradle +++ b/build-logic/tools/build.gradle @@ -1,5 +1,7 @@ plugins { + id "org.jetbrains.kotlin.jvm" version "1.8.0" id "java-library" + id "org.jlleitschuh.gradle.ktlint" version "11.1.0" } java { @@ -7,6 +9,13 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } +// NOTE: For now, in order to run ktlint on this project, you have to manually run ./gradlew :build-logic:tools:ktlintFormat +// Gotta figure out how to get it auto-included in the normal ./gradlew ktlintFormat +ktlint { + // Use a newer version to resolve https://github.com/JLLeitschuh/ktlint-gradle/issues/507 + version = "0.47.1" +} + dependencies { implementation libs.dnsjava -} \ No newline at end of file +} diff --git a/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.java b/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.java deleted file mode 100644 index a0a2905c7..000000000 --- a/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.signal.buildtools; - -import org.xbill.DNS.ARecord; -import org.xbill.DNS.Lookup; -import org.xbill.DNS.Record; -import org.xbill.DNS.Resolver; -import org.xbill.DNS.SimpleResolver; -import org.xbill.DNS.Type; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public final class StaticIpResolver { - - private StaticIpResolver() {} - - public static String resolveToBuildConfig(String hostName) { - String[] ips = resolve(hostName); - StringBuilder builder = new StringBuilder(); - builder.append("new String[]{"); - for (int i = 0; i < ips.length; i++) { - builder.append("\"").append(ips[i]).append("\""); - if (i < ips.length - 1) { - builder.append(","); - } - } - return builder.append("}").toString(); - } - - private static String[] resolve(String hostname) { - Set ips = new HashSet<>(); - - // Run several resolves to mitigate DNS round robin - for (int i = 0; i < 10; i++) { - ips.addAll(resolveOnce(hostname)); - } - - return ips.stream().sorted().toArray(String[]::new); - } - - private static List resolveOnce(String hostName) { - try { - Resolver resolver = new SimpleResolver("1.1.1.1"); - Lookup lookup = doLookup(hostName); - - lookup.setResolver(resolver); - - Record[] records = lookup.run(); - - if (records != null) { - return Arrays.stream(records) - .filter(r -> r.getType() == Type.A) - .map(r -> (ARecord) r) - .map(ARecord::getAddress) - .map(InetAddress::getHostAddress) - .collect(Collectors.toList()); - } else { - throw new IllegalStateException("Failed to resolve host! " + hostName); - } - } catch (UnknownHostException e) { - throw new IllegalStateException("Failed to resolve host! " + hostName); - } - } - - private static Lookup doLookup(String hostname) throws UnknownHostException { - try { - return new Lookup(hostname); - } catch (Throwable e) { - throw new UnknownHostException(); - } - } -} diff --git a/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt b/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt new file mode 100644 index 000000000..e54ffc015 --- /dev/null +++ b/build-logic/tools/src/main/java/org/signal/buildtools/StaticIpResolver.kt @@ -0,0 +1,87 @@ +package org.signal.buildtools + +import org.xbill.DNS.ARecord +import org.xbill.DNS.Lookup +import org.xbill.DNS.Record +import org.xbill.DNS.SimpleResolver +import org.xbill.DNS.Type +import java.net.UnknownHostException +import kotlin.streams.toList + +/** + * A tool to resolve hostname to static IPs. + * Feeds into our custom DNS resolver to provide a static IP fallback for our services. + */ +object StaticIpResolver { + + /** + * Resolves a hostname to a list of IPs, represented as a Java array declaration. e.g. + * + * ```java + * new String[]{"192.168.1.1", "192.168.1.2"} + * ``` + * + * This is intended to be injected as a BuildConfig. + */ + @JvmStatic + fun resolveToBuildConfig(hostName: String): String { + val ips: List = resolve(hostName) + val builder = StringBuilder() + + builder.append("new String[]{") + + ips.forEachIndexed { i, ip -> + builder.append("\"").append(ip).append("\"") + + if (i < ips.size - 1) { + builder.append(",") + } + } + + return builder.append("}").toString() + } + + private fun resolve(hostname: String): List { + val ips: MutableSet = mutableSetOf() + + // Run several resolves to mitigate DNS round robin + for (i in 1..10) { + ips.addAll(resolveOnce(hostname)) + } + + return ips.stream().sorted().toList() + } + + private fun resolveOnce(hostName: String): List { + try { + val resolver = SimpleResolver("1.1.1.1") + val lookup: Lookup = doLookup(hostName) + + lookup.setResolver(resolver) + + val records: Array? = lookup.run() + + if (records != null) { + return records + .filter { it.type == Type.A } + .map { it as ARecord } + .map { it.address } + .map { it.hostAddress } + .filterNotNull() + } else { + throw IllegalStateException("Failed to resolve host! $hostName") + } + } catch (e: UnknownHostException) { + throw IllegalStateException("Failed to resolve host! $hostName") + } + } + + @Throws(UnknownHostException::class) + private fun doLookup(hostname: String): Lookup { + try { + return Lookup(hostname) + } catch (e: Throwable) { + throw UnknownHostException() + } + } +}