< prev index next >

src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java

Print this page


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


< prev index next >