2323import io .netty .channel .ChannelFactory ;
2424import io .netty .channel .ChannelFuture ;
2525import io .netty .channel .ChannelFutureListener ;
26+ import io .netty .channel .ChannelHandler ;
27+ import io .netty .channel .ChannelHandlerAdapter ;
2628import io .netty .channel .ChannelHandlerContext ;
2729import io .netty .channel .ChannelInboundHandlerAdapter ;
2830import io .netty .channel .ChannelInitializer ;
4345import io .netty .handler .codec .dns .DnsRecord ;
4446import io .netty .handler .codec .dns .DnsRecordType ;
4547import io .netty .handler .codec .dns .DnsResponse ;
46- import io .netty .handler .codec .dns .TcpDnsQueryEncoder ;
47- import io .netty .handler .codec .dns .TcpDnsResponseDecoder ;
4848import io .netty .resolver .DefaultHostsFileEntriesResolver ;
4949import io .netty .resolver .HostsFileEntries ;
5050import io .netty .resolver .HostsFileEntriesResolver ;
@@ -121,6 +121,13 @@ public class DnsNameResolver extends InetNameResolver {
121121 private static final InternetProtocolFamily [] IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
122122 {InternetProtocolFamily .IPv6 , InternetProtocolFamily .IPv4 };
123123
124+ private static final ChannelHandler NOOP_HANDLER = new ChannelHandlerAdapter () {
125+ @ Override
126+ public boolean isSharable () {
127+ return true ;
128+ }
129+ };
130+
124131 static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES ;
125132 static final String [] DEFAULT_SEARCH_DOMAINS ;
126133 private static final UnixResolverOptions DEFAULT_OPTIONS ;
@@ -227,7 +234,6 @@ protected DnsResponse decodeResponse(ChannelHandlerContext ctx, DatagramPacket p
227234 }
228235 };
229236 private static final DatagramDnsQueryEncoder DATAGRAM_ENCODER = new DatagramDnsQueryEncoder ();
230- private static final TcpDnsQueryEncoder TCP_ENCODER = new TcpDnsQueryEncoder ();
231237
232238 private final Promise <Channel > channelReadyPromise ;
233239 private final Channel ch ;
@@ -273,6 +279,7 @@ protected DnsServerAddressStream initialValue() {
273279 private final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory ;
274280 private final boolean completeOncePreferredResolved ;
275281 private final Bootstrap socketBootstrap ;
282+ private final boolean retryWithTcpOnTimeout ;
276283
277284 private final int maxNumConsolidation ;
278285 private final Map <String , Future <List <InetAddress >>> inflightLookups ;
@@ -376,44 +383,18 @@ public DnsNameResolver(
376383 String [] searchDomains ,
377384 int ndots ,
378385 boolean decodeIdn ) {
379- this (eventLoop , channelFactory , null , resolveCache , NoopDnsCnameCache .INSTANCE , authoritativeDnsServerCache ,
386+ this (eventLoop , channelFactory , null , false , resolveCache ,
387+ NoopDnsCnameCache .INSTANCE , authoritativeDnsServerCache , null ,
380388 dnsQueryLifecycleObserverFactory , queryTimeoutMillis , resolvedAddressTypes , recursionDesired ,
381389 maxQueriesPerResolve , traceEnabled , maxPayloadSize , optResourceEnabled , hostsFileEntriesResolver ,
382- dnsServerAddressStreamProvider , searchDomains , ndots , decodeIdn , false );
383- }
384-
385- DnsNameResolver (
386- EventLoop eventLoop ,
387- ChannelFactory <? extends DatagramChannel > channelFactory ,
388- ChannelFactory <? extends SocketChannel > socketChannelFactory ,
389- final DnsCache resolveCache ,
390- final DnsCnameCache cnameCache ,
391- final AuthoritativeDnsServerCache authoritativeDnsServerCache ,
392- DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory ,
393- long queryTimeoutMillis ,
394- ResolvedAddressTypes resolvedAddressTypes ,
395- boolean recursionDesired ,
396- int maxQueriesPerResolve ,
397- boolean traceEnabled ,
398- int maxPayloadSize ,
399- boolean optResourceEnabled ,
400- HostsFileEntriesResolver hostsFileEntriesResolver ,
401- DnsServerAddressStreamProvider dnsServerAddressStreamProvider ,
402- String [] searchDomains ,
403- int ndots ,
404- boolean decodeIdn ,
405- boolean completeOncePreferredResolved ) {
406- this (eventLoop , channelFactory , socketChannelFactory , resolveCache , cnameCache , authoritativeDnsServerCache ,
407- null , dnsQueryLifecycleObserverFactory , queryTimeoutMillis , resolvedAddressTypes ,
408- recursionDesired , maxQueriesPerResolve , traceEnabled , maxPayloadSize , optResourceEnabled ,
409- hostsFileEntriesResolver , dnsServerAddressStreamProvider , searchDomains , ndots , decodeIdn ,
410- completeOncePreferredResolved , 0 );
390+ dnsServerAddressStreamProvider , searchDomains , ndots , decodeIdn , false , 0 );
411391 }
412392
413393 DnsNameResolver (
414394 EventLoop eventLoop ,
415395 ChannelFactory <? extends DatagramChannel > channelFactory ,
416396 ChannelFactory <? extends SocketChannel > socketChannelFactory ,
397+ boolean retryWithTcpOnTimeout ,
417398 final DnsCache resolveCache ,
418399 final DnsCnameCache cnameCache ,
419400 final AuthoritativeDnsServerCache authoritativeDnsServerCache ,
@@ -457,6 +438,7 @@ public DnsNameResolver(
457438 this .ndots = ndots >= 0 ? ndots : DEFAULT_OPTIONS .ndots ();
458439 this .decodeIdn = decodeIdn ;
459440 this .completeOncePreferredResolved = completeOncePreferredResolved ;
441+ this .retryWithTcpOnTimeout = retryWithTcpOnTimeout ;
460442 if (socketChannelFactory == null ) {
461443 socketBootstrap = null ;
462444 } else {
@@ -465,7 +447,7 @@ public DnsNameResolver(
465447 .group (executor ())
466448 .channelFactory (socketChannelFactory )
467449 .attr (DNS_PIPELINE_ATTRIBUTE , Boolean .TRUE )
468- .handler (TCP_ENCODER );
450+ .handler (NOOP_HANDLER );
469451 if (queryTimeoutMillis > 0 && queryTimeoutMillis <= Integer .MAX_VALUE ) {
470452 // Set the connect timeout to the same as queryTimeout as otherwise it might take a long
471453 // time for the query to fail in case of a connection timeout.
@@ -1354,8 +1336,9 @@ final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(
13541336 final int payloadSize = isOptResourceEnabled () ? maxPayloadSize () : 0 ;
13551337 try {
13561338 DnsQueryContext queryContext = new DatagramDnsQueryContext (ch , channelReadyPromise , nameServerAddr ,
1357- queryContextManager , payloadSize , isRecursionDesired (), question , additionals , castPromise );
1358- ChannelFuture future = queryContext .writeQuery (queryTimeoutMillis (), flush );
1339+ queryContextManager , payloadSize , isRecursionDesired (), queryTimeoutMillis (), question , additionals ,
1340+ castPromise , socketBootstrap , retryWithTcpOnTimeout );
1341+ ChannelFuture future = queryContext .writeQuery (flush );
13591342 queryLifecycleObserver .queryWritten (nameServerAddr , future );
13601343 return castPromise ;
13611344 } catch (Exception e ) {
@@ -1400,94 +1383,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) {
14001383 return ;
14011384 }
14021385
1403- // Check if the response was truncated and if we can fallback to TCP to retry.
1404- if (!res .isTruncated () || socketBootstrap == null ) {
1405- qCtx .finishSuccess (res );
1406- return ;
1407- }
1408-
1409- socketBootstrap .connect (res .sender ()).addListener (new ChannelFutureListener () {
1410- @ Override
1411- public void operationComplete (ChannelFuture future ) {
1412- if (!future .isSuccess ()) {
1413- logger .debug ("{} Unable to fallback to TCP [{}: {}]" ,
1414- ch , queryId , res .sender (), future .cause ());
1415-
1416- // TCP fallback failed, just use the truncated response.
1417- qCtx .finishSuccess (res );
1418- return ;
1419- }
1420- final Channel tcpCh = future .channel ();
1421-
1422- Promise <AddressedEnvelope <DnsResponse , InetSocketAddress >> promise =
1423- tcpCh .eventLoop ().newPromise ();
1424- final int payloadSize = isOptResourceEnabled () ? maxPayloadSize () : 0 ;
1425- final TcpDnsQueryContext tcpCtx = new TcpDnsQueryContext (tcpCh , channelReadyPromise ,
1426- (InetSocketAddress ) tcpCh .remoteAddress (), queryContextManager , payloadSize ,
1427- isRecursionDesired (), qCtx .question (), EMPTY_ADDITIONALS , promise );
1428-
1429- tcpCh .pipeline ().addLast (new TcpDnsResponseDecoder ());
1430- tcpCh .pipeline ().addLast (new ChannelInboundHandlerAdapter () {
1431- @ Override
1432- public void channelRead (ChannelHandlerContext ctx , Object msg ) {
1433- Channel tcpCh = ctx .channel ();
1434- DnsResponse response = (DnsResponse ) msg ;
1435- int queryId = response .id ();
1436-
1437- if (logger .isDebugEnabled ()) {
1438- logger .debug ("{} RECEIVED: TCP [{}: {}], {}" , tcpCh , queryId ,
1439- tcpCh .remoteAddress (), response );
1440- }
1441-
1442- DnsQueryContext foundCtx = queryContextManager .get (res .sender (), queryId );
1443- if (foundCtx != null && foundCtx .isDone ()) {
1444- logger .debug ("{} Received a DNS response for a query that was timed out or cancelled " +
1445- ": TCP [{}: {}]" , tcpCh , queryId , res .sender ());
1446- response .release ();
1447- } else if (foundCtx == tcpCtx ) {
1448- tcpCtx .finishSuccess (new AddressedEnvelopeAdapter (
1449- (InetSocketAddress ) ctx .channel ().remoteAddress (),
1450- (InetSocketAddress ) ctx .channel ().localAddress (),
1451- response ));
1452- } else {
1453- response .release ();
1454- tcpCtx .finishFailure ("Received TCP DNS response with unexpected ID" , null , false );
1455- if (logger .isDebugEnabled ()) {
1456- logger .debug ("{} Received a DNS response with an unexpected ID: TCP [{}: {}]" ,
1457- tcpCh , queryId , tcpCh .remoteAddress ());
1458- }
1459- }
1460- }
1461-
1462- @ Override
1463- public void exceptionCaught (ChannelHandlerContext ctx , Throwable cause ) {
1464- if (tcpCtx .finishFailure (
1465- "TCP fallback error" , cause , false ) && logger .isDebugEnabled ()) {
1466- logger .debug ("{} Error during processing response: TCP [{}: {}]" ,
1467- ctx .channel (), queryId ,
1468- ctx .channel ().remoteAddress (), cause );
1469- }
1470- }
1471- });
1472-
1473- promise .addListener (
1474- new FutureListener <AddressedEnvelope <DnsResponse , InetSocketAddress >>() {
1475- @ Override
1476- public void operationComplete (
1477- Future <AddressedEnvelope <DnsResponse , InetSocketAddress >> future ) {
1478- if (future .isSuccess ()) {
1479- qCtx .finishSuccess (future .getNow ());
1480- res .release ();
1481- } else {
1482- // TCP fallback failed, just use the truncated response.
1483- qCtx .finishSuccess (res );
1484- }
1485- tcpCh .close ();
1486- }
1487- });
1488- tcpCtx .writeQuery (queryTimeoutMillis (), true );
1489- }
1490- });
1386+ // The context will handle truncation itself.
1387+ qCtx .finishSuccess (res , res .isTruncated ());
14911388 }
14921389
14931390 @ Override
@@ -1505,113 +1402,4 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
15051402 }
15061403 }
15071404 }
1508-
1509- private static final class AddressedEnvelopeAdapter implements AddressedEnvelope <DnsResponse , InetSocketAddress > {
1510- private final InetSocketAddress sender ;
1511- private final InetSocketAddress recipient ;
1512- private final DnsResponse response ;
1513-
1514- AddressedEnvelopeAdapter (InetSocketAddress sender , InetSocketAddress recipient , DnsResponse response ) {
1515- this .sender = sender ;
1516- this .recipient = recipient ;
1517- this .response = response ;
1518- }
1519-
1520- @ Override
1521- public DnsResponse content () {
1522- return response ;
1523- }
1524-
1525- @ Override
1526- public InetSocketAddress sender () {
1527- return sender ;
1528- }
1529-
1530- @ Override
1531- public InetSocketAddress recipient () {
1532- return recipient ;
1533- }
1534-
1535- @ Override
1536- public AddressedEnvelope <DnsResponse , InetSocketAddress > retain () {
1537- response .retain ();
1538- return this ;
1539- }
1540-
1541- @ Override
1542- public AddressedEnvelope <DnsResponse , InetSocketAddress > retain (int increment ) {
1543- response .retain (increment );
1544- return this ;
1545- }
1546-
1547- @ Override
1548- public AddressedEnvelope <DnsResponse , InetSocketAddress > touch () {
1549- response .touch ();
1550- return this ;
1551- }
1552-
1553- @ Override
1554- public AddressedEnvelope <DnsResponse , InetSocketAddress > touch (Object hint ) {
1555- response .touch (hint );
1556- return this ;
1557- }
1558-
1559- @ Override
1560- public int refCnt () {
1561- return response .refCnt ();
1562- }
1563-
1564- @ Override
1565- public boolean release () {
1566- return response .release ();
1567- }
1568-
1569- @ Override
1570- public boolean release (int decrement ) {
1571- return response .release (decrement );
1572- }
1573-
1574- @ Override
1575- public boolean equals (Object obj ) {
1576- if (this == obj ) {
1577- return true ;
1578- }
1579-
1580- if (!(obj instanceof AddressedEnvelope )) {
1581- return false ;
1582- }
1583-
1584- @ SuppressWarnings ("unchecked" )
1585- final AddressedEnvelope <?, SocketAddress > that = (AddressedEnvelope <?, SocketAddress >) obj ;
1586- if (sender () == null ) {
1587- if (that .sender () != null ) {
1588- return false ;
1589- }
1590- } else if (!sender ().equals (that .sender ())) {
1591- return false ;
1592- }
1593-
1594- if (recipient () == null ) {
1595- if (that .recipient () != null ) {
1596- return false ;
1597- }
1598- } else if (!recipient ().equals (that .recipient ())) {
1599- return false ;
1600- }
1601-
1602- return response .equals (obj );
1603- }
1604-
1605- @ Override
1606- public int hashCode () {
1607- int hashCode = response .hashCode ();
1608- if (sender () != null ) {
1609- hashCode = hashCode * 31 + sender ().hashCode ();
1610- }
1611- if (recipient () != null ) {
1612- hashCode = hashCode * 31 + recipient ().hashCode ();
1613- }
1614- return hashCode ;
1615- }
1616- }
16171405}
0 commit comments