1 /*
2 * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.jndi.dns;
27
28 import java.io.IOException;
29 import java.net.DatagramSocket;
30 import java.net.DatagramPacket;
31 import java.net.InetAddress;
32 import java.net.Socket;
33 import java.security.SecureRandom;
34 import javax.naming.*;
35
36 import java.util.Collections;
37 import java.util.Map;
38 import java.util.HashMap;
39
40 import sun.security.jca.JCAUtil;
41
42 // Some of this code began life as part of sun.javaos.net.DnsClient
43 // originally by sritchie@eng 1/96. It was first hacked up for JNDI
44 // use by caveh@eng 6/97.
45
46
47 /**
48 * The DnsClient class performs DNS client operations in support of DnsContext.
49 *
50 */
51
52 public class DnsClient {
65 private static final int FORMAT_ERROR = 1;
66 private static final int SERVER_FAILURE = 2;
67 private static final int NAME_ERROR = 3;
68 private static final int NOT_IMPL = 4;
69 private static final int REFUSED = 5;
70
71 private static final String[] rcodeDescription = {
72 "No error",
73 "DNS format error",
74 "DNS server failure",
75 "DNS name not found",
76 "DNS operation not supported",
77 "DNS service refused"
78 };
79
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();
83 private InetAddress[] servers;
84 private int[] serverPorts;
85 private int timeout; // initial timeout on UDP queries in ms
86 private int retries; // number of UDP retries
87
88 private final Object udpSocketLock = new Object();
89 private static final DNSDatagramSocketFactory factory =
90 new DNSDatagramSocketFactory(random);
91
92 // Requests sent
93 private Map<Integer, ResourceRecord> reqs;
94
95 // Responses received
96 private Map<Integer, byte[]> resps;
97
98 //-------------------------------------------------------------------------
99
100 /*
101 * Each server is of the form "server[:port]". IPv6 literal host names
102 * include delimiting brackets.
103 * "timeout" is the initial timeout interval (in ms) for UDP queries,
104 * and "retries" gives the number of retries per server.
105 */
106 public DnsClient(String[] servers, int timeout, int retries)
107 throws NamingException {
108 this.timeout = timeout;
109 this.retries = retries;
110 this.servers = new InetAddress[servers.length];
111 serverPorts = new int[servers.length];
112
113 for (int i = 0; i < servers.length; i++) {
114
115 // Is optional port given?
116 int colon = servers[i].indexOf(':',
117 servers[i].indexOf(']') + 1);
118
119 serverPorts[i] = (colon < 0)
120 ? DEFAULT_PORT
121 : Integer.parseInt(servers[i].substring(colon + 1));
122 String server = (colon < 0)
123 ? servers[i]
220 if (msg == null) {
221 if (resps.size() > 0) {
222 msg = lookupResponse(xid);
223 }
224 if (msg == null) { // try next server or retry
225 continue;
226 }
227 }
228 Header hdr = new Header(msg, msg.length);
229
230 if (auth && !hdr.authoritative) {
231 caughtException = new NameNotFoundException(
232 "DNS response not authoritative");
233 doNotRetry[i] = true;
234 continue;
235 }
236 if (hdr.truncated) { // message is truncated -- try TCP
237
238 // Try each server, starting with the one that just
239 // provided the truncated message.
240 for (int j = 0; j < servers.length; j++) {
241 int ij = (i + j) % servers.length;
242 if (doNotRetry[ij]) {
243 continue;
244 }
245 try {
246 Tcp tcp =
247 new Tcp(servers[ij], serverPorts[ij]);
248 byte[] msg2;
249 try {
250 msg2 = doTcpQuery(tcp, pkt);
251 } finally {
252 tcp.close();
253 }
254 Header hdr2 = new Header(msg2, msg2.length);
255 if (hdr2.query) {
256 throw new CommunicationException(
257 "DNS error: expecting response");
258 }
259 checkResponseCode(hdr2);
260
261 if (!auth || hdr2.authoritative) {
262 // Got a valid response
263 hdr = hdr2;
264 msg = msg2;
265 break;
266 } else {
267 doNotRetry[ij] = true;
310 throw (NamingException) caughtException;
311 }
312 // A network timeout or other error occurred.
313 NamingException ne = new CommunicationException("DNS error");
314 ne.setRootCause(caughtException);
315 throw ne;
316 }
317
318 ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
319 throws NamingException {
320
321 int xid = random.nextInt(TRANSACTION_ID_BOUND);
322
323 Packet pkt = makeQueryPacket(zone, xid, qclass,
324 ResourceRecord.QTYPE_AXFR, recursion);
325 Exception caughtException = null;
326
327 // Try each name server.
328 for (int i = 0; i < servers.length; i++) {
329 try {
330 Tcp tcp = new Tcp(servers[i], serverPorts[i]);
331 byte[] msg;
332 try {
333 msg = doTcpQuery(tcp, pkt);
334 Header hdr = new Header(msg, msg.length);
335 // Check only rcode as per
336 // draft-ietf-dnsext-axfr-clarify-04
337 checkResponseCode(hdr);
338 ResourceRecords rrs =
339 new ResourceRecords(msg, msg.length, hdr, true);
340 if (rrs.getFirstAnsType() != ResourceRecord.TYPE_SOA) {
341 throw new CommunicationException(
342 "DNS error: zone xfer doesn't begin with SOA");
343 }
344
345 if (rrs.answer.size() == 1 ||
346 rrs.getLastAnsType() != ResourceRecord.TYPE_SOA) {
347 // The response is split into multiple DNS messages.
348 do {
349 msg = continueTcpQuery(tcp);
350 if (msg == null) {
445
446 int len = pkt.length();
447 // Send 2-byte message length, then send message.
448 tcp.out.write(len >> 8);
449 tcp.out.write(len);
450 tcp.out.write(pkt.getData(), 0, len);
451 tcp.out.flush();
452
453 byte[] msg = continueTcpQuery(tcp);
454 if (msg == null) {
455 throw new IOException("DNS error: no response");
456 }
457 return msg;
458 }
459
460 /*
461 * Returns the next DNS message from the TCP socket, or null on EOF.
462 */
463 private byte[] continueTcpQuery(Tcp tcp) throws IOException {
464
465 int lenHi = tcp.in.read(); // high-order byte of response length
466 if (lenHi == -1) {
467 return null; // EOF
468 }
469 int lenLo = tcp.in.read(); // low-order byte of response length
470 if (lenLo == -1) {
471 throw new IOException("Corrupted DNS response: bad length");
472 }
473 int len = (lenHi << 8) | lenLo;
474 byte[] msg = new byte[len];
475 int pos = 0; // next unfilled position in msg
476 while (len > 0) {
477 int n = tcp.in.read(msg, pos, len);
478 if (n == -1) {
479 throw new IOException(
480 "Corrupted DNS response: too little data");
481 }
482 len -= n;
483 pos += n;
484 }
485 return msg;
486 }
487
488 private Packet makeQueryPacket(DnsName fqdn, int xid,
489 int qclass, int qtype, boolean recursion) {
490 int qnameLen = fqdn.getOctets();
491 int pktLen = DNS_HDR_SIZE + qnameLen + 4;
492 Packet pkt = new Packet(pktLen);
493
494 short flags = recursion ? Header.RD_BIT : 0;
495
496 pkt.putShort(xid, IDENT_OFFSET);
497 pkt.putShort(flags, FLAGS_OFFSET);
665 case FORMAT_ERROR:
666 default:
667 throw new NamingException(msg);
668 }
669 }
670
671 //-------------------------------------------------------------------------
672
673 private static final boolean debug = false;
674
675 private static void dprint(String mess) {
676 if (debug) {
677 System.err.println("DNS: " + mess);
678 }
679 }
680
681 }
682
683 class Tcp {
684
685 private Socket sock;
686 java.io.InputStream in;
687 java.io.OutputStream out;
688
689 Tcp(InetAddress server, int port) throws IOException {
690 sock = new Socket(server, port);
691 sock.setTcpNoDelay(true);
692 out = new java.io.BufferedOutputStream(sock.getOutputStream());
693 in = new java.io.BufferedInputStream(sock.getInputStream());
694 }
695
696 void close() throws IOException {
697 sock.close();
698 }
699 }
700
701 /*
702 * javaos emulation -cj
703 */
704 class Packet {
705 byte buf[];
706
707 Packet(int len) {
708 buf = new byte[len];
709 }
710
711 Packet(byte data[], int len) {
712 buf = new byte[len];
713 System.arraycopy(data, 0, buf, 0, len);
714 }
715
716 void putInt(int x, int off) {
717 buf[off + 0] = (byte)(x >> 24);
718 buf[off + 1] = (byte)(x >> 16);
|
1 /*
2 * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.jndi.dns;
27
28 import java.io.IOException;
29 import java.net.DatagramSocket;
30 import java.net.DatagramPacket;
31 import java.net.InetAddress;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.net.SocketTimeoutException;
35 import java.security.SecureRandom;
36 import javax.naming.*;
37
38 import java.util.Collections;
39 import java.util.Map;
40 import java.util.HashMap;
41
42 import sun.security.jca.JCAUtil;
43
44 // Some of this code began life as part of sun.javaos.net.DnsClient
45 // originally by sritchie@eng 1/96. It was first hacked up for JNDI
46 // use by caveh@eng 6/97.
47
48
49 /**
50 * The DnsClient class performs DNS client operations in support of DnsContext.
51 *
52 */
53
54 public class DnsClient {
67 private static final int FORMAT_ERROR = 1;
68 private static final int SERVER_FAILURE = 2;
69 private static final int NAME_ERROR = 3;
70 private static final int NOT_IMPL = 4;
71 private static final int REFUSED = 5;
72
73 private static final String[] rcodeDescription = {
74 "No error",
75 "DNS format error",
76 "DNS server failure",
77 "DNS name not found",
78 "DNS operation not supported",
79 "DNS service refused"
80 };
81
82 private static final int DEFAULT_PORT = 53;
83 private static final int TRANSACTION_ID_BOUND = 0x10000;
84 private static final SecureRandom random = JCAUtil.getSecureRandom();
85 private InetAddress[] servers;
86 private int[] serverPorts;
87 private int timeout; // initial timeout on UDP and TCP queries in ms
88 private int retries; // number of UDP retries
89
90 private final Object udpSocketLock = new Object();
91 private static final DNSDatagramSocketFactory factory =
92 new DNSDatagramSocketFactory(random);
93
94 // Requests sent
95 private Map<Integer, ResourceRecord> reqs;
96
97 // Responses received
98 private Map<Integer, byte[]> resps;
99
100 //-------------------------------------------------------------------------
101
102 /*
103 * Each server is of the form "server[:port]". IPv6 literal host names
104 * include delimiting brackets.
105 * "timeout" is the initial timeout interval (in ms) for queries,
106 * and "retries" gives the number of retries per server.
107 */
108 public DnsClient(String[] servers, int timeout, int retries)
109 throws NamingException {
110 this.timeout = timeout;
111 this.retries = retries;
112 this.servers = new InetAddress[servers.length];
113 serverPorts = new int[servers.length];
114
115 for (int i = 0; i < servers.length; i++) {
116
117 // Is optional port given?
118 int colon = servers[i].indexOf(':',
119 servers[i].indexOf(']') + 1);
120
121 serverPorts[i] = (colon < 0)
122 ? DEFAULT_PORT
123 : Integer.parseInt(servers[i].substring(colon + 1));
124 String server = (colon < 0)
125 ? servers[i]
222 if (msg == null) {
223 if (resps.size() > 0) {
224 msg = lookupResponse(xid);
225 }
226 if (msg == null) { // try next server or retry
227 continue;
228 }
229 }
230 Header hdr = new Header(msg, msg.length);
231
232 if (auth && !hdr.authoritative) {
233 caughtException = new NameNotFoundException(
234 "DNS response not authoritative");
235 doNotRetry[i] = true;
236 continue;
237 }
238 if (hdr.truncated) { // message is truncated -- try TCP
239
240 // Try each server, starting with the one that just
241 // provided the truncated message.
242 int retryTimeout = (timeout * (1 << retry));
243 for (int j = 0; j < servers.length; j++) {
244 int ij = (i + j) % servers.length;
245 if (doNotRetry[ij]) {
246 continue;
247 }
248 try {
249 Tcp tcp =
250 new Tcp(servers[ij], serverPorts[ij], retryTimeout);
251 byte[] msg2;
252 try {
253 msg2 = doTcpQuery(tcp, pkt);
254 } finally {
255 tcp.close();
256 }
257 Header hdr2 = new Header(msg2, msg2.length);
258 if (hdr2.query) {
259 throw new CommunicationException(
260 "DNS error: expecting response");
261 }
262 checkResponseCode(hdr2);
263
264 if (!auth || hdr2.authoritative) {
265 // Got a valid response
266 hdr = hdr2;
267 msg = msg2;
268 break;
269 } else {
270 doNotRetry[ij] = true;
313 throw (NamingException) caughtException;
314 }
315 // A network timeout or other error occurred.
316 NamingException ne = new CommunicationException("DNS error");
317 ne.setRootCause(caughtException);
318 throw ne;
319 }
320
321 ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
322 throws NamingException {
323
324 int xid = random.nextInt(TRANSACTION_ID_BOUND);
325
326 Packet pkt = makeQueryPacket(zone, xid, qclass,
327 ResourceRecord.QTYPE_AXFR, recursion);
328 Exception caughtException = null;
329
330 // Try each name server.
331 for (int i = 0; i < servers.length; i++) {
332 try {
333 Tcp tcp = new Tcp(servers[i], serverPorts[i], timeout);
334 byte[] msg;
335 try {
336 msg = doTcpQuery(tcp, pkt);
337 Header hdr = new Header(msg, msg.length);
338 // Check only rcode as per
339 // draft-ietf-dnsext-axfr-clarify-04
340 checkResponseCode(hdr);
341 ResourceRecords rrs =
342 new ResourceRecords(msg, msg.length, hdr, true);
343 if (rrs.getFirstAnsType() != ResourceRecord.TYPE_SOA) {
344 throw new CommunicationException(
345 "DNS error: zone xfer doesn't begin with SOA");
346 }
347
348 if (rrs.answer.size() == 1 ||
349 rrs.getLastAnsType() != ResourceRecord.TYPE_SOA) {
350 // The response is split into multiple DNS messages.
351 do {
352 msg = continueTcpQuery(tcp);
353 if (msg == null) {
448
449 int len = pkt.length();
450 // Send 2-byte message length, then send message.
451 tcp.out.write(len >> 8);
452 tcp.out.write(len);
453 tcp.out.write(pkt.getData(), 0, len);
454 tcp.out.flush();
455
456 byte[] msg = continueTcpQuery(tcp);
457 if (msg == null) {
458 throw new IOException("DNS error: no response");
459 }
460 return msg;
461 }
462
463 /*
464 * Returns the next DNS message from the TCP socket, or null on EOF.
465 */
466 private byte[] continueTcpQuery(Tcp tcp) throws IOException {
467
468 int lenHi = tcp.read(); // high-order byte of response length
469 if (lenHi == -1) {
470 return null; // EOF
471 }
472 int lenLo = tcp.read(); // low-order byte of response length
473 if (lenLo == -1) {
474 throw new IOException("Corrupted DNS response: bad length");
475 }
476 int len = (lenHi << 8) | lenLo;
477 byte[] msg = new byte[len];
478 int pos = 0; // next unfilled position in msg
479 while (len > 0) {
480 int n = tcp.read(msg, pos, len);
481 if (n == -1) {
482 throw new IOException(
483 "Corrupted DNS response: too little data");
484 }
485 len -= n;
486 pos += n;
487 }
488 return msg;
489 }
490
491 private Packet makeQueryPacket(DnsName fqdn, int xid,
492 int qclass, int qtype, boolean recursion) {
493 int qnameLen = fqdn.getOctets();
494 int pktLen = DNS_HDR_SIZE + qnameLen + 4;
495 Packet pkt = new Packet(pktLen);
496
497 short flags = recursion ? Header.RD_BIT : 0;
498
499 pkt.putShort(xid, IDENT_OFFSET);
500 pkt.putShort(flags, FLAGS_OFFSET);
668 case FORMAT_ERROR:
669 default:
670 throw new NamingException(msg);
671 }
672 }
673
674 //-------------------------------------------------------------------------
675
676 private static final boolean debug = false;
677
678 private static void dprint(String mess) {
679 if (debug) {
680 System.err.println("DNS: " + mess);
681 }
682 }
683
684 }
685
686 class Tcp {
687
688 private final Socket sock;
689 private final java.io.InputStream in;
690 final java.io.OutputStream out;
691 private int timeoutLeft;
692
693 Tcp(InetAddress server, int port, int timeout) throws IOException {
694 sock = new Socket();
695 try {
696 long start = System.currentTimeMillis();
697 sock.connect(new InetSocketAddress(server, port), timeout);
698 timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start));
699 if (timeoutLeft <= 0)
700 throw new SocketTimeoutException();
701
702 sock.setTcpNoDelay(true);
703 out = new java.io.BufferedOutputStream(sock.getOutputStream());
704 in = new java.io.BufferedInputStream(sock.getInputStream());
705 } catch (Exception e) {
706 try {
707 sock.close();
708 } catch (IOException ex) {
709 e.addSuppressed(ex);
710 }
711 throw e;
712 }
713 }
714
715 void close() throws IOException {
716 sock.close();
717 }
718
719 private interface SocketReadOp {
720 int read() throws IOException;
721 }
722
723 private int readWithTimeout(SocketReadOp reader) throws IOException {
724 if (timeoutLeft <= 0)
725 throw new SocketTimeoutException();
726
727 sock.setSoTimeout(timeoutLeft);
728 long start = System.currentTimeMillis();
729 try {
730 return reader.read();
731 }
732 finally {
733 timeoutLeft -= System.currentTimeMillis() - start;
734 }
735 }
736
737 int read() throws IOException {
738 return readWithTimeout(() -> in.read());
739 }
740
741 int read(byte b[], int off, int len) throws IOException {
742 return readWithTimeout(() -> in.read(b, off, len));
743 }
744 }
745
746 /*
747 * javaos emulation -cj
748 */
749 class Packet {
750 byte buf[];
751
752 Packet(int len) {
753 buf = new byte[len];
754 }
755
756 Packet(byte data[], int len) {
757 buf = new byte[len];
758 System.arraycopy(data, 0, buf, 0, len);
759 }
760
761 void putInt(int x, int off) {
762 buf[off + 0] = (byte)(x >> 24);
763 buf[off + 1] = (byte)(x >> 16);
|