8195976: Add JNDI test com/sun/jndi/dns/AttributeTests/GetAny.java

Reviewed-by: vtewari, rriggs, alanb, jjiang, xiaofeya
This commit is contained in:
Chris Yin 2018-02-13 12:19:37 +08:00
parent 0e1817736c
commit a0382a29e5
5 changed files with 832 additions and 0 deletions

@ -0,0 +1,110 @@
#
# Copyright (c) 2018, 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.
#
# 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.
#
################################################################################
# Capture file for GetAny.java
#
# NOTE: This hexadecimal dump of DNS protocol messages was generated by
# running the GetAny application program against a real DNS
# server along with DNSTracer
#
################################################################################
# DNS Request
0000: 32 72 01 00 00 01 00 00 00 00 00 00 05 68 6F 73 2r...........hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 01 ...
# DNS Response
0000: 32 72 85 80 00 01 00 06 00 01 00 01 05 68 6F 73 2r...........hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 01 C0 0C 00 10 00 01 00 00 8C A0 00 15 14 ................
0030: 41 20 76 65 72 79 20 70 6F 70 75 6C 61 72 20 68 A very popular h
0040: 6F 73 74 2E C0 0C 00 1D 00 01 00 00 8C A0 00 10 ost.............
0050: 00 12 16 13 88 97 1A 35 69 68 41 38 00 9B 16 58 .......5ihA8...X
0060: C0 0C 00 0D 00 01 00 00 8C A0 00 13 0C 54 68 65 .............The
0070: 20 4F 72 69 67 69 6E 61 6C 05 53 75 6E 6E 79 C0 Original.Sunny.
0080: 0C 00 0F 00 01 00 00 8C A0 00 11 00 0A 05 72 65 ..............re
0090: 6C 61 79 04 6F 68 69 6F 02 75 73 00 C0 0C 00 0F lay.ohio.us.....
00A0: 00 01 00 00 8C A0 00 10 00 14 05 72 65 6C 61 79 ...........relay
00B0: 05 74 65 78 61 73 C0 98 C0 0C 00 01 00 01 00 01 .texas..........
00C0: 51 80 00 04 01 02 04 01 C0 12 00 02 00 01 00 00 Q...............
00D0: 8C A0 00 05 02 6E 73 C0 12 C0 D4 00 01 00 01 00 .....ns.........
00E0: 00 8C A0 00 04 7F 00 00 01 .........
# DNS Request
0000: 41 80 01 00 00 01 00 00 00 00 00 00 05 68 6F 73 A............hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 FF ...
# DNS Response
0000: 41 80 85 80 00 01 00 06 00 01 00 01 05 68 6F 73 A............hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 FF C0 0C 00 10 00 01 00 00 8C A0 00 15 14 ................
0030: 41 20 76 65 72 79 20 70 6F 70 75 6C 61 72 20 68 A very popular h
0040: 6F 73 74 2E C0 0C 00 1D 00 01 00 00 8C A0 00 10 ost.............
0050: 00 12 16 13 88 97 1A 35 69 68 41 38 00 9B 16 58 .......5ihA8...X
0060: C0 0C 00 0D 00 01 00 00 8C A0 00 13 0C 54 68 65 .............The
0070: 20 4F 72 69 67 69 6E 61 6C 05 53 75 6E 6E 79 C0 Original.Sunny.
0080: 0C 00 0F 00 01 00 00 8C A0 00 12 00 14 05 72 65 ..............re
0090: 6C 61 79 05 74 65 78 61 73 02 75 73 00 C0 0C 00 lay.texas.us....
00A0: 0F 00 01 00 00 8C A0 00 0F 00 0A 05 72 65 6C 61 ............rela
00B0: 79 04 6F 68 69 6F C0 99 C0 0C 00 01 00 01 00 01 y.ohio..........
00C0: 51 80 00 04 01 02 04 01 C0 12 00 02 00 01 00 00 Q...............
00D0: 8C A0 00 05 02 6E 73 C0 12 C0 D4 00 01 00 01 00 .....ns.........
00E0: 00 8C A0 00 04 7F 00 00 01 .........
# DNS Request
0000: 65 2E 01 00 00 01 00 00 00 00 00 00 05 68 6F 73 e............hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 01 ...
# DNS Response
0000: 65 2E 85 80 00 01 00 06 00 01 00 01 05 68 6F 73 e............hos
0010: 74 31 07 64 6F 6D 61 69 6E 31 03 63 6F 6D 00 00 t1.domain1.com..
0020: FF 00 01 C0 0C 00 10 00 01 00 00 8C A0 00 15 14 ................
0030: 41 20 76 65 72 79 20 70 6F 70 75 6C 61 72 20 68 A very popular h
0040: 6F 73 74 2E C0 0C 00 1D 00 01 00 00 8C A0 00 10 ost.............
0050: 00 12 16 13 88 97 1A 35 69 68 41 38 00 9B 16 58 .......5ihA8...X
0060: C0 0C 00 0D 00 01 00 00 8C A0 00 13 0C 54 68 65 .............The
0070: 20 4F 72 69 67 69 6E 61 6C 05 53 75 6E 6E 79 C0 Original.Sunny.
0080: 0C 00 0F 00 01 00 00 8C A0 00 11 00 0A 05 72 65 ..............re
0090: 6C 61 79 04 6F 68 69 6F 02 75 73 00 C0 0C 00 0F lay.ohio.us.....
00A0: 00 01 00 00 8C A0 00 10 00 14 05 72 65 6C 61 79 ...........relay
00B0: 05 74 65 78 61 73 C0 98 C0 0C 00 01 00 01 00 01 .texas..........
00C0: 51 80 00 04 01 02 04 01 C0 12 00 02 00 01 00 00 Q...............
00D0: 8C A0 00 05 02 6E 73 C0 12 C0 D4 00 01 00 01 00 .....ns.........
00E0: 00 8C A0 00 04 7F 00 00 01 .........

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018, 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.
*
* 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.
*/
/*
* @test
* @bug 8195976
* @summary Tests that we can get the attributes of a DNS entry using special
* qualifiers.
* @modules java.xml.bind
* java.base/sun.security.util
* @library ../lib/
* @build DNSTestUtils DNSServer DNSTracer
* @run main GetAny
*/
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Hashtable;
public class GetAny {
private static final String KEY = "host1";
private static final String[] MANDATORY = { "A", "MX", "HINFO", "TXT", "29"
// "LOC"
};
private static final String[] OPTIONAL = {};
public static void main(String argv[]) throws Exception {
// Create socket on localhost only to avoid possible noise packet
DatagramSocket socket = new DatagramSocket(0,
InetAddress.getLoopbackAddress());
// initialize test
Hashtable<Object, Object> env;
env = DNSTestUtils.initEnv(socket, GetAny.class.getName(), argv);
DirContext ctx = null;
try {
// connect to server
ctx = new InitialDirContext(env);
// Any type from IN class
Attributes retAttrs = ctx.getAttributes(KEY, new String[] { "*" });
DNSTestUtils.verifySchema(retAttrs, MANDATORY, OPTIONAL);
retAttrs = ctx.getAttributes(KEY, new String[] { "* *" });
DNSTestUtils.verifySchema(retAttrs, MANDATORY, OPTIONAL);
retAttrs = ctx.getAttributes(KEY, new String[] { "IN *" });
DNSTestUtils.verifySchema(retAttrs, MANDATORY, OPTIONAL);
} finally {
DNSTestUtils.cleanup(ctx);
}
}
}

@ -0,0 +1,264 @@
/*
* Copyright (c) 2018, 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.
*
* 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.
*/
import sun.security.util.HexDumpEncoder;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.regex.MatchResult;
/*
* A dummy DNS server.
*
* Loads a sequence of DNS messages from a capture file into its cache.
* It listens for DNS UDP requests, finds match request in cache and sends the
* corresponding DNS responses.
*
* The capture file contains an DNS protocol exchange in the hexadecimal
* dump format emitted by HexDumpEncoder:
*
* xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
*
* Typically, DNS protocol exchange is generated by DNSTracer who captures
* communication messages between DNS application program and real DNS server
*/
public class DNSServer implements Runnable {
public class Pair<F, S> {
private F first;
private S second;
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public void setFirst(F first) {
this.first = first;
}
public void setSecond(S second) {
this.second = second;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
}
public static final int DNS_HEADER_SIZE = 12;
public static final int DNS_PACKET_SIZE = 512;
static HexDumpEncoder encoder = new HexDumpEncoder();
private DatagramSocket socket;
private String filename;
private boolean loop;
private final List<Pair<byte[], byte[]>> cache = new ArrayList<>();
private ByteBuffer reqBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
public DNSServer(DatagramSocket socket, String filename) {
this(socket, filename, false);
}
public DNSServer(DatagramSocket socket, String filename, boolean loop) {
this.socket = socket;
this.filename = filename;
this.loop = loop;
}
public void run() {
try {
System.out.println(
"DNSServer: Loading DNS cache data from : " + filename);
loadCaptureFile(filename);
System.out.println(
"DNSServer: listening on port " + socket.getLocalPort());
System.out.println("DNSServer: loop playback: " + loop);
int playbackIndex = 0;
while (playbackIndex < cache.size()) {
DatagramPacket reqPacket = new DatagramPacket(reqBuffer.array(),
reqBuffer.array().length);
socket.receive(reqPacket);
System.out.println(
"DNSServer: received query message from " + reqPacket
.getSocketAddress());
if (!verifyRequestMsg(reqPacket, playbackIndex)) {
throw new RuntimeException(
"DNSServer: Error: Failed to verify DNS request. "
+ "Not identical request message : \n"
+ encoder.encodeBuffer(
Arrays.copyOf(reqPacket.getData(),
reqPacket.getLength())));
}
byte[] payload = generateResponsePayload(reqPacket,
playbackIndex);
socket.send(new DatagramPacket(payload, payload.length,
reqPacket.getSocketAddress()));
System.out.println(
"DNSServer: send response message to " + reqPacket
.getSocketAddress());
playbackIndex++;
if (loop && playbackIndex >= cache.size()) {
playbackIndex = 0;
}
}
System.out.println(
"DNSServer: Done for all cached messages playback");
} catch (Exception e) {
System.err.println("DNSServer: Error: " + e);
}
}
/*
* Load a capture file containing an DNS protocol exchange in the
* hexadecimal dump format emitted by sun.misc.HexDumpEncoder:
*
* xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
*/
private void loadCaptureFile(String filename) throws IOException {
StringBuilder hexString = new StringBuilder();
String pattern = "(....): (..) (..) (..) (..) (..) (..) (..) (..) "
+ "(..) (..) (..) (..) (..) (..) (..) (..).*";
try (Scanner fileScanner = new Scanner(Paths.get(filename))) {
while (fileScanner.hasNextLine()) {
try (Scanner lineScanner = new Scanner(
fileScanner.nextLine())) {
if (lineScanner.findInLine(pattern) == null) {
continue;
}
MatchResult result = lineScanner.match();
for (int i = 1; i <= result.groupCount(); i++) {
String digits = result.group(i);
if (digits.length() == 4) {
if (digits.equals("0000")) { // start-of-message
if (hexString.length() > 0) {
addToCache(hexString.toString());
hexString.delete(0, hexString.length());
}
}
continue;
} else if (digits.equals(" ")) { // short message
continue;
}
hexString.append(digits);
}
}
}
}
addToCache(hexString.toString());
}
/*
* Add an DNS encoding to the cache (by request message key).
*/
private void addToCache(String hexString) {
byte[] encoding = DatatypeConverter.parseHexBinary(hexString);
if (encoding.length < DNS_HEADER_SIZE) {
throw new RuntimeException("Invalid DNS message : " + hexString);
}
if (getQR(encoding) == 0) {
// a query message, create entry in cache
cache.add(new Pair<>(encoding, null));
System.out.println(
" adding DNS query message with ID " + getID(encoding)
+ " to the cache");
} else {
// a response message, attach it to the query entry
if (!cache.isEmpty() && (getID(getLatestCacheEntry().getFirst())
== getID(encoding))) {
getLatestCacheEntry().setSecond(encoding);
System.out.println(
" adding DNS response message associated to ID "
+ getID(encoding) + " in the cache");
} else {
throw new RuntimeException(
"Invalid DNS message : " + hexString);
}
}
}
/*
* ID: A 16 bit identifier assigned by the program that generates any
* kind of query. This identifier is copied the corresponding reply and
* can be used by the requester to match up replies to outstanding queries.
*/
private static int getID(byte[] encoding) {
return ByteBuffer.wrap(encoding, 0, 2).getShort();
}
/*
* QR: A one bit field that specifies whether this message is
* a query (0), or a response (1) after ID
*/
private static int getQR(byte[] encoding) {
return encoding[2] & (0x01 << 7);
}
private Pair<byte[], byte[]> getLatestCacheEntry() {
return cache.get(cache.size() - 1);
}
private boolean verifyRequestMsg(DatagramPacket packet, int playbackIndex) {
byte[] cachedRequest = cache.get(playbackIndex).getFirst();
return Arrays.equals(Arrays
.copyOfRange(packet.getData(), 2, packet.getLength()),
Arrays.copyOfRange(cachedRequest, 2, cachedRequest.length));
}
private byte[] generateResponsePayload(DatagramPacket packet,
int playbackIndex) {
byte[] resMsg = cache.get(playbackIndex).getSecond();
byte[] payload = Arrays.copyOf(resMsg, resMsg.length);
// replace the ID with same with real request
payload[0] = packet.getData()[0];
payload[1] = packet.getData()[1];
return payload;
}
}

@ -0,0 +1,246 @@
/*
* Copyright (c) 2018, 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.
*
* 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.
*/
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import java.io.PrintStream;
import java.net.DatagramSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Hashtable;
public class DNSTestUtils {
public static final String TEST_DNS_SERVER_THREAD = "test.dns.server.thread";
public static final String TEST_DNS_ROOT_URL = "test.dns.root.url";
public static final int HOSTS_LOOKUP_MAX_DEPTH = 3;
protected static boolean debug = true;
/*
* Check that attrs contains the mandatory attributes and the right
* objectclass attribute
*/
public static boolean checkSchema(Attributes attrs, String[] mandatory,
String[] optional) {
// Check mandatory attributes
for (String mandatoryAttr : mandatory) {
if (attrs.get(mandatoryAttr) == null) {
debug("missing mandatory attribute: " + mandatoryAttr);
return false;
}
}
// Check optional attributes
int optMissing = 0;
for (String optionalAttr : optional) {
if (attrs.get(optionalAttr) == null) {
debug("warning: missing optional attribute: " + optionalAttr);
++optMissing;
}
}
if (attrs.size() > (mandatory.length + (optional.length
- optMissing))) {
debug("too many attributes: " + attrs);
return false;
}
return true;
}
/*
* Process command line arguments and init env
*/
public static Hashtable<Object, Object> initEnv(DatagramSocket socket,
String testname, String[] args) {
Hashtable<Object, Object> env = new Hashtable<>();
// set some default parameters if no additional specified
env.put("DNS_DOMAIN", "domain1.com.");
env.put("FOREIGN_DOMAIN", "Central.Sun.COM.");
env.put("FOREIGN_LEAF", "sunweb");
// set defaults for some JNDI properties
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.dns.DnsContextFactory");
boolean traceEnable = false;
boolean loopPlayback = false;
for (int i = 0; i < args.length; i++) {
if ((args[i].equals("-D")) && (args.length > i + 1)) {
extractProperty(args[++i], env);
} else if (args[i].startsWith("-D")) {
extractProperty(args[i].substring(2), env);
} else if (args[i].equalsIgnoreCase("-trace")) {
traceEnable = true;
} else if (args[i].equalsIgnoreCase("-loop")) {
loopPlayback = true;
}
}
debug = Boolean.valueOf(System.getProperty("debug", "true"));
if (env.get("DNS_SERVER") != null) {
String port = (String) env.get("DNS_PORT");
String portSuffix = (port == null) ? "" : ":" + port;
String url = "dns://" + env.get("DNS_SERVER") + portSuffix;
env.put(Context.PROVIDER_URL, url);
env.put(Context.PROVIDER_URL, url + "/" + env.get("DNS_DOMAIN"));
}
Runnable inst = null;
if (traceEnable) {
inst = createDNSTracer(socket, testname, env);
} else {
if (socket != null) {
inst = createDNSServer(socket, testname, loopPlayback);
} else {
// for tests which run against remote server
// or no server required
debug("Skip local DNS Server creation "
+ "since DatagramSocket is null");
}
}
if (inst != null) {
env.put(TEST_DNS_SERVER_THREAD, startServer(inst));
String url = "dns://localhost:" + socket.getLocalPort();
env.put(TEST_DNS_ROOT_URL, url);
env.put(Context.PROVIDER_URL, url + "/" + env.get("DNS_DOMAIN"));
}
return env;
}
/*
* Clean-up the directory context.
*/
public static void cleanup(Context ctx) {
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// ignore
}
}
}
private static void extractProperty(String propString,
Hashtable<Object, Object> env) {
int index;
if ((index = propString.indexOf('=')) > 0) {
env.put(propString.substring(0, index),
propString.substring(index + 1));
} else {
throw new RuntimeException(
"Failed to extract test args property from " + propString);
}
}
public static DNSTracer createDNSTracer(DatagramSocket socket,
String testname, Hashtable<Object, Object> env) {
if (socket == null) {
throw new RuntimeException("Error: failed to create DNSTracer "
+ "since DatagramSocket is null");
}
try {
PrintStream outStream = new PrintStream(getCaptureFile(testname));
return new DNSTracer(socket, outStream,
(String) env.get("DNS_SERVER"),
Integer.parseInt((String) env.get("DNS_PORT")));
} catch (Exception e) {
throw new RuntimeException(
"Error: failed to create DNSTracer : " + e.getMessage(), e);
}
}
public static DNSServer createDNSServer(DatagramSocket socket,
String testname, boolean loop) {
if (socket == null) {
throw new RuntimeException("Error: failed to create DNSServer "
+ "since DatagramSocket is null");
}
String path = getCaptureFile(testname);
if (Files.exists(Paths.get(path))) {
return new DNSServer(socket, path, loop);
} else {
throw new RuntimeException(
"Error: failed to create DNSServer, not found dns "
+ "cache file " + path);
}
}
public static Thread startServer(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.start();
return thread;
}
public static String getCaptureFile(String testname) {
return Paths.get(System.getProperty("test.src"))
.resolve(testname + ".dns").toString();
}
public static void enableHostsFile(String hostsFile) {
System.out.println("Enable jdk.net.hosts.file = " + hostsFile);
System.setProperty("jdk.net.hosts.file", hostsFile);
}
public static void enableHostsFile(int depth) {
Path path = Paths.get(System.getProperty("test.src", "."))
.toAbsolutePath();
for (int i = depth; i >= 0; i--) {
Path filePath = path.resolve("hosts");
if (Files.exists(filePath) && !Files.isDirectory(filePath)) {
enableHostsFile(filePath.toString());
break;
}
path = path.getParent();
if (path == null) {
break;
}
}
}
public static void debug(Object object) {
if (debug) {
System.out.println(object);
}
}
public static void verifySchema(Attributes attrs, String[] mandatory,
String[] optional) {
debug(attrs);
if (!checkSchema(attrs, mandatory, optional)) {
throw new RuntimeException("Check schema failed.");
}
}
}

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018, 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.
*
* 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.
*/
import sun.security.util.HexDumpEncoder;
import java.io.PrintStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Arrays;
/*
* A DNS UDP message tracer.
*
* It listens for DNS UDP requests, forward request to real DNS server, receives
* response message and sends back to requester, at same time dump all messages
* into capture file
*
* The capture file contains an DNS protocol exchange in the hexadecimal
* dump format emitted by HexDumpEncoder:
*
* xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
*
* Typically, the capture file data will be used by DNSServer for playback
*/
public class DNSTracer implements Runnable {
public static final int DNS_DEFAULT_PORT = 53;
public static final int DNS_PACKET_SIZE = 512;
static HexDumpEncoder encoder = new HexDumpEncoder();
private DatagramSocket inSocket;
private SocketAddress dnsServerAddress;
private ByteBuffer reqBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
private ByteBuffer resBuffer = ByteBuffer.allocate(DNS_PACKET_SIZE);
private PrintStream out = null;
public DNSTracer(DatagramSocket socket, String dnsHostname) {
this(socket, dnsHostname, DNS_DEFAULT_PORT);
}
public DNSTracer(DatagramSocket socket, PrintStream outStream,
String dnsHostname) {
this(socket, outStream, dnsHostname, DNS_DEFAULT_PORT);
}
public DNSTracer(DatagramSocket socket, String dnsHostname, int dnsPort) {
this(socket, System.out, dnsHostname, dnsPort);
}
public DNSTracer(DatagramSocket socket, PrintStream outStream,
String dnsHostname, int dnsPort) {
inSocket = socket;
out = outStream;
dnsServerAddress = new InetSocketAddress(dnsHostname, dnsPort);
}
public void run() {
System.out.println(
"DNSTracer: listening on port " + inSocket.getLocalPort());
System.out.println("DNSTracer: will forward request to server "
+ dnsServerAddress);
try (DatagramSocket outSocket = new DatagramSocket()) {
while (true) {
DatagramPacket reqPacket = new DatagramPacket(reqBuffer.array(),
reqBuffer.array().length);
inSocket.receive(reqPacket);
out.println("-> " + reqPacket.getSocketAddress());
out.println();
// dump dns request data
out.println(encoder.encodeBuffer(
Arrays.copyOf(reqPacket.getData(),
reqPacket.getLength())));
out.println();
outSocket.send(new DatagramPacket(reqPacket.getData(),
reqPacket.getLength(), dnsServerAddress));
DatagramPacket resPacket = new DatagramPacket(resBuffer.array(),
resBuffer.array().length);
outSocket.receive(resPacket);
out.println("<- " + resPacket.getSocketAddress());
out.println();
// dump dns response data
out.println(encoder.encodeBuffer(
Arrays.copyOf(resPacket.getData(),
resPacket.getLength())));
out.println();
inSocket.send(new DatagramPacket(resPacket.getData(),
resPacket.getLength(), reqPacket.getSocketAddress()));
}
} catch (SocketException se) {
if (inSocket.isClosed()) {
out.flush();
System.out.println("DNSTracer: Exit");
} else {
se.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}