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