package net.pms.configuration; /* * PS3 Media Server, for streaming any medias to your PS3. * Copyright (C) 2011 Zsombor G. * * This program 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; version 2 * of the License only. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * IP Filter class, which supports multiple wildcards, ranges. For example : * 127.0.0.1,192.168.0-1.* * * @author zsombor * */ public class IpFilter { private final static String IP_FILTER_RULE_CHAR = "0123456789-.* "; private final static Pattern PATTERN = Pattern.compile("(([0-9]*)(-([0-9]*))?)"); private static final Logger logger = LoggerFactory.getLogger(IpFilter.class); interface Predicate { boolean match(InetAddress addr); } static class HostNamePredicate implements Predicate { String name; public HostNamePredicate(String n) { this.name = n.toLowerCase().trim(); } @Override public boolean match(InetAddress addr) { return addr.getHostName().contains(name); } @Override public String toString() { return name; } } static class IpPredicate implements Predicate { static class ByteRule { int min; int max; public ByteRule(int a, int b) { this.min = a; this.max = b; if (b > -1 && a > -1 && b < a) { this.max = a; this.min = b; } } boolean match(int value) { if (min >= 0 && value < min) { return false; } if (max >= 0 && value > max) { return false; } return true; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); if (min >= 0) { builder.append(min); if (max > min) { builder.append('-').append(max); } else { if (max < 0) { builder.append('-'); } } } else { if (max >= 0) { builder.append('-').append(max); } else { builder.append('*'); } } return builder.toString(); } } List<ByteRule> rules; public IpPredicate(String[] tags) { this.rules = new ArrayList<ByteRule>(tags.length); for (String s : tags) { s = s.trim(); rules.add(parseTag(s)); } } ByteRule parseTag(String s) { if ("*".equals(s)) { return new ByteRule(-1, -1); } else { Matcher matcher = PATTERN.matcher(s); if (matcher.matches()) { String start = matcher.group(2); String middle = matcher.group(3); String ending = matcher.group(4); if (valid(start)) { // x , x-, x-y int x = Integer.parseInt(start); if (valid(ending)) { // x-y return new ByteRule(x, Integer.parseInt(ending)); } else { if (valid(middle)) { // x - return new ByteRule(x, -1); } else { // x return new ByteRule(x, x); } } } else { // -y if (valid(ending)) { return new ByteRule(-1, Integer.parseInt(ending)); } } } } throw new IllegalArgumentException("Tag is not understood:" + s); } private static boolean valid(String s) { return s != null && s.length() > 0; } @Override public boolean match(InetAddress addr) { byte[] b = addr.getAddress(); for (int i = 0; i < rules.size() && i < b.length; i++) { int value = b[i] < 0 ? (int) b[i] + 256 : b[i]; if (!rules.get(i).match(value)) { return false; } } return true; } @Override public String toString() { StringBuilder b = new StringBuilder(); for (ByteRule r : rules) { if (b.length() > 0) { b.append('.'); } b.append(r); } return b.toString(); } } String rawFilter; List<Predicate> matchers = new ArrayList<Predicate>(); Set<String> logged = new HashSet<String>(); public IpFilter() { } public IpFilter(String f) { setRawFilter(f); } public synchronized String getRawFilter() { return rawFilter; } public synchronized void setRawFilter(String rawFilter) { if (this.rawFilter != null && this.rawFilter.equals(rawFilter)) { return; } this.matchers.clear(); this.logged.clear(); this.rawFilter = rawFilter; if (rawFilter != null) { String[] rules = rawFilter.split("[,;]"); for (String r : rules) { Predicate p = parse(r); if (p != null) { matchers.add(p); } } } } @Override public String toString() { return "IpFilter:" + getNormalizedFilter(); } public String getNormalizedFilter() { StringBuilder b = new StringBuilder(); for (Predicate r : matchers) { if (b.length() > 0) { b.append(','); } b.append(r); } return b.toString(); } /** * Decides whether or not the IP address matches this filter. If the filter * is empty, false is returned. * * @param addr The address to match. * @return True if the address matches, false otherwise. */ public boolean isMatch(InetAddress addr) { for (Predicate predicate : matchers) { if (predicate.match(addr)) { return true; } } return false; } public boolean allowed(InetAddress addr) { boolean log = isFirstDecision(addr); if (matchers.size() == 0) { if (log) { logger.info("No IP filter specified, access granted to " + addr); } return true; } for (Predicate p : matchers) { if (p.match(addr)) { if (log) { logger.info("Access granted to " + addr + " by rule: " + p); } return true; } } if (log) { logger.info("Access denied to " + addr); } return false; } Predicate parse(String rule) { rule = rule.trim(); if (rule.length() == 0) { return null; } for (int i = 0; i < rule.length(); i++) { if (IP_FILTER_RULE_CHAR.indexOf(rule.charAt(i)) == -1) { return new HostNamePredicate(rule); } } String[] tags = rule.split("\\."); return new IpPredicate(tags); } private synchronized boolean isFirstDecision(InetAddress addr) { String ip = addr.getHostAddress(); if (!logged.contains(ip)) { logged.add(ip); return true; } return false; } private static void eq(String name, Object obj, Object obj2) { if (obj != null && obj.equals(obj2)) { logger.debug("EQ: " + name + '=' + obj); } else { throw new RuntimeException(name + " expected : '" + obj + "' <> actual : '" + obj2 + "'"); } } public static void main(String[] args) { eq("f1", "192.168.0.1,192.168.0.5", new IpFilter(" 192.168.0.1, 192.168.0.5").getNormalizedFilter()); eq("f2", "192.168.0.*,192.1-6.3-.5", new IpFilter(" 192.168.0.*, 192.1-6.3-.5").getNormalizedFilter()); eq("f3", "2-3.5,myhost", new IpFilter(" 3-2. 5;myhost").getNormalizedFilter()); } }