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