package se.unlogic.eagledns;
import org.apache.log4j.Logger;
import org.xbill.DNS.Address;
import org.xbill.DNS.CNAMERecord;
import org.xbill.DNS.DClass;
import org.xbill.DNS.DNAMERecord;
import org.xbill.DNS.ExtendedFlags;
import org.xbill.DNS.Flags;
import org.xbill.DNS.Header;
import org.xbill.DNS.Message;
import org.xbill.DNS.NSRecord;
import org.xbill.DNS.Name;
import org.xbill.DNS.NameTooLongException;
import org.xbill.DNS.OPTRecord;
import org.xbill.DNS.Opcode;
import org.xbill.DNS.RRset;
import org.xbill.DNS.Rcode;
import org.xbill.DNS.Record;
import org.xbill.DNS.Section;
import org.xbill.DNS.SetResponse;
import org.xbill.DNS.TSIG;
import org.xbill.DNS.TSIGRecord;
import org.xbill.DNS.Type;
import org.xbill.DNS.Zone;
import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.settings.SettingNode;
import se.unlogic.standardutils.settings.XMLSettingNode;
import se.unlogic.standardutils.string.StringUtils;
import se.unlogic.standardutils.time.MillisecondTimeUnits;
import se.unlogic.standardutils.timer.RunnableTimerTask;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* EagleDNS copyright Robert "Unlogic" Olofsson (unlogic@unlogic.se)
*
* Based on the jnamed class from the dnsjava project (http://www.dnsjava.org/) copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
*
* @author Robert "Unlogic" Olofsson
* @author Michael Neale, Red Hat (JBoss division)
*/
public class EagleDNS implements Runnable, EagleManager {
public static final String VERSION = "Eagle DNS 1.0";
static final int FLAG_DNSSECOK = 1;
static final int FLAG_SIGONLY = 2;
private final Logger log = Logger.getLogger(this.getClass());
private final ConcurrentHashMap<Name, CachedPrimaryZone> primaryZoneMap = new ConcurrentHashMap<Name, CachedPrimaryZone>();
private final ConcurrentHashMap<Name, CachedSecondaryZone> secondaryZoneMap = new ConcurrentHashMap<Name, CachedSecondaryZone>();
private final HashMap<Name, TSIG> TSIGs = new HashMap<Name, TSIG>();
private final HashMap<String, ZoneProvider> zoneProviders = new HashMap<String, ZoneProvider>();
private int tcpThreadPoolSize = 20;
private int udpThreadPoolSize = 20;
private int tcpThreadPoolShutdownTimeout = 60;
private int udpThreadPoolShutdownTimeout = 60;
private ArrayList<TCPSocketMonitor> tcpMonitorThreads = new ArrayList<TCPSocketMonitor>();
private ArrayList<UDPSocketMonitor> udpMonitorThreads = new ArrayList<UDPSocketMonitor>();
private ThreadPoolExecutor tcpThreadPool;
private ThreadPoolExecutor udpThreadPool;
private int axfrTimeout = 60;
private String remotePassword;
private Integer remotePort;
private LoginHandler loginHandler;
private Timer secondaryZoneUpdateTimer;
private RunnableTimerTask timerTask;
private String configFilePath;
private boolean shutdown = false;
public EagleDNS() throws UnknownHostException {
}
public void setConfigFilePath(String configFilePath)
{
this.configFilePath = configFilePath;
}
public void setConfigClassPath(String path)
{
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
this.configFilePath = url.getPath();
}
public void start() throws UnknownHostException {
//URL logurl = Thread.currentThread().getContextClassLoader().getResource("log4j.xml");
//DOMConfigurator.configure(logurl);
//DOMConfigurator.configure("conf/log4j.xml");
log.info(VERSION + " starting...");
XMLSettingNode configFile;
try {
log.debug("Parsing config file..." + configFilePath);
configFile = new XMLSettingNode(configFilePath);
} catch (Exception e) {
log.fatal("Unable to open config file " + configFilePath + ", aborting startup!");
System.out.println("Unable to open config file " + configFilePath + ", aborting startup!");
return;
}
List<Integer> ports = configFile.getIntegers("/Config/System/Port");
if (ports.isEmpty()) {
log.debug("No ports found in config file " + configFilePath + ", using default port 53");
ports.add(new Integer(53));
}
List<InetAddress> addresses = new ArrayList<InetAddress>();
List<String> addressStrings = configFile.getStrings("/Config/System/Address");
if (addressStrings == null || addressStrings.isEmpty()) {
log.debug("No addresses found in config, listening on all addresses (0.0.0.0)");
addresses.add(Address.getByAddress("0.0.0.0"));
} else {
for (String addressString : addressStrings) {
try {
addresses.add(Address.getByAddress(addressString));
} catch (UnknownHostException e) {
log.error("Invalid address " + addressString + " specified in config file, skipping address " + e);
}
}
if (addresses.isEmpty()) {
log.fatal("None of the " + addressStrings.size() + " addresses specified in the config file are valid, aborting startup!\n" + "Correct the addresses or remove them from the config file if you want to listen on all interfaces.");
return;
}
}
Integer tcpThreadPoolSize = configFile.getInteger("/Config/System/TCPThreadPoolSize");
if (tcpThreadPoolSize != null) {
log.debug("Setting TCP thread pool size to " + tcpThreadPoolSize);
this.tcpThreadPoolSize = tcpThreadPoolSize;
}
Integer tcpThreadPoolShutdownTimeout = configFile.getInteger("/Config/System/TCPThreadPoolShutdownTimeout");
if (tcpThreadPoolShutdownTimeout != null) {
log.debug("Setting TCP thread pool shutdown timeout to " + tcpThreadPoolSize + " seconds");
this.tcpThreadPoolShutdownTimeout = tcpThreadPoolShutdownTimeout;
}
Integer udpThreadPoolSize = configFile.getInteger("/Config/System/UDPThreadPoolSize");
if (udpThreadPoolSize != null) {
log.debug("Setting UDP thread pool size to " + udpThreadPoolSize);
this.udpThreadPoolSize = udpThreadPoolSize;
}
Integer udpThreadPoolShutdownTimeout = configFile.getInteger("/Config/System/UDPThreadPoolShutdownTimeout");
if (udpThreadPoolShutdownTimeout != null) {
log.debug("Setting UDP thread pool shutdown timeout to " + udpThreadPoolSize + " seconds");
this.udpThreadPoolShutdownTimeout = udpThreadPoolShutdownTimeout;
}
this.remotePassword = configFile.getString("/Config/System/RemoteManagementPassword");
log.debug("Remote management password set to " + remotePassword);
this.remotePort = configFile.getInteger("/Config/System/RemoteManagementPort");
log.debug("Remote management port set to " + remotePort);
Integer axfrTimeout = configFile.getInteger("/Config/System/AXFRTimeout");
if (axfrTimeout != null) {
log.debug("Setting AXFR timeout to " + axfrTimeout);
this.axfrTimeout = axfrTimeout;
}
// TODO TSIG stuff
List<XMLSettingNode> zoneProviderElements = configFile.getSettings("/Config/ZoneProviders/ZoneProvider");
for (XMLSettingNode settingNode : zoneProviderElements) {
String name = settingNode.getString("Name");
if (StringUtils.isEmpty(name)) {
log.error("ZoneProvider element with no name set found in config, ignoring element.");
continue;
}
String className = settingNode.getString("Class");
if (StringUtils.isEmpty(className)) {
log.error("ZoneProvider element with no class set found in config, ignoring element.");
continue;
}
try {
log.debug("Instantiating zone provider " + name + " (" + className + ")");
ZoneProvider zoneProvider = (ZoneProvider) Class.forName(className).newInstance();
log.debug("Zone provider " + name + " successfully instantiated");
List<XMLSettingNode> propertyElements = settingNode.getSettings("Properties/Property");
for (SettingNode propertyElement : propertyElements) {
String propertyName = propertyElement.getString("@name");
if (StringUtils.isEmpty(propertyName)) {
log.error("Property element with no name set found in config, ignoring element");
continue;
}
String value = propertyElement.getString(".");
log.debug("Found value " + value + " for property " + propertyName);
try {
Method method = zoneProvider.getClass().getMethod("set" + StringUtils.toFirstLetterUppercase(propertyName), String.class);
ReflectionUtils.fixMethodAccess(method);
log.debug("Setting property " + propertyName);
try {
method.invoke(zoneProvider, value);
} catch (IllegalArgumentException e) {
log.error("Unable to set property " + propertyName + " on zone provider " + name + " (" + className + ")", e);
} catch (InvocationTargetException e) {
log.error("Unable to set property " + propertyName + " on zone provider " + name + " (" + className + ")", e);
}
} catch (SecurityException e) {
log.error("Unable to find matching setter method for property " + propertyName + " in zone provider " + name + " (" + className + ")", e);
} catch (NoSuchMethodException e) {
log.error("Unable to find matching setter method for property " + propertyName + " in zone provider " + name + " (" + className + ")", e);
}
}
try {
if (zoneProvider instanceof ZoneProviderUpdatable) {
((ZoneProviderUpdatable) zoneProvider).setChangeListener(new ZoneChangeCallback() {
public void zoneDataChanged() {
reloadZones();
}
});
}
zoneProvider.init(name);
log.debug("Zone provider " + name + " (" + className + ") successfully initialized!");
this.zoneProviders.put(name, zoneProvider);
} catch (Throwable e) {
log.error("Error initializing zone provider " + name + " (" + className + ")", e);
}
} catch (InstantiationException e) {
log.error("Unable to create instance of class " + className + " for zone provider " + name, e);
} catch (IllegalAccessException e) {
log.error("Unable to create instance of class " + className + " for zone provider " + name, e);
} catch (ClassNotFoundException e) {
log.error("Unable to create instance of class " + className + " for zone provider " + name, e);
}
}
if (zoneProviders.isEmpty()) {
log.fatal("No zone providers found or started, aborting startup!");
return;
}
this.reloadZones();
if(remotePassword == null || remotePort == null){
log.debug("Remote managed port and/or password not set, remote managent will not be available.");
}else{
log.debug("Starting remote interface on port " + remotePort);
this.loginHandler = new LoginHandler(this, this.remotePassword);
try {
EagleLogin eagleLogin = (EagleLogin) UnicastRemoteObject.exportObject(loginHandler,remotePort);
UnicastRemoteObject.exportObject(this,remotePort);
Registry registry = LocateRegistry.createRegistry(remotePort);
registry.bind("eagleLogin", eagleLogin);
} catch (AccessException e) {
log.fatal("Unable to start remote manangement interface, aborting startup!",e);
return;
} catch (RemoteException e) {
log.fatal("Unable to start remote manangement interface, aborting startup!",e);
return;
} catch (AlreadyBoundException e) {
log.fatal("Unable to start remote manangement interface, aborting startup!",e);
return;
}
}
log.debug("Initializing TCP thread pool...");
this.tcpThreadPool = new ThreadPoolExecutor(this.tcpThreadPoolSize, this.tcpThreadPoolSize, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
log.debug("Initializing UDP thread pool...");
this.udpThreadPool = new ThreadPoolExecutor(this.udpThreadPoolSize, this.udpThreadPoolSize, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
Iterator<InetAddress> iaddr = addresses.iterator();
while (iaddr.hasNext()) {
InetAddress addr = iaddr.next();
Iterator<Integer> iport = ports.iterator();
while (iport.hasNext()) {
int port = iport.next().intValue();
try {
this.udpMonitorThreads.add(new UDPSocketMonitor(this, addr, port));
} catch (SocketException e) {
log.error("Unable to open UDP server socket on address " + addr + ":" + port + ", " + e);
}
try {
this.tcpMonitorThreads.add(new TCPSocketMonitor(this, addr, port));
} catch (IOException e) {
log.error("Unable to open TCP server socket on address " + addr + ":" + port + ", " + e);
}
}
}
if (this.tcpMonitorThreads.isEmpty() && this.udpMonitorThreads.isEmpty()) {
log.fatal("Not bound on any sockets, aborting startup!");
return;
}
log.debug("Starting secondary zone update timer...");
this.timerTask = new RunnableTimerTask(this);
this.secondaryZoneUpdateTimer = new Timer();
this.secondaryZoneUpdateTimer.schedule(timerTask, MillisecondTimeUnits.SECOND * 60, MillisecondTimeUnits.SECOND * 60);
log.info(VERSION + " started with " + this.primaryZoneMap.size() + " primary zones and " + this.secondaryZoneMap.size() + " secondary zones");
}
protected CountDownLatch shutdownLatch = new CountDownLatch(1);
public synchronized void shutdown() {
new Thread(){@Override
public void run(){
//RMI thread workaround
actualShutdown();
}}.start();
try
{
log.debug("Awaiting shutdown latch");
shutdownLatch.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
void actualShutdown(){
log.debug("In shutdown...");
if (shutdown == false) {
try
{
log.info("Shutting down " + VERSION + "...");
shutdown = true;
log.debug("Stopping secondary zone update timer...");
timerTask.cancel();
secondaryZoneUpdateTimer.cancel();
log.debug("Stopping TCP thread pool...");
tcpThreadPool.shutdown();
try {
tcpThreadPool.awaitTermination(tcpThreadPoolShutdownTimeout, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
log.error("Timeout waiting " + tcpThreadPoolShutdownTimeout + " seconds for TCP thread pool to shutdown, forcing thread pool shutdown...");
tcpThreadPool.shutdownNow();
}
log.debug("Stopping UDP thread pool...");
udpThreadPool.shutdown();
try {
udpThreadPool.awaitTermination(udpThreadPoolShutdownTimeout, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
log.error("Timeout waiting " + udpThreadPoolShutdownTimeout + " seconds for UDP thread pool to shutdown, forcing thread pool shutdown...");
udpThreadPool.shutdownNow();
}
log.debug("Stopping sockets...");
for (UDPSocketMonitor monitor : udpMonitorThreads)
{
try
{
monitor.closeSocket();
}
catch (IOException e)
{
}
}
for (TCPSocketMonitor monitor : tcpMonitorThreads)
{
try
{
monitor.closeSocket();
}
catch (IOException e)
{
}
}
log.info(VERSION + " stopped");
}
finally
{
shutdownLatch.countDown();
}
//System.exit(0);
}
}
public synchronized void reloadZones() {
this.primaryZoneMap.clear();
this.secondaryZoneMap.clear();
for (Entry<String, ZoneProvider> zoneProviderEntry : this.zoneProviders.entrySet()) {
log.debug("Getting primary zones from zone provider " + zoneProviderEntry.getKey());
Collection<Zone> primaryZones;
try {
primaryZones = zoneProviderEntry.getValue().getPrimaryZones();
} catch (Throwable e) {
log.error("Error getting primary zones from zone provider " + zoneProviderEntry.getKey(), e);
continue;
}
if (primaryZones != null) {
for (Zone zone : primaryZones) {
log.debug("Got zone " + zone.getOrigin());
this.primaryZoneMap.put(zone.getOrigin(), new CachedPrimaryZone(zone, zoneProviderEntry.getValue()));
}
}
log.debug("Getting secondary zones from zone provider " + zoneProviderEntry.getKey());
Collection<SecondaryZone> secondaryZones;
try {
secondaryZones = zoneProviderEntry.getValue().getSecondaryZones();
} catch (Throwable e) {
log.error("Error getting secondary zones from zone provider " + zoneProviderEntry.getKey(), e);
continue;
}
if (secondaryZones != null) {
for (SecondaryZone zone : secondaryZones) {
log.debug("Got zone " + zone.getZoneName() + " (" + zone.getRemoteServerAddress() + ")");
CachedSecondaryZone cachedSecondaryZone = new CachedSecondaryZone(zoneProviderEntry.getValue(), zone);
this.secondaryZoneMap.put(cachedSecondaryZone.getSecondaryZone().getZoneName(), cachedSecondaryZone);
}
}
}
}
// @SuppressWarnings("unused")
// private void addPrimaryZone(String zname, String zonefile) throws IOException {
// Name origin = null;
// if (zname != null) {
// origin = Name.fromString(zname, Name.root);
// }
// Zone newzone = new Zone(origin, zonefile);
// primaryZoneMap.put(newzone.getOrigin(), newzone);
// }
//
// @SuppressWarnings("unused")
// private void addSecondaryZone(String zone, String remote) throws IOException, ZoneTransferException {
// Name zname = Name.fromString(zone, Name.root);
// Zone newzone = new Zone(zname, DClass.IN, remote);
// primaryZoneMap.put(zname, newzone);
// }
@SuppressWarnings("unused")
private void addTSIG(String algstr, String namestr, String key) throws IOException {
Name name = Name.fromString(namestr, Name.root);
TSIGs.put(name, new TSIG(algstr, namestr, key));
}
private Zone findBestZone(Name name) {
Zone foundzone = getZone(name);
if (foundzone != null) {
return foundzone;
}
int labels = name.labels();
for (int i = 1; i < labels; i++) {
Name tname = new Name(name, i);
foundzone = getZone(tname);
if (foundzone != null) {
return foundzone;
}
}
return null;
}
private Zone getZone(Name name) {
CachedPrimaryZone cachedPrimaryZone = this.primaryZoneMap.get(name);
if (cachedPrimaryZone != null) {
return cachedPrimaryZone.getZone();
}
CachedSecondaryZone cachedSecondaryZone = this.secondaryZoneMap.get(name);
if (cachedSecondaryZone != null && cachedSecondaryZone.getSecondaryZone().getZoneCopy() != null) {
return cachedSecondaryZone.getSecondaryZone().getZoneCopy();
}
return null;
}
private RRset findExactMatch(Name name, int type, int dclass, boolean glue) {
Zone zone = findBestZone(name);
if (zone != null) {
return zone.findExactMatch(name, type);
}
return null;
}
private void addRRset(Name name, Message response, RRset rrset, int section, int flags) {
for (int s = 1; s <= section; s++) {
if (response.findRRset(name, rrset.getType(), s)) {
return;
}
}
if ((flags & FLAG_SIGONLY) == 0) {
Iterator<?> it = rrset.rrs();
while (it.hasNext()) {
Record r = (Record) it.next();
if (r.getName().isWild() && !name.isWild()) {
r = r.withName(name);
}
response.addRecord(r, section);
}
}
if ((flags & (FLAG_SIGONLY | FLAG_DNSSECOK)) != 0) {
Iterator<?> it = rrset.sigs();
while (it.hasNext()) {
Record r = (Record) it.next();
if (r.getName().isWild() && !name.isWild()) {
r = r.withName(name);
}
response.addRecord(r, section);
}
}
}
private final void addSOA(Message response, Zone zone) {
response.addRecord(zone.getSOA(), Section.AUTHORITY);
}
private final void addNS(Message response, Zone zone, int flags) {
RRset nsRecords = zone.getNS();
addRRset(nsRecords.getName(), response, nsRecords, Section.AUTHORITY, flags);
}
private void addGlue(Message response, Name name, int flags) {
RRset a = findExactMatch(name, Type.A, DClass.IN, true);
if (a == null) {
return;
}
addRRset(name, response, a, Section.ADDITIONAL, flags);
}
private void addAdditional2(Message response, int section, int flags) {
Record[] records = response.getSectionArray(section);
for (Record r : records) {
Name glueName = r.getAdditionalName();
if (glueName != null) {
addGlue(response, glueName, flags);
}
}
}
private final void addAdditional(Message response, int flags) {
addAdditional2(response, Section.ANSWER, flags);
addAdditional2(response, Section.AUTHORITY, flags);
}
private byte addAnswer(Message response, Name name, int type, int dclass, int iterations, int flags) {
SetResponse sr;
byte rcode = Rcode.NOERROR;
if (iterations > 6) {
return Rcode.NOERROR;
}
if (type == Type.SIG || type == Type.RRSIG) {
type = Type.ANY;
flags |= FLAG_SIGONLY;
}
Zone zone = findBestZone(name);
if (zone != null) {
sr = zone.findRecords(name, type);
if (sr.isNXDOMAIN()) {
response.getHeader().setRcode(Rcode.NXDOMAIN);
if (zone != null) {
addSOA(response, zone);
if (iterations == 0) {
response.getHeader().setFlag(Flags.AA);
}
}
rcode = Rcode.NXDOMAIN;
} else if (sr.isNXRRSET()) {
if (zone != null) {
addSOA(response, zone);
if (iterations == 0) {
response.getHeader().setFlag(Flags.AA);
}
}
} else if (sr.isDelegation()) {
RRset nsRecords = sr.getNS();
addRRset(nsRecords.getName(), response, nsRecords, Section.AUTHORITY, flags);
} else if (sr.isCNAME()) {
CNAMERecord cname = sr.getCNAME();
RRset rrset = new RRset(cname);
addRRset(name, response, rrset, Section.ANSWER, flags);
if (zone != null && iterations == 0) {
response.getHeader().setFlag(Flags.AA);
}
rcode = addAnswer(response, cname.getTarget(), type, dclass, iterations + 1, flags);
} else if (sr.isDNAME()) {
DNAMERecord dname = sr.getDNAME();
RRset rrset = new RRset(dname);
addRRset(name, response, rrset, Section.ANSWER, flags);
Name newname;
try {
newname = name.fromDNAME(dname);
} catch (NameTooLongException e) {
return Rcode.YXDOMAIN;
}
rrset = new RRset(new CNAMERecord(name, dclass, 0, newname));
addRRset(name, response, rrset, Section.ANSWER, flags);
if (zone != null && iterations == 0) {
response.getHeader().setFlag(Flags.AA);
}
rcode = addAnswer(response, newname, type, dclass, iterations + 1, flags);
} else if (sr.isSuccessful()) {
RRset[] rrsets = sr.answers();
for (RRset rrset : rrsets) {
addRRset(name, response, rrset, Section.ANSWER, flags);
}
if (zone != null) {
addNS(response, zone, flags);
if (iterations == 0) {
response.getHeader().setFlag(Flags.AA);
}
}
}
}
return rcode;
}
private byte[] doAXFR(Name name, Message query, TSIG tsig, TSIGRecord qtsig, Socket s) {
boolean first = true;
Zone zone = this.findBestZone(name);
if (zone == null) {
return errorMessage(query, Rcode.REFUSED);
}
// Check that the IP requesting the AXFR is present as a NS in this zone
boolean axfrAllowed = false;
Iterator<?> nsIterator = zone.getNS().rrs();
while (nsIterator.hasNext()) {
NSRecord record = (NSRecord) nsIterator.next();
try {
String nsIP = InetAddress.getByName(record.getTarget().toString()).getHostAddress();
if (s.getInetAddress().getHostAddress().equals(nsIP)) {
axfrAllowed = true;
break;
}
} catch (UnknownHostException e) {
log.warn("Unable to resolve hostname of nameserver " + record.getTarget() + " in zone " + zone.getOrigin() + " while processing AXFR request from " + s.getRemoteSocketAddress());
}
}
if (!axfrAllowed) {
log.warn("AXFR request of zone " + zone.getOrigin() + " from " + s.getRemoteSocketAddress() + " refused!");
return errorMessage(query, Rcode.REFUSED);
}
Iterator<?> it = zone.AXFR();
try {
DataOutputStream dataOut;
dataOut = new DataOutputStream(s.getOutputStream());
int id = query.getHeader().getID();
while (it.hasNext()) {
RRset rrset = (RRset) it.next();
Message response = new Message(id);
Header header = response.getHeader();
header.setFlag(Flags.QR);
header.setFlag(Flags.AA);
addRRset(rrset.getName(), response, rrset, Section.ANSWER, FLAG_DNSSECOK);
if (tsig != null) {
tsig.applyStream(response, qtsig, first);
qtsig = response.getTSIG();
}
first = false;
byte[] out = response.toWire();
dataOut.writeShort(out.length);
dataOut.write(out);
}
} catch (IOException ex) {
log.warn("AXFR failed", ex);
}
try {
s.close();
} catch (IOException ex) {
}
return null;
}
/*
* Note: a null return value means that the caller doesn't need to do
* anything. Currently this only happens if this is an AXFR request over
* TCP.
*/
byte[] generateReply(Message query, byte[] in, int length, Socket socket) throws IOException {
Header header;
// boolean badversion;
int maxLength;
int flags = 0;
header = query.getHeader();
if (header.getFlag(Flags.QR)) {
return null;
}
if (header.getRcode() != Rcode.NOERROR) {
return errorMessage(query, Rcode.FORMERR);
}
if (header.getOpcode() != Opcode.QUERY) {
return errorMessage(query, Rcode.NOTIMP);
}
Record queryRecord = query.getQuestion();
TSIGRecord queryTSIG = query.getTSIG();
TSIG tsig = null;
if (queryTSIG != null) {
tsig = TSIGs.get(queryTSIG.getName());
if (tsig == null || tsig.verify(query, in, length, null) != Rcode.NOERROR) {
return formerrMessage(in);
}
}
OPTRecord queryOPT = query.getOPT();
if (queryOPT != null && queryOPT.getVersion() > 0) {
// badversion = true;
}
if (socket != null) {
maxLength = 65535;
} else if (queryOPT != null) {
maxLength = Math.max(queryOPT.getPayloadSize(), 512);
} else {
maxLength = 512;
}
if (queryOPT != null && (queryOPT.getFlags() & ExtendedFlags.DO) != 0) {
flags = FLAG_DNSSECOK;
}
Message response = new Message(query.getHeader().getID());
response.getHeader().setFlag(Flags.QR);
if (query.getHeader().getFlag(Flags.RD)) {
response.getHeader().setFlag(Flags.RD);
}
response.addRecord(queryRecord, Section.QUESTION);
Name name = queryRecord.getName();
int type = queryRecord.getType();
int dclass = queryRecord.getDClass();
if (type == Type.AXFR && socket != null) {
return doAXFR(name, query, tsig, queryTSIG, socket);
}
if (!Type.isRR(type) && type != Type.ANY) {
return errorMessage(query, Rcode.NOTIMP);
}
byte rcode = addAnswer(response, name, type, dclass, 0, flags);
if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) {
return errorMessage(query, rcode);
}
addAdditional(response, flags);
if (queryOPT != null) {
int optflags = (flags == FLAG_DNSSECOK) ? ExtendedFlags.DO : 0;
OPTRecord opt = new OPTRecord((short) 4096, rcode, (byte) 0, optflags);
response.addRecord(opt, Section.ADDITIONAL);
}
response.setTSIG(tsig, Rcode.NOERROR, queryTSIG);
return response.toWire(maxLength);
}
private byte[] buildErrorMessage(Header header, int rcode, Record question) {
Message response = new Message();
response.setHeader(header);
for (int i = 0; i < 4; i++) {
response.removeAllRecords(i);
}
if (rcode == Rcode.SERVFAIL) {
response.addRecord(question, Section.QUESTION);
}
header.setRcode(rcode);
return response.toWire();
}
byte[] formerrMessage(byte[] in) {
Header header;
try {
header = new Header(in);
} catch (IOException e) {
return null;
}
return buildErrorMessage(header, Rcode.FORMERR, null);
}
private byte[] errorMessage(Message query, int rcode) {
return buildErrorMessage(query.getHeader(), rcode, query.getQuestion());
}
protected void UDPClient(DatagramSocket socket, DatagramPacket inDataPacket) {
}
public static String toString(Record record) {
if (record == null) {
return null;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(record.getName());
stringBuilder.append(" ");
stringBuilder.append(record.getTTL());
stringBuilder.append(" ");
stringBuilder.append(DClass.string(record.getDClass()));
stringBuilder.append(" ");
stringBuilder.append(Type.string(record.getType()));
String rdata = record.rdataToString();
if (!rdata.equals("")) {
stringBuilder.append(" ");
stringBuilder.append(rdata);
}
return stringBuilder.toString();
}
public void run() {
log.debug("Checking secondary zones...");
for(CachedSecondaryZone cachedSecondaryZone : this.secondaryZoneMap.values()){
SecondaryZone secondaryZone = cachedSecondaryZone.getSecondaryZone();
if(secondaryZone.getZoneCopy() == null || secondaryZone.getDownloaded() == null || (System.currentTimeMillis() - secondaryZone.getDownloaded().getTime()) > (secondaryZone.getZoneCopy().getSOA().getRefresh() * 1000)){
cachedSecondaryZone.update(this.axfrTimeout);
}
}
}
protected ThreadPoolExecutor getTcpThreadPool() {
return tcpThreadPool;
}
protected ThreadPoolExecutor getUdpThreadPool() {
return udpThreadPool;
}
public boolean isShutdown() {
return shutdown;
}
}