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.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.util.Arrays; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.commons.lang3.SerializationUtils; /** * Representa uma Subnet de IPv6. * * <h2>Mecanismo de busca</h2> * A busca de um bloco AS realizada atravs de um mapa ordenado em rvore, * onde a chave o primeiro endereo IP do bloco, * convetido em inteiro de 64 bits, e o valor o bloco propriamente dito. * O endereo IP da consulta convertido em inteiro de 64 bits e localiza-se * o endereo no mapa imediatamente inferior ou igual ao endereo do IP. * Por conta do Java no trabalhar com unsigned int, * a busca feita de forma circular, ou seja, * se no retornar na primeira busca, o ltimo registro do mapa retornado. * Se algum bloco for encontrado, * feito um teste se o endereo do IP est contido no bloco encontrado. * Se entiver dentro, o bloco encontrado considerado. * A busca consome o tempo de O(log2(n)). * * @author Leandro Carlos Rodrigues <leandro@spfbl.net> */ public final class SubnetIPv6 extends Subnet { private static final long serialVersionUID = 1L; private final long address; // Primeiro endereo do bloco, primeiros 64 bits. private final long mask; // Mscara da subrede, primeiros 64 bits. /** * Construtor do blocos de pases. * @param inetnum o endereamento CIDR do bloco. * @param server o server que possui as informaes daquele bloco. */ protected SubnetIPv6(String inetnum, String server) { super(inetnum, server); // Endereamento do bloco. this.mask = getMaskNet(inetnum); this.address = getAddressNet(inetnum) & mask; // utiliza a mscara para garantir que o endereo passado seja o primeiro endereo do bloco. } /** * Retorna o primeiro endereo do bloco em inteiro de 64 bits. * @return o primeiro endereo do bloco em inteiro de 64 bits. */ public long getFirstAddress() { return address; } /** * Retorna o ltimo endereo do bloco em inteiro de 64 bits. * @return o ltimo endereo do bloco em inteiro de 64 bits. */ public long getLastAddress() { return address | ~mask; } /** * Construtor do blocos de alocados para ASs. * @param result o resultado WHOIS do bloco. * @throws QueryException se houver alguma falha da atualizao do registro. */ private SubnetIPv6(String result) throws ProcessException { super(result); // Endereamento do bloco. this.mask = getMaskNet(getInetnum()); this.address = getAddressNet(getInetnum()) & mask; // utiliza a mscara para garantir que o endereo passado seja o primeiro endereo do bloco. } @Override protected boolean refresh() throws ProcessException { boolean isInetnum = super.refresh(); // Atualiza flag de atualizao. CHANGED = true; return isInetnum; } /** * Retorna o endereo IP em inteiro de 64 bits da notao CIDR. * @param inetnum endereo de bloco em notao CIDR. * @return o endereo IP em inteiro de 64 bits da notao CIDR. */ private static long getAddressNet(String inetnum) { int index = inetnum.indexOf('/'); String ip = inetnum.substring(0, index); return getAddressIP(ip); } /** * Divide o endereo IPv6 em 16 bytes, * cada um na ordem correta dos blocos. * @param ip o endereo IPv6. * @return um vetor de 16 bytes representando o endereo IPv6. */ public static byte[] splitByte(String ip) { byte[] address = new byte[16]; int k = 0; for (short block : split(ip)) { address[k++] |= block >>> 8; address[k++] |= block; } return address; } public static short[] split(String ip, short[] mask) { short[] address = split(ip); for (int i = 0; i < 8; i++) { address[i] &= mask[i]; } return address; } /** * Retorna a mcara IPv6 em 8 partes. * @param size o tamanho em bits da mscara que deve ser criada. * @return a mcara IPv6 em 8 partes. */ public static short[] getMaskIPv6(String size) { return getMaskIPv6(Integer.parseInt(size)); } /** * Retorna a mcara IPv6 em 8 partes. * @param size o tamanho em bits da mscara que deve ser criada. * @return a mcara IPv6 em 8 partes. */ public static short[] getMaskIPv6(int size) { short[] mask = new short[8]; int n = size / 16; int r = size % 16; int i; for (i = 0; i < n; i++) { mask[i] = (short) 0xFFFF; } if (i < mask.length && r > 0) { mask[i] = (short) (0xFFFF << 16 - r); } return mask; } public static String reverse(String ip) { String reverse = ""; byte[] address = splitByte(ip); for (byte octeto : address) { String hexPart = Integer.toHexString((int) octeto & 0xFF); if (hexPart.length() == 1) { hexPart = "0" + hexPart; } for (char digit : hexPart.toCharArray()) { reverse = digit + "." + reverse; } } return reverse; } public static boolean isSLAAC(String ip) { if (SubnetIPv6.isValidIPv6(ip)) { byte[] byteArray = splitByte(ip); return (byteArray[11] & 0xFF) == 0xFF && (byteArray[12] & 0xFF) == 0xFE; } else { return false; } } public static String expandIPv6(String ip) { short[] splitedIP = split(ip); int p1 = splitedIP[0] & 0xFFFF; int p2 = splitedIP[1] & 0xFFFF; int p3 = splitedIP[2] & 0xFFFF; int p4 = splitedIP[3] & 0xFFFF; int p5 = splitedIP[4] & 0xFFFF; int p6 = splitedIP[5] & 0xFFFF; int p7 = splitedIP[6] & 0xFFFF; int p8 = splitedIP[7] & 0xFFFF; return String.format("%4s", Integer.toHexString(p1)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p2)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p3)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p4)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p5)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p6)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p7)).replace(' ', '0') + ":" + String.format("%4s", Integer.toHexString(p8)).replace(' ', '0'); } public static String expandCIDRv6(String cidr) { int index = cidr.indexOf('/'); String ip = cidr.substring(0, index); String mask = cidr.substring(index); ip = expandIPv6(ip); cidr = ip + mask; return cidr; } /** * Meio mais seguro de padronizar os endereos IP. * @param ip o endereo IPv6. * @return o endereo IPv6 padronizado. */ public static String normalizeIPv6(String ip) { short[] splitedIP = split(ip); int p1 = splitedIP[0] & 0xFFFF; int p2 = splitedIP[1] & 0xFFFF; int p3 = splitedIP[2] & 0xFFFF; int p4 = splitedIP[3] & 0xFFFF; int p5 = splitedIP[4] & 0xFFFF; int p6 = splitedIP[5] & 0xFFFF; int p7 = splitedIP[6] & 0xFFFF; int p8 = splitedIP[7] & 0xFFFF; return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8); } /** * Divide o endereo IPv6 em 8 inteiros, * cada um na ordem correta dos blocos. * @param ip o endereo IPv6. * @return um vetor de 8 inteiros representando o endereo IPv6. */ public static short[] split(String ip) { int k = 0; short[] address = new short[8]; int beginIndex = 0; int endIndex; int count = 0; // Converte do inicio ao final. while ((endIndex = ip.indexOf(':', beginIndex)) != -1) { if (beginIndex == endIndex) { // Encontrou a abreviao central. break; } else { String block = ip.substring(beginIndex, endIndex); address[k++] |= Integer.valueOf(block, 16); beginIndex = endIndex + 1; count++; } } k = 7; count = 8 - count; // Calcula quantos blocos faltaram. endIndex = ip.length() - 1; // Converte invertido do final ao inicio. while (count-- > 0 && (beginIndex = ip.lastIndexOf(':', endIndex)) != -1) { if (beginIndex == endIndex) { // Encontrou a abreviao central. break; } else { String block = ip.substring(beginIndex + 1, endIndex + 1); address[k--] |= Integer.valueOf(block, 16); endIndex = beginIndex - 1; } } return address; } public static byte[] address(String ip) { byte[] address = new byte[16]; short[] splitArray = split(ip); address[0] = (byte) (splitArray[0] >>> 8 & 0xFFFF); address[1] = (byte) (splitArray[0] & 0xFFFF); address[2] = (byte) (splitArray[1] >>> 8 & 0xFFFF); address[3] = (byte) (splitArray[1] & 0xFFFF); address[4] = (byte) (splitArray[2] >>> 8 & 0xFFFF); address[5] = (byte) (splitArray[2] & 0xFFFF); address[6] = (byte) (splitArray[3] >>> 8 & 0xFFFF); address[7] = (byte) (splitArray[3] & 0xFFFF); address[8] = (byte) (splitArray[4] >>> 8 & 0xFFFF); address[9] = (byte) (splitArray[4] & 0xFFFF); address[10] = (byte) (splitArray[5] >>> 8 & 0xFFFF); address[11] = (byte) (splitArray[5] & 0xFFFF); address[12] = (byte) (splitArray[6] >>> 8 & 0xFFFF); address[13] = (byte) (splitArray[6] & 0xFFFF); address[14] = (byte) (splitArray[7] >>> 8 & 0xFFFF); address[15] = (byte) (splitArray[7] & 0xFFFF); return address; } /** * Retorna o endereo IP em inteiro de 64 bits da notao IP. * Para fins de roteamento, os primeiros 64 so suficientes. * @param ip endereo de IP em notao IP. * @return o endereo IP em inteiro de 64 bits da notao IPv6. */ protected static long getAddressIP(String ip) { long address = 0; short[] splitedAddress = split(ip); for (int i = 0; i < 4; i++) { address += (long) splitedAddress[i] & 0xFFFF; if (i < 3) { address <<= 16; } } return address; } /** * Retorna a mscara em inteiro de 64 bits da notao CIDR. * @param inetnum endereo de bloco em notao CIDR. * @return a mscara em inteiro de 64 bits da notao CIDR. */ private static long getMaskNet(String inetnum) { int index = inetnum.indexOf('/'); int mask = Integer.parseInt(inetnum.substring(index + 1)); return 0xFFFFFFFFFFFFFFFFL << 64 - mask; } /** * Verifica se um IP vlido na notao de IP. * @param ip o IP a ser verificado. * @return verdadeiro se um IP vlido na notao de IPv6. */ public static boolean isValidIPv6(String ip) { if (ip == null) { return false; } else { ip = ip.trim(); ip = ip.toLowerCase(); return Pattern.matches("^" + "([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" + "([0-9a-fA-F]{1,4}:){1,7}:|" + "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + "([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" + "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" + "([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" + "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" + "[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" + ":((:[0-9a-fA-F]{1,4}){1,7}|:)|" + "fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}" + "$", ip); } } public static boolean isReservedIPv6(String ip) { if (ip == null) { return false; } else if (SubnetIPv6.containsIP("0000::/8", ip)) { return true; } else if (SubnetIPv6.containsIP("0100::/8", ip)) { return true; } else if (SubnetIPv6.containsIP("0200::/7", ip)) { return true; } else if (SubnetIPv6.containsIP("0400::/6", ip)) { return true; } else if (SubnetIPv6.containsIP("0800::/5", ip)) { return true; } else if (SubnetIPv6.containsIP("1000::/4", ip)) { return true; } else if (SubnetIPv6.containsIP("2001::/32", ip)) { return true; } else if (SubnetIPv6.containsIP("2001:10::/28", ip)) { return true; } else if (SubnetIPv6.containsIP("2001:20::/28", ip)) { return true; } else if (SubnetIPv6.containsIP("2001:db8::/32", ip)) { return true; } else if (SubnetIPv6.containsIP("2002::/16", ip)) { return true; } else if (SubnetIPv6.containsIP("4000::/3", ip)) { return true; } else if (SubnetIPv6.containsIP("6000::/3", ip)) { return true; } else if (SubnetIPv6.containsIP("8000::/3", ip)) { return true; } else if (SubnetIPv6.containsIP("a000::/3", ip)) { return true; } else if (SubnetIPv6.containsIP("c000::/3", ip)) { return true; } else if (SubnetIPv6.containsIP("e000::/4", ip)) { return true; } else if (SubnetIPv6.containsIP("f000::/5", ip)) { return true; } else if (SubnetIPv6.containsIP("f800::/6", ip)) { return true; } else if (SubnetIPv6.containsIP("fc00::/7", ip)) { return true; } else if (SubnetIPv6.containsIP("fe00::/9", ip)) { return true; } else if (SubnetIPv6.containsIP("fe80::/10", ip)) { return true; } else if (SubnetIPv6.containsIP("fec0::/10", ip)) { return true; } else if (SubnetIPv6.containsIP("ff00::/8", ip)) { return true; } else { return false; } } private static BigInteger ADDRESS_MIN = new BigInteger("0"); private static BigInteger ADDRESS_MAX = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16); private static BigInteger ADDRESS_UNIT = new BigInteger("1"); private static BigInteger ADDRESS_OCTET = new BigInteger("FFFF", 16); public static String getNextIPv6(String ip) { if (ip == null) { return null; } else if (SubnetIPv6.isValidIPv6(ip)) { BigInteger address = new BigInteger(1, address(ip)); if (address.equals(ADDRESS_MAX)) { return null; } else { address = address.add(ADDRESS_UNIT); int p8 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p7 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p6 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p5 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p4 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p3 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p2 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p1 = address.and(ADDRESS_OCTET).intValue(); return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8); } } else { return null; } } public static String getPreviousIPv6(String ip) { if (ip == null) { return null; } else if (SubnetIPv6.isValidIPv6(ip)) { BigInteger address = new BigInteger(1, address(ip)); if (address.equals(ADDRESS_MIN)) { return null; } else { address = address.subtract(ADDRESS_UNIT); int p8 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p7 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p6 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p5 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p4 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p3 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p2 = address.and(ADDRESS_OCTET).intValue(); address = address.shiftRight(16); int p1 = address.and(ADDRESS_OCTET).intValue(); return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8); } } else { return null; } } /** * Verifica se um CIDR vlido na notao de IPv6. * @param cidr o CIDR a ser verificado. * @return verdadeiro se um CIDR vlido na notao de IPv6. */ public static boolean isValidCIDRv6(String cidr) { if (cidr == null) { return false; } else { cidr = cidr.trim(); cidr = cidr.toLowerCase(); return Pattern.matches("^" + "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" + "([0-9a-fA-F]{1,4}:){1,7}:|" + "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + "([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" + "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" + "([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" + "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" + "[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" + ":((:[0-9a-fA-F]{1,4}){1,7}|:)|" + "fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,})" + "/[0-9]{1,3}$", cidr); } } public static boolean isReverseIPv6(String reverse) { reverse = reverse.trim(); reverse = reverse.toLowerCase(); return Pattern.matches("^" + "(\\.?[a-f0-9]{1,4})" + "(\\.[a-f0-9]{1,4}){31}" + "$\\.?", reverse); } public static String reverseToIPv6(String reverse) { reverse = reverse.replace(".", ""); char[] charArray = reverse.toCharArray(); StringBuilder builder = new StringBuilder(); for (int index = charArray.length - 1; index >= 0; index--) { char digit = charArray[index]; builder.append(digit); if (index % 4 == 0) { builder.append(':'); } } String ip = builder.toString(); return SubnetIPv6.normalizeIPv6(ip); } /** * Mapa de blocos IP de ASs com busca em rvore binria log2(n). */ private static final TreeMap<String, SubnetIPv6> MAP = new TreeMap<String, SubnetIPv6>(); /** * Remove registro de bloco de IP para AS do cache. * @param ip o IP cujo bloco deve ser removido. * @return o registro de bloco removido, se existir. */ public static synchronized SubnetIPv6 removeSubnet(String ip) { // Busca eficiente O(log2(n)). // Este mtodo s funciona se o mapa no tiver interseco de blocos. String key = expandIPv6(ip); key = MAP.floorKey(key); if (key == null) { return null; } else { SubnetIPv6 subnet = MAP.remove(key); // Atualiza flag de atualizao. CHANGED = true; return subnet; } } /** * Flag que indica se o cache foi modificado. */ private static boolean CHANGED = false; protected static synchronized TreeSet<Subnet> getSubnetSet() { TreeSet<Subnet> subnetSet = new TreeSet<Subnet>(); subnetSet.addAll(MAP.values()); return subnetSet; } /** * Atualiza o bloco de IP de AS de um determinado IP. * @param ip o IP cujo bloco deve ser retornado. * @throws ProcessException se houver falha no processamento. */ public static synchronized void refreshSubnet(String ip) throws ProcessException { SubnetIPv6 subnet; String key = MAP.floorKey(expandIPv6(ip)); while (key != null) { subnet = MAP.get(key); if (subnet.contains(ip)) { // Atualizando campos do registro. if (!subnet.refresh()) { // Domnio real do resultado WHOIS no bate com o registro. // Pode haver mudana na distribuio dos blocos. // Apagando registro de bloco do cache. MAP.remove(key); CHANGED = true; // Segue para nova consulta. break; } } else { key = MAP.lowerKey(key); } } // No encontrou a sub-rede em cache. // Selecionando servidor da pesquisa WHOIS. String server = getWhoisServer(ip); // Fazer a consulta no WHOIS. String result = Server.whois(ip, server); subnet = new SubnetIPv6(result); subnet.server = server; // Temporrio at final de transio. key = getFirstIPv6(subnet.getInetnum()); key = expandIPv6(key); MAP.put(key, subnet); CHANGED = true; } public static String getFirstIPv6(String inetnum) { int index = inetnum.indexOf('/'); String ip = inetnum.substring(0, index); String size = inetnum.substring(index + 1); int sizeInt = Integer.parseInt(size); short[] mask = SubnetIPv6.getMaskIPv6(sizeInt); short[] address = SubnetIPv6.split(ip, mask); int p1 = address[0] & 0xFFFF; int p2 = address[1] & 0xFFFF; int p3 = address[2] & 0xFFFF; int p4 = address[3] & 0xFFFF; int p5 = address[4] & 0xFFFF; int p6 = address[5] & 0xFFFF; int p7 = address[6] & 0xFFFF; int p8 = address[7] & 0xFFFF; return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8); } public static String getLastIPv6(String inetnum) { int index = inetnum.indexOf('/'); String ip = inetnum.substring(0, index); String size = inetnum.substring(index + 1); int sizeInt = Integer.parseInt(size); short[] mask = SubnetIPv6.getMaskIPv6(sizeInt); short[] address = SubnetIPv6.split(ip, mask); int p1 = (address[0] & 0xFFFF) ^ (~mask[0] & 0xFFFF); int p2 = (address[1] & 0xFFFF) ^ (~mask[1] & 0xFFFF); int p3 = (address[2] & 0xFFFF) ^ (~mask[2] & 0xFFFF); int p4 = (address[3] & 0xFFFF) ^ (~mask[3] & 0xFFFF); int p5 = (address[4] & 0xFFFF) ^ (~mask[4] & 0xFFFF); int p6 = (address[5] & 0xFFFF) ^ (~mask[5] & 0xFFFF); int p7 = (address[6] & 0xFFFF) ^ (~mask[6] & 0xFFFF); int p8 = (address[7] & 0xFFFF) ^ (~mask[7] & 0xFFFF); return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8); } /** * Corrige o endereo da notao CIDR para sem abreviao. * @param inetnum o endereo com notao CIDR sem abreviao. * @return o endereo da notao CIDR sem abreviao. */ public static String normalizeCIDRv6(String inetnum) { if (inetnum == null) { return null; } else { int index = inetnum.indexOf('/'); String ip = inetnum.substring(0, index); String size = inetnum.substring(index + 1); int sizeInt = Integer.parseInt(size); if (sizeInt < 0 || sizeInt > 128) { return null; } else { short[] mask = SubnetIPv6.getMaskIPv6(sizeInt); short[] address = SubnetIPv6.split(ip, mask); int p1 = address[0] & 0xFFFF; int p2 = address[1] & 0xFFFF; int p3 = address[2] & 0xFFFF; int p4 = address[3] & 0xFFFF; int p5 = address[4] & 0xFFFF; int p6 = address[5] & 0xFFFF; int p7 = address[6] & 0xFFFF; int p8 = address[7] & 0xFFFF; return Integer.toHexString(p1) + ":" + Integer.toHexString(p2) + ":" + Integer.toHexString(p3) + ":" + Integer.toHexString(p4) + ":" + Integer.toHexString(p5) + ":" + Integer.toHexString(p6) + ":" + Integer.toHexString(p7) + ":" + Integer.toHexString(p8) + "/" + sizeInt; } } } public static String getInetnum(String ip) { try { SubnetIPv6 subnet = getSubnet(ip); return normalizeCIDRv6(subnet.get("inetnum", false)); } catch (ProcessException ex) { if (ex.getMessage().equals("ERROR: SERVER NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else if (ex.getMessage().equals("ERROR: SUBNET NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else { Server.logError(ex); return null; } } } public static String getOwnerID(String ip) { try { SubnetIPv6 subnet = getSubnet(ip); return subnet.get("ownerid", false); } catch (ProcessException ex) { if (ex.getMessage().equals("ERROR: SERVER NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else if (ex.getMessage().equals("ERROR: SUBNET NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else { Server.logError(ex); return null; } } } public static String getOwnerC(String ip) { try { SubnetIPv6 subnet = getSubnet(ip); return subnet.get("owner-c", false); } catch (ProcessException ex) { if (ex.getMessage().equals("ERROR: SERVER NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else if (ex.getMessage().equals("ERROR: SUBNET NOT FOUND")) { return null; } else if (ex.getMessage().equals("ERROR: WHOIS QUERY LIMIT")) { return null; } else { Server.logError(ex); return null; } } } private static synchronized SubnetIPv6 newSubnet(String ip) throws ProcessException { // Server.logTrace("quering new WHOIS IPv6"); // Selecionando servidor da pesquisa WHOIS. String server = getWhoisServer(ip); // Fazer a consulta no WHOIS. String result = Server.whois(ip, server); SubnetIPv6 subnet = new SubnetIPv6(result); subnet.server = server; // Temporrio at final de transio. String key = getFirstIPv6(subnet.getInetnum()); key = expandIPv6(key); MAP.put(key, subnet); CHANGED = true; return subnet; } /** * Retorna o bloco de IP de AS de um determinado IP. * @param ip o IP cujo bloco deve ser retornado. * @return o registro de bloco IPv6 de AS de um determinado IP. * @throws ProcessException se houver falha no processamento. */ public static SubnetIPv6 getSubnet(String ip) throws ProcessException { SubnetIPv6 subnet; String key = MAP.floorKey(expandIPv6(ip)); while (key != null) { subnet = MAP.get(key); if (subnet.contains(ip)) { if (subnet.isRegistryExpired()) { // Registro expirado. // Atualizando campos do registro. if (subnet.refresh()) { // Bloco do resultado WHOIS bate com o bloco do registro. return subnet; } else if (MAP.remove(key) != null) { // Domnio real do resultado WHOIS no bate com o registro. // Pode haver mudana na distribuio dos blocos. // Apagando registro de bloco do cache. CHANGED = true; // Segue para nova consulta. break; } } else { return subnet; } } else { key = MAP.lowerKey(key); } } // No encontrou a sub-rede em cache. return newSubnet(ip); } private static synchronized TreeMap<String, SubnetIPv6> getMap() { TreeMap<String, SubnetIPv6> map = new TreeMap<String, SubnetIPv6>(); map.putAll(MAP); return map; } /** * Armazenamento de cache em disco. */ public static void store() { if (CHANGED) { try { // Server.logTrace("storing subnet6.map"); long time = System.currentTimeMillis(); TreeMap<String, SubnetIPv6> map = getMap(); File file = new File("./data/subnet6.map"); FileOutputStream outputStream = new FileOutputStream(file); try { SerializationUtils.serialize(map, outputStream); // Atualiza flag de atualizao. CHANGED = false; } finally { outputStream.close(); } Server.logStore(time, file); } catch (Exception ex) { Server.logError(ex); } } } private static synchronized SubnetIPv6 put(String key, SubnetIPv6 subnet) { return MAP.put(key, subnet); } /** * Carregamento de cache do disco. */ public static void load() { long time = System.currentTimeMillis(); File file = new File("./data/subnet6.map"); if (file.exists()) { try { TreeMap<Object, Object> map; FileInputStream fileInputStream = new FileInputStream(file); try { map = SerializationUtils.deserialize(fileInputStream); } finally { fileInputStream.close(); } for (Object value : map.values()) { if (value instanceof SubnetIPv6) { SubnetIPv6 sub6 = (SubnetIPv6) value; sub6.normalize(); String cidr = sub6.getInetnum(); String ip = getFirstIPv6(cidr); String key = expandIPv6(ip); put(key, sub6); } } Server.logLoad(time, file); } catch (Exception ex) { Server.logError(ex); } } } public static boolean containsIPv6(String cidr, String ip) { if (isValidCIDRv6(cidr) && isValidIPv6(ip)) { int index = cidr.lastIndexOf('/'); String size = cidr.substring(index + 1); String address = cidr.substring(0, index); short[] mask = SubnetIPv6.getMaskIPv6(size); short[] address1 = SubnetIPv6.split(address, mask); short[] address2 = SubnetIPv6.split(ip, mask); return Arrays.equals(address1, address2); } else { return false; } } /** * Verifica se o endereo IP passado faz parte do bloco. * @param ip o endereo IP em notao IPv6. * @return verdadeiro se o endereo IP passado faz parte do bloco. */ public boolean contains(String ip) { return contains(getAddressIP(ip)); } /** * Verifica se o endereo IP passado faz parte do bloco. * @param ip o endereo IP em inteiro de 64 bits. * @return verdadeiro se o endereo IP passado faz parte do bloco. */ public boolean contains(long ip) { return this.address == (ip & mask); } public int compareTo(SubnetIPv6 other) { return new Long(this.address).compareTo(other.address); } /** * Mapa completo dos blocos alocados aos pases. */ private static final TreeMap<String, SubnetIPv6> SERVER_MAP = new TreeMap<String, SubnetIPv6>(); /** * Adiciona um servidor WHOIS na lista com seu respecitivo bloco. * @param inetnum o endereo de bloco em notao CIDR. * @param server o servidor WHOIS responsvel por aquele bloco. */ private static void addServer(String inetnum, String server) { try { SubnetIPv6 subnet = new SubnetIPv6(inetnum, server); String ip = getFirstIPv6(subnet.getInetnum()); ip = expandIPv6(ip); SERVER_MAP.put(ip, subnet); } catch (Exception ex) { Server.logError(ex); } } // Temporrio @Override public String getWhoisServer() throws ProcessException { String ip = getFirstIPv6(getInetnum()); return getWhoisServer(ip); } /** * Retorna o servidor que possui a informao de bloco IPv6 de AS de um IP. * @param address endereo IP em inteiro de 64 bits. * @return o servidor que possui a informao de bloco IPv6 de AS de um IP. * @throws QueryException se o bloco no for encontrado para o IP especificado. */ private static String getWhoisServer(String ip) throws ProcessException { // Busca eficiente O(log2(n)). ip = expandIPv6(ip); String key = SERVER_MAP.floorKey(ip); if (key == null) { throw new ProcessException("ERROR: SERVER NOT FOUND"); } else { SubnetIPv6 subnet = SERVER_MAP.get(key); if (subnet.contains(ip)) { return subnet.getServer(); } else { throw new ProcessException("ERROR: SERVER NOT FOUND"); } } } /** * Construo do mapa dos blocos alocados. * Temporrio at implementao de busca pelo whois.iana.org. */ static { addServer("2001:1280::/25", Server.WHOIS_BR); addServer("2801:80::/26", Server.WHOIS_BR); addServer("2804::/16", Server.WHOIS_BR); } }