Java tutorial
/* * This file is part of SPFBL. * * SPFBL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SPFBL 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 for more details. * * You should have received a copy of the GNU General Public License * along with SPFBL. If not, see <http://www.gnu.org/licenses/>. */ package net.spfbl.dns; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import net.spfbl.core.Server; import net.spfbl.spf.SPF; import net.spfbl.whois.SubnetIPv4; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.TreeSet; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import net.spfbl.core.Analise; import net.spfbl.data.Block; import net.spfbl.core.Client; import net.spfbl.core.Client.Permission; import net.spfbl.core.Core; import net.spfbl.core.NormalDistribution; import net.spfbl.data.Generic; import net.spfbl.data.Ignore; import net.spfbl.data.White; import net.spfbl.dnsbl.ServerDNSBL; import net.spfbl.whois.Domain; import net.spfbl.whois.SubnetIPv6; import org.apache.commons.lang3.SerializationUtils; import org.xbill.DNS.ARecord; import org.xbill.DNS.DClass; 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.Rcode; import org.xbill.DNS.Record; import org.xbill.DNS.SOARecord; import org.xbill.DNS.Section; import org.xbill.DNS.TXTRecord; import org.xbill.DNS.Type; import org.xbill.DNS.WireParseException; /** * Servidor de consulta DNSBL. * * @author Leandro Carlos Rodrigues <leandro@spfbl.net> */ public final class QueryDNS extends Server { private final int PORT; private final DatagramSocket SERVER_SOCKET; /** * Mapa para cache dos registros DNS consultados. */ private static final HashMap<String, Zone> MAP = new HashMap<String, Zone>(); private static final long SERIAL = 2015102500; /** * Flag que indica se o cache foi modificado. */ private static boolean CHANGED = false; private static synchronized Zone dropExact(String token) { Zone ret = MAP.remove(token); if (ret == null) { return null; } else { CHANGED = true; return ret; } } private static synchronized boolean putExact(String key, ServerDNSBL server) { Zone ret = MAP.put(key, new Zone(server)); if (ret == null) { return false; } else if (server.getHostName().equals(ret.getHostName())) { return false; } else { return CHANGED = true; } } private static synchronized boolean putExact(String key, Zone zone) { Zone ret = MAP.put(key, zone); if (zone.equals(ret)) { return false; } else { return CHANGED = true; } } private static synchronized TreeSet<String> keySet() { TreeSet<String> keySet = new TreeSet<String>(); keySet.addAll(MAP.keySet()); return keySet; } private static synchronized HashMap<String, Zone> getMap() { HashMap<String, Zone> map = new HashMap<String, Zone>(); map.putAll(MAP); return map; } public static synchronized HashMap<String, Zone> getDNSBLMap() { HashMap<String, Zone> map = new HashMap<String, Zone>(); for (String key : MAP.keySet()) { Zone zone = MAP.get(key); if (zone.isDNSBL()) { map.put(key, zone); } } return map; } public static synchronized HashMap<String, Zone> getDNSWLMap() { HashMap<String, Zone> map = new HashMap<String, Zone>(); for (String key : MAP.keySet()) { Zone zone = MAP.get(key); if (zone.isDNSWL()) { map.put(key, zone); } } return map; } private static synchronized boolean containsExact(String host) { return MAP.containsKey(host); } private static synchronized Zone getExact(String host) { return MAP.get(host); } public static synchronized TreeSet<Zone> getValues() { TreeSet<Zone> serverSet = new TreeSet<Zone>(); serverSet.addAll(MAP.values()); return serverSet; } /** * Adiciona um registro DNS no mapa de cache. */ public static boolean addDNSBL(String hostname, String message) { if (hostname == null) { return false; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); Zone server = new Zone(Zone.Type.DNSBL, hostname, message); return putExact(hostname, server); } else { return false; } } /** * Adiciona um registro DNS no mapa de cache. */ public static boolean addDNSWL(String hostname, String message) { if (hostname == null) { return false; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); Zone server = new Zone(Zone.Type.DNSWL, hostname, message); return putExact(hostname, server); } else { return false; } } public static boolean set(String hostname, String message) { if (hostname == null) { return false; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); Zone server = getExact(hostname); if (server == null) { return false; } else { server.setMessage(message); return CHANGED = true; } } else { return false; } } private static Zone get(String hostname) { if (hostname == null) { return null; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); return getExact(hostname); } else { return null; } } public static TreeSet<Zone> dropAllDNSBL() { TreeSet<Zone> serverSet = new TreeSet<Zone>(); for (Zone zone : getValues()) { if (zone != null && zone.isDNSBL()) { String hostname = zone.getHostName(); zone = dropExact(hostname); if (zone != null) { serverSet.add(zone); } } } return serverSet; } public static TreeSet<Zone> dropAllDNSWL() { TreeSet<Zone> serverSet = new TreeSet<Zone>(); for (Zone zone : getValues()) { if (zone != null && zone.isDNSWL()) { String hostname = zone.getHostName(); zone = dropExact(hostname); if (zone != null) { serverSet.add(zone); } } } return serverSet; } public static Zone dropDNSBL(String hostname) { if (hostname == null) { return null; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); Zone zone = dropExact(hostname); if (zone.isDNSBL()) { return zone; } else if (putExact(hostname, zone)) { return null; } else { return zone; } } else { return null; } } public static Zone dropDNSWL(String hostname) { if (hostname == null) { return null; } else if (Domain.isHostname(hostname)) { hostname = Domain.normalizeHostname(hostname, true); Zone zone = dropExact(hostname); if (zone.isDNSWL()) { return zone; } else if (putExact(hostname, zone)) { return null; } else { return zone; } } else { return null; } } public static void store() { if (CHANGED) { try { // Server.logTrace("storing zone.map"); long time = System.currentTimeMillis(); File file = new File("./data/zone.map"); HashMap<String, Zone> map = getMap(); FileOutputStream outputStream = new FileOutputStream(file); try { SerializationUtils.serialize(map, outputStream); CHANGED = false; } finally { outputStream.close(); } Server.logStore(time, file); } catch (Exception ex) { Server.logError(ex); } } } public static void load() { long time = System.currentTimeMillis(); File file = new File("./data/zone.map"); if (file.exists()) { try { Map<String, Zone> map; FileInputStream fileInputStream = new FileInputStream(file); try { map = SerializationUtils.deserialize(fileInputStream); } finally { fileInputStream.close(); } for (String key : map.keySet()) { Zone value = map.get(key); putExact(key, value); } CHANGED = false; Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } else { file = new File("./data/dnsbl.map"); if (file.exists()) { try { Map<String, ServerDNSBL> map; FileInputStream fileInputStream = new FileInputStream(file); try { map = SerializationUtils.deserialize(fileInputStream); } finally { fileInputStream.close(); } for (String key : map.keySet()) { ServerDNSBL value = map.get(key); putExact(key, value); } CHANGED = false; Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } } } /** * Configurao e intanciamento do servidor. * @throws java.net.SocketException se houver falha durante o bind. */ public QueryDNS(int port) throws SocketException { super("SERVERDNS"); setPriority(Thread.NORM_PRIORITY); // Criando conexes. Server.logDebug("binding DNS socket on port " + port + "..."); PORT = port; SERVER_SOCKET = new DatagramSocket(port); Server.logTrace(getName() + " thread allocation."); } private int CONNECTION_ID = 1; /** * Representa uma conexo ativa. * Serve para processar todas as requisies. */ private class Connection extends Thread { /** * O poll de pacotes de consulta a serem processados. */ private DatagramPacket PACKET = null; private final Semaphore SEMAPHORE = new Semaphore(0); private long time = 0; public Connection() { super("DNSUDP" + Core.CENTENA_FORMAT.format(CONNECTION_ID++)); // Toda connexo recebe prioridade mnima. setPriority(Thread.NORM_PRIORITY); Server.logTrace(getName() + " thread allocation."); } /** * Processa um pacote de consulta. * @param packet o pacote de consulta a ser processado. */ private void process(DatagramPacket packet, long time) { this.PACKET = packet; this.time = time; this.SEMAPHORE.release(); } /** * Fecha esta conexo liberando a thread. */ private void close() { Server.logDebug("closing " + getName() + "..."); PACKET = null; SEMAPHORE.release(); } public DatagramPacket getPacket() { if (continueListenning()) { try { SEMAPHORE.acquire(); interrupted = false; } catch (InterruptedException ex) { Server.logError(ex); interrupted = true; } return PACKET; } else { return null; } } public void clearPacket() { time = 0; PACKET = null; } private NormalDistribution frequency = null; private long last = 0; private final int limit = 10; private long getIdleTimeMillis() { if (last == 0) { return 0; } else { return System.currentTimeMillis() - last; } } private boolean isDead() { int frequencyInt = frequency.getMaximumInt(); long idleTimeInt = getIdleTimeMillis(); return idleTimeInt > frequencyInt * 5 && idleTimeInt > 3600000; } private boolean isCongested() { if (frequency == null) { return false; } else if (isDead()) { return false; } else { return frequency.getMaximumInt() < limit; } } private Float getInterval() { long current = System.currentTimeMillis(); Float interval; if (last == 0) { interval = null; } else { interval = (float) (current - last); } last = current; return interval; } private boolean addQuery() { Float interval = getInterval(); if (interval == null) { return false; } else if (frequency == null) { frequency = new NormalDistribution(interval < 1000 ? 1000 : interval); return true; } else { frequency.addElement(interval); return true; } } private boolean interrupted = false; /** * Processamento da consulta e envio do resultado. * Aproveita a thead para realizar procedimentos em background. */ @Override public void run() { try { do { DatagramPacket packet; while ((packet = getPacket()) != null) { InetAddress ipAddress = packet.getAddress(); String origin = ipAddress.getHostAddress(); String query = "ERROR"; String result = "IGNORED"; String tag = "DNSQR"; try { byte[] data = packet.getData(); tag = "DNSQR"; // Processando consulta DNS. Message message = new Message(data); Header header = message.getHeader(); Record question = message.getQuestion(); if (question == null) { query = "NO QUESTION"; result = "IGNORED"; } else { Name name = question.getName(); String type = Type.string(question.getType()); query = name.toString(); if (interrupted) { result = "INTERRUPTED"; } else { // Identificao do cliente. Client client = Client.create(ipAddress, "DNSBL"); if (client == null) { result = "IGNORED"; } else if (client.hasPermission(Permission.NONE)) { this.addQuery(); client.addQuery(); origin += ' ' + client.getDomain(); result = "IGNORED"; } else if (isCongested() && client.isAbusing()) { this.addQuery(); client.addQuery(); origin += ' ' + client.getDomain(); result = "IGNORED"; } else { this.addQuery(); client.addQuery(); origin += ' ' + client.getDomain(); long ttl = 3600; // Uma hora padro. String host = Domain.extractHost(query, false); Zone zone = null; String clientQuery = null; if (host == null) { result = "NXDOMAIN"; } else { int index = host.length() - 1; host = host.substring(0, index); String hostname = null; String reverse = ""; if ((zone = getExact('.' + host)) == null) { while ((index = host.lastIndexOf('.', index)) != -1) { reverse = host.substring(0, index); hostname = host.substring(index); if ((zone = getExact(hostname)) == null) { index--; } else { break; } } } if (zone == null) { // No existe zona cadastrada. result = "NXDOMAIN"; } else if (type.equals("A") && zone.isHostName(host)) { // O A o prprio servidor. if ((result = Core.getHostname()) == null) { result = "NXDOMAIN"; } else { InetAddress address = InetAddress.getByName(result); result = address.getHostAddress(); } } else if (type.equals("NS") && zone.isHostName(host)) { // O NS o prprio servidor. if ((result = Core.getHostname()) == null) { result = "NXDOMAIN"; } else { result += '.'; } } else if (host.equals(hostname)) { // Consulta do prprio hostname do servidor. result = "NXDOMAIN"; } else if (reverse.length() == 0) { // O reverso invlido. result = "NXDOMAIN"; } else if (SubnetIPv4.isValidIPv4(reverse)) { // A consulta um IPv4. clientQuery = SubnetIPv4.reverseToIPv4(reverse); if (clientQuery.equals("127.0.0.1")) { // Consulta de teste para negativo. result = "NXDOMAIN"; } else if (clientQuery.equals("127.0.0.2")) { // Consulta de teste para positivo. result = "127.0.0.2"; ttl = 0; } else if (clientQuery.equals("127.0.0.3")) { if (client.isPassive()) { result = "NXDOMAIN"; } else { // Consulta de teste para positivo. result = "127.0.0.3"; ttl = 0; } } else if (zone.isDNSBL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (Block.containsCIDR(clientQuery)) { if (status == SPF.Status.RED) { result = "127.0.0.2"; ttl = 604800; // Sete dias. } else if (status == SPF.Status.YELLOW) { Analise.processToday(clientQuery); result = "127.0.0.2"; ttl = 432000; // Cinco dias. } else if (client.isPassive()) { Analise.processToday(clientQuery); result = "NXDOMAIN"; } else { Analise.processToday(clientQuery); result = "127.0.0.3"; ttl = 259200; // Trs dias. } } else if (status == SPF.Status.RED) { Analise.processToday(clientQuery); result = "127.0.0.2"; ttl = 86400; // Um dia. // } else if (Block.containsHREF(clientQuery)) { // result = "127.0.0.4"; // ttl = 86400; // Um dia. } else { Analise.processToday(clientQuery); result = "NXDOMAIN"; } } else if (zone.isDNSWL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (status != SPF.Status.GREEN) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Block.containsCIDR(clientQuery)) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Ignore.containsCIDR(clientQuery)) { if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; } else { result = "127.0.0.3"; } ttl = 604800; // Sete dias. } else if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; ttl = 259200; // Trs dias. } else if (White.containsIP(clientQuery)) { result = "127.0.0.4"; } else { result = "NXDOMAIN"; ttl = 86400; // Um dia. } } else { result = "NXDOMAIN"; } } else if (SubnetIPv6.isReverseIPv6(reverse)) { // A consulta um IPv6. clientQuery = SubnetIPv6.reverseToIPv6(reverse); if (clientQuery.equals("0:0:0:0:0:ffff:7f00:1")) { // Consulta de teste para negativo. result = "NXDOMAIN"; } else if (clientQuery.equals("0:0:0:0:0:ffff:7f00:2")) { // Consulta de teste para positivo. result = "127.0.0.2"; ttl = 0; } else if (clientQuery.equals("0:0:0:0:0:ffff:7f00:3")) { if (client.isPassive()) { result = "NXDOMAIN"; } else { // Consulta de teste para positivo. result = "127.0.0.3"; ttl = 0; } } else if (zone.isDNSBL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (Block.containsCIDR(clientQuery)) { if (status == SPF.Status.RED) { result = "127.0.0.2"; ttl = 604800; // Sete dias. } else if (status == SPF.Status.YELLOW) { Analise.processToday(clientQuery); result = "127.0.0.2"; ttl = 432000; // Cinco dias. } else if (client.isPassive()) { Analise.processToday(clientQuery); result = "NXDOMAIN"; } else { Analise.processToday(clientQuery); result = "127.0.0.3"; ttl = 259200; // Trs dias. } } else if (status == SPF.Status.RED) { Analise.processToday(clientQuery); result = "127.0.0.2"; ttl = 86400; // Um dia. // } else if (Block.containsHREF(clientQuery)) { // result = "127.0.0.4"; // ttl = 86400; // Um dia. } else if (status == SPF.Status.YELLOW) { Analise.processToday(clientQuery); result = "NXDOMAIN"; } else if (SubnetIPv6.isSLAAC(clientQuery)) { result = "NXDOMAIN"; } else { Analise.processToday(clientQuery); result = "NXDOMAIN"; } } else if (zone.isDNSWL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (status != SPF.Status.GREEN) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Block.containsCIDR(clientQuery)) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Ignore.containsCIDR(clientQuery)) { if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; } else { result = "127.0.0.3"; } ttl = 604800; // Sete dias. } else if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; ttl = 259200; // Trs dias. } else if (White.containsIP(clientQuery)) { result = "127.0.0.4"; } else { result = "NXDOMAIN"; ttl = 86400; // Um dia. } } else { result = "NXDOMAIN"; } } else if ((clientQuery = zone.extractDomain(host)) != null) { if (clientQuery.equals(".invalid")) { // Consulta de teste para negativo. result = "NXDOMAIN"; } else if (clientQuery.equals(".test")) { // Consulta de teste para positivo. result = "127.0.0.2"; ttl = 0; } else if (zone.isDNSBL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (Generic.containsDynamic(clientQuery)) { Analise.processToday(clientQuery); if (status == SPF.Status.GREEN) { result = "127.0.0.3"; ttl = 432000; // Cinco dias. } else { result = "127.0.0.2"; ttl = 604800; // Sete dias. } } else if (Block.containsDomain(clientQuery, true)) { if (status == SPF.Status.RED) { result = "127.0.0.2"; ttl = 604800; // Sete dias. } else if (status == SPF.Status.YELLOW) { result = "127.0.0.2"; ttl = 432000; // Cinco dias. } else if (client.isPassive()) { result = "NXDOMAIN"; } else { result = "127.0.0.3"; ttl = 259200; // Trs dias. } } else if (status == SPF.Status.RED) { Analise.processToday(clientQuery); result = "127.0.0.2"; ttl = 86400; // Um dia. } else if (Block.containsHREF(clientQuery)) { result = "127.0.0.4"; ttl = 86400; // Um dia. } else { Analise.processToday(clientQuery); result = "NXDOMAIN"; } } else if (zone.isDNSWL()) { SPF.Status status = SPF.getStatus(clientQuery, false); if (status != SPF.Status.GREEN) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Generic.containsGenericSoft(clientQuery)) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Block.containsDomain(clientQuery, true)) { result = "NXDOMAIN"; ttl = 86400; // Um dia. } else if (Ignore.containsHost(clientQuery)) { if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; } else { result = "127.0.0.3"; } ttl = 604800; // Sete dias. } else if (SPF.isGood(clientQuery)) { result = "127.0.0.2"; ttl = 259200; // Trs dias. } else if (White.containsDomain(clientQuery)) { result = "127.0.0.4"; } else { result = "NXDOMAIN"; ttl = 86400; // Um dia. } } else { result = "NXDOMAIN"; } clientQuery = Domain.normalizeHostname(clientQuery, false); } else { // No est listado. result = "NXDOMAIN"; } } if (zone == null) { tag = "DNSQR"; } else { tag = zone.getTypeName(); } if (type.equals("TXT") && result.startsWith("127.0.0.")) { if (zone == null) { result = "NXDOMAIN"; } else { String information = zone.getMessage(null, clientQuery); if (information == null) { result = "NXDOMAIN"; } else { result = information; } } } // Alterando mensagem DNS para resposta. header.setFlag(Flags.QR); header.setFlag(Flags.AA); if (result.equals("NXDOMAIN")) { header.setRcode(Rcode.NXDOMAIN); if (zone != null) { long refresh = 1800; long retry = 900; long expire = 604800; long minimum = 300; name = new Name(zone.getHostName().substring(1) + '.'); SOARecord soa = new SOARecord(name, DClass.IN, ttl, name, name, SERIAL, refresh, retry, expire, minimum); message.addRecord(soa, Section.AUTHORITY); } } else if (type.equals("TXT")) { TXTRecord txt = new TXTRecord(name, DClass.IN, ttl, result); message.addRecord(txt, Section.ANSWER); } else if (result.startsWith("127.0.0.")) { InetAddress address = InetAddress.getByName(result); ARecord a = new ARecord(name, DClass.IN, ttl, address); message.addRecord(a, Section.ANSWER); } else if (type.equals("NS")) { Name hostname = Name.fromString(result); NSRecord ns = new NSRecord(name, DClass.IN, ttl, hostname); message.addRecord(ns, Section.ANSWER); } else { InetAddress address = InetAddress.getByName(result); ARecord a = new ARecord(name, DClass.IN, ttl, address); message.addRecord(a, Section.ANSWER); } result = ttl + " " + result; // Enviando resposta. int portDestiny = packet.getPort(); byte[] sendData = message.toWire(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress, portDestiny); SERVER_SOCKET.send(sendPacket); } query = type + " " + query; } } } catch (SocketException ex) { // Houve fechamento do socket. result = "CLOSED"; } catch (WireParseException ex) { // Ignorar consultas invlidas. query = "UNPARSEABLE"; result = "IGNORED"; } catch (Exception ex) { Server.logError(ex); result = "ERROR"; } finally { Server.logQuery(time, tag, origin, query, result); clearPacket(); // Oferece a conexo ociosa na ltima posio da lista. offer(this); CONNECION_SEMAPHORE.release(); } } } while (interrupted); } catch (Exception ex) { Server.logError(ex); } finally { CONNECTION_COUNT--; Server.logTrace(getName() + " thread closed."); } } } /** * Pool de conexes ativas. */ private final LinkedList<Connection> CONNECTION_POLL = new LinkedList<Connection>(); private final LinkedList<Connection> CONNECTION_USE = new LinkedList<Connection>(); /** * Semforo que controla o pool de conexes. */ private final Semaphore CONNECION_SEMAPHORE = new Semaphore(0); /** * Quantidade total de conexes intanciadas. */ private int CONNECTION_COUNT = 0; private static byte CONNECTION_LIMIT = 16; public static void setConnectionLimit(String limit) { if (limit != null && limit.length() > 0) { try { setConnectionLimit(Integer.parseInt(limit)); } catch (Exception ex) { Server.logError("invalid DNS connection limit '" + limit + "'."); } } } public static void setConnectionLimit(int limit) { if (limit < 1 || limit > Byte.MAX_VALUE) { Server.logError("invalid DNS connection limit '" + limit + "'."); } else { CONNECTION_LIMIT = (byte) limit; } } private synchronized Connection poll() { return CONNECTION_POLL.poll(); } private synchronized void use(Connection connection) { CONNECTION_USE.offer(connection); } private synchronized void offer(Connection connection) { CONNECTION_USE.remove(connection); CONNECTION_POLL.offer(connection); } /** * Coleta uma conexo ociosa ou inicia uma nova. * @return uma conexo ociosa ou nova se no houver ociosa. */ private Connection pollConnection() { try { if (CONNECION_SEMAPHORE.tryAcquire(500, TimeUnit.MILLISECONDS)) { Connection connection = poll(); if (connection == null) { CONNECION_SEMAPHORE.release(); } else { use(connection); } return connection; } else if (CONNECTION_COUNT < CONNECTION_LIMIT) { // Cria uma nova conexo se no houver coneces ociosas. // O servidor aumenta a capacidade conforme a demanda. Server.logDebug("creating DNSUDP" + Core.CENTENA_FORMAT.format(CONNECTION_ID) + "..."); Connection connection = new Connection(); connection.start(); CONNECTION_COUNT++; return connection; } else { // Se no houver liberao, ignorar consulta DNS. // O MX que fizer a consulta ter um TIMEOUT // considerando assim o IP como no listado. return null; } } catch (Exception ex) { Server.logError(ex); return null; } } /** * Inicializao do servio. */ @Override public void run() { try { Server.logInfo("listening DNS on UDP port " + PORT + "."); while (continueListenning()) { try { byte[] receiveData = new byte[1024]; DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length); SERVER_SOCKET.receive(packet); if (continueListenning()) { long time = System.currentTimeMillis(); Connection connection = pollConnection(); if (connection == null) { InetAddress ipAddress = packet.getAddress(); String result = "TOO MANY CONNECTIONS\n"; Server.logQueryDNSBL(time, ipAddress, null, result); } else { try { connection.process(packet, time); } catch (IllegalThreadStateException ex) { // Houve problema na liberao do processo. InetAddress ipAddress = packet.getAddress(); String result = "ERROR: FATAL\n"; Server.logError(ex); Server.logQueryDNSBL(time, ipAddress, null, result); offer(connection); } } } } catch (SocketException ex) { // Conexo fechada externamente pelo mtodo close(). } } } catch (Exception ex) { Server.logError(ex); } finally { Server.logInfo("querie DNS server closed."); } } /** * Fecha todas as conexes e finaliza o servidor UDP. * @throws Exception se houver falha em algum fechamento. */ @Override protected void close() { while (CONNECTION_COUNT > 0) { try { Connection connection = poll(); if (connection == null) { CONNECION_SEMAPHORE.tryAcquire(500, TimeUnit.MILLISECONDS); } else { connection.close(); } } catch (Exception ex) { Server.logError(ex); } } Server.logDebug("unbinding DNS socket on port " + PORT + "..."); SERVER_SOCKET.close(); } }