2020
2121import com .google .common .annotations .VisibleForTesting ;
2222import com .google .common .base .Preconditions ;
23+ import com .google .common .base .Verify ;
2324import io .grpc .Attributes ;
2425import io .grpc .EquivalentAddressGroup ;
2526import io .grpc .NameResolver ;
2627import io .grpc .Status ;
2728import io .grpc .internal .SharedResourceHolder .Resource ;
2829import java .net .InetAddress ;
2930import java .net .InetSocketAddress ;
31+ import java .net .SocketAddress ;
3032import java .net .URI ;
33+ import java .net .UnknownHostException ;
3134import java .util .ArrayList ;
3235import java .util .Arrays ;
3336import java .util .Collections ;
3841import java .util .concurrent .TimeUnit ;
3942import java .util .logging .Level ;
4043import java .util .logging .Logger ;
44+ import java .util .regex .Pattern ;
4145import javax .annotation .Nullable ;
4246import javax .annotation .concurrent .GuardedBy ;
4347import javax .naming .NamingEnumeration ;
4953 * A DNS-based {@link NameResolver}.
5054 *
5155 * <p>Each {@code A} or {@code AAAA} record emits an {@link EquivalentAddressGroup} in the list
52- * passed to {@link NameResolver.Listener#onUpdate }
56+ * passed to {@link NameResolver.Listener#onAddresses(List, Attributes) }
5357 *
5458 * @see DnsNameResolverProvider
5559 */
@@ -59,8 +63,16 @@ final class DnsNameResolver extends NameResolver {
5963
6064 private static final boolean JNDI_AVAILABLE = jndiAvailable ();
6165
66+ // From https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md
67+ private static final String SERVICE_CONFIG_NAME_PREFIX = "_grpc_config." ;
68+ // From https://github.com/grpc/proposal/blob/master/A5-grpclb-in-dns.md
69+ private static final String GRPCLB_NAME_PREFIX = "_grpclb._tcp." ;
70+
71+ private static final String jndiProperty =
72+ System .getProperty ("io.grpc.internal.DnsNameResolverProvider.enable_jndi" , "false" );
73+
6274 @ VisibleForTesting
63- static boolean enableJndi = false ;
75+ static boolean enableJndi = Boolean . parseBoolean ( jndiProperty ) ;
6476
6577 private DelegateResolver delegateResolver = pickDelegateResolver ();
6678
@@ -174,11 +186,19 @@ public void run() {
174186 return ;
175187 }
176188 // Each address forms an EAG
177- ArrayList <EquivalentAddressGroup > servers = new ArrayList <EquivalentAddressGroup >();
189+ List <EquivalentAddressGroup > servers = new ArrayList <EquivalentAddressGroup >();
178190 for (InetAddress inetAddr : resolvedInetAddrs .addresses ) {
179191 servers .add (new EquivalentAddressGroup (new InetSocketAddress (inetAddr , port )));
180192 }
181- savedListener .onAddresses (servers , Attributes .EMPTY );
193+ servers .addAll (resolvedInetAddrs .balancerAddresses );
194+
195+ Attributes .Builder attrs = Attributes .newBuilder ();
196+ if (!resolvedInetAddrs .txtRecords .isEmpty ()) {
197+ attrs .set (
198+ GrpcAttributes .NAME_RESOLVER_ATTR_DNS_TXT ,
199+ Collections .unmodifiableList (new ArrayList <String >(resolvedInetAddrs .txtRecords )));
200+ }
201+ savedListener .onAddresses (servers , attrs .build ());
182202 } finally {
183203 synchronized (DnsNameResolver .this ) {
184204 resolving = false ;
@@ -278,10 +298,16 @@ abstract static class DelegateResolver {
278298 static final class ResolutionResults {
279299 final List <InetAddress > addresses ;
280300 final List <String > txtRecords ;
301+ final List <EquivalentAddressGroup > balancerAddresses ;
281302
282- ResolutionResults (List <InetAddress > addresses , List <String > txtRecords ) {
303+ ResolutionResults (
304+ List <InetAddress > addresses ,
305+ List <String > txtRecords ,
306+ List <EquivalentAddressGroup > balancerAddresses ) {
283307 this .addresses = Collections .unmodifiableList (checkNotNull (addresses , "addresses" ));
284308 this .txtRecords = Collections .unmodifiableList (checkNotNull (txtRecords , "txtRecords" ));
309+ this .balancerAddresses =
310+ Collections .unmodifiableList (checkNotNull (balancerAddresses , "balancerAddresses" ));
285311 }
286312 }
287313
@@ -305,14 +331,16 @@ ResolutionResults resolve(String host) throws Exception {
305331 ResolutionResults jdkResults = jdkResovler .resolve (host );
306332 List <InetAddress > addresses = jdkResults .addresses ;
307333 List <String > txtRecords = Collections .emptyList ();
334+ List <EquivalentAddressGroup > balancerAddresses = Collections .emptyList ();
308335 try {
309336 ResolutionResults jdniResults = jndiResovler .resolve (host );
310337 txtRecords = jdniResults .txtRecords ;
338+ balancerAddresses = jdniResults .balancerAddresses ;
311339 } catch (Exception e ) {
312340 logger .log (Level .SEVERE , "Failed to resolve TXT results" , e );
313341 }
314342
315- return new ResolutionResults (addresses , txtRecords );
343+ return new ResolutionResults (addresses , txtRecords , balancerAddresses );
316344 }
317345 }
318346
@@ -328,7 +356,8 @@ static final class JdkResolver extends DelegateResolver {
328356 ResolutionResults resolve (String host ) throws Exception {
329357 return new ResolutionResults (
330358 Arrays .asList (InetAddress .getAllByName (host )),
331- Collections .<String >emptyList ());
359+ Collections .<String >emptyList (),
360+ Collections .<EquivalentAddressGroup >emptyList ());
332361 }
333362 }
334363
@@ -340,26 +369,84 @@ ResolutionResults resolve(String host) throws Exception {
340369 @ VisibleForTesting
341370 static final class JndiResolver extends DelegateResolver {
342371
343- private static final String [] rrTypes = new String []{ "TXT" } ;
372+ private static final Pattern whitespace = Pattern . compile ( " \\ s+" ) ;
344373
345374 @ Override
346375 ResolutionResults resolve (String host ) throws NamingException {
376+ List <String > serviceConfigTxtRecords = Collections .emptyList ();
377+ String serviceConfigHostname = SERVICE_CONFIG_NAME_PREFIX + host ;
378+ if (logger .isLoggable (Level .FINER )) {
379+ logger .log (
380+ Level .FINER , "About to query TXT records for {0}" , new Object []{serviceConfigHostname });
381+ }
382+ try {
383+ serviceConfigTxtRecords = getAllRecords ("TXT" , "dns:///" + serviceConfigHostname );
384+ } catch (NamingException e ) {
385+
386+ if (logger .isLoggable (Level .FINE )) {
387+ logger .log (Level .FINE , "Unable to look up " + serviceConfigHostname , e );
388+ }
389+ }
390+
391+ String grpclbHostname = GRPCLB_NAME_PREFIX + host ;
392+ if (logger .isLoggable (Level .FINER )) {
393+ logger .log (
394+ Level .FINER , "About to query SRV records for {0}" , new Object []{grpclbHostname });
395+ }
396+ List <EquivalentAddressGroup > balancerAddresses = Collections .emptyList ();
397+ try {
398+ List <String > grpclbSrvRecords = getAllRecords ("SRV" , "dns:///" + grpclbHostname );
399+ balancerAddresses = new ArrayList <EquivalentAddressGroup >(grpclbSrvRecords .size ());
400+ for (String srvRecord : grpclbSrvRecords ) {
401+ try {
402+ String [] parts = whitespace .split (srvRecord );
403+ Verify .verify (parts .length == 4 , "Bad SRV Record: %s, " , srvRecord );
404+ String srvHostname = parts [3 ];
405+ int port = Integer .parseInt (parts [2 ]);
406+
407+ InetAddress [] addrs = InetAddress .getAllByName (srvHostname );
408+ List <SocketAddress > sockaddrs = new ArrayList <SocketAddress >(addrs .length );
409+ for (InetAddress addr : addrs ) {
410+ sockaddrs .add (new InetSocketAddress (addr , port ));
411+ }
412+ Attributes attrs = Attributes .newBuilder ()
413+ .set (GrpcAttributes .ATTR_LB_ADDR_AUTHORITY , srvHostname )
414+ .build ();
415+ balancerAddresses .add (
416+ new EquivalentAddressGroup (Collections .unmodifiableList (sockaddrs ), attrs ));
417+ } catch (UnknownHostException e ) {
418+ logger .log (Level .WARNING , "Can't find address for SRV record" + srvRecord , e );
419+ } catch (RuntimeException e ) {
420+ logger .log (Level .WARNING , "Failed to construct SRV record" + srvRecord , e );
421+ }
422+ }
423+ } catch (NamingException e ) {
424+ if (logger .isLoggable (Level .FINE )) {
425+ logger .log (Level .FINE , "Unable to look up " + serviceConfigHostname , e );
426+ }
427+ }
347428
429+ return new ResolutionResults (
430+ /*addresses=*/ Collections .<InetAddress >emptyList (),
431+ serviceConfigTxtRecords ,
432+ Collections .unmodifiableList (balancerAddresses ));
433+ }
434+
435+ private List <String > getAllRecords (String recordType , String name ) throws NamingException {
348436 InitialDirContext dirContext = new InitialDirContext ();
349- javax . naming . directory . Attributes attrs = dirContext . getAttributes ( "dns:///" + host , rrTypes ) ;
350- List < InetAddress > addresses = new ArrayList < InetAddress >( );
351- List <String > txtRecords = new ArrayList <String >();
437+ String [] rrType = new String []{ recordType } ;
438+ javax . naming . directory . Attributes attrs = dirContext . getAttributes ( name , rrType );
439+ List <String > records = new ArrayList <String >();
352440
353441 NamingEnumeration <? extends Attribute > rrGroups = attrs .getAll ();
354442 try {
355443 while (rrGroups .hasMore ()) {
356444 Attribute rrEntry = rrGroups .next ();
357- assert Arrays .asList (rrTypes ).contains (rrEntry .getID ());
445+ assert Arrays .asList (rrType ).contains (rrEntry .getID ());
358446 NamingEnumeration <?> rrValues = rrEntry .getAll ();
359447 try {
360448 while (rrValues .hasMore ()) {
361- String rrValue = (String ) rrValues .next ();
362- txtRecords .add (rrValue );
449+ records .add (String .valueOf (rrValues .next ()));
363450 }
364451 } finally {
365452 rrValues .close ();
@@ -368,8 +455,7 @@ ResolutionResults resolve(String host) throws NamingException {
368455 } finally {
369456 rrGroups .close ();
370457 }
371-
372- return new ResolutionResults (addresses , txtRecords );
458+ return records ;
373459 }
374460 }
375461}
0 commit comments