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.whois; import net.spfbl.core.Server; import net.spfbl.core.ProcessException; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Serializable; import java.io.StringReader; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.regex.Pattern; import javax.naming.CommunicationException; import javax.naming.InvalidNameException; import javax.naming.NameNotFoundException; import javax.naming.OperationNotSupportedException; import javax.naming.ServiceUnavailableException; import net.spfbl.core.Core; import org.apache.commons.lang3.SerializationUtils; /** * Representa o registro de domnio de um resultado WHOIS. * * A chave primria dos registros o atributo domain. * * <h2>Mecanismo de busca</h2> * A busca no cache realizada com esta chave. * Porm possvel buscar um registro de domnio pelo host. * Caso o TLD do domnio seja conhecido, * o host convertido em domnio e a busca realizada em O(1). * Caso o TLD do host no seja conhecido, * Uma consulta no WHOIS realizada pelo, * onde o mesmo retorna o registro do domnio correto. * Com posse deste domnio correto, * o novo TLD encontrado e adicionado no conjunto de TLDs conhecidos. * O mecanismo totalmente automtico, portanto no existe * necessidade de manter e administrar uma lista de TLDs manualmente. * * @author Leandro Carlos Rodrigues <leandro@spfbl.net> */ public class Domain implements Serializable, Comparable<Domain> { private static final long serialVersionUID = 1L; private final String domain; // Domnio real indicado pelo WHOIS. private String owner; // Nome do dono do domnio. private String ownerid; // Identificao do dono do domnio. private String responsible; // Responsvel pelo domnio. private String country; // Pas onde o domnio foi registrado. private String owner_c; // Cdigo do dono do domnio. private String admin_c; // Cdigo do administrador do domnio. private String tech_c; // Cdigo do responsvel tcnico do domnio. private String billing_c; // Cdigo do responsvel pelos pagamentos do domnio. private Date created; // Data de criao do domnio pelo dono atual. private Date expires; // Data de expirao do registro do domnio. private Date changed; // Data da alterao do registro do domnio. private String provider; // Provedor de acesso do domnio. private String status; // Status atual do domnio. private String dsrecord; private String dsstatus; private String dslastok; private String saci; private String web_whois; /** * Lista dos servidores de nome do domnio. */ private final ArrayList<String> nameServerList = new ArrayList<String>(); private String server = null; // Servidor onde a informao do domnio pode ser encontrada. private long lastRefresh = 0; // ltima vez que houve atualizao do registro em milisegundos. private boolean reduced = false; // Diz se a ltima consulta foi reduzida. private int queries = 1; // Contador de consultas. private static int REFRESH_TIME = 21; // Prazo mximo que o registro deve permanecer em cache em dias. /** * Atualiza o tempo de expirao do registro de domnio. * @param time tempo em dias que os registros de domno devem ser atualizados. */ public static void setRefreshTime(int time) { REFRESH_TIME = time; } /** * Verifica se o registro atual expirou. * @return verdadeiro se o registro atual expirou. */ public boolean isRegistryExpired() { long expiredTime = (System.currentTimeMillis() - lastRefresh) / Server.DAY_TIME; if (isGraceTime()) { return expiredTime > 0; } else { return expiredTime > REFRESH_TIME; } } /** * Verifica se o registro atual est com informao reduzida. * @return verdadeiro se o registro atual com informao reduzida. */ public boolean isReduced() { return reduced; } /** * Extrai o host de um endereo de e-mail. * @param address o endereo que contm o host. * @return o host do endereo de e-mail. */ public static String extractHost(String address, boolean pontuacao) { if (address == null) { return null; } else if (address.length() == 0) { return null; } else if (address.contains("@")) { // O endereo um e-mail. // Extrair a parte do host. int index = address.indexOf('@'); if (!pontuacao) { index++; } return Core.removerAcentuacao(address.substring(index)).toLowerCase(); } else if (!Domain.isHostname(address)) { return null; } else if (pontuacao && !address.startsWith(".")) { return "." + Core.removerAcentuacao(address).toLowerCase(); } else if (pontuacao && address.startsWith(".")) { return Core.removerAcentuacao(address).toLowerCase(); } else if (!pontuacao && !address.startsWith(".")) { return Core.removerAcentuacao(address).toLowerCase(); } else { return Core.removerAcentuacao(address.substring(1)).toLowerCase(); } } public static boolean isOfficialTLD(String address) { if (address.contains("@")) { int index = address.lastIndexOf('@') + 1; address = '.' + address.substring(index); return TLD_SET.contains(address); } else { return TLD_SET.contains(address); } } public static boolean isDomain(String address) { if ((address = extractHost(address, true)) == null) { return false; } else { int index = address.indexOf('.', 1); if (index == -1) { return !TLD_SET.contains(address); } else { String tld = address.substring(index); return TLD_SET.contains(tld); } } } /** * Extrai o domnio pelos TLDs conhecidos. * @param address o endereo que contm o domnio. * @param pontuacao se o ponto deve ser mantido na resposta. * @return o domnio pelos TLDs conhecidos. * @throws ProcessException se o endereo for um TLD. */ public static String extractDomain(String address, boolean pontuacao) throws ProcessException { if ((address = extractHost(address, true)) == null) { return null; } else if (isOfficialTLD(address)) { throw new ProcessException("ERROR: RESERVED"); } else { int lastIndex = address.length() - 1; int beginIndex = 1; while (beginIndex < lastIndex) { int endIndex = address.indexOf('.', beginIndex); if (endIndex == -1) { break; } else { String tld = address.substring(endIndex); if (TLD_SET.contains(tld)) { if (pontuacao) { return address.substring(beginIndex - 1); } else { return address.substring(beginIndex); } } beginIndex = endIndex + 1; } } beginIndex = address.lastIndexOf('.'); int endIndex = address.length(); if (pontuacao) { return "." + address.substring(beginIndex + 1, endIndex); } else { return address.substring(beginIndex + 1, endIndex); } } } /** * Extrai o TLD do endereo. * @param address o endereo que contm o TLD. * @param ponto se o ponto de ser mantido. * @return o TLDs do endereo. * @throws ProcessException se houve faha na extrao do domnio. */ public static String extractTLD(String address, boolean ponto) throws ProcessException { int lastIndex = address.length() - 1; int beginIndex = 0; while (beginIndex < lastIndex) { int endIndex = address.indexOf('.', beginIndex); if (endIndex == -1) { break; } else { String tld = address.substring(endIndex); if (TLD_SET.contains(tld)) { if (ponto) { return tld; } else { return tld.substring(1); } } beginIndex = endIndex + 1; } } beginIndex = address.lastIndexOf('.'); if (beginIndex == -1) { return (ponto ? "." : "") + address; } else if (beginIndex == 0) { return ponto ? address : address.substring(1); } else if (ponto) { return address.substring(beginIndex); } else { return address.substring(beginIndex + 1); } } /** * Verifica se o endereo contm um domnio. * @param address o endereo a ser verificado. * @return verdadeiro se o endereo contm um domnio. */ public static boolean containsDomain(String address) { if (address == null) { return false; } else { address = address.trim(); if (SubnetIPv4.isValidIPv4(address)) { return false; } else { address = address.toLowerCase(); return Pattern.matches( "^([a-zA-Z0-9._%+=-]+@)?" + "(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9])" + "(\\.([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]))*)" + "$", address); } } } /** * Verifica se o endereo contm um domnio. * @param address o endereo a ser verificado. * @return verdadeiro se o endereo contm um domnio. */ public static boolean isHostname(String address) { if (address == null) { return false; } else { address = address.trim(); if (SubnetIPv4.isValidIPv4(address)) { return false; } else { address = address.toLowerCase(); return Pattern.matches( "^\\.?" + "(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9_])" + "(\\.([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]))*)" + "\\.?$", address); } } } /** * Extrai o host de um endereo de e-mail. * @param address o endereo que contm o host. * @param pontuacao se o arroba deve ser mantido na resposta. * @return o host do endereo de e-mail. */ public static String normalizeHostname(String address, boolean pontuacao) { if (address == null) { return null; } else { address = address.replace(" ", ""); if (address.endsWith(".")) { address = address.substring(0, address.length() - 1); } if (address.length() == 0) { return null; } else if (address.contains("@")) { // O endereo um e-mail. // Extrair a parte do host. int index = address.indexOf('@'); if (!pontuacao) { index++; } return Core.removerAcentuacao(address.substring(index)).toLowerCase(); } else if (pontuacao && !address.startsWith(".")) { return "." + Core.removerAcentuacao(address).toLowerCase(); } else if (pontuacao && address.startsWith(".")) { return Core.removerAcentuacao(address).toLowerCase(); } else if (!pontuacao && !address.startsWith(".")) { return Core.removerAcentuacao(address).toLowerCase(); } else { return Core.removerAcentuacao(address.substring(1)).toLowerCase(); } } } /** * Verifica se o endereo um e-mail vlido. * @param address o endereo a ser verificado. * @return verdadeiro se o endereo um e-mail vlido. */ public static boolean isEmail(String address) { if (address == null) { return false; } else { address = address.trim(); address = address.toLowerCase(); if (Pattern.matches("^" + "[0-9a-zA-Z--?--?----._%/+=-]+" + "@" + "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9])" + "(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]))*)" + "$", address)) { int index = address.indexOf('@'); String domain = address.substring(index + 1); return Domain.isHostname(domain); } else { return false; } } } public static boolean isValidEmail(String address) { if (address == null) { return false; } else { address = address.trim(); address = address.toLowerCase(); if (Pattern .matches( "^[0-9a-zA-Z._-]+" + "@" + "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9])" + "(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]))*)" + "$", address)) { int index = address.indexOf('@'); String domain = address.substring(index + 1); return Domain.isHostname(domain); } else { return false; } } } /** * Verifica se o endereo um TLD vlido. * @param address o endereo a ser verificado. * @return verdadeiro se o endereo um TLD vlido. */ public static boolean isValidTLD(String address) { address = address.trim(); address = address.toLowerCase(); return Pattern.matches("^(\\.([a-z0-9]|[a-z0-9][a-z0-9-]+[a-z0-9])+)+$", address); } /** * Conjunto de todos os top domain level (TLD) conhecidos. */ public static final HashSet<String> TLD_SET = new HashSet<String>(); /** * Flag que indica se o cache foi modificado. */ private static boolean TLD_CHANGED = false; /** * Intancia um novo registro de domnio. * @param result o resultado do WHOIS. * @throws QueryException se houver alguma falha da atualizao do registro. */ private Domain(String result) throws ProcessException { this.domain = refresh(result); } private boolean refresh() throws ProcessException { server = getWhoisServer(domain); // Temporrio at final de transio. String result = Server.whois(domain, server); String domainResult = refresh(result); return domain.equals(domainResult); } public static synchronized TreeSet<String> getTLDSet() throws ProcessException { TreeSet<String> tldSet = new TreeSet<String>(); tldSet.addAll(TLD_SET); return tldSet; } public static synchronized boolean addTLD(String tld) throws ProcessException { if (tld.charAt(0) != '.') { // Corrigir TLD sem ponto. tld = "." + tld; } if (Domain.isValidTLD(tld)) { tld = tld.toLowerCase(); if (TLD_SET.add(tld)) { // Atualiza flag de atualizao. TLD_CHANGED = true; return true; } else { return false; } } else { throw new ProcessException("ERROR: TLD INVALID"); } } public static synchronized boolean dropExactTLD(String tld) throws ProcessException { if (TLD_SET.remove(tld)) { // Atualiza flag de atualizao. TLD_CHANGED = true; return true; } else { return false; } } public static TreeSet<String> dropAllTLD() throws ProcessException { TreeSet<String> tldSet = new TreeSet<String>(); for (String tld : getTLDSet()) { if (dropExactTLD(tld)) { tldSet.add(tld); } } return tldSet; } public static boolean removeTLD(String tld) throws ProcessException { if (tld.charAt(0) != '.') { // Corrigir TLD sem ponto. tld = "." + tld; } if (Domain.isValidTLD(tld)) { tld = tld.toLowerCase(); return dropExactTLD(tld); } else { throw new ProcessException("ERROR: TLD INVALID"); } } /** * Atualiza os campos do registro com resultado do WHOIS. * @param result o resultado do WHOIS. * @return o domnio real apresentado no resultado do WHOIS. * @throws QueryException se houver alguma falha da atualizao do registro. */ private String refresh(String result) throws ProcessException { try { String ownerNew = null; String owneridNew = null; String responsibleNew = null; String countryNew = null; String owner_cNew = null; String admin_cNew = null; String tech_cNew = null; String billing_cNew = null; Date createdNew = null; Date expiresNew = null; Date changedNew = null; String providerNew = null; String statusNew = null; String dsrecordNew = null; String dsstatusNew = null; String dslastokNew = null; String saciNew = null; String web_whoisNew = null; ArrayList<String> nameServerListNew = new ArrayList<String>(); boolean reducedNew = false; String domainResult = null; SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd"); BufferedReader reader = new BufferedReader(new StringReader(result)); try { String line; while ((line = reader.readLine()) != null) { try { line = line.trim(); if (line.startsWith("domain:")) { int index = line.indexOf(':') + 1; domainResult = line.substring(index).trim(); // Remove a verso de domnios com acentuao. index = domainResult.lastIndexOf(' ') + 1; domainResult = domainResult.substring(index); // Descobre o TLD do domnio e adiciona no conjunto. index = domainResult.indexOf('.'); String tld = domainResult.substring(index); addTLD(tld); } else if (line.startsWith("owner:")) { int index = line.indexOf(':') + 1; ownerNew = line.substring(index).trim(); } else if (line.startsWith("ownerid:")) { int index = line.indexOf(':') + 1; owneridNew = line.substring(index).trim(); } else if (line.startsWith("p.a. to:")) { // Este cammpo "p.a. to" (power of attorney to) // equivalente ao ownerid. A diferena que // neste caso o ownerid do procurador invs // do prprio dono extrangeiro representado. // https://registro.br/dominio/reg-estrangeiros.html int index = line.indexOf(':') + 1; owneridNew = line.substring(index).trim(); } else if (line.startsWith("responsible:")) { int index = line.indexOf(':') + 1; responsibleNew = line.substring(index).trim(); } else if (line.startsWith("country:")) { int index = line.indexOf(':') + 1; countryNew = line.substring(index).trim(); } else if (line.startsWith("owner-c:")) { int index = line.indexOf(':') + 1; owner_cNew = line.substring(index).trim(); } else if (line.startsWith("admin-c:")) { int index = line.indexOf(':') + 1; admin_cNew = line.substring(index).trim(); } else if (line.startsWith("tech-c:")) { int index = line.indexOf(':') + 1; tech_cNew = line.substring(index).trim(); } else if (line.startsWith("billing-c:")) { int index = line.indexOf(':') + 1; billing_cNew = line.substring(index).trim(); } else if (line.startsWith("nserver:")) { int index = line.indexOf(':') + 1; String nserver = line.substring(index).trim(); line = reader.readLine().trim(); index = line.indexOf(':') + 1; String nsstat = line.substring(index).trim(); line = reader.readLine().trim(); index = line.indexOf(':') + 1; String nslastaa = line.substring(index).trim(); NameServer ns = NameServer.getNameServer(nserver); ns.setStat(nsstat); ns.setLastAA(nslastaa); nameServerListNew.add(nserver); } else if (line.startsWith("created:")) { int index = line.indexOf(':') + 1; String valor = line.substring(index).trim(); if (valor.equals("multiple points")) { valor = null; } else if (valor.startsWith("before ")) { index = line.indexOf(' ') - 1; valor = valor.substring(index).trim(); } if (valor != null && valor.length() > 7) { valor = valor.substring(0, 8); createdNew = dateFormatter.parse(valor); } } else if (line.startsWith("changed:")) { int index = line.indexOf(':') + 1; String valor = line.substring(index).trim(); if (valor.length() > 7) { valor = valor.substring(0, 8); changedNew = dateFormatter.parse(valor); } } else if (line.startsWith("expires:")) { int index = line.indexOf(':') + 1; String valor = line.substring(index).trim(); if (valor.length() > 7) { valor = valor.substring(0, 8); expiresNew = dateFormatter.parse(valor); } } else if (line.startsWith("status:")) { int index = line.indexOf(':') + 1; statusNew = line.substring(index).trim(); } else if (line.startsWith("dsrecord:")) { int index = line.indexOf(':') + 1; dsrecordNew = line.substring(index).trim(); } else if (line.startsWith("dsstatus:")) { int index = line.indexOf(':') + 1; dsstatusNew = line.substring(index).trim(); } else if (line.startsWith("dslastok:")) { int index = line.indexOf(':') + 1; dslastokNew = line.substring(index).trim(); } else if (line.startsWith("saci:")) { int index = line.indexOf(':') + 1; saciNew = line.substring(index).trim(); } else if (line.startsWith("web-whois:")) { int index = line.indexOf(':') + 1; web_whoisNew = line.substring(index).trim(); } else if (line.startsWith("provider:")) { int index = line.indexOf(':') + 1; providerNew = line.substring(index).trim(); } else if (line.startsWith("nic-hdl-br:")) { try { String person = null; String e_mail = null; String created2 = null; String changed2 = null; String provider2 = null; String country2 = null; int index = line.indexOf(':') + 1; String nic_hdl_br = line.substring(index).trim(); while ((line = reader.readLine().trim()).length() > 0) { index = line.indexOf(':') + 1; if (line.startsWith("person:")) { person = line.substring(index).trim(); } else if (line.startsWith("e-mail:")) { e_mail = line.substring(index).trim(); } else if (line.startsWith("created:")) { created2 = line.substring(index).trim(); } else if (line.startsWith("changed:")) { changed2 = line.substring(index).trim(); } else if (line.startsWith("provider:")) { provider2 = line.substring(index).trim(); } else if (line.startsWith("country:")) { country2 = line.substring(index).trim(); } else { Server.logError("Linha no reconhecida: " + line); } } Handle handle = Handle.getHandle(nic_hdl_br); handle.setPerson(person); handle.setEmail(e_mail); handle.setCreated(created2); handle.setChanged(changed2); handle.setProvider(provider2); handle.setCountry(country2); } catch (ProcessException ex) { Server.logError(ex); } } else if (line.startsWith("ticket:")) { // Do nothing. } else if (line.startsWith("% No match for domain")) { throw new ProcessException("ERROR: DOMAIN NOT FOUND"); } else if (line.startsWith("% release process: ")) { throw new ProcessException("ERROR: WAITING"); } else if (line.startsWith("% reserved: CG")) { throw new ProcessException("ERROR: RESERVED"); } else if (line.startsWith("% Permission denied.")) { throw new ProcessException("ERROR: WHOIS DENIED"); } else if (line.startsWith("% Permisso negada.")) { throw new ProcessException("ERROR: WHOIS DENIED"); } else if (line.startsWith("% Maximum concurrent connections limit exceeded")) { throw new ProcessException("ERROR: WHOIS CONCURRENT"); } else if (line.startsWith("% Query rate limit exceeded. Reduced information.")) { // Informao reduzida devido ao estouro de limite de consultas. Server.removeWhoisQueryHour(); reducedNew = true; } else if (line.startsWith("% Query rate limit exceeded")) { // Restrio total devido ao estouro de limite de consultas. Server.removeWhoisQueryDay(); throw new ProcessException("ERROR: WHOIS QUERY LIMIT"); } else if (line.length() > 0 && Character.isLetter(line.charAt(0))) { Server.logError("Linha no reconhecida: " + line); } } catch (NumberFormatException ex) { Server.logError(ex); Server.logError(line); } } } finally { reader.close(); } if (domainResult == null) { throw new ProcessException("ERROR: DOMAIN NOT FOUND"); } else { this.owner = ownerNew; if (owneridNew != null) { // Associar ownerid somente se retornar valor. this.ownerid = owneridNew; } this.responsible = responsibleNew; this.country = countryNew; this.owner_c = owner_cNew; this.admin_c = admin_cNew; this.tech_c = tech_cNew; this.billing_c = billing_cNew; this.created = createdNew; this.expires = expiresNew; this.changed = changedNew; this.provider = providerNew; this.status = statusNew; this.dsrecord = dsrecordNew; this.dsstatus = dsstatusNew; this.dslastok = dslastokNew; this.saci = saciNew; this.web_whois = web_whoisNew; this.nameServerList.clear(); this.nameServerList.addAll(nameServerListNew); this.reduced = reducedNew; DOMAIN_CHANGED = true; this.lastRefresh = System.currentTimeMillis(); this.queries = 1; // Retorna o domnio real indicado pelo WHOIS. return domainResult; } } catch (ProcessException ex) { throw ex; } catch (Exception ex) { Server.logError(ex); throw new ProcessException("ERROR: PARSING " + result, ex); } } public Owner getOwner() throws ProcessException { return Owner.getOwner(ownerid); } public Handle getOwnerHandle() { return Handle.getHandle(owner_c); } public Handle getAdminHandle() { return Handle.getHandle(admin_c); } public Handle getTechHandle() { return Handle.getHandle(tech_c); } public Handle getBillingHandle() { return Handle.getHandle(billing_c); } public static boolean isGraceTime(String address) { try { if (address == null) { return false; } else if (address.endsWith(".br")) { Domain domain = Domain.getDomain(address); if (domain == null) { return false; } else { return domain.isGraceTime(); } } else { return false; } } catch (ProcessException ex) { if (ex.isErrorMessage("WAITING")) { return true; } else if (ex.isErrorMessage("DOMAIN NOT FOUND")) { return false; } else if (ex.isErrorMessage("TOO MANY CONNECTIONS")) { return false; } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { return false; } else { Server.logError(ex); return false; } } } public int getLifeTime() { if (created == null) { return 0; } else { return (int) ((System.currentTimeMillis() - created.getTime()) / 86400000); } } public boolean isGraceTime() { if (expires == null && getLifeTime() < 7) { String domain = getDomain(); int beginIndex = domain.indexOf('.', 1); int endIndex = domain.length(); String tld = domain.substring(beginIndex, endIndex); if (tld.equals(".br")) { return false; } else if (tld.equals(".edu.br")) { return false; } else if (tld.equals(".mil.br")) { return false; } else if (tld.equals(".gov.br")) { return false; } else if (tld.equals(".leg.br")) { return false; } else if (tld.equals(".def.br")) { return false; } else if (tld.equals(".jus.br")) { return false; } else if (tld.equals(".mp.br")) { return false; } else { return tld.endsWith(".br"); } // } else { // return expires.equals(created); } else { return false; } } /** * Retorna o valor de um campo do registro ou o valor de uma funo. * @param key o campo do registro cujo valor deve ser retornado. * @return o valor de um campo do registro ou o valor de uma funo. * @throws ProcessException se houver falha no processamento. */ public String get(String key, boolean updated) throws ProcessException { if (key.equals("domain")) { return domain; } else if (key.equals("owner")) { return owner; } else if (key.equals("responsible")) { return responsible; } else if (key.equals("country")) { return country; } else if (key.equals("owner-c")) { return owner_c; } else if (key.equals("admin-c")) { return admin_c; } else if (key.equals("tech-c")) { return tech_c; } else if (key.equals("billing-c")) { return billing_c; } else if (key.equals("created")) { if (created == null) { return null; } else { return new SimpleDateFormat("yyyyMMdd").format(created); } } else if (key.equals("expires")) { if (expires == null) { return null; } else { return new SimpleDateFormat("yyyyMMdd").format(expires); } } else if (key.equals("changed")) { if (changed == null) { return null; } else { return new SimpleDateFormat("yyyyMMdd").format(changed); } } else if (key.equals("provider")) { return provider; } else if (key.equals("status")) { return status; } else if (key.equals("dsrecord")) { return dsrecord; } else if (key.equals("dsstatus")) { return dsstatus; } else if (key.equals("dslastok")) { return dslastok; } else if (key.equals("saci")) { return saci; } else if (key.equals("web-whois")) { return web_whois; } else if (key.equals("nserver")) { return nameServerList.toString(); } else if (key.equals("EXPIRATION")) { // Funo que retorna o tempo em dias para expirao do domnio. if (expires == null) { return "NEVER"; } else { long lifetime = (System.currentTimeMillis() - expires.getTime()) / 86400000; return Long.toString(lifetime); } } else if (key.equals("EXPIRED")) { // Funo que retorna o se o domnio est expirado. if (expires == null) { return "false"; } else if (created.after(expires)) { return "true"; } else { return "false"; } } else if (key.startsWith("owner/")) { int index = key.indexOf('/') + 1; key = key.substring(index); return getOwner().get(key, updated); } else if (key.startsWith("owner-c/")) { int index = key.indexOf('/') + 1; key = key.substring(index); Handle owner = getOwnerHandle(); if (owner == null) { return null; } else { return owner.get(key); } } else if (key.startsWith("admin-c/")) { int index = key.indexOf('/') + 1; key = key.substring(index); Handle admin = getAdminHandle(); if (admin == null) { return null; } else { return admin.get(key); } } else if (key.startsWith("tech-c/")) { int index = key.indexOf('/') + 1; key = key.substring(index); Handle tech = getTechHandle(); if (tech == null) { return null; } else { return tech.get(key); } } else if (key.startsWith("billing-c/")) { int index = key.indexOf('/') + 1; key = key.substring(index); Handle billing = getBillingHandle(); if (billing == null) { return null; } else { return billing.get(key); } } else if (key.startsWith("nserver/")) { int index = key.indexOf('/') + 1; key = key.substring(index); TreeSet<String> resultSet = new TreeSet<String>(); for (String nserver : nameServerList) { NameServer nameServer = NameServer.getNameServer(nserver); String result = nameServer.get(key); resultSet.add(nserver + "=" + result); } return resultSet.toString(); } else if (reduced && updated) { // Ultima consulta reduzida. // Demais campos esto comprometidos. throw new ProcessException("ERROR: WHOIS QUERY LIMIT"); } else if (key.equals("ownerid")) { return ownerid; } else { return null; } } public static String getValue(String address, String key) { if (address == null || key == null) { return null; } else { try { Domain domain = Domain.getDomain(address); if (domain == null) { return null; } else { return domain.get(key, false); } } catch (ProcessException ex) { if (ex.isErrorMessage("NSLOOKUP")) { return null; } else if (ex.isErrorMessage("WAITING")) { return null; } else if (ex.isErrorMessage("DOMAIN NOT FOUND")) { return null; } else if (ex.isErrorMessage("RESERVED")) { return null; } else if (ex.isErrorMessage("WHOIS CONNECTION FAIL")) { return null; } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { return null; } else if (ex.isErrorMessage("TOO MANY CONNECTIONS")) { return null; } else { Server.logError(ex); return null; } } } } public static Date getCreated(String address) { if (address == null) { return null; } else { try { Domain domain = Domain.getDomain(address); if (domain == null) { return null; } else { return domain.created; } } catch (ProcessException ex) { if (ex.getMessage().equals("ERROR: NSLOOKUP")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else if (ex.getMessage().equals("ERROR: DOMAIN NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else { Server.logError(ex); return null; } } } } public Date getCreated() { return created; } /** * Armazenamento de cache em disco. */ public static void store() { storeDomain(); storeTLD(); } private static synchronized HashMap<String, Domain> getDomainMap() { HashMap<String, Domain> map = new HashMap<String, Domain>(); map.putAll(MAP); return map; } private static void storeDomain() { if (DOMAIN_CHANGED) { try { // Server.logTrace("storing domain.map"); long time = System.currentTimeMillis(); File file = new File("./data/domain.map"); HashMap<String, Domain> map = getDomainMap(); FileOutputStream outputStream = new FileOutputStream(file); try { SerializationUtils.serialize(map, outputStream); // Atualiza flag de atualizao. DOMAIN_CHANGED = false; } finally { outputStream.close(); } Server.logStore(time, file); } catch (Exception ex) { Server.logError(ex); } } } private static synchronized TreeSet<String> getSetTLD() { TreeSet<String> set = new TreeSet<String>(); set.addAll(TLD_SET); return set; } private static void storeTLD() { if (TLD_CHANGED) { try { // Server.logTrace("storing tld.map"); long time = System.currentTimeMillis(); File file = new File("./data/tld.set"); TreeSet<String> set = getSetTLD(); FileOutputStream outputStream = new FileOutputStream(file); try { SerializationUtils.serialize(set, outputStream); // Atualiza flag de atualizao. TLD_CHANGED = false; } finally { outputStream.close(); } Server.logStore(time, file); } catch (Exception ex) { Server.logError(ex); } } } private static synchronized Domain put(String key, Domain domain) { return MAP.put(key, domain); } private static synchronized void addAll(Collection<String> set) { for (String tld : set) { if (!tld.startsWith(".")) { tld = '.' + tld; } TLD_SET.add(tld); } } /** * Carregamento de cache do disco. */ public static void load() { long time = System.currentTimeMillis(); File file = new File("./data/domain.map"); if (file.exists()) { try { HashMap<String, Object> map; FileInputStream fileInputStream = new FileInputStream(file); try { map = SerializationUtils.deserialize(fileInputStream); } finally { fileInputStream.close(); } for (String key : map.keySet()) { Object value = map.get(key); if (value instanceof Domain) { Domain domain = (Domain) value; put(key, domain); } } Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } time = System.currentTimeMillis(); file = new File("./data/tld.set"); if (file.exists()) { try { Collection<String> set; FileInputStream fileInputStream = new FileInputStream(file); try { set = SerializationUtils.deserialize(fileInputStream); } finally { fileInputStream.close(); } addAll(set); Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } } /** * Verifica a existncia de registros DNS do host. * @param host o host que deve ser consultado. * @throws QueryException se o host no tiver * registrado em DNS ou se houver falha de DNS. */ private static void checkHost(String host) throws ProcessException { long time = System.currentTimeMillis(); try { // Verifica se o domnio tem algum registro de diretrio vlido. Server.getAttributesDNS(host, null); } catch (NameNotFoundException ex) { // Server.logCheckDNS(time, host, "NXDOMAIN"); throw new ProcessException("ERROR: DOMAIN NOT FOUND"); } catch (CommunicationException ex) { Server.logCheckDNS(time, host, "TIMEOUT"); } catch (ServiceUnavailableException ex) { Server.logCheckDNS(time, host, "SERVFAIL"); } catch (InvalidNameException ex) { Server.logCheckDNS(time, host, "INVALID"); } catch (OperationNotSupportedException ex) { Server.logCheckDNS(time, host, "REFUSED"); } catch (Exception ex) { // Houve uma falha indefinida para encontrar os registros. Server.logError(ex); } } /** * Retorna o sevidor WHOIS para um determinado host. * @param host o host cujo servior WHOIS tem as informaes do domnio. * @return o sevidor WHOIS para um determinado host. * @throws QueryException se nenhum servidor WHOIS for encontrado para o host. */ private static String getWhoisServer(String host) throws ProcessException { if (host.endsWith(".br")) { return "whois.nic.br"; } else { throw new ProcessException("ERROR: SERVER NOT FOUND"); } } /** * Mapa de domnios com busca de hash O(1). */ private static final HashMap<String, Domain> MAP = new HashMap<String, Domain>(); public synchronized boolean drop() { if (MAP.remove(getDomain()) != null) { DOMAIN_CHANGED = true; return true; } else { return false; } } /** * Remove registro de domnio do cache. * @param host o host cujo registro de domnio deve ser removido. * @return o registro de domnio removido, se existir. * @throws ProcessException se houver falha no processamento. */ public static synchronized Domain removeDomain(String host) throws ProcessException { String key = extractDomain(host, false); Domain domain = MAP.remove(key); // Atualiza flag de atualizao. DOMAIN_CHANGED = true; return domain; } /** * Flag que indica se o cache foi modificado. */ private static boolean DOMAIN_CHANGED = false; public static synchronized TreeSet<Domain> getDomainSet() { TreeSet<Domain> domainSet = new TreeSet<Domain>(); domainSet.addAll(MAP.values()); return domainSet; } public static synchronized TreeSet<String> getDomainNameSet() { TreeSet<String> domainSet = new TreeSet<String>(); domainSet.addAll(MAP.keySet()); return domainSet; } /** * Atualiza em background todos os registros adicionados no conjunto. */ public static boolean backgroundRefresh() { Domain domainMax = null; for (Domain domain : getDomainSet()) { if (domain.isReduced() || domain.isRegistryExpired()) { if (domain.queries > 3) { if (domainMax == null) { domainMax = domain; } else if (domainMax.queries < domain.queries) { domainMax = domain; } else if (domainMax.lastRefresh > domain.lastRefresh) { domainMax = domain; } } } } if (domainMax == null) { return false; } else { try { // Atualizando campos do registro. return domainMax.refresh(); } catch (ProcessException ex) { if (ex.isErrorMessage("WAITING")) { domainMax.drop(); } else if (ex.isErrorMessage("DOMAIN NOT FOUND")) { domainMax.drop(); } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { // Fazer nada. } else if (ex.isErrorMessage("WHOIS CONNECTION FAIL")) { // Fazer nada. } else if (ex.isErrorMessage("TOO MANY CONNECTIONS")) { // Fazer nada. } else { Server.logError(ex); } return false; } catch (Exception ex) { Server.logError(ex); return false; } } } /** * Atualiza o registro de domnio de um determinado host. * @param address o endereo cujo registro de domnio deve ser atualizado. * @throws ProcessException se houver falha no processamento. */ public static synchronized void refreshDomain(String address) throws ProcessException { String key = extractDomain(address, false); // Busca eficiente O(1). if (MAP.containsKey(key)) { // Domnio encontrado. Domain domain = MAP.get(key); // Atualizando campos do registro. if (!domain.refresh()) { // Domnio real do resultado WHOIS no bate com o registro. // Pode haver a criao de uma nova TLD. // Apagando registro de domnio do cache. if (MAP.remove(domain.getDomain()) != null) { // Atualiza flag de atualizao. DOMAIN_CHANGED = true; } // Segue para nova consulta. } } else { // Extrair o host se for e-mail. String host = extractHost(address, false); // No encontrou o dominio em cache. // Selecionando servidor da pesquisa WHOIS. String server = getWhoisServer(host); // Verifica o DNS do host antes de fazer a consulta no WHOIS. // Evita consulta desnecessria no WHOIS. checkHost(host); // Domnio existente. // Realizando a consulta no WHOIS. String result = Server.whois(host, server); try { Domain domain = new Domain(result); domain.server = server; // Temporrio at final de transio. // Adicinando registro em cache. MAP.put(domain.getDomain(), domain); DOMAIN_CHANGED = true; } catch (ProcessException ex) { if (ex.isErrorMessage("RESERVED")) { // A chave de busca um TLD. if (TLD_SET.add(host)) { // Atualiza flag de atualizao. TLD_CHANGED = true; } } throw ex; } } } public static String getOwnerID(String address) { if (address == null) { return null; } else if (address.endsWith(".br")) { try { Domain domain = getDomain(address); if (domain == null) { return null; } else { return domain.get("ownerid", false); } } catch (ProcessException ex) { if (ex.isErrorMessage("NSLOOKUP")) { return null; } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { return null; } else if (ex.isErrorMessage("DOMAIN NOT FOUND")) { return null; } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { return null; } else if (ex.isErrorMessage("WHOIS QUERY LIMIT")) { return null; } else if (ex.isErrorMessage("TOO MANY CONNECTIONS")) { return null; } else if (ex.isErrorMessage("WHOIS CONNECTION FAIL")) { return null; } else if (ex.isErrorMessage("RESERVED")) { return null; } else if (ex.isErrorMessage("WAITING")) { return null; } else { Server.logError(ex); return null; } } } else { return null; } } public static String getOwnerC(String address) { if (address.endsWith(".br")) { try { Domain domain = getDomain(address); if (domain == null) { return null; } else { return domain.get("owner-c", false); } } catch (ProcessException ex) { if (ex.getMessage().equals("ERROR: NSLOOKUP")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else if (ex.getMessage().equals("ERROR: DOMAIN NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else { Server.logError(ex); return null; } } } else { return null; } } public static String revert(String hostname) { if (hostname == null) { return null; } else { StringTokenizer tokenizer = new StringTokenizer(hostname, "."); String result = tokenizer.nextToken(); while (tokenizer.hasMoreTokens()) { result = tokenizer.nextToken() + '.' + result; } return '.' + result; } } // private static final Semaphore SEMAPHORE_NEW = new Semaphore(1); private static synchronized Domain newDomain(String host) throws ProcessException { // try { // Server.logTrace("quering new WHOIS domain"); // Selecionando servidor da pesquisa WHOIS. String server = getWhoisServer(host); // Domnio existente. // Realizando a consulta no WHOIS. String result = Server.whois(host, server); try { Domain domain = new Domain(result); domain.server = server; // Temporrio at final de transio. // Adicinando registro em cache. MAP.put(domain.getDomain(), domain); DOMAIN_CHANGED = true; return domain; } catch (ProcessException ex) { if (ex.isErrorMessage("RESERVED")) { // A chave de busca um TLD. if (TLD_SET.add(host)) { // Atualiza flag de atualizao. TLD_CHANGED = true; } } throw ex; } // } finally { // SEMAPHORE_NEW.release(); // } } /** * Retorna o registro de domnio de um determinado host. * @param address o endereo cujo registro de domnio deve ser retornado. * @return o registro de domnio de um determinado endereo. * @throws ProcessException se houver falha no processamento. */ public static Domain getDomain(String address) throws ProcessException { String key = extractDomain(address, false); Domain domain = MAP.get(key); // Busca eficiente O(1). if (domain != null) { // Domnio encontrado. domain.queries++; if (domain.isRegistryExpired()) { // Registro desatualizado. // Atualizando campos do registro. if (domain.refresh()) { // Domnio real do resultado WHOIS bate com o registro. return domain; } else if (MAP.remove(domain.getDomain()) != null) { // Domnio real do resultado WHOIS no bate com o registro. // Pode haver a criao de uma nova TLD. // Apagando registro de domnio do cache. DOMAIN_CHANGED = true; // Segue para nova consulta. } } else { // Registro atualizado. return domain; } } // Extrair o host se for e-mail. String host = extractHost(address, false); // Verifica o DNS do host antes de fazer a consulta no WHOIS. // Evita consulta desnecessria no WHOIS. checkHost(host); // if (SEMAPHORE_NEW.tryAcquire()) { return newDomain(host); // } else { // return null; // } } /** * Retorna o domnio do registro. * @return o domnio do registro. */ public String getDomain() { return domain; } @Override public int hashCode() { return domain.hashCode(); } @Override public boolean equals(Object other) { if (other instanceof Domain) { return equals((Domain) other); } else { return false; } } /** * Verifica se o registro atual o mesmo de outro. * @param other o outro registro a ser comparado. * @return verdadeiro se o registro passado igual ao atual. */ public boolean equals(Domain other) { if (other == null) { return false; } else { return this.domain.equals(other.domain); } } @Override public int compareTo(Domain other) { return this.domain.compareTo(other.domain); } @Override public String toString() { return domain; } }