package com.limegroup.gnutella.filters;
import org.limewire.core.settings.FilterSettings;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.limegroup.gnutella.ConnectionManager;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.QueryRequest;
/**
* A filter that blocks query requests with the same query and TTL as recently
* seen query requests. The TTL check prevents the filter from interfering with
* dynamic querying.
*/
@Singleton
public class RepetitiveQueryFilter implements SpamFilter {
private static final Log LOG = LogFactory.getLog(RepetitiveQueryFilter.class);
/** Recent incoming queries. */
private final String[] recentQueries;
/** The TTLs of the recent queries. */
private final byte[] recentTTLs;
/** The index of the recent query that should be replaced next. */
private int roundRobin = 0;
/** The number of repetitive queries dropped this session. */
private long dropped = 0;
private final ConnectionManager connectionManager;
@Inject
RepetitiveQueryFilter(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
int size = FilterSettings.REPETITIVE_QUERY_FILTER_SIZE.getValue();
recentQueries = new String[size];
recentTTLs = new byte[size];
}
@Override
public boolean allow(Message m) {
if(!(m instanceof QueryRequest))
return true;
if(recentQueries.length == 0)
return true;
if(connectionManager.isShieldedLeaf())
return true;
QueryRequest q = (QueryRequest)m;
// Don't drop browses or "what's new" queries
if(q.isBrowseHostQuery() || q.isWhatIsNewRequest())
return true;
String query = q.getQuery();
assert query != null;
// Don't drop URN queries (though they may be dropped elsewhere)
if(query.equals(QueryRequest.DEFAULT_URN_QUERY) && q.hasQueryUrns())
return true;
byte ttl = q.getTTL();
// Drop repetitive queries
for(int i = 0; i < recentQueries.length; i++) {
if(query.equals(recentQueries[i]) && ttl == recentTTLs[i]) {
LOG.debugf("Repetitive query blocked: {0}, {1}", query, ttl);
dropped++;
return false;
}
}
recentQueries[roundRobin] = query;
recentTTLs[roundRobin] = ttl;
roundRobin = (roundRobin + 1) % recentQueries.length;
return true;
}
}