package com.subgraph.orchid.circuits.path; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.subgraph.orchid.Router; import com.subgraph.orchid.data.HexDigest; import com.subgraph.orchid.data.IPv4Address; /** * Implements configuration options: * * ExcludeNodes,ExcludeExitNodes,ExitNodes,EntryNodes * */ public class ConfigNodeFilter implements RouterFilter { private final static Pattern NETMASK_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)/(\\d+)$"); private final static Pattern ADDRESS_BITS_PATTERN = Pattern.compile("^(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)$"); private final static Pattern IDENTITY_PATTERN = Pattern.compile("^[A-Fa-f0-9]{40}$"); private final static Pattern COUNTRYCODE_PATTERN = Pattern.compile("^\\{([A-Za-z]{2})\\}$"); private final static Pattern ROUTERNAME_PATTERN = Pattern.compile("^\\w{1,19}$"); static class MaskFilter implements RouterFilter { private final int network; private final int bits; private final int mask; static int createMask(final int maskBitCount) { return maskBitCount == 0 ? 0 : (1 << 31) >> (maskBitCount - 1); } MaskFilter(IPv4Address network, int bits) { this.bits = bits; this.mask = createMask(bits); this.network = network.getAddressData() & mask; } public boolean filter(Router router) { final int routerAddress = router.getAddress().getAddressData(); return (routerAddress & mask) == network; } public String toString() { IPv4Address a = new IPv4Address(network); return a.toString() + "/" + bits; } } static class IdentityFilter implements RouterFilter { private final HexDigest identity; IdentityFilter(HexDigest identity) { this.identity = identity; } public boolean filter(Router router) { return router.getIdentityHash().equals(identity); } } static class NameFilter implements RouterFilter { private final String name; NameFilter(String name) { this.name = name; } public boolean filter(Router router) { return name.equals(router.getNickname()); } } static class CountryCodeFilter implements RouterFilter { private final String countryCode; public CountryCodeFilter(String countryCode) { this.countryCode = countryCode; } public boolean filter(Router router) { return countryCode.equalsIgnoreCase(router.getCountryCode()); } } static boolean isAddressString(String s) { Matcher matcher = NETMASK_PATTERN.matcher(s); if(!matcher.matches()) { return false; } try { for(int i = 1; i < 5; i++) { if(!isValidOctetString(matcher.group(i))) { return false; } } return isValidMaskValue(matcher.group(5)); } catch (NumberFormatException e) { return false; } } private static boolean isValidOctetString(String s) { int n = Integer.parseInt(s); return n >= 0 && n <= 255; } private static boolean isValidMaskValue(String s) { int n = Integer.parseInt(s); return n > 0 && n <= 32; } static boolean isIdentityString(String s) { return IDENTITY_PATTERN.matcher(s).matches(); } static boolean isCountryCodeString(String s) { return COUNTRYCODE_PATTERN.matcher(s).matches(); } static boolean isNameString(String s) { return ROUTERNAME_PATTERN.matcher(s).matches(); } static RouterFilter createFilterFor(String s) { if(isAddressString(s)) { return createAddressFilter(s); } else if(isCountryCodeString(s)) { return createCountryCodeFilter(s); } else if(isIdentityString(s)) { return createIdentityFilter(s); } else if (isNameString(s)) { return createNameFilter(s); } else { return null; } } private static RouterFilter createAddressFilter(String s) { final Matcher matcher = ADDRESS_BITS_PATTERN.matcher(s); if(!matcher.matches()) { throw new IllegalArgumentException(); } final IPv4Address network = IPv4Address.createFromString(matcher.group(1)); final int bits = Integer.parseInt(matcher.group(2)); return new MaskFilter(network, bits); } private static RouterFilter createIdentityFilter(String s) { if(isIdentityString(s)) { throw new IllegalArgumentException(); } final HexDigest identity = HexDigest.createFromString(s); return new IdentityFilter(identity); } private static RouterFilter createCountryCodeFilter(String s) { final Matcher matcher = COUNTRYCODE_PATTERN.matcher(s); if(!matcher.matches()) { throw new IllegalArgumentException(); } return new CountryCodeFilter(matcher.group(1)); } private static RouterFilter createNameFilter(String s) { if(!isNameString(s)) { throw new IllegalArgumentException(); } return new NameFilter(s); } static ConfigNodeFilter createFromStrings(List<String> stringList) { final List<RouterFilter> filters = new ArrayList<RouterFilter>(); for(String s: stringList) { RouterFilter f = createFilterFor(s); if(f != null) { filters.add(f); } } return new ConfigNodeFilter(filters); } private final List<RouterFilter> filterList; private ConfigNodeFilter(List<RouterFilter> filterList) { this.filterList = filterList; } public boolean filter(Router router) { for(RouterFilter f: filterList) { if(f.filter(router)) { return true; } } return false; } boolean isEmpty() { return filterList.isEmpty(); } }