diff -r 3e78d4a02113 -r 4a8ca39187ef jdk/src/share/classes/com/sun/jndi/dns/DnsClient.java --- a/jdk/src/share/classes/com/sun/jndi/dns/DnsClient.java Tue Jan 21 06:45:46 2014 +0400 +++ b/jdk/src/share/classes/com/sun/jndi/dns/DnsClient.java Tue Jan 21 10:49:19 2014 +0000 @@ -30,13 +30,14 @@ import java.net.DatagramPacket; import java.net.InetAddress; import java.net.Socket; +import java.security.SecureRandom; import javax.naming.*; import java.util.Collections; import java.util.Map; import java.util.HashMap; -import java.util.Set; -import java.util.HashSet; + +import sun.security.jca.JCAUtil; // Some of this code began life as part of sun.javaos.net.DnsClient // originally by sritchie@eng 1/96. It was first hacked up for JNDI @@ -77,6 +78,8 @@ }; private static final int DEFAULT_PORT = 53; + private static final int TRANSACTION_ID_BOUND = 0x10000; + private static final SecureRandom random = JCAUtil.getSecureRandom(); private InetAddress[] servers; private int[] serverPorts; private int timeout; // initial timeout on UDP queries in ms @@ -85,7 +88,7 @@ private DatagramSocket udpSocket; // Requests sent - private Set reqs; + private Map reqs; // Responses received private Map resps; @@ -134,7 +137,8 @@ throw ne; } } - reqs = Collections.synchronizedSet(new HashSet()); + reqs = Collections.synchronizedMap( + new HashMap()); resps = Collections.synchronizedMap(new HashMap()); } @@ -153,10 +157,6 @@ } } - - private int ident = 0; // used to set the msg ID field - private Object identLock = new Object(); - /* * If recursion is true, recursion is requested on the query. * If auth is true, only authoritative responses are accepted; other @@ -167,15 +167,19 @@ throws NamingException { int xid; - synchronized (identLock) { - ident = 0xFFFF & (ident + 1); - xid = ident; - } + Packet pkt; + ResourceRecord collision; - // enqueue the outstanding request - reqs.add(xid); + do { + // Generate a random transaction ID + xid = random.nextInt(TRANSACTION_ID_BOUND); + pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion); - Packet pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion); + // enqueue the outstanding request + collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(), + pkt.length(), Header.HEADER_SIZE, true, false)); + + } while (collision != null); Exception caughtException = null; boolean[] doNotRetry = new boolean[servers.length]; @@ -305,11 +309,8 @@ ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion) throws NamingException { - int xid; - synchronized (identLock) { - ident = 0xFFFF & (ident + 1); - xid = ident; - } + int xid = random.nextInt(TRANSACTION_ID_BOUND); + Packet pkt = makeQueryPacket(zone, xid, qclass, ResourceRecord.QTYPE_AXFR, recursion); Exception caughtException = null; @@ -390,6 +391,7 @@ DatagramPacket opkt = new DatagramPacket( pkt.getData(), pkt.length(), server, port); DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000); + // Packets may only be sent to or received from this server address udpSocket.connect(server, port); int pktTimeout = (timeout * (1 << retry)); try { @@ -542,6 +544,9 @@ * Checks the header of an incoming DNS response. * Returns true if it matches the given xid and throws a naming * exception, if appropriate, based on the response code. + * + * Also checks that the domain name, type and class in the response + * match those in the original query. */ private boolean isMatchResponse(byte[] pkt, int xid) throws NamingException { @@ -551,7 +556,7 @@ throw new CommunicationException("DNS error: expecting response"); } - if (!reqs.contains(xid)) { // already received, ignore the response + if (!reqs.containsKey(xid)) { // already received, ignore the response return false; } @@ -560,14 +565,47 @@ if (debug) { dprint("XID MATCH:" + xid); } + checkResponseCode(hdr); + if (!hdr.query && hdr.numQuestions == 1) { - checkResponseCode(hdr); - // remove the response for the xid if received by some other thread. - synchronized (queuesLock) { - resps.remove(xid); - reqs.remove(xid); + ResourceRecord rr = new ResourceRecord(pkt, pkt.length, + Header.HEADER_SIZE, true, false); + + // Retrieve the original query + ResourceRecord query = reqs.get(xid); + int qtype = query.getType(); + int qclass = query.getRrclass(); + DnsName qname = query.getName(); + + // Check that the type/class/name in the query section of the + // response match those in the original query + if ((qtype == ResourceRecord.QTYPE_STAR || + qtype == rr.getType()) && + (qclass == ResourceRecord.QCLASS_STAR || + qclass == rr.getRrclass()) && + qname.equals(rr.getName())) { + + if (debug) { + dprint("MATCH NAME:" + qname + " QTYPE:" + qtype + + " QCLASS:" + qclass); + } + + // Remove the response for the xid if received by some other + // thread. + synchronized (queuesLock) { + resps.remove(xid); + reqs.remove(xid); + } + return true; + + } else { + if (debug) { + dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype + + " QCLASS:" + qclass); + } + } } - return true; + return false; } // @@ -576,7 +614,7 @@ // enqueue only the first response, responses for retries are ignored. // synchronized (queuesLock) { - if (reqs.contains(hdr.xid)) { // enqueue only the first response + if (reqs.containsKey(hdr.xid)) { // enqueue only the first response resps.put(hdr.xid, pkt); } }