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(); |