package mireka.filter.misc; import java.util.Deque; import java.util.LinkedList; import javax.annotation.concurrent.ThreadSafe; /** * Tarpit maintains a list about attempts to send mail to non-existent users and * calculates a wait duration which should be used to slow down clients. */ @ThreadSafe public class Tarpit { private final long VALIDITY_DURATION = 30000; private final long WAIT_BY_MARK = 1000; private final long MAX_WAIT = 5000; /** * The count of expirations the system will store. Add one extra place so * that the wait duration does not drop temporarily when a mark expires * while otherwise a dictionary attack is ongoing. */ private final int MAX_EXPIRATIONS = (int) Math.ceil((double) MAX_WAIT / WAIT_BY_MARK) + 1; /** * new entries must be added to the head */ private Deque<Long> markExpirations = new LinkedList<Long>(); public synchronized void addRejection() { removeExpiredMarks(); Long expiration = System.currentTimeMillis() + VALIDITY_DURATION; markExpirations.addFirst(expiration); if (markExpirations.size() > MAX_EXPIRATIONS) markExpirations.removeLast(); } private void removeExpiredMarks() { long now = System.currentTimeMillis(); while (!markExpirations.isEmpty() && markExpirations.peekLast() <= now) markExpirations.removeLast(); } public synchronized long waitDuration() { removeExpiredMarks(); long waitDuration = markExpirations.size() * WAIT_BY_MARK; waitDuration = Math.min(MAX_WAIT, waitDuration); return waitDuration; } }