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 //------------------------------------------------------------------------- |
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) { |