package net.i2p.client.streaming.impl; import java.util.concurrent.atomic.AtomicInteger; import net.i2p.data.Hash; import net.i2p.util.ObjectCounter; import net.i2p.util.RandomSource; import net.i2p.util.SimpleTimer; import net.i2p.util.SimpleTimer2; /** * Count how often we have received an incoming connection * This offers basic DOS protection but is not a complete solution. * * @since 0.7.14 */ class ConnThrottler { private final ObjectCounter<Hash> counter; private volatile int _max; private volatile int _totalMax; private final AtomicInteger _currentTotal; /* * @param max per-peer, 0 for unlimited * @param totalMax for all peers, 0 for unlimited * @param period ms */ ConnThrottler(int max, int totalMax, long period, SimpleTimer2 timer) { _max = max; _totalMax = totalMax; this.counter = new ObjectCounter<Hash>(); _currentTotal = new AtomicInteger(); // shorten the initial period by a random amount // to prevent correlation across destinations // and identification of router startup time timer.addPeriodicEvent(new Cleaner(), (period / 2) + RandomSource.getInstance().nextLong(period / 2), period); } /* * @param max per-peer, 0 for unlimited * @param totalMax for all peers, 0 for unlimited * @since 0.9.3 */ public void updateLimits(int max, int totalMax) { _max = max; _totalMax = totalMax; } /** * Checks both individual and total. Increments before checking. */ boolean shouldThrottle(Hash h) { // do this first, so we don't increment total if individual throttled if (_max > 0 && this.counter.increment(h) > _max) return true; if (_totalMax > 0 && _currentTotal.incrementAndGet() > _totalMax) return true; return false; } /** * Checks individual count only. Does not increment. * @since 0.9.3 */ boolean isThrottled(Hash h) { if (_max > 0) return this.counter.count(h) > _max; return false; } private class Cleaner implements SimpleTimer.TimedEvent { public void timeReached() { if (_totalMax > 0) _currentTotal.set(0); if (_max > 0) ConnThrottler.this.counter.clear(); } } }