src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java
branchaefimov-dns-client-branch
changeset 59101 258033faefc9
parent 58971 465a15dd6bed
equal deleted inserted replaced
59100:b92aac38b046 59101:258033faefc9
    35 import java.io.InputStream;
    35 import java.io.InputStream;
    36 import java.io.OutputStream;
    36 import java.io.OutputStream;
    37 import java.net.DatagramPacket;
    37 import java.net.DatagramPacket;
    38 import java.net.InetAddress;
    38 import java.net.InetAddress;
    39 import java.net.InetSocketAddress;
    39 import java.net.InetSocketAddress;
       
    40 import java.net.PortUnreachableException;
    40 import java.net.Socket;
    41 import java.net.Socket;
    41 import java.net.SocketTimeoutException;
    42 import java.net.SocketTimeoutException;
    42 import java.net.UnknownHostException;
    43 import java.net.UnknownHostException;
    43 import java.nio.ByteBuffer;
       
    44 import java.nio.channels.DatagramChannel;
    44 import java.nio.channels.DatagramChannel;
    45 import java.security.AccessController;
    45 import java.security.AccessController;
    46 import java.security.PrivilegedAction;
    46 import java.security.PrivilegedAction;
    47 import java.security.SecureRandom;
    47 import java.security.SecureRandom;
    48 import java.util.ArrayList;
    48 import java.util.ArrayList;
       
    49 import java.util.Arrays;
    49 import java.util.Collections;
    50 import java.util.Collections;
    50 import java.util.HashMap;
    51 import java.util.HashMap;
    51 import java.util.List;
    52 import java.util.List;
    52 import java.util.Map;
    53 import java.util.Map;
    53 import java.util.concurrent.locks.ReentrantLock;
    54 import java.util.concurrent.locks.ReentrantLock;
    96     private static final int TRANSACTION_ID_BOUND = 0x10000;
    97     private static final int TRANSACTION_ID_BOUND = 0x10000;
    97     private List<InetAddress> servers;
    98     private List<InetAddress> servers;
    98     private List<Integer> serverPorts;
    99     private List<Integer> serverPorts;
    99     private int timeout;                // initial timeout on UDP and TCP queries in ms
   100     private int timeout;                // initial timeout on UDP and TCP queries in ms
   100     private int retries;                // number of UDP retries
   101     private int retries;                // number of UDP retries
   101 
       
   102 
   102 
   103     private static final SecureRandom random;
   103     private static final SecureRandom random;
   104 
   104 
   105     static {
   105     static {
   106         var pa = (PrivilegedAction<SecureRandom>) () -> JCAUtil.getSecureRandom();
   106         var pa = (PrivilegedAction<SecureRandom>) () -> JCAUtil.getSecureRandom();
   128     public DnsClient(List<String> servers, int timeout, int retries) {
   128     public DnsClient(List<String> servers, int timeout, int retries) {
   129         this.timeout = timeout;
   129         this.timeout = timeout;
   130         this.retries = retries;
   130         this.retries = retries;
   131         var serversList = new ArrayList<InetAddress>();
   131         var serversList = new ArrayList<InetAddress>();
   132         var serverPortsList = new ArrayList<Integer>();
   132         var serverPortsList = new ArrayList<Integer>();
       
   133 
       
   134         if (DEBUG) {
       
   135             System.err.println("DNS Client: servers list:" + servers);
       
   136         }
   133 
   137 
   134         for (String serverString : servers) {
   138         for (String serverString : servers) {
   135 
   139 
   136             // Is optional port given?
   140             // Is optional port given?
   137             int colon = serverString.indexOf(':',
   141             int colon = serverString.indexOf(':',
   303                                 }
   307                                 }
   304                             } // servers
   308                             } // servers
   305                         }
   309                         }
   306                         return new ResourceRecords(msg, msg.length, hdr, false);
   310                         return new ResourceRecords(msg, msg.length, hdr, false);
   307 
   311 
       
   312                     } catch (PortUnreachableException e) {
       
   313                         if (caughtException == null) {
       
   314                             caughtException = e;
       
   315                         }
       
   316                         doNotRetry[i] = true;
   308                     } catch (IOException e) {
   317                     } catch (IOException e) {
   309                         if (DEBUG) {
   318                         if (DEBUG) {
   310                             dprint("Caught IOException:" + e);
   319                             dprint("Caught IOException:" + e);
   311                         }
   320                         }
   312                         if (caughtException == null) {
   321                         if (caughtException == null) {
   313                             caughtException = e;
   322                             caughtException = e;
   314                         }
   323                         }
   315                         // Use reflection to allow pre-1.4 compilation.
       
   316                         // This won't be needed much longer.
       
   317                         if (e.getClass().getName().equals(
       
   318                                 "java.net.PortUnreachableException")) {
       
   319                             doNotRetry[i] = true;
       
   320                         }
       
   321                         // doNotRetry set - needs to be added
       
   322                     } catch (DnsNameNotFoundException e) {
   324                     } catch (DnsNameNotFoundException e) {
   323                         // This is authoritative, so return immediately
   325                         // This is authoritative, so return immediately
   324                         throw e;
   326                         throw e;
   325                     } catch (DnsCommunicationException e) {
   327                     } catch (DnsCommunicationException e) {
   326                         if (caughtException == null) {
   328                         if (caughtException == null) {
   392                     }
   394                     }
   393                     dc.socket().setSoTimeout(timeoutLeft);
   395                     dc.socket().setSoTimeout(timeoutLeft);
   394                     long start = System.currentTimeMillis();
   396                     long start = System.currentTimeMillis();
   395 
   397 
   396 
   398 
       
   399                     dc.socket().receive(ipkt);
   397                     byte[] data = ipkt.getData();
   400                     byte[] data = ipkt.getData();
   398                     ByteBuffer bb = ByteBuffer.wrap(data);
   401                     int length = ipkt.getLength();
   399                     dc.read(bb);
       
   400                     long end = System.currentTimeMillis();
   402                     long end = System.currentTimeMillis();
   401 
   403 
   402                     if (isMatchResponse(data, xid)) {
   404                     if (isMatchResponse(data, length, xid)) {
   403                         return data;
   405                         return data;
   404                     }
   406                     }
   405                     timeoutLeft = pktTimeout - ((int) (end - start));
   407                     timeoutLeft = pktTimeout - ((int) (end - start));
   406                 } while (timeoutLeft > minTimeout);
   408                 } while (timeoutLeft > minTimeout);
   407 
   409 
   535      * exception, if appropriate, based on the response code.
   537      * exception, if appropriate, based on the response code.
   536      *
   538      *
   537      * Also checks that the domain name, type and class in the response
   539      * Also checks that the domain name, type and class in the response
   538      * match those in the original query.
   540      * match those in the original query.
   539      */
   541      */
   540     private boolean isMatchResponse(byte[] pkt, int xid)
   542     private boolean isMatchResponse(byte[] pkt, int length, int xid)
   541             throws DnsResolverException {
   543             throws DnsResolverException {
   542 
   544 
   543         Header hdr = new Header(pkt, pkt.length);
   545         Header hdr = new Header(pkt, length);
   544         if (hdr.query) {
   546         if (hdr.query) {
   545             throw new DnsResolverException("DNS error: expecting response");
   547             throw new DnsResolverException("DNS error: expecting response");
   546         }
   548         }
   547 
   549 
   548         if (!reqs.containsKey(xid)) { // already received, ignore the response
   550         if (!reqs.containsKey(xid)) { // already received, ignore the response
   555                 dprint("XID MATCH:" + xid);
   557                 dprint("XID MATCH:" + xid);
   556             }
   558             }
   557             checkResponseCode(hdr);
   559             checkResponseCode(hdr);
   558             if (!hdr.query && hdr.numQuestions == 1) {
   560             if (!hdr.query && hdr.numQuestions == 1) {
   559 
   561 
   560                 ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
   562                 ResourceRecord rr = new ResourceRecord(pkt, length,
   561                         Header.HEADER_SIZE, true, false);
   563                         Header.HEADER_SIZE, true, false);
   562 
   564 
   563                 // Retrieve the original query
   565                 // Retrieve the original query
   564                 ResourceRecord query = reqs.get(xid);
   566                 ResourceRecord query = reqs.get(xid);
   565                 int qtype = query.getType();
   567                 int qtype = query.getType();
   606         // enqueue only the first response, responses for retries are ignored.
   608         // enqueue only the first response, responses for retries are ignored.
   607         //
   609         //
   608         queuesLock.lock();
   610         queuesLock.lock();
   609         try {
   611         try {
   610             if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
   612             if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
   611                 resps.put(hdr.xid, pkt);
   613                 resps.put(hdr.xid, Arrays.copyOf(pkt, length));
   612             }
   614             }
   613         } finally {
   615         } finally {
   614             queuesLock.unlock();
   616             queuesLock.unlock();
   615         }
   617         }
   616 
   618