src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java
branchaefimov-dns-client-branch
changeset 58870 35c438a6d45c
child 58971 465a15dd6bed
equal deleted inserted replaced
58869:cc66ac8c7646 58870:35c438a6d45c
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package jdk.dns.client.internal;
       
    27 
       
    28 import jdk.dns.client.ex.DnsCommunicationException;
       
    29 import jdk.dns.client.ex.DnsNameNotFoundException;
       
    30 import jdk.dns.client.ex.DnsOperationNotSupportedException;
       
    31 import jdk.dns.client.ex.DnsResolverException;
       
    32 import jdk.dns.client.ex.DnsServiceUnavailableException;
       
    33 
       
    34 import java.io.IOException;
       
    35 import java.net.DatagramPacket;
       
    36 import java.net.InetAddress;
       
    37 import java.net.InetSocketAddress;
       
    38 import java.net.Socket;
       
    39 import java.net.SocketTimeoutException;
       
    40 import java.net.UnknownHostException;
       
    41 import java.nio.ByteBuffer;
       
    42 import java.nio.channels.DatagramChannel;
       
    43 import java.security.AccessController;
       
    44 import java.security.PrivilegedAction;
       
    45 import java.security.SecureRandom;
       
    46 import java.util.ArrayList;
       
    47 import java.util.Collection;
       
    48 import java.util.Collections;
       
    49 import java.util.HashMap;
       
    50 import java.util.List;
       
    51 import java.util.Map;
       
    52 import java.util.concurrent.locks.ReentrantLock;
       
    53 
       
    54 import sun.net.util.IPAddressUtil;
       
    55 import sun.security.jca.JCAUtil;
       
    56 
       
    57 // Some of this code began life as part of sun.javaos.net.DnsClient
       
    58 // originally by sritchie@eng 1/96.  It was first hacked up for JNDI
       
    59 // use by caveh@eng 6/97.
       
    60 
       
    61 
       
    62 /**
       
    63  * The DnsClient class performs DNS client operations in support of DnsContext.
       
    64  */
       
    65 
       
    66 public class DnsClient {
       
    67 
       
    68     // DNS packet header field offsets
       
    69     private static final int IDENT_OFFSET = 0;
       
    70     private static final int FLAGS_OFFSET = 2;
       
    71     private static final int NUMQ_OFFSET = 4;
       
    72     private static final int NUMANS_OFFSET = 6;
       
    73     private static final int NUMAUTH_OFFSET = 8;
       
    74     private static final int NUMADD_OFFSET = 10;
       
    75     private static final int DNS_HDR_SIZE = 12;
       
    76 
       
    77     // DNS response codes
       
    78     private static final int NO_ERROR = 0;
       
    79     private static final int FORMAT_ERROR = 1;
       
    80     private static final int SERVER_FAILURE = 2;
       
    81     private static final int NAME_ERROR = 3;
       
    82     private static final int NOT_IMPL = 4;
       
    83     private static final int REFUSED = 5;
       
    84 
       
    85     private static final String[] rcodeDescription = {
       
    86             "No error",
       
    87             "DNS format error",
       
    88             "DNS server failure",
       
    89             "DNS name not found",
       
    90             "DNS operation not supported",
       
    91             "DNS service refused"
       
    92     };
       
    93 
       
    94     private static final int DEFAULT_PORT = 53;
       
    95     private static final int TRANSACTION_ID_BOUND = 0x10000;
       
    96     private List<InetAddress> servers;
       
    97     private List<Integer> serverPorts;
       
    98     private int timeout;                // initial timeout on UDP and TCP queries in ms
       
    99     private int retries;                // number of UDP retries
       
   100 
       
   101 
       
   102 
       
   103     private static final SecureRandom random;
       
   104 
       
   105     static {
       
   106         var pa = (PrivilegedAction<SecureRandom>) () -> JCAUtil.getSecureRandom();
       
   107         random = System.getSecurityManager() == null ? pa.run()
       
   108                 : AccessController.doPrivileged(pa);
       
   109     }
       
   110 
       
   111     private static final DnsDatagramChannelFactory factory =
       
   112             new DnsDatagramChannelFactory(random);
       
   113 
       
   114     // Requests sent
       
   115     private Map<Integer, ResourceRecord> reqs;
       
   116 
       
   117     // Responses received
       
   118     private Map<Integer, byte[]> resps;
       
   119 
       
   120     //-------------------------------------------------------------------------
       
   121 
       
   122     /*
       
   123      * Each server is of the form "server[:port]".  IPv6 literal host names
       
   124      * include delimiting brackets.
       
   125      * "timeout" is the initial timeout interval (in ms) for queries,
       
   126      * and "retries" gives the number of retries per server.
       
   127      */
       
   128     public DnsClient(List<String> servers, int timeout, int retries)
       
   129             throws UnknownHostException {
       
   130         this.timeout = timeout;
       
   131         this.retries = retries;
       
   132         var serversList = new ArrayList<InetAddress>();
       
   133         var serverPortsList = new ArrayList<Integer>();
       
   134 
       
   135         for (String serverString:servers) {
       
   136 
       
   137             // Is optional port given?
       
   138             int colon = serverString.indexOf(':',
       
   139                     serverString.indexOf(']') + 1);
       
   140 
       
   141             int serverPort = (colon < 0) ? DEFAULT_PORT
       
   142                     : Integer.parseInt(serverString.substring(colon + 1));
       
   143             String server = (colon < 0)
       
   144                     ? serverString
       
   145                     : serverString.substring(0, colon);
       
   146 
       
   147             var pa = (PrivilegedAction<byte[]>) () -> {
       
   148                 if (IPAddressUtil.isIPv4LiteralAddress(server)) {
       
   149                     return IPAddressUtil.textToNumericFormatV4(server);
       
   150                 } else if (IPAddressUtil.isIPv6LiteralAddress(server)) {
       
   151                     return IPAddressUtil.textToNumericFormatV6(server);
       
   152                 }
       
   153                 return null;
       
   154             };
       
   155             byte[] addr = System.getSecurityManager() == null ?
       
   156                     pa.run() : AccessController.doPrivileged(pa);
       
   157             if (addr != null) {
       
   158                 serversList.add(InetAddress.getByAddress(server, addr));
       
   159                 serverPortsList.add(serverPort);
       
   160             }
       
   161         }
       
   162         this.servers = Collections.unmodifiableList(serversList);
       
   163         this.serverPorts = Collections.unmodifiableList(serverPortsList);
       
   164         reqs = Collections.synchronizedMap(
       
   165                 new HashMap<>());
       
   166         resps = Collections.synchronizedMap(new HashMap<>());
       
   167     }
       
   168 
       
   169     DatagramChannel getDatagramChannel() throws DnsResolverException {
       
   170         try {
       
   171             return factory.open();
       
   172         } catch (java.net.SocketException e) {
       
   173             throw new DnsResolverException("Can't create datagram channel", e);
       
   174         }
       
   175     }
       
   176 
       
   177     @SuppressWarnings("deprecation")
       
   178     protected void finalize() {
       
   179         close();
       
   180     }
       
   181 
       
   182     // A lock to access the request and response queues in tandem.
       
   183     private ReentrantLock queuesLock = new ReentrantLock();
       
   184 
       
   185     public void close() {
       
   186         queuesLock.lock();
       
   187         try {
       
   188             reqs.clear();
       
   189             resps.clear();
       
   190         } finally {
       
   191             queuesLock.unlock();
       
   192         }
       
   193     }
       
   194 
       
   195     /*
       
   196      * If recursion is true, recursion is requested on the query.
       
   197      * If auth is true, only authoritative responses are accepted; other
       
   198      * responses throw NameNotFoundException.
       
   199      */
       
   200     ResourceRecords query(DnsName fqdn, int qclass, int qtype,
       
   201                           boolean recursion, boolean auth)
       
   202             throws DnsResolverException {
       
   203 
       
   204         int xid;
       
   205         Packet pkt;
       
   206         ResourceRecord collision;
       
   207 
       
   208         do {
       
   209             // Generate a random transaction ID
       
   210             xid = random.nextInt(TRANSACTION_ID_BOUND);
       
   211             pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
       
   212 
       
   213             // enqueue the outstanding request
       
   214             collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(),
       
   215                     pkt.length(), Header.HEADER_SIZE, true, false));
       
   216 
       
   217         } while (collision != null);
       
   218 
       
   219         Exception caughtException = null;
       
   220         boolean[] doNotRetry = new boolean[servers.size()];
       
   221 
       
   222         try {
       
   223             //
       
   224             // The UDP retry strategy is to try the 1st server, and then
       
   225             // each server in order. If no answer, double the timeout
       
   226             // and try each server again.
       
   227             //
       
   228             for (int retry = 0; retry < retries; retry++) {
       
   229 
       
   230                 // Try each name server.
       
   231                 for (int i = 0; i < servers.size(); i++) {
       
   232                     if (doNotRetry[i]) {
       
   233                         continue;
       
   234                     }
       
   235 
       
   236                     // send the request packet and wait for a response.
       
   237                     try {
       
   238                         if (DEBUG) {
       
   239                             dprint("SEND ID (" + (retry + 1) + "): " + xid);
       
   240                         }
       
   241 
       
   242                         byte[] msg = doUdpQuery(pkt, servers.get(i), serverPorts.get(i), retry, xid);
       
   243                         //
       
   244                         // If the matching response is not got within the
       
   245                         // given timeout, check if the response was enqueued
       
   246                         // by some other thread, if not proceed with the next
       
   247                         // server or retry.
       
   248                         //
       
   249                         if (msg == null) {
       
   250                             if (resps.size() > 0) {
       
   251                                 msg = lookupResponse(xid);
       
   252                             }
       
   253                             if (msg == null) { // try next server or retry
       
   254                                 continue;
       
   255                             }
       
   256                         }
       
   257                         Header hdr = new Header(msg, msg.length);
       
   258 
       
   259                         if (auth && !hdr.authoritative) {
       
   260                             caughtException = new DnsNameNotFoundException(
       
   261                                     "DNS response not authoritative");
       
   262                             doNotRetry[i] = true;
       
   263                             continue;
       
   264                         }
       
   265                         if (hdr.truncated) {  // message is truncated -- try TCP
       
   266 
       
   267                             // Try each server, starting with the one that just
       
   268                             // provided the truncated message.
       
   269                             int retryTimeout = (timeout * (1 << retry));
       
   270                             for (int j = 0; j < servers.size(); j++) {
       
   271                                 int ij = (i + j) % servers.size();
       
   272                                 if (doNotRetry[ij]) {
       
   273                                     continue;
       
   274                                 }
       
   275                                 try {
       
   276                                     Tcp tcp =
       
   277                                             new Tcp(servers.get(ij), serverPorts.get(ij), retryTimeout);
       
   278                                     byte[] msg2;
       
   279                                     try {
       
   280                                         msg2 = doTcpQuery(tcp, pkt);
       
   281                                     } finally {
       
   282                                         tcp.close();
       
   283                                     }
       
   284                                     Header hdr2 = new Header(msg2, msg2.length);
       
   285                                     if (hdr2.query) {
       
   286                                         throw new DnsCommunicationException(
       
   287                                                 "DNS error: expecting response");
       
   288                                     }
       
   289                                     checkResponseCode(hdr2);
       
   290 
       
   291                                     if (!auth || hdr2.authoritative) {
       
   292                                         // Got a valid response
       
   293                                         hdr = hdr2;
       
   294                                         msg = msg2;
       
   295                                         break;
       
   296                                     } else {
       
   297                                         doNotRetry[ij] = true;
       
   298                                     }
       
   299                                 } catch (Exception e) {
       
   300                                     // Try next server, or use UDP response
       
   301                                 }
       
   302                             } // servers
       
   303                         }
       
   304                         return new ResourceRecords(msg, msg.length, hdr, false);
       
   305 
       
   306                     } catch (IOException e) {
       
   307                         if (DEBUG) {
       
   308                             dprint("Caught IOException:" + e);
       
   309                         }
       
   310                         if (caughtException == null) {
       
   311                             caughtException = e;
       
   312                         }
       
   313                         // Use reflection to allow pre-1.4 compilation.
       
   314                         // This won't be needed much longer.
       
   315                         if (e.getClass().getName().equals(
       
   316                                 "java.net.PortUnreachableException")) {
       
   317                             doNotRetry[i] = true;
       
   318                         }
       
   319                         // doNotRetry set - needs to be added
       
   320                     } catch (DnsNameNotFoundException e) {
       
   321                         // This is authoritative, so return immediately
       
   322                         throw e;
       
   323                     } catch (DnsCommunicationException e) {
       
   324                         if (caughtException == null) {
       
   325                             caughtException = e;
       
   326                         }
       
   327                     } catch (DnsResolverException e) {
       
   328                         if (caughtException == null) {
       
   329                             caughtException = e;
       
   330                         }
       
   331                         doNotRetry[i] = true;
       
   332                     }
       
   333                 } // servers
       
   334             } // retries
       
   335 
       
   336         } finally {
       
   337             reqs.remove(xid); // cleanup
       
   338         }
       
   339 
       
   340         if (caughtException instanceof DnsResolverException) {
       
   341             throw (DnsResolverException) caughtException;
       
   342         }
       
   343         // A network timeout or other error occurred.
       
   344         throw new DnsResolverException("DNS error", caughtException);
       
   345     }
       
   346 
       
   347     /**
       
   348      * Tries to retrieve a UDP packet matching the given xid
       
   349      * received within the timeout.
       
   350      * If a packet with different xid is received, the received packet
       
   351      * is enqueued with the corresponding xid in 'resps'.
       
   352      */
       
   353     private byte[] doUdpQuery(Packet pkt, InetAddress server,
       
   354                               int port, int retry, int xid)
       
   355             throws IOException, DnsResolverException {
       
   356 
       
   357         int minTimeout = 50; // msec after which there are no retries.
       
   358 
       
   359 
       
   360         try (DatagramChannel dc = getDatagramChannel()) {
       
   361             DatagramPacket opkt = new DatagramPacket(pkt.getData(), pkt.length(), server, port);
       
   362             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
       
   363             // Packets may only be sent to or received from this server address
       
   364             // TODO: Revisit
       
   365             var pa = (PrivilegedAction<Void>) () -> {dc.socket().connect(server, port); return null;};
       
   366             if (System.getSecurityManager() == null) {
       
   367                 pa.run();
       
   368             } else {
       
   369                 AccessController.doPrivileged(pa);
       
   370             }
       
   371 
       
   372 
       
   373             int pktTimeout = (timeout * (1 << retry));
       
   374             try {
       
   375                 dc.socket().send(opkt);
       
   376 
       
   377                 // timeout remaining after successive 'receive()'
       
   378                 int timeoutLeft = pktTimeout;
       
   379                 int cnt = 0;
       
   380                 do {
       
   381                     if (DEBUG) {
       
   382                         cnt++;
       
   383                         dprint("Trying RECEIVE(" +
       
   384                                 cnt + ") retry(" + (retry + 1) +
       
   385                                 ") for:" + xid + "    sock-timeout:" +
       
   386                                 timeoutLeft + " ms.");
       
   387                     }
       
   388                     dc.socket().setSoTimeout(timeoutLeft);
       
   389                     long start = System.currentTimeMillis();
       
   390 
       
   391 
       
   392                     byte[] data = ipkt.getData();
       
   393                     ByteBuffer bb = ByteBuffer.wrap(data);
       
   394                     dc.read(bb);
       
   395                     long end = System.currentTimeMillis();
       
   396 
       
   397                     if (isMatchResponse(data, xid)) {
       
   398                         return data;
       
   399                     }
       
   400                     timeoutLeft = pktTimeout - ((int) (end - start));
       
   401                 } while (timeoutLeft > minTimeout);
       
   402 
       
   403             } finally {
       
   404                 dc.disconnect();
       
   405             }
       
   406             return null; // no matching packet received within the timeout
       
   407         }
       
   408     }
       
   409 
       
   410     /*
       
   411      * Sends a TCP query, and returns the first DNS message in the response.
       
   412      */
       
   413     private byte[] doTcpQuery(Tcp tcp, Packet pkt) throws IOException {
       
   414 
       
   415         int len = pkt.length();
       
   416         // Send 2-byte message length, then send message.
       
   417         tcp.out.write(len >> 8);
       
   418         tcp.out.write(len);
       
   419         tcp.out.write(pkt.getData(), 0, len);
       
   420         tcp.out.flush();
       
   421 
       
   422         byte[] msg = continueTcpQuery(tcp);
       
   423         if (msg == null) {
       
   424             throw new IOException("DNS error: no response");
       
   425         }
       
   426         return msg;
       
   427     }
       
   428 
       
   429     /*
       
   430      * Returns the next DNS message from the TCP socket, or null on EOF.
       
   431      */
       
   432     private byte[] continueTcpQuery(Tcp tcp) throws IOException {
       
   433 
       
   434         int lenHi = tcp.read();      // high-order byte of response length
       
   435         if (lenHi == -1) {
       
   436             return null;        // EOF
       
   437         }
       
   438         int lenLo = tcp.read();      // low-order byte of response length
       
   439         if (lenLo == -1) {
       
   440             throw new IOException("Corrupted DNS response: bad length");
       
   441         }
       
   442         int len = (lenHi << 8) | lenLo;
       
   443         byte[] msg = new byte[len];
       
   444         int pos = 0;                    // next unfilled position in msg
       
   445         while (len > 0) {
       
   446             int n = tcp.read(msg, pos, len);
       
   447             if (n == -1) {
       
   448                 throw new IOException(
       
   449                         "Corrupted DNS response: too little data");
       
   450             }
       
   451             len -= n;
       
   452             pos += n;
       
   453         }
       
   454         return msg;
       
   455     }
       
   456 
       
   457     private Packet makeQueryPacket(DnsName fqdn, int xid,
       
   458                                    int qclass, int qtype, boolean recursion) {
       
   459         int qnameLen = fqdn.getOctets();
       
   460         int pktLen = DNS_HDR_SIZE + qnameLen + 4;
       
   461         Packet pkt = new Packet(pktLen);
       
   462 
       
   463         short flags = recursion ? Header.RD_BIT : 0;
       
   464 
       
   465         pkt.putShort(xid, IDENT_OFFSET);
       
   466         pkt.putShort(flags, FLAGS_OFFSET);
       
   467         pkt.putShort(1, NUMQ_OFFSET);
       
   468         pkt.putShort(0, NUMANS_OFFSET);
       
   469         pkt.putInt(0, NUMAUTH_OFFSET);
       
   470 
       
   471         makeQueryName(fqdn, pkt, DNS_HDR_SIZE);
       
   472         pkt.putShort(qtype, DNS_HDR_SIZE + qnameLen);
       
   473         pkt.putShort(qclass, DNS_HDR_SIZE + qnameLen + 2);
       
   474 
       
   475         return pkt;
       
   476     }
       
   477 
       
   478     // Builds a query name in pkt according to the RFC spec.
       
   479     private void makeQueryName(DnsName fqdn, Packet pkt, int off) {
       
   480 
       
   481         // Loop through labels, least-significant first.
       
   482         for (int i = fqdn.size() - 1; i >= 0; i--) {
       
   483             String label = fqdn.get(i);
       
   484             int len = label.length();
       
   485 
       
   486             pkt.putByte(len, off++);
       
   487             for (int j = 0; j < len; j++) {
       
   488                 pkt.putByte(label.charAt(j), off++);
       
   489             }
       
   490         }
       
   491         if (!fqdn.hasRootLabel()) {
       
   492             pkt.putByte(0, off);
       
   493         }
       
   494     }
       
   495 
       
   496     //-------------------------------------------------------------------------
       
   497 
       
   498     private byte[] lookupResponse(Integer xid) throws DnsResolverException {
       
   499         //
       
   500         // Check the queued responses: some other thread in between
       
   501         // received the response for this request.
       
   502         //
       
   503         if (DEBUG) {
       
   504             dprint("LOOKUP for: " + xid +
       
   505                     "\tResponse Q:" + resps);
       
   506         }
       
   507         byte[] pkt;
       
   508         if ((pkt = resps.get(xid)) != null) {
       
   509             checkResponseCode(new Header(pkt, pkt.length));
       
   510             queuesLock.lock();
       
   511             try {
       
   512                 resps.remove(xid);
       
   513                 reqs.remove(xid);
       
   514             } finally {
       
   515                 queuesLock.unlock();
       
   516             }
       
   517 
       
   518             if (DEBUG) {
       
   519                 dprint("FOUND (" + Thread.currentThread() +
       
   520                         ") for:" + xid);
       
   521             }
       
   522         }
       
   523         return pkt;
       
   524     }
       
   525 
       
   526     /*
       
   527      * Checks the header of an incoming DNS response.
       
   528      * Returns true if it matches the given xid and throws a naming
       
   529      * exception, if appropriate, based on the response code.
       
   530      *
       
   531      * Also checks that the domain name, type and class in the response
       
   532      * match those in the original query.
       
   533      */
       
   534     private boolean isMatchResponse(byte[] pkt, int xid)
       
   535             throws DnsResolverException {
       
   536 
       
   537         Header hdr = new Header(pkt, pkt.length);
       
   538         if (hdr.query) {
       
   539             throw new DnsCommunicationException("DNS error: expecting response");
       
   540         }
       
   541 
       
   542         if (!reqs.containsKey(xid)) { // already received, ignore the response
       
   543             return false;
       
   544         }
       
   545 
       
   546         // common case- the request sent matches the subsequent response read
       
   547         if (hdr.xid == xid) {
       
   548             if (DEBUG) {
       
   549                 dprint("XID MATCH:" + xid);
       
   550             }
       
   551             checkResponseCode(hdr);
       
   552             if (!hdr.query && hdr.numQuestions == 1) {
       
   553 
       
   554                 ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
       
   555                         Header.HEADER_SIZE, true, false);
       
   556 
       
   557                 // Retrieve the original query
       
   558                 ResourceRecord query = reqs.get(xid);
       
   559                 int qtype = query.getType();
       
   560                 int qclass = query.getRrclass();
       
   561                 DnsName qname = query.getName();
       
   562 
       
   563                 // Check that the type/class/name in the query section of the
       
   564                 // response match those in the original query
       
   565                 if ((qtype == ResourceRecord.QTYPE_STAR ||
       
   566                         qtype == rr.getType()) &&
       
   567                         (qclass == ResourceRecord.QCLASS_STAR ||
       
   568                                 qclass == rr.getRrclass()) &&
       
   569                         qname.equals(rr.getName())) {
       
   570 
       
   571                     if (DEBUG) {
       
   572                         dprint("MATCH NAME:" + qname + " QTYPE:" + qtype +
       
   573                                 " QCLASS:" + qclass);
       
   574                     }
       
   575 
       
   576                     // Remove the response for the xid if received by some other
       
   577                     // thread.
       
   578                     queuesLock.lock();
       
   579                     try {
       
   580                         resps.remove(xid);
       
   581                         reqs.remove(xid);
       
   582                     } finally {
       
   583                         queuesLock.unlock();
       
   584                     }
       
   585                     return true;
       
   586 
       
   587                 } else {
       
   588                     if (DEBUG) {
       
   589                         dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype +
       
   590                                 " QCLASS:" + qclass);
       
   591                     }
       
   592                 }
       
   593             }
       
   594             return false;
       
   595         }
       
   596 
       
   597         //
       
   598         // xid mis-match: enqueue the response, it may belong to some other
       
   599         // thread that has not yet had a chance to read its response.
       
   600         // enqueue only the first response, responses for retries are ignored.
       
   601         //
       
   602         queuesLock.lock();
       
   603         try {
       
   604             if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
       
   605                 resps.put(hdr.xid, pkt);
       
   606             }
       
   607         } finally {
       
   608             queuesLock.unlock();
       
   609         }
       
   610 
       
   611         if (DEBUG) {
       
   612             dprint("NO-MATCH SEND ID:" +
       
   613                     xid + " RECVD ID:" + hdr.xid +
       
   614                     "    Response Q:" + resps +
       
   615                     "    Reqs size:" + reqs.size());
       
   616         }
       
   617         return false;
       
   618     }
       
   619 
       
   620     /*
       
   621      * Throws an exception if appropriate for the response code of a
       
   622      * given header.
       
   623      */
       
   624     private void checkResponseCode(Header hdr) throws DnsResolverException {
       
   625 
       
   626         int rcode = hdr.rcode;
       
   627         if (rcode == NO_ERROR) {
       
   628             return;
       
   629         }
       
   630         String msg = (rcode < rcodeDescription.length)
       
   631                 ? rcodeDescription[rcode]
       
   632                 : "DNS error";
       
   633 
       
   634         msg += " [response code " + rcode + "]";
       
   635 
       
   636         switch (rcode) {
       
   637             case SERVER_FAILURE:
       
   638                 throw new DnsServiceUnavailableException(msg);
       
   639             case NAME_ERROR:
       
   640                 throw new DnsNameNotFoundException(msg);
       
   641             case NOT_IMPL:
       
   642             case REFUSED:
       
   643                 throw new DnsOperationNotSupportedException(msg);
       
   644             case FORMAT_ERROR:
       
   645             default:
       
   646                 throw new DnsResolverException(msg);
       
   647         }
       
   648     }
       
   649 
       
   650     //-------------------------------------------------------------------------
       
   651 
       
   652     private static final boolean DEBUG = java.security.AccessController.doPrivileged(
       
   653             (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("jdk.dns.client.debug"));
       
   654 
       
   655     private static void dprint(String mess) {
       
   656         if (DEBUG) {
       
   657             System.err.println("DNS: " + mess);
       
   658         }
       
   659     }
       
   660 
       
   661 }
       
   662 
       
   663 class Tcp {
       
   664 
       
   665     private final Socket sock;
       
   666     private final java.io.InputStream in;
       
   667     final java.io.OutputStream out;
       
   668     private int timeoutLeft;
       
   669 
       
   670     Tcp(InetAddress server, int port, int timeout) throws IOException {
       
   671         sock = new Socket();
       
   672         try {
       
   673             long start = System.currentTimeMillis();
       
   674             sock.connect(new InetSocketAddress(server, port), timeout);
       
   675             timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start));
       
   676             if (timeoutLeft <= 0)
       
   677                 throw new SocketTimeoutException();
       
   678 
       
   679             sock.setTcpNoDelay(true);
       
   680             out = new java.io.BufferedOutputStream(sock.getOutputStream());
       
   681             in = new java.io.BufferedInputStream(sock.getInputStream());
       
   682         } catch (Exception e) {
       
   683             try {
       
   684                 sock.close();
       
   685             } catch (IOException ex) {
       
   686                 e.addSuppressed(ex);
       
   687             }
       
   688             throw e;
       
   689         }
       
   690     }
       
   691 
       
   692     void close() throws IOException {
       
   693         sock.close();
       
   694     }
       
   695 
       
   696     private interface SocketReadOp {
       
   697         int read() throws IOException;
       
   698     }
       
   699 
       
   700     private int readWithTimeout(SocketReadOp reader) throws IOException {
       
   701         if (timeoutLeft <= 0)
       
   702             throw new SocketTimeoutException();
       
   703 
       
   704         sock.setSoTimeout(timeoutLeft);
       
   705         long start = System.currentTimeMillis();
       
   706         try {
       
   707             return reader.read();
       
   708         } finally {
       
   709             timeoutLeft -= System.currentTimeMillis() - start;
       
   710         }
       
   711     }
       
   712 
       
   713     int read() throws IOException {
       
   714         return readWithTimeout(in::read);
       
   715     }
       
   716 
       
   717     int read(byte b[], int off, int len) throws IOException {
       
   718         return readWithTimeout(() -> in.read(b, off, len));
       
   719     }
       
   720 }
       
   721 
       
   722 /*
       
   723  * javaos emulation -cj
       
   724  */
       
   725 class Packet {
       
   726     byte[] buf;
       
   727 
       
   728     Packet(int len) {
       
   729         buf = new byte[len];
       
   730     }
       
   731 
       
   732     Packet(byte data[], int len) {
       
   733         buf = new byte[len];
       
   734         System.arraycopy(data, 0, buf, 0, len);
       
   735     }
       
   736 
       
   737     void putInt(int x, int off) {
       
   738         buf[off] = (byte) (x >> 24);
       
   739         buf[off + 1] = (byte) (x >> 16);
       
   740         buf[off + 2] = (byte) (x >> 8);
       
   741         buf[off + 3] = (byte) x;
       
   742     }
       
   743 
       
   744     void putShort(int x, int off) {
       
   745         buf[off] = (byte) (x >> 8);
       
   746         buf[off + 1] = (byte) x;
       
   747     }
       
   748 
       
   749     void putByte(int x, int off) {
       
   750         buf[off] = (byte) x;
       
   751     }
       
   752 
       
   753     void putBytes(byte src[], int src_offset, int dst_offset, int len) {
       
   754         System.arraycopy(src, src_offset, buf, dst_offset, len);
       
   755     }
       
   756 
       
   757     int length() {
       
   758         return buf.length;
       
   759     }
       
   760 
       
   761     byte[] getData() {
       
   762         return buf;
       
   763     }
       
   764 }