src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java
branchaefimov-dns-client-branch
changeset 58971 465a15dd6bed
parent 58870 35c438a6d45c
child 59101 258033faefc9
equal deleted inserted replaced
58970:027e4cb87353 58971:465a15dd6bed
    25 
    25 
    26 package jdk.dns.client.internal;
    26 package jdk.dns.client.internal;
    27 
    27 
    28 import jdk.dns.client.ex.DnsCommunicationException;
    28 import jdk.dns.client.ex.DnsCommunicationException;
    29 import jdk.dns.client.ex.DnsNameNotFoundException;
    29 import jdk.dns.client.ex.DnsNameNotFoundException;
    30 import jdk.dns.client.ex.DnsOperationNotSupportedException;
       
    31 import jdk.dns.client.ex.DnsResolverException;
    30 import jdk.dns.client.ex.DnsResolverException;
    32 import jdk.dns.client.ex.DnsServiceUnavailableException;
    31 
    33 
    32 import java.io.BufferedInputStream;
       
    33 import java.io.BufferedOutputStream;
    34 import java.io.IOException;
    34 import java.io.IOException;
       
    35 import java.io.InputStream;
       
    36 import java.io.OutputStream;
    35 import java.net.DatagramPacket;
    37 import java.net.DatagramPacket;
    36 import java.net.InetAddress;
    38 import java.net.InetAddress;
    37 import java.net.InetSocketAddress;
    39 import java.net.InetSocketAddress;
    38 import java.net.Socket;
    40 import java.net.Socket;
    39 import java.net.SocketTimeoutException;
    41 import java.net.SocketTimeoutException;
    42 import java.nio.channels.DatagramChannel;
    44 import java.nio.channels.DatagramChannel;
    43 import java.security.AccessController;
    45 import java.security.AccessController;
    44 import java.security.PrivilegedAction;
    46 import java.security.PrivilegedAction;
    45 import java.security.SecureRandom;
    47 import java.security.SecureRandom;
    46 import java.util.ArrayList;
    48 import java.util.ArrayList;
    47 import java.util.Collection;
       
    48 import java.util.Collections;
    49 import java.util.Collections;
    49 import java.util.HashMap;
    50 import java.util.HashMap;
    50 import java.util.List;
    51 import java.util.List;
    51 import java.util.Map;
    52 import java.util.Map;
    52 import java.util.concurrent.locks.ReentrantLock;
    53 import java.util.concurrent.locks.ReentrantLock;
    97     private List<Integer> serverPorts;
    98     private List<Integer> serverPorts;
    98     private int timeout;                // initial timeout on UDP and TCP queries in ms
    99     private int timeout;                // initial timeout on UDP and TCP queries in ms
    99     private int retries;                // number of UDP retries
   100     private int retries;                // number of UDP retries
   100 
   101 
   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();
   107         random = System.getSecurityManager() == null ? pa.run()
   107         random = System.getSecurityManager() == null ? pa.run()
   123      * Each server is of the form "server[:port]".  IPv6 literal host names
   123      * Each server is of the form "server[:port]".  IPv6 literal host names
   124      * include delimiting brackets.
   124      * include delimiting brackets.
   125      * "timeout" is the initial timeout interval (in ms) for queries,
   125      * "timeout" is the initial timeout interval (in ms) for queries,
   126      * and "retries" gives the number of retries per server.
   126      * and "retries" gives the number of retries per server.
   127      */
   127      */
   128     public DnsClient(List<String> servers, int timeout, int retries)
   128     public DnsClient(List<String> servers, int timeout, int retries) {
   129             throws UnknownHostException {
       
   130         this.timeout = timeout;
   129         this.timeout = timeout;
   131         this.retries = retries;
   130         this.retries = retries;
   132         var serversList = new ArrayList<InetAddress>();
   131         var serversList = new ArrayList<InetAddress>();
   133         var serverPortsList = new ArrayList<Integer>();
   132         var serverPortsList = new ArrayList<Integer>();
   134 
   133 
   135         for (String serverString:servers) {
   134         for (String serverString : servers) {
   136 
   135 
   137             // Is optional port given?
   136             // Is optional port given?
   138             int colon = serverString.indexOf(':',
   137             int colon = serverString.indexOf(':',
   139                     serverString.indexOf(']') + 1);
   138                     serverString.indexOf(']') + 1);
   140 
   139 
   153                 return null;
   152                 return null;
   154             };
   153             };
   155             byte[] addr = System.getSecurityManager() == null ?
   154             byte[] addr = System.getSecurityManager() == null ?
   156                     pa.run() : AccessController.doPrivileged(pa);
   155                     pa.run() : AccessController.doPrivileged(pa);
   157             if (addr != null) {
   156             if (addr != null) {
   158                 serversList.add(InetAddress.getByAddress(server, addr));
   157                 try {
   159                 serverPortsList.add(serverPort);
   158                     serversList.add(InetAddress.getByAddress(server, addr));
       
   159                     serverPortsList.add(serverPort);
       
   160                 } catch (UnknownHostException e) {
       
   161                     // Malformed IP address is specified - will ignore it
       
   162                 }
   160             }
   163             }
   161         }
   164         }
   162         this.servers = Collections.unmodifiableList(serversList);
   165         this.servers = Collections.unmodifiableList(serversList);
   163         this.serverPorts = Collections.unmodifiableList(serverPortsList);
   166         this.serverPorts = Collections.unmodifiableList(serverPortsList);
   164         reqs = Collections.synchronizedMap(
   167         reqs = Collections.synchronizedMap(
   255                             }
   258                             }
   256                         }
   259                         }
   257                         Header hdr = new Header(msg, msg.length);
   260                         Header hdr = new Header(msg, msg.length);
   258 
   261 
   259                         if (auth && !hdr.authoritative) {
   262                         if (auth && !hdr.authoritative) {
   260                             caughtException = new DnsNameNotFoundException(
   263                             caughtException = new DnsResolverException("DNS response not authoritative");
   261                                     "DNS response not authoritative");
       
   262                             doNotRetry[i] = true;
   264                             doNotRetry[i] = true;
   263                             continue;
   265                             continue;
   264                         }
   266                         }
   265                         if (hdr.truncated) {  // message is truncated -- try TCP
   267                         if (hdr.truncated) {  // message is truncated -- try TCP
   266 
   268 
   281                                     } finally {
   283                                     } finally {
   282                                         tcp.close();
   284                                         tcp.close();
   283                                     }
   285                                     }
   284                                     Header hdr2 = new Header(msg2, msg2.length);
   286                                     Header hdr2 = new Header(msg2, msg2.length);
   285                                     if (hdr2.query) {
   287                                     if (hdr2.query) {
   286                                         throw new DnsCommunicationException(
   288                                         throw new DnsResolverException(
   287                                                 "DNS error: expecting response");
   289                                                 "DNS error: expecting response");
   288                                     }
   290                                     }
   289                                     checkResponseCode(hdr2);
   291                                     checkResponseCode(hdr2);
   290 
   292 
   291                                     if (!auth || hdr2.authoritative) {
   293                                     if (!auth || hdr2.authoritative) {
   360         try (DatagramChannel dc = getDatagramChannel()) {
   362         try (DatagramChannel dc = getDatagramChannel()) {
   361             DatagramPacket opkt = new DatagramPacket(pkt.getData(), pkt.length(), server, port);
   363             DatagramPacket opkt = new DatagramPacket(pkt.getData(), pkt.length(), server, port);
   362             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
   364             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
   363             // Packets may only be sent to or received from this server address
   365             // Packets may only be sent to or received from this server address
   364             // TODO: Revisit
   366             // TODO: Revisit
   365             var pa = (PrivilegedAction<Void>) () -> {dc.socket().connect(server, port); return null;};
   367             var pa = (PrivilegedAction<Void>) () -> {
       
   368                 dc.socket().connect(server, port);
       
   369                 return null;
       
   370             };
   366             if (System.getSecurityManager() == null) {
   371             if (System.getSecurityManager() == null) {
   367                 pa.run();
   372                 pa.run();
   368             } else {
   373             } else {
   369                 AccessController.doPrivileged(pa);
   374                 AccessController.doPrivileged(pa);
   370             }
   375             }
   459         int qnameLen = fqdn.getOctets();
   464         int qnameLen = fqdn.getOctets();
   460         int pktLen = DNS_HDR_SIZE + qnameLen + 4;
   465         int pktLen = DNS_HDR_SIZE + qnameLen + 4;
   461         Packet pkt = new Packet(pktLen);
   466         Packet pkt = new Packet(pktLen);
   462 
   467 
   463         short flags = recursion ? Header.RD_BIT : 0;
   468         short flags = recursion ? Header.RD_BIT : 0;
       
   469         // flags = (short) (flags | Header.CD_BIT | Header.AD_BIT);
   464 
   470 
   465         pkt.putShort(xid, IDENT_OFFSET);
   471         pkt.putShort(xid, IDENT_OFFSET);
   466         pkt.putShort(flags, FLAGS_OFFSET);
   472         pkt.putShort(flags, FLAGS_OFFSET);
   467         pkt.putShort(1, NUMQ_OFFSET);
   473         pkt.putShort(1, NUMQ_OFFSET);
   468         pkt.putShort(0, NUMANS_OFFSET);
   474         pkt.putShort(0, NUMANS_OFFSET);
   534     private boolean isMatchResponse(byte[] pkt, int xid)
   540     private boolean isMatchResponse(byte[] pkt, int xid)
   535             throws DnsResolverException {
   541             throws DnsResolverException {
   536 
   542 
   537         Header hdr = new Header(pkt, pkt.length);
   543         Header hdr = new Header(pkt, pkt.length);
   538         if (hdr.query) {
   544         if (hdr.query) {
   539             throw new DnsCommunicationException("DNS error: expecting response");
   545             throw new DnsResolverException("DNS error: expecting response");
   540         }
   546         }
   541 
   547 
   542         if (!reqs.containsKey(xid)) { // already received, ignore the response
   548         if (!reqs.containsKey(xid)) { // already received, ignore the response
   543             return false;
   549             return false;
   544         }
   550         }
   560                 int qclass = query.getRrclass();
   566                 int qclass = query.getRrclass();
   561                 DnsName qname = query.getName();
   567                 DnsName qname = query.getName();
   562 
   568 
   563                 // Check that the type/class/name in the query section of the
   569                 // Check that the type/class/name in the query section of the
   564                 // response match those in the original query
   570                 // response match those in the original query
   565                 if ((qtype == ResourceRecord.QTYPE_STAR ||
   571                 if ((qtype == ResourceRecord.TYPE_ANY ||
   566                         qtype == rr.getType()) &&
   572                         qtype == rr.getType()) &&
   567                         (qclass == ResourceRecord.QCLASS_STAR ||
   573                         (qclass == ResourceRecord.QCLASS_STAR ||
   568                                 qclass == rr.getRrclass()) &&
   574                                 qclass == rr.getRrclass()) &&
   569                         qname.equals(rr.getName())) {
   575                         qname.equals(rr.getName())) {
   570 
   576 
   630         String msg = (rcode < rcodeDescription.length)
   636         String msg = (rcode < rcodeDescription.length)
   631                 ? rcodeDescription[rcode]
   637                 ? rcodeDescription[rcode]
   632                 : "DNS error";
   638                 : "DNS error";
   633 
   639 
   634         msg += " [response code " + rcode + "]";
   640         msg += " [response code " + rcode + "]";
   635 
   641         throw new DnsResolverException(msg);
   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     }
   642     }
   649 
   643 
   650     //-------------------------------------------------------------------------
   644     //-------------------------------------------------------------------------
   651 
   645 
   652     private static final boolean DEBUG = java.security.AccessController.doPrivileged(
   646     private static final boolean DEBUG = java.security.AccessController.doPrivileged(
   661 }
   655 }
   662 
   656 
   663 class Tcp {
   657 class Tcp {
   664 
   658 
   665     private final Socket sock;
   659     private final Socket sock;
   666     private final java.io.InputStream in;
   660     private final InputStream in;
   667     final java.io.OutputStream out;
   661     final OutputStream out;
   668     private int timeoutLeft;
   662     private int timeoutLeft;
   669 
   663 
   670     Tcp(InetAddress server, int port, int timeout) throws IOException {
   664     Tcp(InetAddress server, int port, int timeout) throws IOException {
   671         sock = new Socket();
   665         sock = new Socket();
   672         try {
   666         try {
   675             timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start));
   669             timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start));
   676             if (timeoutLeft <= 0)
   670             if (timeoutLeft <= 0)
   677                 throw new SocketTimeoutException();
   671                 throw new SocketTimeoutException();
   678 
   672 
   679             sock.setTcpNoDelay(true);
   673             sock.setTcpNoDelay(true);
   680             out = new java.io.BufferedOutputStream(sock.getOutputStream());
   674             out = new BufferedOutputStream(sock.getOutputStream());
   681             in = new java.io.BufferedInputStream(sock.getInputStream());
   675             in = new BufferedInputStream(sock.getInputStream());
   682         } catch (Exception e) {
   676         } catch (Exception e) {
   683             try {
   677             try {
   684                 sock.close();
   678                 sock.close();
   685             } catch (IOException ex) {
   679             } catch (IOException ex) {
   686                 e.addSuppressed(ex);
   680                 e.addSuppressed(ex);