net.spfbl.whois.AutonomousSystem.java Source code

Java tutorial

Introduction

Here is the source code for net.spfbl.whois.AutonomousSystem.java

Source

/*
 * 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.Date;
import java.util.HashMap;
import java.util.TreeSet;
import org.apache.commons.lang3.SerializationUtils;

/**
 *
 * @author Leandro Carlos Rodrigues <leandro@spfbl.net>
 */
public class AutonomousSystem implements Serializable, Comparable<AutonomousSystem> {

    private static final long serialVersionUID = 1L;

    private final String aut_num;
    private String owner;
    private String ownerid;
    private String responsible;
    private String country;
    private String owner_c;
    private String routing_c;
    private String abuse_c;
    private Date created;
    private Date changed;

    /**
     * Lista dos blocos alocados ao AS.
     */
    private final TreeSet<String> inetnumSet = new TreeSet<String>();

    private final String server; // Servidor onde a informao do AS 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 = 84; // Prazo mximo que o registro deve permanecer em cache em dias.

    /**
     * Formatao padro dos campos de data do WHOIS.
     */
    private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd");

    /**
     * Atualiza o tempo de expirao do registro de AS.
     * @param time tempo em dias que os registros de domno devem ser atualizados.
     */
    protected 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;
        return expiredTime > REFRESH_TIME;
    }

    /**
     * Verifica se o registro atual est quase expirando.
     * @return verdadeiro se o registro atual est quase expirando.
     */
    public boolean isRegistryAlmostExpired() {
        long expiredTime = (System.currentTimeMillis() - lastRefresh) / Server.DAY_TIME;
        return expiredTime > (2 * REFRESH_TIME / 3); // Dois teros antes da expirao.
    }

    private AutonomousSystem(String aut_num, String server) throws ProcessException {
        this.aut_num = aut_num;
        this.server = server;
        refresh();
    }

    private void refresh() throws ProcessException {
        String result = Server.whois(aut_num, server);
        refresh(result);
        // Atualiza flag de atualizao.
        AS_CHANGED = true;
    }

    /**
     * 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 void refresh(String result) throws ProcessException {
        try {
            boolean reducedLocal = false;
            String autnumResult = null;
            BufferedReader reader = new BufferedReader(new StringReader(result));
            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    if (line.startsWith("aut-num:")) {
                        int index = line.indexOf(':') + 1;
                        autnumResult = line.substring(index).trim();
                    } else if (line.startsWith("owner:")) {
                        int index = line.indexOf(':') + 1;
                        owner = line.substring(index).trim();
                    } else if (line.startsWith("ownerid:")) {
                        int index = line.indexOf(':') + 1;
                        ownerid = line.substring(index).trim();
                    } else if (line.startsWith("responsible:")) {
                        int index = line.indexOf(':') + 1;
                        responsible = line.substring(index).trim();
                    } else if (line.startsWith("country:")) {
                        int index = line.indexOf(':') + 1;
                        country = line.substring(index).trim();
                    } else if (line.startsWith("owner-c:")) {
                        int index = line.indexOf(':') + 1;
                        owner_c = line.substring(index).trim();
                    } else if (line.startsWith("routing-c:")) {
                        int index = line.indexOf(':') + 1;
                        routing_c = line.substring(index).trim();
                    } else if (line.startsWith("abuse-c:")) {
                        int index = line.indexOf(':') + 1;
                        abuse_c = line.substring(index).trim();
                    } else if (line.startsWith("inetnum:")) {
                        int index = line.indexOf(':') + 1;
                        String inetnum = line.substring(index).trim();
                        inetnumSet.add(inetnum);
                    } else if (line.startsWith("created:")) {
                        int index = line.indexOf(':') + 1;
                        String valor = line.substring(index).trim();
                        created = DATE_FORMATTER.parse(valor);
                    } else if (line.startsWith("changed:")) {
                        int index = line.indexOf(':') + 1;
                        String valor = line.substring(index).trim();
                        changed = DATE_FORMATTER.parse(valor);
                    } else if (line.startsWith("nic-hdl-br:")) {
                        int index = line.indexOf(':') + 1;
                        String nic_hdl_br = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String person = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String e_mail;
                        if (reducedLocal) {
                            e_mail = null;
                        } else {
                            e_mail = line.substring(index).trim();
                            line = reader.readLine().trim();
                            index = line.indexOf(':') + 1;
                        }
                        String created2 = line.substring(index).trim();
                        line = reader.readLine().trim();
                        index = line.indexOf(':') + 1;
                        String changed2 = line.substring(index).trim();
                        Handle handle = Handle.getHandle(nic_hdl_br);
                        handle.setPerson(person);
                        handle.setEmail(e_mail);
                        handle.setCreated(created2);
                        handle.setChanged(changed2);
                    } else if (line.startsWith("% No match for domain")) {
                        throw new ProcessException("ERROR: DOMAIN NOT FOUND");
                    } else if (line.startsWith("% release process: waiting")) {
                        throw new ProcessException("ERROR: WAITING");
                    } else if (line.startsWith("% reserved:    CG")) {
                        throw new ProcessException("ERROR: RESERVED");
                    } else if (line.startsWith("% Maximum concurrent connections limit exceeded")) {
                        throw new ProcessException("ERROR: WHOIS CONCURRENT");
                    } 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("% Query rate limit exceeded. Reduced information.")) {
                        // Informao reduzida devido ao estouro de limite de consultas.
                        Server.removeWhoisQueryHour();
                        reducedLocal = 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);
                    }
                }
            } finally {
                reader.close();
            }
            if (autnumResult == null) {
                throw new ProcessException("ERROR: AS NOT FOUND");
            } else if (!autnumResult.equals(aut_num)) {
                throw new ProcessException("ERROR: PARSING");
            } else {
                this.lastRefresh = System.currentTimeMillis();
                this.reduced = reducedLocal;
                this.queries = 1;
                // Atualiza flag de atualizao.
                AS_CHANGED = true;
            }
        } catch (ProcessException ex) {
            throw ex;
        } catch (Exception ex) {
            Server.logError(ex);
            throw new ProcessException("ERROR: PARSING", ex);
        }
    }

    public Handle getOwner() {
        return Handle.getHandle(owner_c);
    }

    public Handle getRouting() {
        return Handle.getHandle(routing_c);
    }

    public Handle getAbuse() {
        return Handle.getHandle(abuse_c);
    }

    /**
     * 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) throws ProcessException {
        if (key.equals("aut_num")) {
            return aut_num;
        } else if (key.equals("owner")) {
            return owner;
        } else if (reduced) {
            // Ultima consulta com informao reduzida.
            // Demais campos esto comprometidos.
            throw new ProcessException("ERROR: WHOIS QUERY LIMIT");
        } else if (key.equals("ownerid")) {
            return ownerid;
        } 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("routing-c")) {
            return routing_c;
        } else if (key.equals("abuse-c")) {
            return abuse_c;
        } else if (key.equals("created")) {
            if (created == null) {
                return null;
            } else {
                return DATE_FORMATTER.format(created);
            }
        } else if (key.equals("changed")) {
            if (changed == null) {
                return null;
            } else {
                return DATE_FORMATTER.format(changed);
            }
        } else if (key.equals("inetnum")) {
            return inetnumSet.toString();
        } else if (key.startsWith("owner-c/")) {
            int index = key.indexOf('/') + 1;
            key = key.substring(index);
            Handle owner = getOwner();
            if (owner == null) {
                return null;
            } else {
                return owner.get(key);
            }
        } else if (key.startsWith("routing-c/")) {
            int index = key.indexOf('/') + 1;
            key = key.substring(index);
            Handle routing = getRouting();
            if (routing == null) {
                return null;
            } else {
                return routing.get(key);
            }
        } else if (key.startsWith("abuse-c/")) {
            int index = key.indexOf('/') + 1;
            key = key.substring(index);
            Handle abuse = getAbuse();
            if (abuse == null) {
                return null;
            } else {
                return abuse.get(key);
            }
        } else {
            return null;
        }
    }

    public static synchronized HashMap<String, AutonomousSystem> getMap() {
        HashMap<String, AutonomousSystem> map = new HashMap<String, AutonomousSystem>();
        map.putAll(MAP);
        return map;
    }

    /**
     * Armazenamento de cache em disco.
     */
    public static void store() {
        if (AS_CHANGED) {
            try {
                //                Server.logTrace("storing as.map");
                long time = System.currentTimeMillis();
                HashMap<String, AutonomousSystem> map = getMap();
                File file = new File("./data/as.map");
                FileOutputStream outputStream = new FileOutputStream(file);
                try {
                    SerializationUtils.serialize(map, outputStream);
                    // Atualiza flag de atualizao.
                    AS_CHANGED = false;
                } finally {
                    outputStream.close();
                }
                Server.logStore(time, file);
            } catch (Exception ex) {
                Server.logError(ex);
            }
        }
    }

    private static synchronized AutonomousSystem put(String key, AutonomousSystem handle) {
        return MAP.put(key, handle);
    }

    /**
     * Carregamento de cache do disco.
     */
    public static void load() {
        long time = System.currentTimeMillis();
        File file = new File("./data/as.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 AutonomousSystem) {
                        AutonomousSystem as = (AutonomousSystem) value;
                        put(key, as);
                    }
                }
                Server.logLoad(time, file);
            } catch (Exception ex) {
                Server.logError(ex);
            }
        }
    }

    /**
     * Mapa de domnios com busca de hash O(1).
     */
    private static final HashMap<String, AutonomousSystem> MAP = new HashMap<String, AutonomousSystem>();

    //    /**
    //     * Adciiona o registro de domnio no cache.
    //     * @param as o as que deve ser adicionado.
    //     */
    //    private static synchronized void addAutonomousSystem(AutonomousSystem as) {
    //        MAP.put(as.getNumber(), as);
    //        // Atualiza flag de atualizao.
    //        AS_CHANGED = true;
    //    }

    //    /**
    //     * Remove o registro de domnio do cache.
    //     * @param as o as que deve ser removido.
    //     */
    //    private static synchronized void removeAutonomousSystem(AutonomousSystem as) {
    //        if (MAP.remove(as.getNumber()) != null) {
    //            // Atualiza flag de atualizao.
    //            AS_CHANGED = true;
    //        }
    //    }

    /**
     * Flag que indica se o cache foi modificado.
     */
    private static boolean AS_CHANGED = false;

    /**
     * Retorna o registro de AS de um determinado nmero.
     * @param number o nmero cujo registro de AS deve ser retornado.
     * @param server o servidor WHOIS que tem a informao.
     * @return o registro de AS de um determinado nmero.
     * @throws ProcessException se houver falha no processamento.
     */
    public static synchronized AutonomousSystem getAS(String number, String server) throws ProcessException {
        // Busca eficiente O(1).
        if (MAP.containsKey(number)) {
            // Domnio encontrado.
            AutonomousSystem as = MAP.get(number);
            as.queries++;
            if (as.isRegistryExpired()) {
                // Registro desatualizado.
                // Atualizando campos do registro.
                as.refresh();
            } else if (as.isRegistryAlmostExpired()) {
                // Registro quase vencendo.
                // Adicionar no conjunto para atualizao em background.
                REFRESH.add(as);
            }
            return as;
        } else {
            // Realizando a consulta no WHOIS.
            AutonomousSystem as = new AutonomousSystem(number, server);
            // Adicinando registro em cache.
            MAP.put(as.getNumber(), as);
            AS_CHANGED = true;
            return as;
        }
    }

    /**
     * Conjunto de registros para atualizao em background.
     */
    private static final TreeSet<AutonomousSystem> REFRESH = new TreeSet<AutonomousSystem>();

    /**
     * Atualiza em background todos os registros adicionados no conjunto.
     */
    public static synchronized void backgroundRefresh() {
        while (!REFRESH.isEmpty()) {
            AutonomousSystem as = REFRESH.pollFirst();
            try {
                // Atualizando campos do registro.
                as.refresh();
            } catch (Exception ex) {
                Server.logError(ex);
            }
        }
    }

    /**
     * Retorna o nmero do AS.
     * @return o nmero do AS.
     */
    public String getNumber() {
        return aut_num;
    }

    @Override
    public int hashCode() {
        return aut_num.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Domain) {
            return equals((AutonomousSystem) 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(AutonomousSystem other) {
        if (other == null) {
            return false;
        } else {
            return this.aut_num.equals(other.aut_num);
        }
    }

    @Override
    public int compareTo(AutonomousSystem other) {
        return this.aut_num.compareTo(other.aut_num);
    }

    @Override
    public String toString() {
        return aut_num;
    }
}