jdk/src/share/classes/com/sun/jndi/dns/DnsClient.java
changeset 23904 4a8ca39187ef
parent 22992 e7fcc52b1b29
child 25808 e113d0a0fde0
equal deleted inserted replaced
23903:3e78d4a02113 23904:4a8ca39187ef
    28 import java.io.IOException;
    28 import java.io.IOException;
    29 import java.net.DatagramSocket;
    29 import java.net.DatagramSocket;
    30 import java.net.DatagramPacket;
    30 import java.net.DatagramPacket;
    31 import java.net.InetAddress;
    31 import java.net.InetAddress;
    32 import java.net.Socket;
    32 import java.net.Socket;
       
    33 import java.security.SecureRandom;
    33 import javax.naming.*;
    34 import javax.naming.*;
    34 
    35 
    35 import java.util.Collections;
    36 import java.util.Collections;
    36 import java.util.Map;
    37 import java.util.Map;
    37 import java.util.HashMap;
    38 import java.util.HashMap;
    38 import java.util.Set;
    39 
    39 import java.util.HashSet;
    40 import sun.security.jca.JCAUtil;
    40 
    41 
    41 // Some of this code began life as part of sun.javaos.net.DnsClient
    42 // Some of this code began life as part of sun.javaos.net.DnsClient
    42 // originally by sritchie@eng 1/96.  It was first hacked up for JNDI
    43 // originally by sritchie@eng 1/96.  It was first hacked up for JNDI
    43 // use by caveh@eng 6/97.
    44 // use by caveh@eng 6/97.
    44 
    45 
    75         "DNS operation not supported",
    76         "DNS operation not supported",
    76         "DNS service refused"
    77         "DNS service refused"
    77     };
    78     };
    78 
    79 
    79     private static final int DEFAULT_PORT = 53;
    80     private static final int DEFAULT_PORT = 53;
       
    81     private static final int TRANSACTION_ID_BOUND = 0x10000;
       
    82     private static final SecureRandom random = JCAUtil.getSecureRandom();
    80     private InetAddress[] servers;
    83     private InetAddress[] servers;
    81     private int[] serverPorts;
    84     private int[] serverPorts;
    82     private int timeout;                // initial timeout on UDP queries in ms
    85     private int timeout;                // initial timeout on UDP queries in ms
    83     private int retries;                // number of UDP retries
    86     private int retries;                // number of UDP retries
    84 
    87 
    85     private DatagramSocket udpSocket;
    88     private DatagramSocket udpSocket;
    86 
    89 
    87     // Requests sent
    90     // Requests sent
    88     private Set<Integer> reqs;
    91     private Map<Integer, ResourceRecord> reqs;
    89 
    92 
    90     // Responses received
    93     // Responses received
    91     private Map<Integer, byte[]> resps;
    94     private Map<Integer, byte[]> resps;
    92 
    95 
    93     //-------------------------------------------------------------------------
    96     //-------------------------------------------------------------------------
   132                         "Unknown DNS server: " + server);
   135                         "Unknown DNS server: " + server);
   133                 ne.setRootCause(e);
   136                 ne.setRootCause(e);
   134                 throw ne;
   137                 throw ne;
   135             }
   138             }
   136         }
   139         }
   137         reqs = Collections.synchronizedSet(new HashSet<Integer>());
   140         reqs = Collections.synchronizedMap(
       
   141             new HashMap<Integer, ResourceRecord>());
   138         resps = Collections.synchronizedMap(new HashMap<Integer, byte[]>());
   142         resps = Collections.synchronizedMap(new HashMap<Integer, byte[]>());
   139     }
   143     }
   140 
   144 
   141     protected void finalize() {
   145     protected void finalize() {
   142         close();
   146         close();
   150         synchronized (queuesLock) {
   154         synchronized (queuesLock) {
   151             reqs.clear();
   155             reqs.clear();
   152             resps.clear();
   156             resps.clear();
   153         }
   157         }
   154     }
   158     }
   155 
       
   156 
       
   157     private int ident = 0;              // used to set the msg ID field
       
   158     private Object identLock = new Object();
       
   159 
   159 
   160     /*
   160     /*
   161      * If recursion is true, recursion is requested on the query.
   161      * If recursion is true, recursion is requested on the query.
   162      * If auth is true, only authoritative responses are accepted; other
   162      * If auth is true, only authoritative responses are accepted; other
   163      * responses throw NameNotFoundException.
   163      * responses throw NameNotFoundException.
   165     ResourceRecords query(DnsName fqdn, int qclass, int qtype,
   165     ResourceRecords query(DnsName fqdn, int qclass, int qtype,
   166                           boolean recursion, boolean auth)
   166                           boolean recursion, boolean auth)
   167             throws NamingException {
   167             throws NamingException {
   168 
   168 
   169         int xid;
   169         int xid;
   170         synchronized (identLock) {
   170         Packet pkt;
   171             ident = 0xFFFF & (ident + 1);
   171         ResourceRecord collision;
   172             xid = ident;
   172 
   173         }
   173         do {
   174 
   174             // Generate a random transaction ID
   175         // enqueue the outstanding request
   175             xid = random.nextInt(TRANSACTION_ID_BOUND);
   176         reqs.add(xid);
   176             pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
   177 
   177 
   178         Packet pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
   178             // enqueue the outstanding request
       
   179             collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(),
       
   180                 pkt.length(), Header.HEADER_SIZE, true, false));
       
   181 
       
   182         } while (collision != null);
   179 
   183 
   180         Exception caughtException = null;
   184         Exception caughtException = null;
   181         boolean[] doNotRetry = new boolean[servers.length];
   185         boolean[] doNotRetry = new boolean[servers.length];
   182 
   186 
   183         //
   187         //
   303     }
   307     }
   304 
   308 
   305     ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
   309     ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
   306             throws NamingException {
   310             throws NamingException {
   307 
   311 
   308         int xid;
   312         int xid = random.nextInt(TRANSACTION_ID_BOUND);
   309         synchronized (identLock) {
   313 
   310             ident = 0xFFFF & (ident + 1);
       
   311             xid = ident;
       
   312         }
       
   313         Packet pkt = makeQueryPacket(zone, xid, qclass,
   314         Packet pkt = makeQueryPacket(zone, xid, qclass,
   314                                      ResourceRecord.QTYPE_AXFR, recursion);
   315                                      ResourceRecord.QTYPE_AXFR, recursion);
   315         Exception caughtException = null;
   316         Exception caughtException = null;
   316 
   317 
   317         // Try each name server.
   318         // Try each name server.
   388 
   389 
   389         synchronized (udpSocket) {
   390         synchronized (udpSocket) {
   390             DatagramPacket opkt = new DatagramPacket(
   391             DatagramPacket opkt = new DatagramPacket(
   391                     pkt.getData(), pkt.length(), server, port);
   392                     pkt.getData(), pkt.length(), server, port);
   392             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
   393             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
       
   394             // Packets may only be sent to or received from this server address
   393             udpSocket.connect(server, port);
   395             udpSocket.connect(server, port);
   394             int pktTimeout = (timeout * (1 << retry));
   396             int pktTimeout = (timeout * (1 << retry));
   395             try {
   397             try {
   396                 udpSocket.send(opkt);
   398                 udpSocket.send(opkt);
   397 
   399 
   540 
   542 
   541     /*
   543     /*
   542      * Checks the header of an incoming DNS response.
   544      * Checks the header of an incoming DNS response.
   543      * Returns true if it matches the given xid and throws a naming
   545      * Returns true if it matches the given xid and throws a naming
   544      * exception, if appropriate, based on the response code.
   546      * exception, if appropriate, based on the response code.
       
   547      *
       
   548      * Also checks that the domain name, type and class in the response
       
   549      * match those in the original query.
   545      */
   550      */
   546     private boolean isMatchResponse(byte[] pkt, int xid)
   551     private boolean isMatchResponse(byte[] pkt, int xid)
   547                 throws NamingException {
   552                 throws NamingException {
   548 
   553 
   549         Header hdr = new Header(pkt, pkt.length);
   554         Header hdr = new Header(pkt, pkt.length);
   550         if (hdr.query) {
   555         if (hdr.query) {
   551             throw new CommunicationException("DNS error: expecting response");
   556             throw new CommunicationException("DNS error: expecting response");
   552         }
   557         }
   553 
   558 
   554         if (!reqs.contains(xid)) { // already received, ignore the response
   559         if (!reqs.containsKey(xid)) { // already received, ignore the response
   555             return false;
   560             return false;
   556         }
   561         }
   557 
   562 
   558         // common case- the request sent matches the subsequent response read
   563         // common case- the request sent matches the subsequent response read
   559         if (hdr.xid == xid) {
   564         if (hdr.xid == xid) {
   560             if (debug) {
   565             if (debug) {
   561                 dprint("XID MATCH:" + xid);
   566                 dprint("XID MATCH:" + xid);
   562             }
   567             }
   563 
       
   564             checkResponseCode(hdr);
   568             checkResponseCode(hdr);
   565             // remove the response for the xid if received by some other thread.
   569             if (!hdr.query && hdr.numQuestions == 1) {
   566             synchronized (queuesLock) {
   570 
   567                 resps.remove(xid);
   571                 ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
   568                 reqs.remove(xid);
   572                     Header.HEADER_SIZE, true, false);
   569             }
   573 
   570             return true;
   574                 // Retrieve the original query
       
   575                 ResourceRecord query = reqs.get(xid);
       
   576                 int qtype = query.getType();
       
   577                 int qclass = query.getRrclass();
       
   578                 DnsName qname = query.getName();
       
   579 
       
   580                 // Check that the type/class/name in the query section of the
       
   581                 // response match those in the original query
       
   582                 if ((qtype == ResourceRecord.QTYPE_STAR ||
       
   583                     qtype == rr.getType()) &&
       
   584                     (qclass == ResourceRecord.QCLASS_STAR ||
       
   585                     qclass == rr.getRrclass()) &&
       
   586                     qname.equals(rr.getName())) {
       
   587 
       
   588                     if (debug) {
       
   589                         dprint("MATCH NAME:" + qname + " QTYPE:" + qtype +
       
   590                             " QCLASS:" + qclass);
       
   591                     }
       
   592 
       
   593                     // Remove the response for the xid if received by some other
       
   594                     // thread.
       
   595                     synchronized (queuesLock) {
       
   596                         resps.remove(xid);
       
   597                         reqs.remove(xid);
       
   598                     }
       
   599                     return true;
       
   600 
       
   601                 } else {
       
   602                     if (debug) {
       
   603                         dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype +
       
   604                             " QCLASS:" + qclass);
       
   605                     }
       
   606                 }
       
   607             }
       
   608             return false;
   571         }
   609         }
   572 
   610 
   573         //
   611         //
   574         // xid mis-match: enqueue the response, it may belong to some other
   612         // xid mis-match: enqueue the response, it may belong to some other
   575         // thread that has not yet had a chance to read its response.
   613         // thread that has not yet had a chance to read its response.
   576         // enqueue only the first response, responses for retries are ignored.
   614         // enqueue only the first response, responses for retries are ignored.
   577         //
   615         //
   578         synchronized (queuesLock) {
   616         synchronized (queuesLock) {
   579             if (reqs.contains(hdr.xid)) { // enqueue only the first response
   617             if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
   580                 resps.put(hdr.xid, pkt);
   618                 resps.put(hdr.xid, pkt);
   581             }
   619             }
   582         }
   620         }
   583 
   621 
   584         if (debug) {
   622         if (debug) {