77
88import 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+
1020import java .net .Inet4Address ;
1121import java .net .InetAddress ;
1222import java .net .URI ;
1525import java .time .Duration ;
1626import java .time .Instant ;
1727import java .util .Optional ;
28+ import java .util .concurrent .TimeUnit ;
1829import java .util .regex .Pattern ;
1930
2031import 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}
0 commit comments