package com.limegroup.gnutella.spam; import java.util.Arrays; import com.limegroup.gnutella.Assert; import com.limegroup.gnutella.filters.IP; import com.limegroup.gnutella.filters.IPFilter; /** * This is a simple token holding a 4-byte-ip / port pair */ public class AddressToken extends AbstractToken { private static final long serialVersionUID = 3257568416670824244L; private static final int TYPE = TYPE_ADDRESS; /** * must be positive * * This is a heuristic value to prevent an IP address from becoming bad * after only a small number of bad ratings. */ private static final byte INITIAL_GOOD = 20; /** * must be positive * * This value determines how dynamic the filter is. A low MAX value will * allow this Token to get a bad rating after occourring only a few times in * spam, a high value will make it very improbable that the filter will * change its mind about a certain token without user-intervention */ private static final int MAX = 50; private final byte[] _address; private final short _port; private boolean ratingInitialized; private byte _good; private byte _bad; private final int _hashCode; public AddressToken(byte[] address, int port) { Assert.that(address.length == 4); _address = address; _port = (short) port; _hashCode = getHashCode(); ratingInitialized = false; } /* Rating initialization is costly, and many Tokens are created * simply to be replaced by existing tokens in the RatingTable, * so initialize rating lazily. */ private synchronized void initializeRating() { if (ratingInitialized) { return; } // this initial value is an _good = INITIAL_GOOD; IP ip = new IP(_address); int logDistance = IPFilter.instance().getBadHosts().logMinDistanceTo(ip); // Constants 1600 and 3.3 chosen such that: // Same /24 subnet as a banned IP results in a rating of 0.07 // Same /16 subnet as a banned IP results in a rating of 0.01 int bad = (int) (1600 * Math.pow(1+logDistance, -3.3)); while (bad > MAX) { bad /= 2; _good /= 2; } _bad = (byte) bad; ratingInitialized = true; } private int getHashCode() { int hashCode = TYPE; hashCode = (TYPE * hashCode) + _address[0]; hashCode = (TYPE * hashCode) + _address[1]; hashCode = (TYPE * hashCode) + _address[2]; hashCode = (TYPE * hashCode) + _address[3]; hashCode = (TYPE * hashCode) + _port; return hashCode; } /** * implements interface <tt>Token</tt> */ public float getRating() { if (!ratingInitialized) { initializeRating(); } return ((float) _bad )/(_good + _bad + 1); } /** * implements interface <tt>Token</tt> */ public double getImportance() { // Throw out AddressTokens first, since they // can mostly be regenerated from the list of // blocked IP addresses. return Double.NEGATIVE_INFINITY; } /** * implements interface <tt>Token</tt> */ public void rate(int rating) { if (!ratingInitialized) { initializeRating(); } _age = 0; switch (rating) { case RATING_GOOD: _good++; break; case RATING_SPAM: _bad++; break; case RATING_USER_MARKED_GOOD: _bad = 0; break; case RATING_USER_MARKED_SPAM: _bad = (byte) Math.min(_bad + 10, MAX); break; case RATING_CLEARED: _bad = 0; _good = INITIAL_GOOD; break; default: throw new IllegalArgumentException("unknown type of rating"); } if (_good >= MAX || _bad >= MAX) { _good = (byte) (_good * 9 / 10); _bad = (byte) (_bad * 9 / 10); } } /** * implements interface <tt>Token</tt> */ public int getType() { return TYPE; } public final int hashCode() { return _hashCode; } public final boolean equals(Object o) { if (o == null) return false; if ( ! (o instanceof AddressToken)) return false; if (_hashCode != o.hashCode()) { return false; } return _port == ((AddressToken)o)._port && Arrays.equals(_address, ((AddressToken)o)._address); } /** * overrides method from <tt>Object</tt> */ public String toString() { return "" + (0xFF & _address[0]) + "." + (0xFF & _address[1]) + "." + (0xFF & _address[2]) + "." + (0xFF & _address[3]) + ":" + (0xFFFF & _port) + " " + _bad; } }