mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
738
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java
Normal file
738
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java
Normal file
|
@ -0,0 +1,738 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.SecureRandom;
|
||||
import javax.naming.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
|
||||
// Some of this code began life as part of sun.javaos.net.DnsClient
|
||||
// originally by sritchie@eng 1/96. It was first hacked up for JNDI
|
||||
// use by caveh@eng 6/97.
|
||||
|
||||
|
||||
/**
|
||||
* The DnsClient class performs DNS client operations in support of DnsContext.
|
||||
*
|
||||
*/
|
||||
|
||||
public class DnsClient {
|
||||
|
||||
// DNS packet header field offsets
|
||||
private static final int IDENT_OFFSET = 0;
|
||||
private static final int FLAGS_OFFSET = 2;
|
||||
private static final int NUMQ_OFFSET = 4;
|
||||
private static final int NUMANS_OFFSET = 6;
|
||||
private static final int NUMAUTH_OFFSET = 8;
|
||||
private static final int NUMADD_OFFSET = 10;
|
||||
private static final int DNS_HDR_SIZE = 12;
|
||||
|
||||
// DNS response codes
|
||||
private static final int NO_ERROR = 0;
|
||||
private static final int FORMAT_ERROR = 1;
|
||||
private static final int SERVER_FAILURE = 2;
|
||||
private static final int NAME_ERROR = 3;
|
||||
private static final int NOT_IMPL = 4;
|
||||
private static final int REFUSED = 5;
|
||||
|
||||
private static final String[] rcodeDescription = {
|
||||
"No error",
|
||||
"DNS format error",
|
||||
"DNS server failure",
|
||||
"DNS name not found",
|
||||
"DNS operation not supported",
|
||||
"DNS service refused"
|
||||
};
|
||||
|
||||
private static final int DEFAULT_PORT = 53;
|
||||
private static final int TRANSACTION_ID_BOUND = 0x10000;
|
||||
private static final SecureRandom random = JCAUtil.getSecureRandom();
|
||||
private InetAddress[] servers;
|
||||
private int[] serverPorts;
|
||||
private int timeout; // initial timeout on UDP queries in ms
|
||||
private int retries; // number of UDP retries
|
||||
|
||||
private DatagramSocket udpSocket;
|
||||
|
||||
// Requests sent
|
||||
private Map<Integer, ResourceRecord> reqs;
|
||||
|
||||
// Responses received
|
||||
private Map<Integer, byte[]> resps;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Each server is of the form "server[:port]". IPv6 literal host names
|
||||
* include delimiting brackets.
|
||||
* "timeout" is the initial timeout interval (in ms) for UDP queries,
|
||||
* and "retries" gives the number of retries per server.
|
||||
*/
|
||||
public DnsClient(String[] servers, int timeout, int retries)
|
||||
throws NamingException {
|
||||
this.timeout = timeout;
|
||||
this.retries = retries;
|
||||
try {
|
||||
udpSocket = new DatagramSocket();
|
||||
} catch (java.net.SocketException e) {
|
||||
NamingException ne = new ConfigurationException();
|
||||
ne.setRootCause(e);
|
||||
throw ne;
|
||||
}
|
||||
|
||||
this.servers = new InetAddress[servers.length];
|
||||
serverPorts = new int[servers.length];
|
||||
|
||||
for (int i = 0; i < servers.length; i++) {
|
||||
|
||||
// Is optional port given?
|
||||
int colon = servers[i].indexOf(':',
|
||||
servers[i].indexOf(']') + 1);
|
||||
|
||||
serverPorts[i] = (colon < 0)
|
||||
? DEFAULT_PORT
|
||||
: Integer.parseInt(servers[i].substring(colon + 1));
|
||||
String server = (colon < 0)
|
||||
? servers[i]
|
||||
: servers[i].substring(0, colon);
|
||||
try {
|
||||
this.servers[i] = InetAddress.getByName(server);
|
||||
} catch (java.net.UnknownHostException e) {
|
||||
NamingException ne = new ConfigurationException(
|
||||
"Unknown DNS server: " + server);
|
||||
ne.setRootCause(e);
|
||||
throw ne;
|
||||
}
|
||||
}
|
||||
reqs = Collections.synchronizedMap(
|
||||
new HashMap<Integer, ResourceRecord>());
|
||||
resps = Collections.synchronizedMap(new HashMap<Integer, byte[]>());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
close();
|
||||
}
|
||||
|
||||
// A lock to access the request and response queues in tandem.
|
||||
private Object queuesLock = new Object();
|
||||
|
||||
public void close() {
|
||||
udpSocket.close();
|
||||
synchronized (queuesLock) {
|
||||
reqs.clear();
|
||||
resps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If recursion is true, recursion is requested on the query.
|
||||
* If auth is true, only authoritative responses are accepted; other
|
||||
* responses throw NameNotFoundException.
|
||||
*/
|
||||
ResourceRecords query(DnsName fqdn, int qclass, int qtype,
|
||||
boolean recursion, boolean auth)
|
||||
throws NamingException {
|
||||
|
||||
int xid;
|
||||
Packet pkt;
|
||||
ResourceRecord collision;
|
||||
|
||||
do {
|
||||
// Generate a random transaction ID
|
||||
xid = random.nextInt(TRANSACTION_ID_BOUND);
|
||||
pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
|
||||
|
||||
// enqueue the outstanding request
|
||||
collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(),
|
||||
pkt.length(), Header.HEADER_SIZE, true, false));
|
||||
|
||||
} while (collision != null);
|
||||
|
||||
Exception caughtException = null;
|
||||
boolean[] doNotRetry = new boolean[servers.length];
|
||||
|
||||
try {
|
||||
//
|
||||
// The UDP retry strategy is to try the 1st server, and then
|
||||
// each server in order. If no answer, double the timeout
|
||||
// and try each server again.
|
||||
//
|
||||
for (int retry = 0; retry < retries; retry++) {
|
||||
|
||||
// Try each name server.
|
||||
for (int i = 0; i < servers.length; i++) {
|
||||
if (doNotRetry[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// send the request packet and wait for a response.
|
||||
try {
|
||||
if (debug) {
|
||||
dprint("SEND ID (" + (retry + 1) + "): " + xid);
|
||||
}
|
||||
|
||||
byte[] msg = null;
|
||||
msg = doUdpQuery(pkt, servers[i], serverPorts[i],
|
||||
retry, xid);
|
||||
//
|
||||
// If the matching response is not got within the
|
||||
// given timeout, check if the response was enqueued
|
||||
// by some other thread, if not proceed with the next
|
||||
// server or retry.
|
||||
//
|
||||
if (msg == null) {
|
||||
if (resps.size() > 0) {
|
||||
msg = lookupResponse(xid);
|
||||
}
|
||||
if (msg == null) { // try next server or retry
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Header hdr = new Header(msg, msg.length);
|
||||
|
||||
if (auth && !hdr.authoritative) {
|
||||
caughtException = new NameNotFoundException(
|
||||
"DNS response not authoritative");
|
||||
doNotRetry[i] = true;
|
||||
continue;
|
||||
}
|
||||
if (hdr.truncated) { // message is truncated -- try TCP
|
||||
|
||||
// Try each server, starting with the one that just
|
||||
// provided the truncated message.
|
||||
for (int j = 0; j < servers.length; j++) {
|
||||
int ij = (i + j) % servers.length;
|
||||
if (doNotRetry[ij]) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Tcp tcp =
|
||||
new Tcp(servers[ij], serverPorts[ij]);
|
||||
byte[] msg2;
|
||||
try {
|
||||
msg2 = doTcpQuery(tcp, pkt);
|
||||
} finally {
|
||||
tcp.close();
|
||||
}
|
||||
Header hdr2 = new Header(msg2, msg2.length);
|
||||
if (hdr2.query) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: expecting response");
|
||||
}
|
||||
checkResponseCode(hdr2);
|
||||
|
||||
if (!auth || hdr2.authoritative) {
|
||||
// Got a valid response
|
||||
hdr = hdr2;
|
||||
msg = msg2;
|
||||
break;
|
||||
} else {
|
||||
doNotRetry[ij] = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Try next server, or use UDP response
|
||||
}
|
||||
} // servers
|
||||
}
|
||||
return new ResourceRecords(msg, msg.length, hdr, false);
|
||||
|
||||
} catch (IOException e) {
|
||||
if (debug) {
|
||||
dprint("Caught IOException:" + e);
|
||||
}
|
||||
if (caughtException == null) {
|
||||
caughtException = e;
|
||||
}
|
||||
// Use reflection to allow pre-1.4 compilation.
|
||||
// This won't be needed much longer.
|
||||
if (e.getClass().getName().equals(
|
||||
"java.net.PortUnreachableException")) {
|
||||
doNotRetry[i] = true;
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// This is authoritative, so return immediately
|
||||
throw e;
|
||||
} catch (CommunicationException e) {
|
||||
if (caughtException == null) {
|
||||
caughtException = e;
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
if (caughtException == null) {
|
||||
caughtException = e;
|
||||
}
|
||||
doNotRetry[i] = true;
|
||||
}
|
||||
} // servers
|
||||
} // retries
|
||||
|
||||
} finally {
|
||||
reqs.remove(xid); // cleanup
|
||||
}
|
||||
|
||||
if (caughtException instanceof NamingException) {
|
||||
throw (NamingException) caughtException;
|
||||
}
|
||||
// A network timeout or other error occurred.
|
||||
NamingException ne = new CommunicationException("DNS error");
|
||||
ne.setRootCause(caughtException);
|
||||
throw ne;
|
||||
}
|
||||
|
||||
ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
|
||||
throws NamingException {
|
||||
|
||||
int xid = random.nextInt(TRANSACTION_ID_BOUND);
|
||||
|
||||
Packet pkt = makeQueryPacket(zone, xid, qclass,
|
||||
ResourceRecord.QTYPE_AXFR, recursion);
|
||||
Exception caughtException = null;
|
||||
|
||||
// Try each name server.
|
||||
for (int i = 0; i < servers.length; i++) {
|
||||
try {
|
||||
Tcp tcp = new Tcp(servers[i], serverPorts[i]);
|
||||
byte[] msg;
|
||||
try {
|
||||
msg = doTcpQuery(tcp, pkt);
|
||||
Header hdr = new Header(msg, msg.length);
|
||||
// Check only rcode as per
|
||||
// draft-ietf-dnsext-axfr-clarify-04
|
||||
checkResponseCode(hdr);
|
||||
ResourceRecords rrs =
|
||||
new ResourceRecords(msg, msg.length, hdr, true);
|
||||
if (rrs.getFirstAnsType() != ResourceRecord.TYPE_SOA) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: zone xfer doesn't begin with SOA");
|
||||
}
|
||||
|
||||
if (rrs.answer.size() == 1 ||
|
||||
rrs.getLastAnsType() != ResourceRecord.TYPE_SOA) {
|
||||
// The response is split into multiple DNS messages.
|
||||
do {
|
||||
msg = continueTcpQuery(tcp);
|
||||
if (msg == null) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: incomplete zone transfer");
|
||||
}
|
||||
hdr = new Header(msg, msg.length);
|
||||
checkResponseCode(hdr);
|
||||
rrs.add(msg, msg.length, hdr);
|
||||
} while (rrs.getLastAnsType() !=
|
||||
ResourceRecord.TYPE_SOA);
|
||||
}
|
||||
|
||||
// Delete the duplicate SOA record.
|
||||
rrs.answer.removeElementAt(rrs.answer.size() - 1);
|
||||
return rrs;
|
||||
|
||||
} finally {
|
||||
tcp.close();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
caughtException = e;
|
||||
} catch (NameNotFoundException e) {
|
||||
throw e;
|
||||
} catch (NamingException e) {
|
||||
caughtException = e;
|
||||
}
|
||||
}
|
||||
if (caughtException instanceof NamingException) {
|
||||
throw (NamingException) caughtException;
|
||||
}
|
||||
NamingException ne = new CommunicationException(
|
||||
"DNS error during zone transfer");
|
||||
ne.setRootCause(caughtException);
|
||||
throw ne;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to retrieve a UDP packet matching the given xid
|
||||
* received within the timeout.
|
||||
* If a packet with different xid is received, the received packet
|
||||
* is enqueued with the corresponding xid in 'resps'.
|
||||
*/
|
||||
private byte[] doUdpQuery(Packet pkt, InetAddress server,
|
||||
int port, int retry, int xid)
|
||||
throws IOException, NamingException {
|
||||
|
||||
int minTimeout = 50; // msec after which there are no retries.
|
||||
|
||||
synchronized (udpSocket) {
|
||||
DatagramPacket opkt = new DatagramPacket(
|
||||
pkt.getData(), pkt.length(), server, port);
|
||||
DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
|
||||
// Packets may only be sent to or received from this server address
|
||||
udpSocket.connect(server, port);
|
||||
int pktTimeout = (timeout * (1 << retry));
|
||||
try {
|
||||
udpSocket.send(opkt);
|
||||
|
||||
// timeout remaining after successive 'receive()'
|
||||
int timeoutLeft = pktTimeout;
|
||||
int cnt = 0;
|
||||
do {
|
||||
if (debug) {
|
||||
cnt++;
|
||||
dprint("Trying RECEIVE(" +
|
||||
cnt + ") retry(" + (retry + 1) +
|
||||
") for:" + xid + " sock-timeout:" +
|
||||
timeoutLeft + " ms.");
|
||||
}
|
||||
udpSocket.setSoTimeout(timeoutLeft);
|
||||
long start = System.currentTimeMillis();
|
||||
udpSocket.receive(ipkt);
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
byte[] data = ipkt.getData();
|
||||
if (isMatchResponse(data, xid)) {
|
||||
return data;
|
||||
}
|
||||
timeoutLeft = pktTimeout - ((int) (end - start));
|
||||
} while (timeoutLeft > minTimeout);
|
||||
|
||||
} finally {
|
||||
udpSocket.disconnect();
|
||||
}
|
||||
return null; // no matching packet received within the timeout
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a TCP query, and returns the first DNS message in the response.
|
||||
*/
|
||||
private byte[] doTcpQuery(Tcp tcp, Packet pkt) throws IOException {
|
||||
|
||||
int len = pkt.length();
|
||||
// Send 2-byte message length, then send message.
|
||||
tcp.out.write(len >> 8);
|
||||
tcp.out.write(len);
|
||||
tcp.out.write(pkt.getData(), 0, len);
|
||||
tcp.out.flush();
|
||||
|
||||
byte[] msg = continueTcpQuery(tcp);
|
||||
if (msg == null) {
|
||||
throw new IOException("DNS error: no response");
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the next DNS message from the TCP socket, or null on EOF.
|
||||
*/
|
||||
private byte[] continueTcpQuery(Tcp tcp) throws IOException {
|
||||
|
||||
int lenHi = tcp.in.read(); // high-order byte of response length
|
||||
if (lenHi == -1) {
|
||||
return null; // EOF
|
||||
}
|
||||
int lenLo = tcp.in.read(); // low-order byte of response length
|
||||
if (lenLo == -1) {
|
||||
throw new IOException("Corrupted DNS response: bad length");
|
||||
}
|
||||
int len = (lenHi << 8) | lenLo;
|
||||
byte[] msg = new byte[len];
|
||||
int pos = 0; // next unfilled position in msg
|
||||
while (len > 0) {
|
||||
int n = tcp.in.read(msg, pos, len);
|
||||
if (n == -1) {
|
||||
throw new IOException(
|
||||
"Corrupted DNS response: too little data");
|
||||
}
|
||||
len -= n;
|
||||
pos += n;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
private Packet makeQueryPacket(DnsName fqdn, int xid,
|
||||
int qclass, int qtype, boolean recursion) {
|
||||
int qnameLen = fqdn.getOctets();
|
||||
int pktLen = DNS_HDR_SIZE + qnameLen + 4;
|
||||
Packet pkt = new Packet(pktLen);
|
||||
|
||||
short flags = recursion ? Header.RD_BIT : 0;
|
||||
|
||||
pkt.putShort(xid, IDENT_OFFSET);
|
||||
pkt.putShort(flags, FLAGS_OFFSET);
|
||||
pkt.putShort(1, NUMQ_OFFSET);
|
||||
pkt.putShort(0, NUMANS_OFFSET);
|
||||
pkt.putInt(0, NUMAUTH_OFFSET);
|
||||
|
||||
makeQueryName(fqdn, pkt, DNS_HDR_SIZE);
|
||||
pkt.putShort(qtype, DNS_HDR_SIZE + qnameLen);
|
||||
pkt.putShort(qclass, DNS_HDR_SIZE + qnameLen + 2);
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
// Builds a query name in pkt according to the RFC spec.
|
||||
private void makeQueryName(DnsName fqdn, Packet pkt, int off) {
|
||||
|
||||
// Loop through labels, least-significant first.
|
||||
for (int i = fqdn.size() - 1; i >= 0; i--) {
|
||||
String label = fqdn.get(i);
|
||||
int len = label.length();
|
||||
|
||||
pkt.putByte(len, off++);
|
||||
for (int j = 0; j < len; j++) {
|
||||
pkt.putByte(label.charAt(j), off++);
|
||||
}
|
||||
}
|
||||
if (!fqdn.hasRootLabel()) {
|
||||
pkt.putByte(0, off);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
private byte[] lookupResponse(Integer xid) throws NamingException {
|
||||
//
|
||||
// Check the queued responses: some other thread in between
|
||||
// received the response for this request.
|
||||
//
|
||||
if (debug) {
|
||||
dprint("LOOKUP for: " + xid +
|
||||
"\tResponse Q:" + resps);
|
||||
}
|
||||
byte[] pkt;
|
||||
if ((pkt = resps.get(xid)) != null) {
|
||||
checkResponseCode(new Header(pkt, pkt.length));
|
||||
synchronized (queuesLock) {
|
||||
resps.remove(xid);
|
||||
reqs.remove(xid);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
dprint("FOUND (" + Thread.currentThread() +
|
||||
") for:" + xid);
|
||||
}
|
||||
}
|
||||
return pkt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the header of an incoming DNS response.
|
||||
* Returns true if it matches the given xid and throws a naming
|
||||
* exception, if appropriate, based on the response code.
|
||||
*
|
||||
* Also checks that the domain name, type and class in the response
|
||||
* match those in the original query.
|
||||
*/
|
||||
private boolean isMatchResponse(byte[] pkt, int xid)
|
||||
throws NamingException {
|
||||
|
||||
Header hdr = new Header(pkt, pkt.length);
|
||||
if (hdr.query) {
|
||||
throw new CommunicationException("DNS error: expecting response");
|
||||
}
|
||||
|
||||
if (!reqs.containsKey(xid)) { // already received, ignore the response
|
||||
return false;
|
||||
}
|
||||
|
||||
// common case- the request sent matches the subsequent response read
|
||||
if (hdr.xid == xid) {
|
||||
if (debug) {
|
||||
dprint("XID MATCH:" + xid);
|
||||
}
|
||||
checkResponseCode(hdr);
|
||||
if (!hdr.query && hdr.numQuestions == 1) {
|
||||
|
||||
ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
|
||||
Header.HEADER_SIZE, true, false);
|
||||
|
||||
// Retrieve the original query
|
||||
ResourceRecord query = reqs.get(xid);
|
||||
int qtype = query.getType();
|
||||
int qclass = query.getRrclass();
|
||||
DnsName qname = query.getName();
|
||||
|
||||
// Check that the type/class/name in the query section of the
|
||||
// response match those in the original query
|
||||
if ((qtype == ResourceRecord.QTYPE_STAR ||
|
||||
qtype == rr.getType()) &&
|
||||
(qclass == ResourceRecord.QCLASS_STAR ||
|
||||
qclass == rr.getRrclass()) &&
|
||||
qname.equals(rr.getName())) {
|
||||
|
||||
if (debug) {
|
||||
dprint("MATCH NAME:" + qname + " QTYPE:" + qtype +
|
||||
" QCLASS:" + qclass);
|
||||
}
|
||||
|
||||
// Remove the response for the xid if received by some other
|
||||
// thread.
|
||||
synchronized (queuesLock) {
|
||||
resps.remove(xid);
|
||||
reqs.remove(xid);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if (debug) {
|
||||
dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype +
|
||||
" QCLASS:" + qclass);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// xid mis-match: enqueue the response, it may belong to some other
|
||||
// thread that has not yet had a chance to read its response.
|
||||
// enqueue only the first response, responses for retries are ignored.
|
||||
//
|
||||
synchronized (queuesLock) {
|
||||
if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
|
||||
resps.put(hdr.xid, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
dprint("NO-MATCH SEND ID:" +
|
||||
xid + " RECVD ID:" + hdr.xid +
|
||||
" Response Q:" + resps +
|
||||
" Reqs size:" + reqs.size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Throws an exception if appropriate for the response code of a
|
||||
* given header.
|
||||
*/
|
||||
private void checkResponseCode(Header hdr) throws NamingException {
|
||||
|
||||
int rcode = hdr.rcode;
|
||||
if (rcode == NO_ERROR) {
|
||||
return;
|
||||
}
|
||||
String msg = (rcode < rcodeDescription.length)
|
||||
? rcodeDescription[rcode]
|
||||
: "DNS error";
|
||||
msg += " [response code " + rcode + "]";
|
||||
|
||||
switch (rcode) {
|
||||
case SERVER_FAILURE:
|
||||
throw new ServiceUnavailableException(msg);
|
||||
case NAME_ERROR:
|
||||
throw new NameNotFoundException(msg);
|
||||
case NOT_IMPL:
|
||||
case REFUSED:
|
||||
throw new OperationNotSupportedException(msg);
|
||||
case FORMAT_ERROR:
|
||||
default:
|
||||
throw new NamingException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
private static final boolean debug = false;
|
||||
|
||||
private static void dprint(String mess) {
|
||||
if (debug) {
|
||||
System.err.println("DNS: " + mess);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Tcp {
|
||||
|
||||
private Socket sock;
|
||||
java.io.InputStream in;
|
||||
java.io.OutputStream out;
|
||||
|
||||
Tcp(InetAddress server, int port) throws IOException {
|
||||
sock = new Socket(server, port);
|
||||
sock.setTcpNoDelay(true);
|
||||
out = new java.io.BufferedOutputStream(sock.getOutputStream());
|
||||
in = new java.io.BufferedInputStream(sock.getInputStream());
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
sock.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* javaos emulation -cj
|
||||
*/
|
||||
class Packet {
|
||||
byte buf[];
|
||||
|
||||
Packet(int len) {
|
||||
buf = new byte[len];
|
||||
}
|
||||
|
||||
Packet(byte data[], int len) {
|
||||
buf = new byte[len];
|
||||
System.arraycopy(data, 0, buf, 0, len);
|
||||
}
|
||||
|
||||
void putInt(int x, int off) {
|
||||
buf[off + 0] = (byte)(x >> 24);
|
||||
buf[off + 1] = (byte)(x >> 16);
|
||||
buf[off + 2] = (byte)(x >> 8);
|
||||
buf[off + 3] = (byte)x;
|
||||
}
|
||||
|
||||
void putShort(int x, int off) {
|
||||
buf[off + 0] = (byte)(x >> 8);
|
||||
buf[off + 1] = (byte)x;
|
||||
}
|
||||
|
||||
void putByte(int x, int off) {
|
||||
buf[off] = (byte)x;
|
||||
}
|
||||
|
||||
void putBytes(byte src[], int src_offset, int dst_offset, int len) {
|
||||
System.arraycopy(src, src_offset, buf, dst_offset, len);
|
||||
}
|
||||
|
||||
int length() {
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return buf;
|
||||
}
|
||||
}
|
1089
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java
Normal file
1089
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.*;
|
||||
|
||||
import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
import sun.net.dns.ResolverConfiguration; // available since 1.4.1
|
||||
|
||||
|
||||
/**
|
||||
* A DnsContextFactory serves as the initial context factory for DNS.
|
||||
*
|
||||
* <p> When an initial context is being created, the environment
|
||||
* property "java.naming.provider.url" should contain a DNS pseudo-URL
|
||||
* (see DnsUrl) or a space-separated list of them. Multiple URLs must
|
||||
* all have the same domain value.
|
||||
* If the property is not set, the default "dns:" is used.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public class DnsContextFactory implements InitialContextFactory {
|
||||
|
||||
private static final String DEFAULT_URL = "dns:";
|
||||
private static final int DEFAULT_PORT = 53;
|
||||
|
||||
|
||||
public Context getInitialContext(Hashtable<?,?> env) throws NamingException {
|
||||
if (env == null) {
|
||||
env = new Hashtable<>(5);
|
||||
}
|
||||
return urlToContext(getInitCtxUrl(env), env);
|
||||
}
|
||||
|
||||
public static DnsContext getContext(String domain,
|
||||
String[] servers, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
return new DnsContext(domain, servers, env);
|
||||
}
|
||||
|
||||
/*
|
||||
* "urls" are used to determine the servers, but any domain
|
||||
* components are overridden by "domain".
|
||||
*/
|
||||
public static DnsContext getContext(String domain,
|
||||
DnsUrl[] urls, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
String[] servers = serversForUrls(urls);
|
||||
DnsContext ctx = getContext(domain, servers, env);
|
||||
if (platformServersUsed(urls)) {
|
||||
ctx.setProviderUrl(constructProviderUrl(domain, servers));
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public for use by product test suite.
|
||||
*/
|
||||
public static boolean platformServersAvailable() {
|
||||
return !filterNameServers(
|
||||
ResolverConfiguration.open().nameservers(), true
|
||||
).isEmpty();
|
||||
}
|
||||
|
||||
private static Context urlToContext(String url, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
DnsUrl[] urls;
|
||||
try {
|
||||
urls = DnsUrl.fromList(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new ConfigurationException(e.getMessage());
|
||||
}
|
||||
if (urls.length == 0) {
|
||||
throw new ConfigurationException(
|
||||
"Invalid DNS pseudo-URL(s): " + url);
|
||||
}
|
||||
String domain = urls[0].getDomain();
|
||||
|
||||
// If multiple urls, all must have the same domain.
|
||||
for (int i = 1; i < urls.length; i++) {
|
||||
if (!domain.equalsIgnoreCase(urls[i].getDomain())) {
|
||||
throw new ConfigurationException(
|
||||
"Conflicting domains: " + url);
|
||||
}
|
||||
}
|
||||
return getContext(domain, urls, env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all the servers specified in a set of URLs.
|
||||
* If a URL has no host (or port), the servers configured on the
|
||||
* underlying platform are used if possible. If no configured
|
||||
* servers can be found, then fall back to the old behavior of
|
||||
* using "localhost".
|
||||
* There must be at least one URL.
|
||||
*/
|
||||
private static String[] serversForUrls(DnsUrl[] urls)
|
||||
throws NamingException {
|
||||
|
||||
if (urls.length == 0) {
|
||||
throw new ConfigurationException("DNS pseudo-URL required");
|
||||
}
|
||||
|
||||
List<String> servers = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
String server = urls[i].getHost();
|
||||
int port = urls[i].getPort();
|
||||
|
||||
if (server == null && port < 0) {
|
||||
// No server or port given, so look to underlying platform.
|
||||
// ResolverConfiguration does some limited caching, so the
|
||||
// following is reasonably efficient even if called rapid-fire.
|
||||
List<String> platformServers = filterNameServers(
|
||||
ResolverConfiguration.open().nameservers(), false);
|
||||
if (!platformServers.isEmpty()) {
|
||||
servers.addAll(platformServers);
|
||||
continue; // on to next URL (if any, which is unlikely)
|
||||
}
|
||||
}
|
||||
|
||||
if (server == null) {
|
||||
server = "localhost";
|
||||
}
|
||||
servers.add((port < 0)
|
||||
? server
|
||||
: server + ":" + port);
|
||||
}
|
||||
return servers.toArray(new String[servers.size()]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if serversForUrls(urls) would make use of servers
|
||||
* from the underlying platform.
|
||||
*/
|
||||
private static boolean platformServersUsed(DnsUrl[] urls) {
|
||||
if (!platformServersAvailable()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
if (urls[i].getHost() == null &&
|
||||
urls[i].getPort() < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a value for the PROVIDER_URL property (space-separated URL
|
||||
* Strings) that reflects the given domain and servers.
|
||||
* Each server is of the form "server[:port]".
|
||||
* There must be at least one server.
|
||||
* IPv6 literal host names include delimiting brackets.
|
||||
*/
|
||||
private static String constructProviderUrl(String domain,
|
||||
String[] servers) {
|
||||
String path = "";
|
||||
if (!domain.equals(".")) {
|
||||
try {
|
||||
path = "/" + UrlUtil.encode(domain, "ISO-8859-1");
|
||||
} catch (java.io.UnsupportedEncodingException e) {
|
||||
// assert false : "ISO-Latin-1 charset unavailable";
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < servers.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append("dns://").append(servers[i]).append(path);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads environment to find URL(s) of initial context.
|
||||
* Default URL is "dns:".
|
||||
*/
|
||||
private static String getInitCtxUrl(Hashtable<?,?> env) {
|
||||
String url = (String) env.get(Context.PROVIDER_URL);
|
||||
return ((url != null) ? url : DEFAULT_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any DNS server that's not permitted to access
|
||||
* @param input the input server[:port] list, must not be null
|
||||
* @param oneIsEnough return output once there exists one ok
|
||||
* @return the filtered list, all non-permitted input removed
|
||||
*/
|
||||
private static List<String> filterNameServers(List<String> input, boolean oneIsEnough) {
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security == null || input == null || input.isEmpty()) {
|
||||
return input;
|
||||
} else {
|
||||
List<String> output = new ArrayList<>();
|
||||
for (String platformServer: input) {
|
||||
int colon = platformServer.indexOf(':',
|
||||
platformServer.indexOf(']') + 1);
|
||||
|
||||
int p = (colon < 0)
|
||||
? DEFAULT_PORT
|
||||
: Integer.parseInt(
|
||||
platformServer.substring(colon + 1));
|
||||
String s = (colon < 0)
|
||||
? platformServer
|
||||
: platformServer.substring(0, colon);
|
||||
try {
|
||||
security.checkConnect(s, p);
|
||||
output.add(platformServer);
|
||||
if (oneIsEnough) {
|
||||
return output;
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
602
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsName.java
Normal file
602
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsName.java
Normal file
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.naming.*;
|
||||
|
||||
|
||||
/**
|
||||
* {@code DnsName} implements compound names for DNS as specified by
|
||||
* RFCs 1034 and 1035, and as updated and clarified by RFCs 1123 and 2181.
|
||||
*
|
||||
* <p> The labels in a domain name correspond to JNDI atomic names.
|
||||
* Each label must be less than 64 octets in length, and only the
|
||||
* optional root label at the end of the name may be 0 octets long.
|
||||
* The sum of the lengths of all labels in a name, plus the number of
|
||||
* non-root labels plus 1, must be less than 256. The textual
|
||||
* representation of a domain name consists of the labels, escaped as
|
||||
* needed, dot-separated, and ordered right-to-left.
|
||||
*
|
||||
* <p> A label consists of a sequence of octets, each of which may
|
||||
* have any value from 0 to 255.
|
||||
*
|
||||
* <p> <em>Host names</em> are a subset of domain names.
|
||||
* Their labels contain only ASCII letters, digits, and hyphens, and
|
||||
* none may begin or end with a hyphen. While names not conforming to
|
||||
* these rules may be valid domain names, they will not be usable by a
|
||||
* number of DNS applications, and should in most cases be avoided.
|
||||
*
|
||||
* <p> DNS does not specify an encoding (such as UTF-8) to use for
|
||||
* octets with non-ASCII values. As of this writing there is some
|
||||
* work going on in this area, but it is not yet finalized.
|
||||
* {@code DnsName} currently converts any non-ASCII octets into
|
||||
* characters using ISO-LATIN-1 encoding, in effect taking the
|
||||
* value of each octet and storing it directly into the low-order byte
|
||||
* of a Java character and <i>vice versa</i>. As a consequence, no
|
||||
* character in a DNS name will ever have a non-zero high-order byte.
|
||||
* When the work on internationalizing domain names has stabilized
|
||||
* (see for example <i>draft-ietf-idn-idna-10.txt</i>), {@code DnsName}
|
||||
* may be updated to conform to that work.
|
||||
*
|
||||
* <p> Backslash ({@code \}) is used as the escape character in the
|
||||
* textual representation of a domain name. The character sequence
|
||||
* `{@code \DDD}', where {@code DDD} is a 3-digit decimal number
|
||||
* (with leading zeros if needed), represents the octet whose value
|
||||
* is {@code DDD}. The character sequence `{@code \C}', where
|
||||
* {@code C} is a character other than {@code '0'} through
|
||||
* {@code '9'}, represents the octet whose value is that of
|
||||
* {@code C} (again using ISO-LATIN-1 encoding); this is particularly
|
||||
* useful for escaping {@code '.'} or backslash itself. Backslash is
|
||||
* otherwise not allowed in a domain name. Note that escape characters
|
||||
* are interpreted when a name is parsed. So, for example, the character
|
||||
* sequences `{@code S}', `{@code \S}', and `{@code \083}' each
|
||||
* represent the same one-octet name. The {@code toString()} method
|
||||
* does not generally insert escape sequences except where necessary.
|
||||
* If, however, the {@code DnsName} was constructed using unneeded
|
||||
* escapes, those escapes may appear in the {@code toString} result.
|
||||
*
|
||||
* <p> Atomic names passed as parameters to methods of
|
||||
* {@code DnsName}, and those returned by them, are unescaped. So,
|
||||
* for example, <code>(new DnsName()).add("a.b")</code> creates an
|
||||
* object representing the one-label domain name {@code a\.b}, and
|
||||
* calling {@code get(0)} on this object returns {@code "a.b"}.
|
||||
*
|
||||
* <p> While DNS names are case-preserving, comparisons between them
|
||||
* are case-insensitive. When comparing names containing non-ASCII
|
||||
* octets, {@code DnsName} uses case-insensitive comparison
|
||||
* between pairs of ASCII values, and exact binary comparison
|
||||
* otherwise.
|
||||
|
||||
* <p> A {@code DnsName} instance is not synchronized against
|
||||
* concurrent access by multiple threads.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public final class DnsName implements Name {
|
||||
|
||||
// If non-null, the domain name represented by this DnsName.
|
||||
private String domain = "";
|
||||
|
||||
// The labels of this domain name, as a list of strings. Index 0
|
||||
// corresponds to the leftmost (least significant) label: note that
|
||||
// this is the reverse of the ordering used by the Name interface.
|
||||
private ArrayList<String> labels = new ArrayList<>();
|
||||
|
||||
// The number of octets needed to carry this domain name in a DNS
|
||||
// packet. Equal to the sum of the lengths of each label, plus the
|
||||
// number of non-root labels, plus 1. Must remain less than 256.
|
||||
private short octets = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a {@code DnsName} representing the empty domain name.
|
||||
*/
|
||||
public DnsName() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code DnsName} representing a given domain name.
|
||||
*
|
||||
* @param name the domain name to parse
|
||||
* @throws InvalidNameException if {@code name} does not conform
|
||||
* to DNS syntax.
|
||||
*/
|
||||
public DnsName(String name) throws InvalidNameException {
|
||||
parse(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new DnsName with its name components initialized to
|
||||
* the components of "n" in the range [beg,end). Indexing is as
|
||||
* for the Name interface, with 0 being the most significant.
|
||||
*/
|
||||
private DnsName(DnsName n, int beg, int end) {
|
||||
// Compute indexes into "labels", which has least-significant label
|
||||
// at index 0 (opposite to the convention used for "beg" and "end").
|
||||
int b = n.size() - end;
|
||||
int e = n.size() - beg;
|
||||
labels.addAll(n.labels.subList(b, e));
|
||||
|
||||
if (size() == n.size()) {
|
||||
domain = n.domain;
|
||||
octets = n.octets;
|
||||
} else {
|
||||
for (String label: labels) {
|
||||
if (label.length() > 0) {
|
||||
octets += (short) (label.length() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
if (domain == null) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String label: labels) {
|
||||
if (buf.length() > 0 || label.length() == 0) {
|
||||
buf.append('.');
|
||||
}
|
||||
escape(buf, label);
|
||||
}
|
||||
domain = buf.toString();
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this domain name follow <em>host name</em> syntax?
|
||||
*/
|
||||
public boolean isHostName() {
|
||||
for (String label: labels) {
|
||||
if (!isHostNameLabel(label)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public short getOctets() {
|
||||
return octets;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return labels.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int h = 0;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
h = 31 * h + getKey(i).hashCode();
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Name) || (obj instanceof CompositeName)) {
|
||||
return false;
|
||||
}
|
||||
Name n = (Name) obj;
|
||||
return ((size() == n.size()) && // shortcut: do sizes differ?
|
||||
(compareTo(obj) == 0));
|
||||
}
|
||||
|
||||
public int compareTo(Object obj) {
|
||||
Name n = (Name) obj;
|
||||
return compareRange(0, size(), n); // never 0 if sizes differ
|
||||
}
|
||||
|
||||
public boolean startsWith(Name n) {
|
||||
return ((size() >= n.size()) &&
|
||||
(compareRange(0, n.size(), n) == 0));
|
||||
}
|
||||
|
||||
public boolean endsWith(Name n) {
|
||||
return ((size() >= n.size()) &&
|
||||
(compareRange(size() - n.size(), size(), n) == 0));
|
||||
}
|
||||
|
||||
public String get(int pos) {
|
||||
if (pos < 0 || pos >= size()) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
int i = size() - pos - 1; // index of "pos" component in "labels"
|
||||
return labels.get(i);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAll() {
|
||||
return new Enumeration<String>() {
|
||||
int pos = 0;
|
||||
public boolean hasMoreElements() {
|
||||
return (pos < size());
|
||||
}
|
||||
public String nextElement() {
|
||||
if (pos < size()) {
|
||||
return get(pos++);
|
||||
}
|
||||
throw new java.util.NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Name getPrefix(int pos) {
|
||||
return new DnsName(this, 0, pos);
|
||||
}
|
||||
|
||||
public Name getSuffix(int pos) {
|
||||
return new DnsName(this, pos, size());
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
return new DnsName(this, 0, size());
|
||||
}
|
||||
|
||||
public Object remove(int pos) {
|
||||
if (pos < 0 || pos >= size()) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
int i = size() - pos - 1; // index of element to remove in "labels"
|
||||
String label = labels.remove(i);
|
||||
int len = label.length();
|
||||
if (len > 0) {
|
||||
octets -= (short) (len + 1);
|
||||
}
|
||||
domain = null; // invalidate "domain"
|
||||
return label;
|
||||
}
|
||||
|
||||
public Name add(String comp) throws InvalidNameException {
|
||||
return add(size(), comp);
|
||||
}
|
||||
|
||||
public Name add(int pos, String comp) throws InvalidNameException {
|
||||
if (pos < 0 || pos > size()) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
// Check for empty labels: may have only one, and only at end.
|
||||
int len = comp.length();
|
||||
if ((pos > 0 && len == 0) ||
|
||||
(pos == 0 && hasRootLabel())) {
|
||||
throw new InvalidNameException(
|
||||
"Empty label must be the last label in a domain name");
|
||||
}
|
||||
// Check total name length.
|
||||
if (len > 0) {
|
||||
if (octets + len + 1 >= 256) {
|
||||
throw new InvalidNameException("Name too long");
|
||||
}
|
||||
octets += (short) (len + 1);
|
||||
}
|
||||
|
||||
int i = size() - pos; // index for insertion into "labels"
|
||||
verifyLabel(comp);
|
||||
labels.add(i, comp);
|
||||
|
||||
domain = null; // invalidate "domain"
|
||||
return this;
|
||||
}
|
||||
|
||||
public Name addAll(Name suffix) throws InvalidNameException {
|
||||
return addAll(size(), suffix);
|
||||
}
|
||||
|
||||
public Name addAll(int pos, Name n) throws InvalidNameException {
|
||||
if (n instanceof DnsName) {
|
||||
// "n" is a DnsName so we can insert it as a whole, rather than
|
||||
// verifying and inserting it component-by-component.
|
||||
// More code, but less work.
|
||||
DnsName dn = (DnsName) n;
|
||||
|
||||
if (dn.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
// Check for empty labels: may have only one, and only at end.
|
||||
if ((pos > 0 && dn.hasRootLabel()) ||
|
||||
(pos == 0 && hasRootLabel())) {
|
||||
throw new InvalidNameException(
|
||||
"Empty label must be the last label in a domain name");
|
||||
}
|
||||
|
||||
short newOctets = (short) (octets + dn.octets - 1);
|
||||
if (newOctets > 255) {
|
||||
throw new InvalidNameException("Name too long");
|
||||
}
|
||||
octets = newOctets;
|
||||
int i = size() - pos; // index for insertion into "labels"
|
||||
labels.addAll(i, dn.labels);
|
||||
|
||||
// Preserve "domain" if we're appending or prepending,
|
||||
// otherwise invalidate it.
|
||||
if (isEmpty()) {
|
||||
domain = dn.domain;
|
||||
} else if (domain == null || dn.domain == null) {
|
||||
domain = null;
|
||||
} else if (pos == 0) {
|
||||
domain += (dn.domain.equals(".") ? "" : ".") + dn.domain;
|
||||
} else if (pos == size()) {
|
||||
domain = dn.domain + (domain.equals(".") ? "" : ".") + domain;
|
||||
} else {
|
||||
domain = null;
|
||||
}
|
||||
|
||||
} else if (n instanceof CompositeName) {
|
||||
n = (DnsName) n; // force ClassCastException
|
||||
|
||||
} else { // "n" is a compound name, but not a DnsName.
|
||||
// Add labels least-significant first: sometimes more efficient.
|
||||
for (int i = n.size() - 1; i >= 0; i--) {
|
||||
add(pos, n.get(i));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
boolean hasRootLabel() {
|
||||
return (!isEmpty() &&
|
||||
get(0).equals(""));
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method for public comparison methods. Lexicographically
|
||||
* compares components of this name in the range [beg,end) with
|
||||
* all components of "n". Indexing is as for the Name interface,
|
||||
* with 0 being the most significant. Returns negative, zero, or
|
||||
* positive as these name components are less than, equal to, or
|
||||
* greater than those of "n".
|
||||
*/
|
||||
private int compareRange(int beg, int end, Name n) {
|
||||
if (n instanceof CompositeName) {
|
||||
n = (DnsName) n; // force ClassCastException
|
||||
}
|
||||
// Loop through labels, starting with most significant.
|
||||
int minSize = Math.min(end - beg, n.size());
|
||||
for (int i = 0; i < minSize; i++) {
|
||||
String label1 = get(i + beg);
|
||||
String label2 = n.get(i);
|
||||
|
||||
int j = size() - (i + beg) - 1; // index of label1 in "labels"
|
||||
// assert (label1 == labels.get(j));
|
||||
|
||||
int c = compareLabels(label1, label2);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return ((end - beg) - n.size()); // longer range wins
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a key suitable for hashing the label at index i.
|
||||
* Indexing is as for the Name interface, with 0 being the most
|
||||
* significant.
|
||||
*/
|
||||
String getKey(int i) {
|
||||
return keyForLabel(get(i));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parses a domain name, setting the values of instance vars accordingly.
|
||||
*/
|
||||
private void parse(String name) throws InvalidNameException {
|
||||
|
||||
StringBuilder label = new StringBuilder(); // label being parsed
|
||||
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
char c = name.charAt(i);
|
||||
|
||||
if (c == '\\') { // found an escape sequence
|
||||
c = getEscapedOctet(name, i++);
|
||||
if (isDigit(name.charAt(i))) { // sequence is \DDD
|
||||
i += 2; // consume remaining digits
|
||||
}
|
||||
label.append(c);
|
||||
|
||||
} else if (c != '.') { // an unescaped octet
|
||||
label.append(c);
|
||||
|
||||
} else { // found '.' separator
|
||||
add(0, label.toString()); // check syntax, then add label
|
||||
// to end of name
|
||||
label.delete(0, i); // clear buffer for next label
|
||||
}
|
||||
}
|
||||
|
||||
// If name is neither "." nor "", the octets (zero or more)
|
||||
// from the rightmost dot onward are now added as the final
|
||||
// label of the name. Those two are special cases in that for
|
||||
// all other domain names, the number of labels is one greater
|
||||
// than the number of dot separators.
|
||||
if (!name.equals("") && !name.equals(".")) {
|
||||
add(0, label.toString());
|
||||
}
|
||||
|
||||
domain = name; // do this last, since add() sets it to null
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns (as a char) the octet indicated by the escape sequence
|
||||
* at a given position within a domain name.
|
||||
* @throws InvalidNameException if a valid escape sequence is not found.
|
||||
*/
|
||||
private static char getEscapedOctet(String name, int pos)
|
||||
throws InvalidNameException {
|
||||
try {
|
||||
// assert (name.charAt(pos) == '\\');
|
||||
char c1 = name.charAt(++pos);
|
||||
if (isDigit(c1)) { // sequence is `\DDD'
|
||||
char c2 = name.charAt(++pos);
|
||||
char c3 = name.charAt(++pos);
|
||||
if (isDigit(c2) && isDigit(c3)) {
|
||||
return (char)
|
||||
((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));
|
||||
} else {
|
||||
throw new InvalidNameException(
|
||||
"Invalid escape sequence in " + name);
|
||||
}
|
||||
} else { // sequence is `\C'
|
||||
return c1;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new InvalidNameException(
|
||||
"Invalid escape sequence in " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that this label is valid.
|
||||
* @throws InvalidNameException if label is not valid.
|
||||
*/
|
||||
private static void verifyLabel(String label) throws InvalidNameException {
|
||||
if (label.length() > 63) {
|
||||
throw new InvalidNameException(
|
||||
"Label exceeds 63 octets: " + label);
|
||||
}
|
||||
// Check for two-byte characters.
|
||||
for (int i = 0; i < label.length(); i++) {
|
||||
char c = label.charAt(i);
|
||||
if ((c & 0xFF00) != 0) {
|
||||
throw new InvalidNameException(
|
||||
"Label has two-byte char: " + label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does this label conform to host name syntax?
|
||||
*/
|
||||
private static boolean isHostNameLabel(String label) {
|
||||
for (int i = 0; i < label.length(); i++) {
|
||||
char c = label.charAt(i);
|
||||
if (!isHostNameChar(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !(label.startsWith("-") || label.endsWith("-"));
|
||||
}
|
||||
|
||||
private static boolean isHostNameChar(char c) {
|
||||
return (c == '-' ||
|
||||
c >= 'a' && c <= 'z' ||
|
||||
c >= 'A' && c <= 'Z' ||
|
||||
c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
private static boolean isDigit(char c) {
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
/*
|
||||
* Append a label to buf, escaping as needed.
|
||||
*/
|
||||
private static void escape(StringBuilder buf, String label) {
|
||||
for (int i = 0; i < label.length(); i++) {
|
||||
char c = label.charAt(i);
|
||||
if (c == '.' || c == '\\') {
|
||||
buf.append('\\');
|
||||
}
|
||||
buf.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares two labels, ignoring case for ASCII values.
|
||||
* Returns negative, zero, or positive as the first label
|
||||
* is less than, equal to, or greater than the second.
|
||||
* See keyForLabel().
|
||||
*/
|
||||
private static int compareLabels(String label1, String label2) {
|
||||
int min = Math.min(label1.length(), label2.length());
|
||||
for (int i = 0; i < min; i++) {
|
||||
char c1 = label1.charAt(i);
|
||||
char c2 = label2.charAt(i);
|
||||
if (c1 >= 'A' && c1 <= 'Z') {
|
||||
c1 += 'a' - 'A'; // to lower case
|
||||
}
|
||||
if (c2 >= 'A' && c2 <= 'Z') {
|
||||
c2 += 'a' - 'A'; // to lower case
|
||||
}
|
||||
if (c1 != c2) {
|
||||
return (c1 - c2);
|
||||
}
|
||||
}
|
||||
return (label1.length() - label2.length()); // the longer one wins
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a key suitable for hashing a label. Two labels map to
|
||||
* the same key iff they are equal, taking possible case-folding
|
||||
* into account. See compareLabels().
|
||||
*/
|
||||
private static String keyForLabel(String label) {
|
||||
StringBuilder sb = new StringBuilder(label.length());
|
||||
for (int i = 0; i < label.length(); i++) {
|
||||
char c = label.charAt(i);
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
c += 'a' - 'A'; // to lower case
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes only the domain name string, for compactness and to avoid
|
||||
* any implementation dependency.
|
||||
*
|
||||
* @serialData The domain name string.
|
||||
*/
|
||||
private void writeObject(java.io.ObjectOutputStream s)
|
||||
throws java.io.IOException {
|
||||
s.writeObject(toString());
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
try {
|
||||
parse((String) s.readObject());
|
||||
} catch (InvalidNameException e) {
|
||||
// shouldn't happen
|
||||
throw new java.io.StreamCorruptedException(
|
||||
"Invalid name: " + domain);
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 7040187611324710271L;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import javax.naming.*;
|
||||
|
||||
|
||||
/**
|
||||
* A name parser for DNS names.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
class DnsNameParser implements NameParser {
|
||||
|
||||
public Name parse(String name) throws NamingException {
|
||||
return new DnsName(name);
|
||||
}
|
||||
|
||||
|
||||
// Every DnsNameParser is created equal.
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof DnsNameParser);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return DnsNameParser.class.hashCode() + 1;
|
||||
}
|
||||
}
|
119
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java
Normal file
119
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.sun.jndi.toolkit.url.Uri;
|
||||
import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
|
||||
|
||||
/**
|
||||
* A DnsUrl represents a DNS pseudo-URL of the form
|
||||
* <pre>
|
||||
* dns://[host][:port][/[domain]]
|
||||
* or
|
||||
* dns:[/][domain]
|
||||
* </pre>
|
||||
* The host names a DNS server. If the host is not provided, it
|
||||
* indicates that the underlying platform's DNS server(s) should be
|
||||
* used if possible, or that "localhost" should be used otherwise. If
|
||||
* the port is not provided, the DNS default port 53 will be used.
|
||||
* The domain indicates the domain name of the context, and is not
|
||||
* necessarily related to the domain of the server; if it is not
|
||||
* provided, the root domain "." is used. Special characters in
|
||||
* the domain name must be %-escaped as described in RFC 2396.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public class DnsUrl extends Uri {
|
||||
|
||||
private String domain; // domain name of the context
|
||||
|
||||
|
||||
/**
|
||||
* Given a space-separated list of DNS URLs, returns an array of DnsUrl
|
||||
* objects.
|
||||
*/
|
||||
public static DnsUrl[] fromList(String urlList)
|
||||
throws MalformedURLException {
|
||||
|
||||
DnsUrl[] urls = new DnsUrl[(urlList.length() + 1) / 2];
|
||||
int i = 0; // next available index in urls
|
||||
StringTokenizer st = new StringTokenizer(urlList, " ");
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
urls[i++] = new DnsUrl(st.nextToken());
|
||||
}
|
||||
DnsUrl[] trimmed = new DnsUrl[i];
|
||||
System.arraycopy(urls, 0, trimmed, 0, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public DnsUrl(String url) throws MalformedURLException {
|
||||
super(url);
|
||||
|
||||
if (!scheme.equals("dns")) {
|
||||
throw new MalformedURLException(
|
||||
url + " is not a valid DNS pseudo-URL");
|
||||
}
|
||||
|
||||
domain = path.startsWith("/")
|
||||
? path.substring(1)
|
||||
: path;
|
||||
domain = domain.equals("")
|
||||
? "."
|
||||
: UrlUtil.decode(domain);
|
||||
|
||||
// Debug
|
||||
// System.out.println("host=" + host + " port=" + port +
|
||||
// " domain=" + domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain of this URL, or "." if none is provided.
|
||||
* Never null.
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Debug
|
||||
public static void main(String args[]) throws MalformedURLException {
|
||||
DnsUrl[] urls = fromList(args[0]);
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
System.out.println(urls[i].toString());
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
126
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/Header.java
Normal file
126
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/Header.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import javax.naming.*;
|
||||
|
||||
|
||||
/**
|
||||
* The Header class represents the header of a DNS message.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
class Header {
|
||||
|
||||
static final int HEADER_SIZE = 12; // octets in a DNS header
|
||||
|
||||
// Masks and shift amounts for DNS header flag fields.
|
||||
static final short QR_BIT = (short) 0x8000;
|
||||
static final short OPCODE_MASK = (short) 0x7800;
|
||||
static final int OPCODE_SHIFT = 11;
|
||||
static final short AA_BIT = (short) 0x0400;
|
||||
static final short TC_BIT = (short) 0x0200;
|
||||
static final short RD_BIT = (short) 0x0100;
|
||||
static final short RA_BIT = (short) 0x0080;
|
||||
static final short RCODE_MASK = (short) 0x000F;
|
||||
|
||||
int xid; // ID: 16-bit query identifier
|
||||
boolean query; // QR: true if query, false if response
|
||||
int opcode; // OPCODE: 4-bit opcode
|
||||
boolean authoritative; // AA
|
||||
boolean truncated; // TC
|
||||
boolean recursionDesired; // RD
|
||||
boolean recursionAvail; // RA
|
||||
int rcode; // RCODE: 4-bit response code
|
||||
int numQuestions;
|
||||
int numAnswers;
|
||||
int numAuthorities;
|
||||
int numAdditionals;
|
||||
|
||||
/*
|
||||
* Returns a representation of a decoded DNS message header.
|
||||
* Does not modify or store a reference to the msg array.
|
||||
*/
|
||||
Header(byte[] msg, int msgLen) throws NamingException {
|
||||
decode(msg, msgLen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decodes a DNS message header. Does not modify or store a
|
||||
* reference to the msg array.
|
||||
*/
|
||||
private void decode(byte[] msg, int msgLen) throws NamingException {
|
||||
|
||||
try {
|
||||
int pos = 0; // current offset into msg
|
||||
|
||||
if (msgLen < HEADER_SIZE) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: corrupted message header");
|
||||
}
|
||||
|
||||
xid = getShort(msg, pos);
|
||||
pos += 2;
|
||||
|
||||
// Flags
|
||||
short flags = (short) getShort(msg, pos);
|
||||
pos += 2;
|
||||
query = (flags & QR_BIT) == 0;
|
||||
opcode = (flags & OPCODE_MASK) >>> OPCODE_SHIFT;
|
||||
authoritative = (flags & AA_BIT) != 0;
|
||||
truncated = (flags & TC_BIT) != 0;
|
||||
recursionDesired = (flags & RD_BIT) != 0;
|
||||
recursionAvail = (flags & RA_BIT) != 0;
|
||||
rcode = (flags & RCODE_MASK);
|
||||
|
||||
// RR counts
|
||||
numQuestions = getShort(msg, pos);
|
||||
pos += 2;
|
||||
numAnswers = getShort(msg, pos);
|
||||
pos += 2;
|
||||
numAuthorities = getShort(msg, pos);
|
||||
pos += 2;
|
||||
numAdditionals = getShort(msg, pos);
|
||||
pos += 2;
|
||||
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: corrupted message header");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 2-byte unsigned value at msg[pos]. The high
|
||||
* order byte comes first.
|
||||
*/
|
||||
private static int getShort(byte[] msg, int pos) {
|
||||
return (((msg[pos] & 0xFF) << 8) |
|
||||
(msg[pos + 1] & 0xFF));
|
||||
}
|
||||
}
|
156
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/NameNode.java
Normal file
156
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/NameNode.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
|
||||
/**
|
||||
* A NameNode represents a node in the DNS namespace. Each node
|
||||
* has a label, which is its name relative to its parent (so the
|
||||
* node at Sun.COM has label "Sun"). Each node has a hashtable of
|
||||
* children indexed by their labels converted to lower-case.
|
||||
*
|
||||
* <p> A node may be addressed from another by giving a DnsName
|
||||
* consisting of the sequence of labels from one node to the other.
|
||||
*
|
||||
* <p> Each node also has an {@code isZoneCut} flag, used to indicate
|
||||
* if the node is a zone cut. A zone cut is a node with an NS record
|
||||
* that is contained in one zone, but that actually belongs to a child zone.
|
||||
*
|
||||
* <p> All access is unsynchronized.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
class NameNode {
|
||||
|
||||
private String label; // name of this node relative to its
|
||||
// parent, or null for root of a tree
|
||||
private Hashtable<String,NameNode> children = null; // child nodes
|
||||
private boolean isZoneCut = false; // true if this node is a zone cut
|
||||
private int depth = 0; // depth in tree (0 for root)
|
||||
|
||||
NameNode(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a newly-allocated NameNode. Used to allocate new nodes
|
||||
* in a tree. Should be overridden in a subclass to return an object
|
||||
* of the subclass's type.
|
||||
*/
|
||||
protected NameNode newNameNode(String label) {
|
||||
return new NameNode(label);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of this node relative to its parent, or null for
|
||||
* the root of a tree.
|
||||
*/
|
||||
String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the depth of this node in the tree. The depth of the root
|
||||
* is 0.
|
||||
*/
|
||||
int depth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
boolean isZoneCut() {
|
||||
return isZoneCut;
|
||||
}
|
||||
|
||||
void setZoneCut(boolean isZoneCut) {
|
||||
this.isZoneCut = isZoneCut;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the children of this node, or null if there are none.
|
||||
* The caller must not modify the Hashtable returned.
|
||||
*/
|
||||
Hashtable<String,NameNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the child node given the hash key (the down-cased label)
|
||||
* for its name relative to this node, or null if there is no such
|
||||
* child.
|
||||
*/
|
||||
NameNode get(String key) {
|
||||
return (children != null)
|
||||
? children.get(key)
|
||||
: null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the node at the end of a path, or null if the
|
||||
* node does not exist.
|
||||
* The path is specified by the labels of {@code name}, beginning
|
||||
* at index idx.
|
||||
*/
|
||||
NameNode get(DnsName name, int idx) {
|
||||
NameNode node = this;
|
||||
for (int i = idx; i < name.size() && node != null; i++) {
|
||||
node = node.get(name.getKey(i));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the node at the end of a path, creating it and any
|
||||
* intermediate nodes as needed.
|
||||
* The path is specified by the labels of {@code name}, beginning
|
||||
* at index idx.
|
||||
*/
|
||||
NameNode add(DnsName name, int idx) {
|
||||
NameNode node = this;
|
||||
for (int i = idx; i < name.size(); i++) {
|
||||
String label = name.get(i);
|
||||
String key = name.getKey(i);
|
||||
|
||||
NameNode child = null;
|
||||
if (node.children == null) {
|
||||
node.children = new Hashtable<>();
|
||||
} else {
|
||||
child = node.children.get(key);
|
||||
}
|
||||
if (child == null) {
|
||||
child = newNameNode(label);
|
||||
child.depth = node.depth + 1;
|
||||
node.children.put(key, child);
|
||||
}
|
||||
node = child;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
190
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/Resolver.java
Normal file
190
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/Resolver.java
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import javax.naming.*;
|
||||
|
||||
|
||||
/**
|
||||
* The Resolver class performs DNS client operations in support of DnsContext.
|
||||
*
|
||||
* <p> Every DnsName instance passed to or returned from a method of
|
||||
* this class should be fully-qualified and contain a root label (an
|
||||
* empty component at position 0).
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
class Resolver {
|
||||
|
||||
private DnsClient dnsClient;
|
||||
private int timeout; // initial timeout on UDP queries in ms
|
||||
private int retries; // number of UDP retries
|
||||
|
||||
|
||||
/*
|
||||
* Constructs a new Resolver given its servers and timeout parameters.
|
||||
* Each server is of the form "server[:port]".
|
||||
* IPv6 literal host names include delimiting brackets.
|
||||
* There must be at least one server.
|
||||
* "timeout" is the initial timeout interval (in ms) for UDP queries,
|
||||
* and "retries" gives the number of retries per server.
|
||||
*/
|
||||
Resolver(String[] servers, int timeout, int retries)
|
||||
throws NamingException {
|
||||
this.timeout = timeout;
|
||||
this.retries = retries;
|
||||
dnsClient = new DnsClient(servers, timeout, retries);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
dnsClient.close();
|
||||
dnsClient = null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Queries resource records of a particular class and type for a
|
||||
* given domain name.
|
||||
* Useful values of rrclass are ResourceRecord.[Q]CLASS_xxx.
|
||||
* Useful values of rrtype are ResourceRecord.[Q]TYPE_xxx.
|
||||
* If recursion is true, recursion is requested on the query.
|
||||
* If auth is true, only authoritative responses are accepted.
|
||||
*/
|
||||
ResourceRecords query(DnsName fqdn, int rrclass, int rrtype,
|
||||
boolean recursion, boolean auth)
|
||||
throws NamingException {
|
||||
return dnsClient.query(fqdn, rrclass, rrtype, recursion, auth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Queries all resource records of a zone given its domain name and class.
|
||||
* If recursion is true, recursion is requested on the query to find
|
||||
* the name server (and also on the zone transfer, but it won't matter).
|
||||
*/
|
||||
ResourceRecords queryZone(DnsName zone, int rrclass, boolean recursion)
|
||||
throws NamingException {
|
||||
|
||||
DnsClient cl =
|
||||
new DnsClient(findNameServers(zone, recursion), timeout, retries);
|
||||
try {
|
||||
return cl.queryZone(zone, rrclass, recursion);
|
||||
} finally {
|
||||
cl.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the zone of a given domain name. The method is to look
|
||||
* for the first SOA record on the path from the given domain to
|
||||
* the root. This search may be partially bypassed if the zone's
|
||||
* SOA record is received in the authority section of a response.
|
||||
* If recursion is true, recursion is requested on any queries.
|
||||
*/
|
||||
DnsName findZoneName(DnsName fqdn, int rrclass, boolean recursion)
|
||||
throws NamingException {
|
||||
|
||||
fqdn = (DnsName) fqdn.clone();
|
||||
while (fqdn.size() > 1) { // while below root
|
||||
ResourceRecords rrs = null;
|
||||
try {
|
||||
rrs = query(fqdn, rrclass, ResourceRecord.TYPE_SOA,
|
||||
recursion, false);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw e;
|
||||
} catch (NamingException e) {
|
||||
// Ignore error and keep searching up the tree.
|
||||
}
|
||||
if (rrs != null) {
|
||||
if (rrs.answer.size() > 0) { // found zone's SOA
|
||||
return fqdn;
|
||||
}
|
||||
// Look for an SOA record giving the zone's top node.
|
||||
for (int i = 0; i < rrs.authority.size(); i++) {
|
||||
ResourceRecord rr = rrs.authority.elementAt(i);
|
||||
if (rr.getType() == ResourceRecord.TYPE_SOA) {
|
||||
DnsName zone = rr.getName();
|
||||
if (fqdn.endsWith(zone)) {
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fqdn.remove(fqdn.size() - 1); // one step rootward
|
||||
}
|
||||
return fqdn; // no SOA found below root, so
|
||||
// return root
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a zone's SOA record. Returns null if no SOA is found (in
|
||||
* which case "zone" is not actually a zone).
|
||||
* If recursion is true, recursion is requested on the query.
|
||||
*/
|
||||
ResourceRecord findSoa(DnsName zone, int rrclass, boolean recursion)
|
||||
throws NamingException {
|
||||
|
||||
ResourceRecords rrs = query(zone, rrclass, ResourceRecord.TYPE_SOA,
|
||||
recursion, false);
|
||||
for (int i = 0; i < rrs.answer.size(); i++) {
|
||||
ResourceRecord rr = rrs.answer.elementAt(i);
|
||||
if (rr.getType() == ResourceRecord.TYPE_SOA) {
|
||||
return rr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the name servers of a zone. {@code zone} is a fully-qualified
|
||||
* domain name at the top of a zone.
|
||||
* If recursion is true, recursion is requested on the query.
|
||||
*/
|
||||
private String[] findNameServers(DnsName zone, boolean recursion)
|
||||
throws NamingException {
|
||||
|
||||
// %%% As an optimization, could look in authority section of
|
||||
// findZoneName() response first.
|
||||
ResourceRecords rrs =
|
||||
query(zone, ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_NS,
|
||||
recursion, false);
|
||||
String[] ns = new String[rrs.answer.size()];
|
||||
for (int i = 0; i < ns.length; i++) {
|
||||
ResourceRecord rr = rrs.answer.elementAt(i);
|
||||
if (rr.getType() != ResourceRecord.TYPE_NS) {
|
||||
throw new CommunicationException("Corrupted DNS message");
|
||||
}
|
||||
ns[i] = (String) rr.getRdata();
|
||||
|
||||
// Server name will be passed to InetAddress.getByName(), which
|
||||
// may not be able to handle a trailing dot.
|
||||
// assert ns[i].endsWith(".");
|
||||
ns[i] = ns[i].substring(0, ns[i].length() - 1);
|
||||
}
|
||||
return ns;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,616 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
import javax.naming.CommunicationException;
|
||||
import javax.naming.InvalidNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
|
||||
/**
|
||||
* The ResourceRecord class represents a DNS resource record.
|
||||
* The string format is based on the master file representation in
|
||||
* RFC 1035.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public class ResourceRecord {
|
||||
|
||||
/*
|
||||
* Resource record type codes
|
||||
*/
|
||||
static final int TYPE_A = 1;
|
||||
static final int TYPE_NS = 2;
|
||||
static final int TYPE_CNAME = 5;
|
||||
static final int TYPE_SOA = 6;
|
||||
static final int TYPE_PTR = 12;
|
||||
static final int TYPE_HINFO = 13;
|
||||
static final int TYPE_MX = 15;
|
||||
static final int TYPE_TXT = 16;
|
||||
static final int TYPE_AAAA = 28;
|
||||
static final int TYPE_SRV = 33;
|
||||
static final int TYPE_NAPTR = 35;
|
||||
static final int QTYPE_AXFR = 252; // zone transfer
|
||||
static final int QTYPE_STAR = 255; // query type "*"
|
||||
|
||||
/*
|
||||
* Mapping from resource record type codes to type name strings.
|
||||
*/
|
||||
static final String rrTypeNames[] = {
|
||||
null, "A", "NS", null, null,
|
||||
"CNAME", "SOA", null, null, null,
|
||||
null, null, "PTR", "HINFO", null,
|
||||
"MX", "TXT", null, null, null,
|
||||
null, null, null, null, null,
|
||||
null, null, null, "AAAA", null,
|
||||
null, null, null, "SRV", null,
|
||||
"NAPTR"
|
||||
};
|
||||
|
||||
/*
|
||||
* Resource record class codes
|
||||
*/
|
||||
static final int CLASS_INTERNET = 1;
|
||||
static final int CLASS_HESIOD = 2;
|
||||
static final int QCLASS_STAR = 255; // query class "*"
|
||||
|
||||
/*
|
||||
* Mapping from resource record type codes to class name strings.
|
||||
*/
|
||||
static final String rrClassNames[] = {
|
||||
null, "IN", null, null, "HS"
|
||||
};
|
||||
|
||||
/*
|
||||
* Maximum number of compression references in labels.
|
||||
* Used to detect compression loops.
|
||||
*/
|
||||
private static final int MAXIMUM_COMPRESSION_REFERENCES = 16;
|
||||
|
||||
byte[] msg; // DNS message
|
||||
int msgLen; // msg size (in octets)
|
||||
boolean qSection; // true if this RR is part of question section
|
||||
// and therefore has no ttl or rdata
|
||||
int offset; // offset of RR w/in msg
|
||||
int rrlen; // number of octets in encoded RR
|
||||
DnsName name; // name field of RR, including root label
|
||||
int rrtype; // type field of RR
|
||||
String rrtypeName; // name of rrtype
|
||||
int rrclass; // class field of RR
|
||||
String rrclassName; // name of rrclass
|
||||
int ttl = 0; // ttl field of RR
|
||||
int rdlen = 0; // number of octets of rdata
|
||||
Object rdata = null; // rdata -- most are String, unknown are byte[]
|
||||
|
||||
|
||||
/*
|
||||
* Constructs a new ResourceRecord. The encoded data of the DNS
|
||||
* message is contained in msg; data for this RR begins at msg[offset].
|
||||
* If qSection is true this RR is part of a question section. It's
|
||||
* not a true resource record in that case, but is treated as if it
|
||||
* were a shortened one (with no ttl or rdata). If decodeRdata is
|
||||
* false, the rdata is not decoded (and getRdata() will return null)
|
||||
* unless this is an SOA record.
|
||||
*
|
||||
* @throws CommunicationException if a decoded domain name isn't valid.
|
||||
* @throws ArrayIndexOutOfBoundsException given certain other corrupt data.
|
||||
*/
|
||||
ResourceRecord(byte[] msg, int msgLen, int offset,
|
||||
boolean qSection, boolean decodeRdata)
|
||||
throws CommunicationException {
|
||||
|
||||
this.msg = msg;
|
||||
this.msgLen = msgLen;
|
||||
this.offset = offset;
|
||||
this.qSection = qSection;
|
||||
decode(decodeRdata);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String text = name + " " + rrclassName + " " + rrtypeName;
|
||||
if (!qSection) {
|
||||
text += " " + ttl + " " +
|
||||
((rdata != null) ? rdata : "[n/a]");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name field of this RR, including the root label.
|
||||
*/
|
||||
public DnsName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of octets in the encoded RR.
|
||||
*/
|
||||
public int size() {
|
||||
return rrlen;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return rrtype;
|
||||
}
|
||||
|
||||
public int getRrclass() {
|
||||
return rrclass;
|
||||
}
|
||||
|
||||
public Object getRdata() {
|
||||
return rdata;
|
||||
}
|
||||
|
||||
|
||||
public static String getTypeName(int rrtype) {
|
||||
return valueToName(rrtype, rrTypeNames);
|
||||
}
|
||||
|
||||
public static int getType(String typeName) {
|
||||
return nameToValue(typeName, rrTypeNames);
|
||||
}
|
||||
|
||||
public static String getRrclassName(int rrclass) {
|
||||
return valueToName(rrclass, rrClassNames);
|
||||
}
|
||||
|
||||
public static int getRrclass(String className) {
|
||||
return nameToValue(className, rrClassNames);
|
||||
}
|
||||
|
||||
private static String valueToName(int val, String[] names) {
|
||||
String name = null;
|
||||
if ((val > 0) && (val < names.length)) {
|
||||
name = names[val];
|
||||
} else if (val == QTYPE_STAR) { // QTYPE_STAR == QCLASS_STAR
|
||||
name = "*";
|
||||
}
|
||||
if (name == null) {
|
||||
name = Integer.toString(val);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static int nameToValue(String name, String[] names) {
|
||||
if (name.equals("")) {
|
||||
return -1; // invalid name
|
||||
} else if (name.equals("*")) {
|
||||
return QTYPE_STAR; // QTYPE_STAR == QCLASS_STAR
|
||||
}
|
||||
if (Character.isDigit(name.charAt(0))) {
|
||||
try {
|
||||
return Integer.parseInt(name);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < names.length; i++) {
|
||||
if ((names[i] != null) &&
|
||||
name.equalsIgnoreCase(names[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // unknown name
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares two SOA record serial numbers using 32-bit serial number
|
||||
* arithmetic as defined in RFC 1982. Serial numbers are unsigned
|
||||
* 32-bit quantities. Returns a negative, zero, or positive value
|
||||
* as the first serial number is less than, equal to, or greater
|
||||
* than the second. If the serial numbers are not comparable the
|
||||
* result is undefined. Note that the relation is not transitive.
|
||||
*/
|
||||
public static int compareSerialNumbers(long s1, long s2) {
|
||||
long diff = s2 - s1;
|
||||
if (diff == 0) {
|
||||
return 0;
|
||||
} else if ((diff > 0 && diff <= 0x7FFFFFFF) ||
|
||||
(diff < 0 && -diff > 0x7FFFFFFF)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decodes the binary format of the RR.
|
||||
* May throw ArrayIndexOutOfBoundsException given corrupt data.
|
||||
*/
|
||||
private void decode(boolean decodeRdata) throws CommunicationException {
|
||||
int pos = offset; // index of next unread octet
|
||||
|
||||
name = new DnsName(); // NAME
|
||||
pos = decodeName(pos, name);
|
||||
|
||||
rrtype = getUShort(pos); // TYPE
|
||||
rrtypeName = (rrtype < rrTypeNames.length)
|
||||
? rrTypeNames[rrtype]
|
||||
: null;
|
||||
if (rrtypeName == null) {
|
||||
rrtypeName = Integer.toString(rrtype);
|
||||
}
|
||||
pos += 2;
|
||||
|
||||
rrclass = getUShort(pos); // CLASS
|
||||
rrclassName = (rrclass < rrClassNames.length)
|
||||
? rrClassNames[rrclass]
|
||||
: null;
|
||||
if (rrclassName == null) {
|
||||
rrclassName = Integer.toString(rrclass);
|
||||
}
|
||||
pos += 2;
|
||||
|
||||
if (!qSection) {
|
||||
ttl = getInt(pos); // TTL
|
||||
pos += 4;
|
||||
|
||||
rdlen = getUShort(pos); // RDLENGTH
|
||||
pos += 2;
|
||||
|
||||
rdata = (decodeRdata || // RDATA
|
||||
(rrtype == TYPE_SOA))
|
||||
? decodeRdata(pos)
|
||||
: null;
|
||||
if (rdata instanceof DnsName) {
|
||||
rdata = rdata.toString();
|
||||
}
|
||||
pos += rdlen;
|
||||
}
|
||||
|
||||
rrlen = pos - offset;
|
||||
|
||||
msg = null; // free up for GC
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 1-byte unsigned value at msg[pos].
|
||||
*/
|
||||
private int getUByte(int pos) {
|
||||
return (msg[pos] & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 2-byte unsigned value at msg[pos]. The high
|
||||
* order byte comes first.
|
||||
*/
|
||||
private int getUShort(int pos) {
|
||||
return (((msg[pos] & 0xFF) << 8) |
|
||||
(msg[pos + 1] & 0xFF));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 4-byte signed value at msg[pos]. The high
|
||||
* order byte comes first.
|
||||
*/
|
||||
private int getInt(int pos) {
|
||||
return ((getUShort(pos) << 16) | getUShort(pos + 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 4-byte unsigned value at msg[pos]. The high
|
||||
* order byte comes first.
|
||||
*/
|
||||
private long getUInt(int pos) {
|
||||
return (getInt(pos) & 0xffffffffL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name encoded at msg[pos], including the root label.
|
||||
*/
|
||||
private DnsName decodeName(int pos) throws CommunicationException {
|
||||
DnsName n = new DnsName();
|
||||
decodeName(pos, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepends to "n" the domain name encoded at msg[pos], including the root
|
||||
* label. Returns the index into "msg" following the name.
|
||||
*/
|
||||
private int decodeName(int pos, DnsName n) throws CommunicationException {
|
||||
int endPos = -1;
|
||||
int level = 0;
|
||||
try {
|
||||
while (true) {
|
||||
if (level > MAXIMUM_COMPRESSION_REFERENCES)
|
||||
throw new IOException("Too many compression references");
|
||||
int typeAndLen = msg[pos] & 0xFF;
|
||||
if (typeAndLen == 0) { // end of name
|
||||
++pos;
|
||||
n.add(0, "");
|
||||
break;
|
||||
} else if (typeAndLen <= 63) { // regular label
|
||||
++pos;
|
||||
n.add(0, new String(msg, pos, typeAndLen,
|
||||
StandardCharsets.ISO_8859_1));
|
||||
pos += typeAndLen;
|
||||
} else if ((typeAndLen & 0xC0) == 0xC0) { // name compression
|
||||
++level;
|
||||
endPos = pos + 2;
|
||||
pos = getUShort(pos) & 0x3FFF;
|
||||
} else
|
||||
throw new IOException("Invalid label type: " + typeAndLen);
|
||||
}
|
||||
} catch (IOException | InvalidNameException e) {
|
||||
CommunicationException ce =new CommunicationException(
|
||||
"DNS error: malformed packet");
|
||||
ce.initCause(e);
|
||||
throw ce;
|
||||
}
|
||||
if (endPos == -1)
|
||||
endPos = pos;
|
||||
return endPos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata encoded at msg[pos]. The format is dependent
|
||||
* on the rrtype and rrclass values, which have already been set.
|
||||
* The length of the encoded data is rdlen, which has already been
|
||||
* set.
|
||||
* The rdata of records with unknown type/class combinations is
|
||||
* returned in a newly-allocated byte array.
|
||||
*/
|
||||
private Object decodeRdata(int pos) throws CommunicationException {
|
||||
if (rrclass == CLASS_INTERNET) {
|
||||
switch (rrtype) {
|
||||
case TYPE_A:
|
||||
return decodeA(pos);
|
||||
case TYPE_AAAA:
|
||||
return decodeAAAA(pos);
|
||||
case TYPE_CNAME:
|
||||
case TYPE_NS:
|
||||
case TYPE_PTR:
|
||||
return decodeName(pos);
|
||||
case TYPE_MX:
|
||||
return decodeMx(pos);
|
||||
case TYPE_SOA:
|
||||
return decodeSoa(pos);
|
||||
case TYPE_SRV:
|
||||
return decodeSrv(pos);
|
||||
case TYPE_NAPTR:
|
||||
return decodeNaptr(pos);
|
||||
case TYPE_TXT:
|
||||
return decodeTxt(pos);
|
||||
case TYPE_HINFO:
|
||||
return decodeHinfo(pos);
|
||||
}
|
||||
}
|
||||
// Unknown RR type/class
|
||||
byte[] rd = new byte[rdlen];
|
||||
System.arraycopy(msg, pos, rd, 0, rdlen);
|
||||
return rd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an MX record that is encoded at msg[pos].
|
||||
*/
|
||||
private String decodeMx(int pos) throws CommunicationException {
|
||||
int preference = getUShort(pos);
|
||||
pos += 2;
|
||||
DnsName name = decodeName(pos);
|
||||
return (preference + " " + name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an SOA record that is encoded at msg[pos].
|
||||
*/
|
||||
private String decodeSoa(int pos) throws CommunicationException {
|
||||
DnsName mname = new DnsName();
|
||||
pos = decodeName(pos, mname);
|
||||
DnsName rname = new DnsName();
|
||||
pos = decodeName(pos, rname);
|
||||
|
||||
long serial = getUInt(pos);
|
||||
pos += 4;
|
||||
long refresh = getUInt(pos);
|
||||
pos += 4;
|
||||
long retry = getUInt(pos);
|
||||
pos += 4;
|
||||
long expire = getUInt(pos);
|
||||
pos += 4;
|
||||
long minimum = getUInt(pos); // now used as negative TTL
|
||||
pos += 4;
|
||||
|
||||
return (mname + " " + rname + " " + serial + " " +
|
||||
refresh + " " + retry + " " + expire + " " + minimum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an SRV record that is encoded at msg[pos].
|
||||
* See RFC 2782.
|
||||
*/
|
||||
private String decodeSrv(int pos) throws CommunicationException {
|
||||
int priority = getUShort(pos);
|
||||
pos += 2;
|
||||
int weight = getUShort(pos);
|
||||
pos += 2;
|
||||
int port = getUShort(pos);
|
||||
pos += 2;
|
||||
DnsName target = decodeName(pos);
|
||||
return (priority + " " + weight + " " + port + " " + target);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an NAPTR record that is encoded at msg[pos].
|
||||
* See RFC 2915.
|
||||
*/
|
||||
private String decodeNaptr(int pos) throws CommunicationException {
|
||||
int order = getUShort(pos);
|
||||
pos += 2;
|
||||
int preference = getUShort(pos);
|
||||
pos += 2;
|
||||
StringBuffer flags = new StringBuffer();
|
||||
pos += decodeCharString(pos, flags);
|
||||
StringBuffer services = new StringBuffer();
|
||||
pos += decodeCharString(pos, services);
|
||||
StringBuffer regexp = new StringBuffer(rdlen);
|
||||
pos += decodeCharString(pos, regexp);
|
||||
DnsName replacement = decodeName(pos);
|
||||
|
||||
return (order + " " + preference + " " + flags + " " +
|
||||
services + " " + regexp + " " + replacement);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of a TXT record that is encoded at msg[pos].
|
||||
* The rdata consists of one or more <character-string>s.
|
||||
*/
|
||||
private String decodeTxt(int pos) {
|
||||
StringBuffer buf = new StringBuffer(rdlen);
|
||||
int end = pos + rdlen;
|
||||
while (pos < end) {
|
||||
pos += decodeCharString(pos, buf);
|
||||
if (pos < end) {
|
||||
buf.append(' ');
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an HINFO record that is encoded at msg[pos].
|
||||
* The rdata consists of two <character-string>s.
|
||||
*/
|
||||
private String decodeHinfo(int pos) {
|
||||
StringBuffer buf = new StringBuffer(rdlen);
|
||||
pos += decodeCharString(pos, buf);
|
||||
buf.append(' ');
|
||||
pos += decodeCharString(pos, buf);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Decodes the <character-string> at msg[pos] and adds it to buf.
|
||||
* If the string contains one of the meta-characters ' ', '\\', or
|
||||
* '"', then the result is quoted and any embedded '\\' or '"'
|
||||
* chars are escaped with '\\'. Empty strings are also quoted.
|
||||
* Returns the size of the encoded string, including the initial
|
||||
* length octet.
|
||||
*/
|
||||
private int decodeCharString(int pos, StringBuffer buf) {
|
||||
int start = buf.length(); // starting index of this string
|
||||
int len = getUByte(pos++); // encoded string length
|
||||
boolean quoted = (len == 0); // quote string if empty
|
||||
for (int i = 0; i < len; i++) {
|
||||
int c = getUByte(pos++);
|
||||
quoted |= (c == ' ');
|
||||
if ((c == '\\') || (c == '"')) {
|
||||
quoted = true;
|
||||
buf.append('\\');
|
||||
}
|
||||
buf.append((char) c);
|
||||
}
|
||||
if (quoted) {
|
||||
buf.insert(start, '"');
|
||||
buf.append('"');
|
||||
}
|
||||
return (len + 1); // size includes initial octet
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an A record, in dotted-decimal format,
|
||||
* that is encoded at msg[pos].
|
||||
*/
|
||||
private String decodeA(int pos) {
|
||||
return ((msg[pos] & 0xff) + "." +
|
||||
(msg[pos + 1] & 0xff) + "." +
|
||||
(msg[pos + 2] & 0xff) + "." +
|
||||
(msg[pos + 3] & 0xff));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the rdata of an AAAA record, in colon-separated format,
|
||||
* that is encoded at msg[pos]. For example: 4321:0:1:2:3:4:567:89ab.
|
||||
* See RFCs 1886 and 2373.
|
||||
*/
|
||||
private String decodeAAAA(int pos) {
|
||||
int[] addr6 = new int[8]; // the unsigned 16-bit words of the address
|
||||
for (int i = 0; i < 8; i++) {
|
||||
addr6[i] = getUShort(pos);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
// Find longest sequence of two or more zeros, to compress them.
|
||||
int curBase = -1;
|
||||
int curLen = 0;
|
||||
int bestBase = -1;
|
||||
int bestLen = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (addr6[i] == 0) {
|
||||
if (curBase == -1) { // new sequence
|
||||
curBase = i;
|
||||
curLen = 1;
|
||||
} else { // extend sequence
|
||||
++curLen;
|
||||
if ((curLen >= 2) && (curLen > bestLen)) {
|
||||
bestBase = curBase;
|
||||
bestLen = curLen;
|
||||
}
|
||||
}
|
||||
} else { // not in sequence
|
||||
curBase = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If addr begins with at least 6 zeros and is not :: or ::1,
|
||||
// or with 5 zeros followed by 0xffff, use the text format for
|
||||
// IPv4-compatible or IPv4-mapped addresses.
|
||||
if (bestBase == 0) {
|
||||
if ((bestLen == 6) ||
|
||||
((bestLen == 7) && (addr6[7] > 1))) {
|
||||
return ("::" + decodeA(pos - 4));
|
||||
} else if ((bestLen == 5) && (addr6[5] == 0xffff)) {
|
||||
return ("::ffff:" + decodeA(pos - 4));
|
||||
}
|
||||
}
|
||||
|
||||
// If bestBase != -1, compress zeros in [bestBase, bestBase+bestLen)
|
||||
boolean compress = (bestBase != -1);
|
||||
|
||||
StringBuilder sb = new StringBuilder(40);
|
||||
if (bestBase == 0) {
|
||||
sb.append(':');
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (!compress || (i < bestBase) || (i >= bestBase + bestLen)) {
|
||||
sb.append(Integer.toHexString(addr6[i]));
|
||||
if (i < 7) {
|
||||
sb.append(':');
|
||||
}
|
||||
} else if (compress && (i == bestBase)) { // first compressed zero
|
||||
sb.append(':');
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.util.Vector;
|
||||
import javax.naming.*;
|
||||
|
||||
|
||||
/**
|
||||
* The ResourceRecords class represents the resource records in the
|
||||
* four sections of a DNS message.
|
||||
*
|
||||
* The additional records section is currently ignored.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
class ResourceRecords {
|
||||
|
||||
// Four sections: question, answer, authority, additional.
|
||||
// The question section is treated as being made up of (shortened)
|
||||
// resource records, although this isn't technically how it's defined.
|
||||
Vector<ResourceRecord> question = new Vector<>();
|
||||
Vector<ResourceRecord> answer = new Vector<>();
|
||||
Vector<ResourceRecord> authority = new Vector<>();
|
||||
Vector<ResourceRecord> additional = new Vector<>();
|
||||
|
||||
/*
|
||||
* True if these resource records are from a zone transfer. In
|
||||
* that case only answer records are read (as per
|
||||
* draft-ietf-dnsext-axfr-clarify-02.txt). Also, the rdata of
|
||||
* those answer records is not decoded (for efficiency) except
|
||||
* for SOA records.
|
||||
*/
|
||||
boolean zoneXfer;
|
||||
|
||||
/*
|
||||
* Returns a representation of the resource records in a DNS message.
|
||||
* Does not modify or store a reference to the msg array.
|
||||
*/
|
||||
ResourceRecords(byte[] msg, int msgLen, Header hdr, boolean zoneXfer)
|
||||
throws NamingException {
|
||||
if (zoneXfer) {
|
||||
answer.ensureCapacity(8192); // an arbitrary "large" number
|
||||
}
|
||||
this.zoneXfer = zoneXfer;
|
||||
add(msg, msgLen, hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the type field of the first answer record, or -1 if
|
||||
* there are no answer records.
|
||||
*/
|
||||
int getFirstAnsType() {
|
||||
if (answer.size() == 0) {
|
||||
return -1;
|
||||
}
|
||||
return answer.firstElement().getType();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the type field of the last answer record, or -1 if
|
||||
* there are no answer records.
|
||||
*/
|
||||
int getLastAnsType() {
|
||||
if (answer.size() == 0) {
|
||||
return -1;
|
||||
}
|
||||
return answer.lastElement().getType();
|
||||
}
|
||||
|
||||
/*
|
||||
* Decodes the resource records in a DNS message and adds
|
||||
* them to this object.
|
||||
* Does not modify or store a reference to the msg array.
|
||||
*/
|
||||
void add(byte[] msg, int msgLen, Header hdr) throws NamingException {
|
||||
|
||||
ResourceRecord rr;
|
||||
int pos = Header.HEADER_SIZE; // current offset into msg
|
||||
|
||||
try {
|
||||
for (int i = 0; i < hdr.numQuestions; i++) {
|
||||
rr = new ResourceRecord(msg, msgLen, pos, true, false);
|
||||
if (!zoneXfer) {
|
||||
question.addElement(rr);
|
||||
}
|
||||
pos += rr.size();
|
||||
}
|
||||
|
||||
for (int i = 0; i < hdr.numAnswers; i++) {
|
||||
rr = new ResourceRecord(
|
||||
msg, msgLen, pos, false, !zoneXfer);
|
||||
answer.addElement(rr);
|
||||
pos += rr.size();
|
||||
}
|
||||
|
||||
if (zoneXfer) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < hdr.numAuthorities; i++) {
|
||||
rr = new ResourceRecord(msg, msgLen, pos, false, true);
|
||||
authority.addElement(rr);
|
||||
pos += rr.size();
|
||||
}
|
||||
|
||||
// The additional records section is currently ignored.
|
||||
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new CommunicationException(
|
||||
"DNS error: corrupted message");
|
||||
}
|
||||
}
|
||||
}
|
205
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ZoneNode.java
Normal file
205
src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ZoneNode.java
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Date;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
/**
|
||||
* ZoneNode extends NameNode to represent a tree of the zones in the
|
||||
* DNS namespace, along with any intermediate nodes between zones.
|
||||
* A ZoneNode that represents a zone may be "populated" with a
|
||||
* NameNode tree containing the zone's contents.
|
||||
*
|
||||
* <p> A populated zone's contents will be flagged as having expired after
|
||||
* the time specified by the minimum TTL value in the zone's SOA record.
|
||||
*
|
||||
* <p> Since zone cuts aren't directly modeled by a tree of ZoneNodes,
|
||||
* ZoneNode.isZoneCut() always returns false.
|
||||
*
|
||||
* <p> The synchronization strategy is documented in DnsContext.java.
|
||||
*
|
||||
* <p> The zone's contents are accessed via a soft reference, so its
|
||||
* heap space may be reclaimed when necessary. The zone may be
|
||||
* repopulated later.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
class ZoneNode extends NameNode {
|
||||
|
||||
private SoftReference<NameNode> contentsRef = null; // the zone's namespace
|
||||
private long serialNumber = -1; // the zone data's serial number
|
||||
private Date expiration = null; // time when the zone's data expires
|
||||
|
||||
ZoneNode(String label) {
|
||||
super(label);
|
||||
}
|
||||
|
||||
protected NameNode newNameNode(String label) {
|
||||
return new ZoneNode(label);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the contents of this node. If the node was flagged as
|
||||
* expired, it remains so.
|
||||
*/
|
||||
synchronized void depopulate() {
|
||||
contentsRef = null;
|
||||
serialNumber = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is this node currently populated?
|
||||
*/
|
||||
synchronized boolean isPopulated() {
|
||||
return (getContents() != null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the zone's contents, or null if the zone is not populated.
|
||||
*/
|
||||
synchronized NameNode getContents() {
|
||||
return (contentsRef != null)
|
||||
? contentsRef.get()
|
||||
: null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Has this zone's data expired?
|
||||
*/
|
||||
synchronized boolean isExpired() {
|
||||
return ((expiration != null) && expiration.before(new Date()));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the deepest populated zone on the path specified by a
|
||||
* fully-qualified domain name, or null if there is no populated
|
||||
* zone on that path. Note that a node may be depopulated after
|
||||
* being returned.
|
||||
*/
|
||||
ZoneNode getDeepestPopulated(DnsName fqdn) {
|
||||
ZoneNode znode = this;
|
||||
ZoneNode popNode = isPopulated() ? this : null;
|
||||
for (int i = 1; i < fqdn.size(); i++) { // "i=1" to skip root label
|
||||
znode = (ZoneNode) znode.get(fqdn.getKey(i));
|
||||
if (znode == null) {
|
||||
break;
|
||||
} else if (znode.isPopulated()) {
|
||||
popNode = znode;
|
||||
}
|
||||
}
|
||||
return popNode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Populates (or repopulates) a zone given its own fully-qualified
|
||||
* name and its resource records. Returns the zone's new contents.
|
||||
*/
|
||||
NameNode populate(DnsName zone, ResourceRecords rrs) {
|
||||
// assert zone.get(0).equals(""); // zone has root label
|
||||
// assert (zone.size() == (depth() + 1)); // +1 due to root label
|
||||
|
||||
NameNode newContents = new NameNode(null);
|
||||
|
||||
for (int i = 0; i < rrs.answer.size(); i++) {
|
||||
ResourceRecord rr = rrs.answer.elementAt(i);
|
||||
DnsName n = rr.getName();
|
||||
|
||||
// Ignore resource records whose names aren't within the zone's
|
||||
// domain. Also skip records of the zone's top node, since
|
||||
// the zone's root NameNode is already in place.
|
||||
if ((n.size() > zone.size()) && n.startsWith(zone)) {
|
||||
NameNode nnode = newContents.add(n, zone.size());
|
||||
if (rr.getType() == ResourceRecord.TYPE_NS) {
|
||||
nnode.setZoneCut(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The zone's SOA record is the first record in the answer section.
|
||||
ResourceRecord soa = rrs.answer.firstElement();
|
||||
synchronized (this) {
|
||||
contentsRef = new SoftReference<NameNode>(newContents);
|
||||
serialNumber = getSerialNumber(soa);
|
||||
setExpiration(getMinimumTtl(soa));
|
||||
return newContents;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set this zone's data to expire in {@code secsToExpiration} seconds.
|
||||
*/
|
||||
private void setExpiration(long secsToExpiration) {
|
||||
expiration = new Date(System.currentTimeMillis() +
|
||||
1000 * secsToExpiration);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an SOA record's minimum TTL field.
|
||||
*/
|
||||
private static long getMinimumTtl(ResourceRecord soa) {
|
||||
String rdata = (String) soa.getRdata();
|
||||
int pos = rdata.lastIndexOf(' ') + 1;
|
||||
return Long.parseLong(rdata.substring(pos));
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares this zone's serial number with that of an SOA record.
|
||||
* Zone must be populated.
|
||||
* Returns a negative, zero, or positive integer as this zone's
|
||||
* serial number is less than, equal to, or greater than the SOA
|
||||
* record's.
|
||||
* See ResourceRecord.compareSerialNumbers() for a description of
|
||||
* serial number arithmetic.
|
||||
*/
|
||||
int compareSerialNumberTo(ResourceRecord soa) {
|
||||
// assert isPopulated();
|
||||
return ResourceRecord.compareSerialNumbers(serialNumber,
|
||||
getSerialNumber(soa));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an SOA record's serial number.
|
||||
*/
|
||||
private static long getSerialNumber(ResourceRecord soa) {
|
||||
String rdata = (String) soa.getRdata();
|
||||
|
||||
// An SOA record ends with: serial refresh retry expire minimum.
|
||||
// Set "beg" to the space before serial, and "end" to the space after.
|
||||
// We go "backward" to avoid dealing with escaped spaces in names.
|
||||
int beg = rdata.length();
|
||||
int end = -1;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
end = beg;
|
||||
beg = rdata.lastIndexOf(' ', end - 1);
|
||||
}
|
||||
return Long.parseLong(rdata.substring(beg + 1, end));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.url.dns;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.ResolveResult;
|
||||
import com.sun.jndi.dns.*;
|
||||
import com.sun.jndi.toolkit.url.GenericURLDirContext;
|
||||
|
||||
|
||||
/**
|
||||
* A DNS URL context resolves names that are DNS pseudo-URLs.
|
||||
* See com.sun.jndi.dns.DnsUrl for a description of the URL format.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public class dnsURLContext extends GenericURLDirContext {
|
||||
|
||||
public dnsURLContext(Hashtable<?,?> env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the host and port of "url" to a root context connected
|
||||
* to the named DNS server, and returns the domain name as the
|
||||
* remaining name.
|
||||
*/
|
||||
protected ResolveResult getRootURLContext(String url, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
DnsUrl dnsUrl;
|
||||
try {
|
||||
dnsUrl = new DnsUrl(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new InvalidNameException(e.getMessage());
|
||||
}
|
||||
|
||||
DnsUrl[] urls = new DnsUrl[] { dnsUrl };
|
||||
String domain = dnsUrl.getDomain();
|
||||
|
||||
return new ResolveResult(
|
||||
DnsContextFactory.getContext(".", urls, env),
|
||||
new CompositeName().add(domain));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.jndi.url.dns;
|
||||
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.ObjectFactory;
|
||||
|
||||
|
||||
/**
|
||||
* A DNS URL context factory creates contexts that can resolve names
|
||||
* that are DNS pseudo-URLs.
|
||||
* In addition, if given a specific DNS URL (or an array of them), the
|
||||
* factory will resolve all the way to the named context.
|
||||
* See com.sun.jndi.dns.DnsUrl for a description of the URL format.
|
||||
*
|
||||
* @author Scott Seligman
|
||||
*/
|
||||
|
||||
|
||||
public class dnsURLContextFactory implements ObjectFactory {
|
||||
|
||||
public Object getObjectInstance(Object urlInfo, Name name,
|
||||
Context nameCtx, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
if (urlInfo == null) {
|
||||
return (new dnsURLContext(env));
|
||||
} else if (urlInfo instanceof String) {
|
||||
return getUsingURL((String) urlInfo, env);
|
||||
} else if (urlInfo instanceof String[]) {
|
||||
return getUsingURLs((String[]) urlInfo, env);
|
||||
} else {
|
||||
throw (new ConfigurationException(
|
||||
"dnsURLContextFactory.getObjectInstance: " +
|
||||
"argument must be a DNS URL String or an array of them"));
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getUsingURL(String url, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
dnsURLContext urlCtx = new dnsURLContext(env);
|
||||
try {
|
||||
return urlCtx.lookup(url);
|
||||
} finally {
|
||||
urlCtx.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try each URL until lookup() succeeds for one of them.
|
||||
* If all URLs fail, throw one of the exceptions arbitrarily.
|
||||
* Not pretty, but potentially more informative than returning null.
|
||||
*/
|
||||
private static Object getUsingURLs(String[] urls, Hashtable<?,?> env)
|
||||
throws NamingException {
|
||||
|
||||
if (urls.length == 0) {
|
||||
throw (new ConfigurationException(
|
||||
"dnsURLContextFactory: empty URL array"));
|
||||
}
|
||||
dnsURLContext urlCtx = new dnsURLContext(env);
|
||||
try {
|
||||
NamingException ne = null;
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
try {
|
||||
return urlCtx.lookup(urls[i]);
|
||||
} catch (NamingException e) {
|
||||
ne = e;
|
||||
}
|
||||
}
|
||||
throw ne; // failure: throw one of the exceptions caught
|
||||
} finally {
|
||||
urlCtx.close();
|
||||
}
|
||||
}
|
||||
}
|
41
src/jdk.naming.dns/share/classes/module-info.java
Normal file
41
src/jdk.naming.dns/share/classes/module-info.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the implementation of the DNS Java Naming provider.
|
||||
*
|
||||
* @provides javax.naming.spi.InitialContextFactory
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
*/
|
||||
module jdk.naming.dns {
|
||||
requires java.naming;
|
||||
|
||||
// temporary export until NamingManager.getURLContext uses services
|
||||
exports com.sun.jndi.url.dns to java.naming;
|
||||
|
||||
provides javax.naming.spi.InitialContextFactory with
|
||||
com.sun.jndi.dns.DnsContextFactory;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue