Skip to content

Commit cb3c174

Browse files
committed
ui: support DNS SRV lookup & show resolved inetendpoint
Signed-off-by: panxuesen <[email protected]>
1 parent 4ba8794 commit cb3c174

File tree

4 files changed

+85
-14
lines changed

4 files changed

+85
-14
lines changed

Diff for: gradle/libs.versions.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jsr305 = "com.google.code.findbugs:jsr305:3.0.2"
2121
junit = "junit:junit:4.13.2"
2222
kotlinx-coroutines-android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0"
2323
zxing-android-embedded = "com.journeyapps:zxing-android-embedded:4.3.0"
24+
dnsjava = "dnsjava:dnsjava:3.4.2"
2425

2526
[plugins]
2627
android-application = { id = "com.android.application", version.ref = "agp" }

Diff for: tunnel/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ android {
6767
dependencies {
6868
implementation(libs.androidx.annotation)
6969
implementation(libs.androidx.collection)
70+
implementation(libs.dnsjava)
7071
compileOnly(libs.jsr305)
7172
testImplementation(libs.junit)
7273
}

Diff for: tunnel/src/main/java/com/wireguard/config/InetEndpoint.java

+72-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@
77

88
import com.wireguard.util.NonNullForAll;
99

10+
import org.xbill.DNS.DClass;
11+
import org.xbill.DNS.ExtendedResolver;
12+
import org.xbill.DNS.Lookup;
13+
import org.xbill.DNS.Record;
14+
import org.xbill.DNS.Resolver;
15+
import org.xbill.DNS.SRVRecord;
16+
import org.xbill.DNS.SimpleResolver;
17+
import org.xbill.DNS.TextParseException;
18+
import org.xbill.DNS.Type;
19+
1020
import java.net.Inet4Address;
1121
import java.net.InetAddress;
1222
import java.net.URI;
@@ -15,6 +25,7 @@
1525
import java.time.Duration;
1626
import java.time.Instant;
1727
import java.util.Optional;
28+
import java.util.concurrent.TimeUnit;
1829
import java.util.regex.Pattern;
1930

2031
import androidx.annotation.Nullable;
@@ -46,6 +57,11 @@ private InetEndpoint(final String host, final boolean isResolved, final int port
4657
public static InetEndpoint parse(final String endpoint) throws ParseException {
4758
if (FORBIDDEN_CHARACTERS.matcher(endpoint).find())
4859
throw new ParseException(InetEndpoint.class, endpoint, "Forbidden characters");
60+
if (endpoint.contains("_")) {
61+
// SRV records
62+
final String host = endpoint.split(":")[0];
63+
return new InetEndpoint(host, false, 0);
64+
}
4965
final URI uri;
5066
try {
5167
uri = new URI("wg://" + endpoint);
@@ -92,21 +108,60 @@ public Optional<InetEndpoint> getResolved() {
92108
return Optional.of(this);
93109
synchronized (lock) {
94110
//TODO(zx2c4): Implement a real timeout mechanism using DNS TTL
95-
if (Duration.between(lastResolution, Instant.now()).toMinutes() > 1) {
96-
try {
97-
// Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues.
98-
final InetAddress[] candidates = InetAddress.getAllByName(host);
99-
InetAddress address = candidates[0];
100-
for (final InetAddress candidate : candidates) {
101-
if (candidate instanceof Inet4Address) {
102-
address = candidate;
103-
break;
111+
final long ttlTimeout = Duration.between(lastResolution, Instant.now()).toSeconds();
112+
if (ttlTimeout > 60) {
113+
resolved = null;
114+
final String[] target = {host};
115+
final int[] targetPort = {port};
116+
if (host.contains("_")) {
117+
// SRV records
118+
try {
119+
final Lookup lookup = new Lookup(host, Type.SRV, DClass.IN);
120+
final Resolver resolver1 = new SimpleResolver("223.5.5.5");
121+
final Resolver resolver2 = new SimpleResolver("223.6.6.6");
122+
final Resolver[] resolvers = {resolver1, resolver2};
123+
final Resolver extendedResolver = new ExtendedResolver(resolvers);
124+
lookup.setResolver(extendedResolver);
125+
lookup.setCache(null);
126+
final Record[] records = lookup.run();
127+
if (lookup.getResult() == Lookup.SUCCESSFUL) {
128+
for (final Record record : records) {
129+
final SRVRecord srv = (SRVRecord) record;
130+
try {
131+
target[0] = srv.getTarget().toString(true);
132+
targetPort[0] = srv.getPort();
133+
InetAddresses.parse(target[0]);
134+
// Parsing ths host as a numeric address worked, so we don't need to do DNS lookups.
135+
resolved = new InetEndpoint(target[0], true, targetPort[0]);
136+
} catch (final ParseException ignored) {
137+
// Failed to parse the host as a numeric address, so it must be a DNS hostname/FQDN.
138+
}
139+
// use the first SRV record and break out of loop
140+
break;
141+
}
142+
} else {
143+
System.out.println("SRV lookup failed: " + lookup.getErrorString());
144+
}
145+
} catch (final TextParseException | UnknownHostException e) {
146+
System.out.println("SRV lookup failed: " + e.getMessage());
147+
}
148+
}
149+
if (resolved == null) {
150+
try {
151+
// Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues.
152+
final InetAddress[] candidates = InetAddress.getAllByName(target[0]);
153+
InetAddress address = candidates[0];
154+
for (final InetAddress candidate : candidates) {
155+
if (candidate instanceof Inet4Address) {
156+
address = candidate;
157+
break;
158+
}
104159
}
160+
resolved = new InetEndpoint(address.getHostAddress(), true, targetPort[0]);
161+
lastResolution = Instant.now();
162+
} catch (final UnknownHostException e) {
163+
System.out.println("DNS lookup failed: " + e.getMessage());
105164
}
106-
resolved = new InetEndpoint(address.getHostAddress(), true, port);
107-
lastResolution = Instant.now();
108-
} catch (final UnknownHostException e) {
109-
resolved = null;
110165
}
111166
}
112167
return Optional.ofNullable(resolved);
@@ -121,6 +176,9 @@ public int hashCode() {
121176
@Override
122177
public String toString() {
123178
final boolean isBareIpv6 = isResolved && BARE_IPV6.matcher(host).matches();
124-
return (isBareIpv6 ? '[' + host + ']' : host) + ':' + port;
179+
// Only show the port if it's non-zero
180+
if (port > 0)
181+
return (isBareIpv6 ? '[' + host + ']' : host) + ':' + port;
182+
return (isBareIpv6 ? '[' + host + ']' : host);
125183
}
126184
}

Diff for: ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt

+11
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
113113
for (i in 0 until binding.peersLayout.childCount) {
114114
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
115115
?: continue
116+
if (binding.config != null && i < binding.config!!.peers.size) {
117+
val endpoint = binding.config!!.peers[i].endpoint.get()
118+
val resolved = endpoint.resolved.get()
119+
if (resolved.host != endpoint.host) {
120+
if (endpoint.port != 0) {
121+
peer.endpointText.text = "${endpoint.host}:${endpoint.port}\n${resolved.host}:${resolved.port}"
122+
} else {
123+
peer.endpointText.text = "${endpoint.host}\n${resolved.host}:${resolved.port}"
124+
}
125+
}
126+
}
116127
val publicKey = peer.item!!.publicKey
117128
val peerStats = statistics.peer(publicKey)
118129
if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) {

0 commit comments

Comments
 (0)