7
7
8
8
import com .wireguard .util .NonNullForAll ;
9
9
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
+
10
20
import java .net .Inet4Address ;
11
21
import java .net .InetAddress ;
12
22
import java .net .URI ;
15
25
import java .time .Duration ;
16
26
import java .time .Instant ;
17
27
import java .util .Optional ;
28
+ import java .util .concurrent .TimeUnit ;
18
29
import java .util .regex .Pattern ;
19
30
20
31
import androidx .annotation .Nullable ;
@@ -46,6 +57,11 @@ private InetEndpoint(final String host, final boolean isResolved, final int port
46
57
public static InetEndpoint parse (final String endpoint ) throws ParseException {
47
58
if (FORBIDDEN_CHARACTERS .matcher (endpoint ).find ())
48
59
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
+ }
49
65
final URI uri ;
50
66
try {
51
67
uri = new URI ("wg://" + endpoint );
@@ -92,21 +108,60 @@ public Optional<InetEndpoint> getResolved() {
92
108
return Optional .of (this );
93
109
synchronized (lock ) {
94
110
//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
+ }
104
159
}
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 ());
105
164
}
106
- resolved = new InetEndpoint (address .getHostAddress (), true , port );
107
- lastResolution = Instant .now ();
108
- } catch (final UnknownHostException e ) {
109
- resolved = null ;
110
165
}
111
166
}
112
167
return Optional .ofNullable (resolved );
@@ -121,6 +176,9 @@ public int hashCode() {
121
176
@ Override
122
177
public String toString () {
123
178
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 );
125
183
}
126
184
}
0 commit comments