forked from I2P_Developers/i2p.i2p
* I2PTunnel HTTPServer:
New POST limiter
This commit is contained in:
203
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ConnThrottler.java
Normal file
203
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ConnThrottler.java
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package net.i2p.i2ptunnel;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.util.Clock;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SimpleTimer2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count how often something happens with a particular peer and all peers.
|
||||||
|
* This offers basic DOS protection but is not a complete solution.
|
||||||
|
*
|
||||||
|
* This is a little different from the one in streaming, in that the
|
||||||
|
* ban time is different from the check time, and we keep a separate
|
||||||
|
* map of throttled peers with individual time stamps.
|
||||||
|
* The streaming version is lightweight but "sloppy" since it
|
||||||
|
* uses a single time bucket for all.
|
||||||
|
*
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
class ConnThrottler {
|
||||||
|
private int _max;
|
||||||
|
private int _totalMax;
|
||||||
|
private long _checkPeriod;
|
||||||
|
private long _throttlePeriod;
|
||||||
|
private long _totalThrottlePeriod;
|
||||||
|
private int _currentTotal;
|
||||||
|
private final Map<Hash, Record> _peers;
|
||||||
|
private long _totalThrottleUntil;
|
||||||
|
private final String _action;
|
||||||
|
private final Log _log;
|
||||||
|
private final DateFormat _fmt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @param max per-peer, 0 for unlimited
|
||||||
|
* @param totalMax for all peers, 0 for unlimited
|
||||||
|
* @param period check window (ms)
|
||||||
|
* @param throttlePeriod how long to ban a peer (ms)
|
||||||
|
* @param totalThrottlePeriod how long to ban all peers (ms)
|
||||||
|
* @param action just a name to note in the log
|
||||||
|
*/
|
||||||
|
public ConnThrottler(int max, int totalMax, long period,
|
||||||
|
long throttlePeriod, long totalThrottlePeriod, String action, Log log) {
|
||||||
|
updateLimits(max, totalMax, period, throttlePeriod, totalThrottlePeriod);
|
||||||
|
_peers = new HashMap(4);
|
||||||
|
_action = action;
|
||||||
|
_log = log;
|
||||||
|
// for logging
|
||||||
|
_fmt = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
|
||||||
|
String systemTimeZone = I2PAppContext.getGlobalContext().getProperty("i2p.systemTimeZone");
|
||||||
|
if (systemTimeZone != null)
|
||||||
|
_fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
|
||||||
|
new Cleaner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All periods in ms
|
||||||
|
* @param max per-peer, 0 for unlimited
|
||||||
|
* @param totalMax for all peers, 0 for unlimited
|
||||||
|
* @since 0.9.3
|
||||||
|
*/
|
||||||
|
public synchronized void updateLimits(int max, int totalMax, long checkPeriod, long throttlePeriod, long totalThrottlePeriod) {
|
||||||
|
_max = max;
|
||||||
|
_totalMax = totalMax;
|
||||||
|
_checkPeriod = Math.max(checkPeriod, 10*1000);
|
||||||
|
_throttlePeriod = Math.max(throttlePeriod, 10*1000);
|
||||||
|
_totalThrottlePeriod = Math.max(totalThrottlePeriod, 10*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks both individual and total. Increments before checking.
|
||||||
|
*/
|
||||||
|
public synchronized boolean shouldThrottle(Hash h) {
|
||||||
|
// all throttled already?
|
||||||
|
if (_totalMax > 0) {
|
||||||
|
if (_totalThrottleUntil > 0) {
|
||||||
|
if (_totalThrottleUntil > Clock.getInstance().now())
|
||||||
|
return true;
|
||||||
|
_totalThrottleUntil = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do this first, so we don't increment total if individual throttled
|
||||||
|
if (_max > 0) {
|
||||||
|
Record rec = _peers.get(h);
|
||||||
|
if (rec != null) {
|
||||||
|
// peer throttled already?
|
||||||
|
if (rec.getUntil() > 0)
|
||||||
|
return true;
|
||||||
|
rec.increment();
|
||||||
|
long now = Clock.getInstance().now();
|
||||||
|
if (rec.countSince(now - _checkPeriod) > _max) {
|
||||||
|
long until = now + _throttlePeriod;
|
||||||
|
String date = _fmt.format(new Date(until));
|
||||||
|
_log.logAlways(Log.WARN, "Throttling " + _action + " until " + date +
|
||||||
|
" after exceeding max of " + _max +
|
||||||
|
" in " + DataHelper.formatDuration(_checkPeriod) +
|
||||||
|
": " + h.toBase64());
|
||||||
|
rec.ban(until);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_peers.put(h, new Record());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_totalMax > 0 && ++_currentTotal > _totalMax) {
|
||||||
|
if (_totalThrottleUntil == 0) {
|
||||||
|
_totalThrottleUntil = Clock.getInstance().now() + _totalThrottlePeriod;
|
||||||
|
String date = _fmt.format(new Date(_totalThrottleUntil));
|
||||||
|
_log.logAlways(Log.WARN, "*** Throttling " + _action + " from ALL peers until " + date +
|
||||||
|
" after exceeding max of " + _max +
|
||||||
|
" in " + DataHelper.formatDuration(_checkPeriod));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start over
|
||||||
|
*/
|
||||||
|
public synchronized void clear() {
|
||||||
|
_currentTotal = 0;
|
||||||
|
_totalThrottleUntil = 0;
|
||||||
|
_peers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep a list of seen times, and a ban-until time.
|
||||||
|
* Caller must sync all methods.
|
||||||
|
*/
|
||||||
|
private static class Record {
|
||||||
|
private final List<Long> times;
|
||||||
|
private long until;
|
||||||
|
|
||||||
|
public Record() {
|
||||||
|
times = new ArrayList(8);
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caller must synch */
|
||||||
|
public int countSince(long time) {
|
||||||
|
for (Iterator<Long> iter = times.iterator(); iter.hasNext(); ) {
|
||||||
|
if (iter.next().longValue() < time)
|
||||||
|
iter.remove();
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return times.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caller must synch */
|
||||||
|
public void increment() {
|
||||||
|
times.add(Long.valueOf(Clock.getInstance().now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caller must synch */
|
||||||
|
public void ban(long untilTime) {
|
||||||
|
until = untilTime;
|
||||||
|
// don't need to save times if banned
|
||||||
|
times.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caller must synch */
|
||||||
|
public long getUntil() {
|
||||||
|
if (until < Clock.getInstance().now())
|
||||||
|
until = 0;
|
||||||
|
return until;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||||
|
/** schedules itself */
|
||||||
|
public Cleaner() {
|
||||||
|
super(SimpleTimer2.getInstance(), _checkPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void timeReached() {
|
||||||
|
synchronized(ConnThrottler.this) {
|
||||||
|
if (_totalMax > 0)
|
||||||
|
_currentTotal = 0;
|
||||||
|
if (_max > 0 && !_peers.isEmpty()) {
|
||||||
|
long then = Clock.getInstance().now() - _checkPeriod;
|
||||||
|
for (Iterator<Record> iter = _peers.values().iterator(); iter.hasNext(); ) {
|
||||||
|
Record rec = iter.next();
|
||||||
|
if (rec.getUntil() <= 0 && rec.countSince(then) <= 0)
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedule(_checkPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,7 @@ import net.i2p.client.streaming.I2PSocket;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.ByteArray;
|
import net.i2p.data.ByteArray;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.util.ByteCache;
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.EventDispatcher;
|
import net.i2p.util.EventDispatcher;
|
||||||
import net.i2p.util.I2PAppThread;
|
import net.i2p.util.I2PAppThread;
|
||||||
@@ -41,6 +42,18 @@ import net.i2p.data.Base32;
|
|||||||
*/
|
*/
|
||||||
public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||||
|
|
||||||
|
/** all of these in SECONDS */
|
||||||
|
public static final String OPT_POST_WINDOW = "postCheckTime";
|
||||||
|
public static final String OPT_POST_BAN_TIME = "postBanTime";
|
||||||
|
public static final String OPT_POST_TOTAL_BAN_TIME = "postTotalBanTime";
|
||||||
|
public static final String OPT_POST_MAX = "maxPosts";
|
||||||
|
public static final String OPT_POST_TOTAL_MAX = "maxTotalPosts";
|
||||||
|
public static final int DEFAULT_POST_WINDOW = 5*60;
|
||||||
|
public static final int DEFAULT_POST_BAN_TIME = 30*60;
|
||||||
|
public static final int DEFAULT_POST_TOTAL_BAN_TIME = 10*60;
|
||||||
|
public static final int DEFAULT_POST_MAX = 3;
|
||||||
|
public static final int DEFAULT_POST_TOTAL_MAX = 10;
|
||||||
|
|
||||||
/** what Host: should we seem to be to the webserver? */
|
/** what Host: should we seem to be to the webserver? */
|
||||||
private String _spoofHost;
|
private String _spoofHost;
|
||||||
private static final String HASH_HEADER = "X-I2P-DestHash";
|
private static final String HASH_HEADER = "X-I2P-DestHash";
|
||||||
@@ -53,6 +66,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
|
private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
|
||||||
private static final long START_INTERVAL = (60 * 1000) * 3;
|
private static final long START_INTERVAL = (60 * 1000) * 3;
|
||||||
private long _startedOn = 0L;
|
private long _startedOn = 0L;
|
||||||
|
private ConnThrottler _postThrottler;
|
||||||
|
|
||||||
private final static byte[] ERR_UNAVAILABLE =
|
private final static byte[] ERR_UNAVAILABLE =
|
||||||
("HTTP/1.1 503 Service Unavailable\r\n"+
|
("HTTP/1.1 503 Service Unavailable\r\n"+
|
||||||
@@ -67,6 +81,19 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
"</body></html>")
|
"</body></html>")
|
||||||
.getBytes();
|
.getBytes();
|
||||||
|
|
||||||
|
private final static byte[] ERR_DENIED =
|
||||||
|
("HTTP/1.1 403 Denied\r\n"+
|
||||||
|
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||||
|
"Cache-control: no-cache\r\n"+
|
||||||
|
"Connection: close\r\n"+
|
||||||
|
"Proxy-Connection: close\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"<html><head><title>403 Denied</title></head>\n"+
|
||||||
|
"<body><h2>403 Denied</h2>\n" +
|
||||||
|
"<p>Denied due to excessive requests. Please try again later.</p>\n" +
|
||||||
|
"</body></html>")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||||
super(host, port, privData, l, notifyThis, tunnel);
|
super(host, port, privData, l, notifyThis, tunnel);
|
||||||
setupI2PTunnelHTTPServer(spoofHost);
|
setupI2PTunnelHTTPServer(spoofHost);
|
||||||
@@ -91,8 +118,57 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
@Override
|
@Override
|
||||||
public void startRunning() {
|
public void startRunning() {
|
||||||
super.startRunning();
|
super.startRunning();
|
||||||
_startedOn = getTunnel().getContext().clock().now();
|
|
||||||
// Would be better if this was set when the inbound tunnel becomes alive.
|
// Would be better if this was set when the inbound tunnel becomes alive.
|
||||||
|
_startedOn = getTunnel().getContext().clock().now();
|
||||||
|
setupPostThrottle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
private void setupPostThrottle() {
|
||||||
|
int pp = getIntOption(OPT_POST_MAX, 0);
|
||||||
|
int pt = getIntOption(OPT_POST_TOTAL_MAX, 0);
|
||||||
|
synchronized(this) {
|
||||||
|
if (pp != 0 || pp != 0 || _postThrottler != null) {
|
||||||
|
long pw = 1000L * getIntOption(OPT_POST_WINDOW, DEFAULT_POST_WINDOW);
|
||||||
|
long pb = 1000L * getIntOption(OPT_POST_BAN_TIME, DEFAULT_POST_BAN_TIME);
|
||||||
|
long px = 1000L * getIntOption(OPT_POST_TOTAL_BAN_TIME, DEFAULT_POST_TOTAL_BAN_TIME);
|
||||||
|
if (_postThrottler == null)
|
||||||
|
_postThrottler = new ConnThrottler(pp, pt, pw, pb, px, "POST", _log);
|
||||||
|
else
|
||||||
|
_postThrottler.updateLimits(pp, pt, pw, pb, px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
private int getIntOption(String opt, int dflt) {
|
||||||
|
Properties opts = getTunnel().getClientOptions();
|
||||||
|
String o = opts.getProperty(opt);
|
||||||
|
if (o != null) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(o);
|
||||||
|
} catch (NumberFormatException nfe) {}
|
||||||
|
}
|
||||||
|
return dflt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
@Override
|
||||||
|
public boolean close(boolean forced) {
|
||||||
|
synchronized(this) {
|
||||||
|
if (_postThrottler != null)
|
||||||
|
_postThrottler.clear();
|
||||||
|
}
|
||||||
|
return super.close(forced);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
@Override
|
||||||
|
public void optionsUpdated(I2PTunnel tunnel) {
|
||||||
|
if (getTunnel() != tunnel)
|
||||||
|
return;
|
||||||
|
setupPostThrottle();
|
||||||
|
super.optionsUpdated(tunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,9 +178,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void blockingHandle(I2PSocket socket) {
|
protected void blockingHandle(I2PSocket socket) {
|
||||||
|
Hash peerHash = socket.getPeerDestination().calculateHash();
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
|
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
|
||||||
" from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
|
" from: " + peerHash + " port " + socket.getPort());
|
||||||
//local is fast, so synchronously. Does not need that many
|
//local is fast, so synchronously. Does not need that many
|
||||||
//threads.
|
//threads.
|
||||||
try {
|
try {
|
||||||
@@ -120,8 +197,26 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
CLIENT_SKIPHEADERS, getTunnel().getContext());
|
CLIENT_SKIPHEADERS, getTunnel().getContext());
|
||||||
long afterHeaders = getTunnel().getContext().clock().now();
|
long afterHeaders = getTunnel().getContext().clock().now();
|
||||||
|
|
||||||
addEntry(headers, HASH_HEADER, socket.getPeerDestination().calculateHash().toBase64());
|
if (_postThrottler != null &&
|
||||||
addEntry(headers, DEST32_HEADER, Base32.encode(socket.getPeerDestination().calculateHash().getData()) + ".b32.i2p");
|
command.length() >= 5 &&
|
||||||
|
command.substring(0, 5).toUpperCase(Locale.US).equals("POST ")) {
|
||||||
|
if (_postThrottler.shouldThrottle(peerHash)) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Refusing POST since peer is throttled: " + peerHash.toBase64());
|
||||||
|
try {
|
||||||
|
// Send a 503, so the user doesn't get an HTTP Proxy error message
|
||||||
|
// and blame his router or the network.
|
||||||
|
socket.getOutputStream().write(ERR_DENIED);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addEntry(headers, HASH_HEADER, peerHash.toBase64());
|
||||||
|
addEntry(headers, DEST32_HEADER, Base32.encode(peerHash.getData()) + ".b32.i2p");
|
||||||
addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64());
|
addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64());
|
||||||
|
|
||||||
// Port-specific spoofhost
|
// Port-specific spoofhost
|
||||||
|
@@ -21,6 +21,7 @@ import net.i2p.data.Signature;
|
|||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
|
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
|
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
|
||||||
|
import net.i2p.i2ptunnel.I2PTunnelHTTPServer;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
||||||
import net.i2p.i2ptunnel.TunnelController;
|
import net.i2p.i2ptunnel.TunnelController;
|
||||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||||
@@ -274,6 +275,30 @@ public class EditBean extends IndexBean {
|
|||||||
return getProperty(tunnel, PROP_MAX_STREAMS, "0");
|
return getProperty(tunnel, PROP_MAX_STREAMS, "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST limits
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public String getPostMax(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_MAX, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPostTotalMax(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPostCheckTime(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_WINDOW, I2PTunnelHTTPServer.DEFAULT_POST_WINDOW) / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPostBanTime(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_BAN_TIME) / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPostTotalBanTime(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_BAN_TIME) / 60;
|
||||||
|
}
|
||||||
|
|
||||||
private int getProperty(int tunnel, String prop, int def) {
|
private int getProperty(int tunnel, String prop, int def) {
|
||||||
TunnelController tun = getController(tunnel);
|
TunnelController tun = getController(tunnel);
|
||||||
if (tun != null) {
|
if (tun != null) {
|
||||||
|
@@ -30,6 +30,7 @@ import net.i2p.data.SessionKey;
|
|||||||
import net.i2p.i2ptunnel.I2PTunnelConnectClient;
|
import net.i2p.i2ptunnel.I2PTunnelConnectClient;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
|
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
|
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
|
||||||
|
import net.i2p.i2ptunnel.I2PTunnelHTTPServer;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelServer;
|
import net.i2p.i2ptunnel.I2PTunnelServer;
|
||||||
import net.i2p.i2ptunnel.TunnelController;
|
import net.i2p.i2ptunnel.TunnelController;
|
||||||
@@ -809,7 +810,7 @@ public class IndexBean {
|
|||||||
public void setReduceTime(String val) {
|
public void setReduceTime(String val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
try {
|
try {
|
||||||
_otherOptions.put("i2cp.reduceIdleTime", "" + (Integer.parseInt(val.trim()) * 60*1000));
|
_otherOptions.put("i2cp.reduceIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000));
|
||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,7 +836,7 @@ public class IndexBean {
|
|||||||
public void setCloseTime(String val) {
|
public void setCloseTime(String val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
try {
|
try {
|
||||||
_otherOptions.put("i2cp.closeIdleTime", "" + (Integer.parseInt(val.trim()) * 60*1000));
|
_otherOptions.put("i2cp.closeIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000));
|
||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -914,6 +915,35 @@ public class IndexBean {
|
|||||||
_otherOptions.put(PROP_MAX_STREAMS, s.trim());
|
_otherOptions.put(PROP_MAX_STREAMS, s.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST limits
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public void setPostMax(String s) {
|
||||||
|
if (s != null)
|
||||||
|
_otherOptions.put(I2PTunnelHTTPServer.OPT_POST_MAX, s.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostTotalMax(String s) {
|
||||||
|
if (s != null)
|
||||||
|
_otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, s.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostCheckTime(String s) {
|
||||||
|
if (s != null)
|
||||||
|
_otherOptions.put(I2PTunnelHTTPServer.OPT_POST_WINDOW, Integer.toString(Integer.parseInt(s.trim()) * 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostBanTime(String s) {
|
||||||
|
if (s != null)
|
||||||
|
_otherOptions.put(I2PTunnelHTTPServer.OPT_POST_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostTotalBanTime(String s) {
|
||||||
|
if (s != null)
|
||||||
|
_otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60));
|
||||||
|
}
|
||||||
|
|
||||||
/** params needed for hashcash and dest modification */
|
/** params needed for hashcash and dest modification */
|
||||||
public void setEffort(String val) {
|
public void setEffort(String val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
@@ -1124,6 +1154,9 @@ public class IndexBean {
|
|||||||
} else if ("httpserver".equals(_type) || "httpbidirserver".equals(_type)) {
|
} else if ("httpserver".equals(_type) || "httpbidirserver".equals(_type)) {
|
||||||
if (_spoofedHost != null)
|
if (_spoofedHost != null)
|
||||||
config.setProperty("spoofedHost", _spoofedHost);
|
config.setProperty("spoofedHost", _spoofedHost);
|
||||||
|
for (String p : _httpServerOpts)
|
||||||
|
if (_otherOptions.containsKey(p))
|
||||||
|
config.setProperty("option." + p, _otherOptions.get(p));
|
||||||
}
|
}
|
||||||
if ("httpbidirserver".equals(_type)) {
|
if ("httpbidirserver".equals(_type)) {
|
||||||
if (_port != null)
|
if (_port != null)
|
||||||
@@ -1180,6 +1213,13 @@ public class IndexBean {
|
|||||||
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
|
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
|
||||||
PROP_MAX_STREAMS
|
PROP_MAX_STREAMS
|
||||||
};
|
};
|
||||||
|
private static final String _httpServerOpts[] = {
|
||||||
|
I2PTunnelHTTPServer.OPT_POST_WINDOW,
|
||||||
|
I2PTunnelHTTPServer.OPT_POST_BAN_TIME,
|
||||||
|
I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME,
|
||||||
|
I2PTunnelHTTPServer.OPT_POST_MAX,
|
||||||
|
I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient
|
* do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient
|
||||||
@@ -1190,7 +1230,7 @@ public class IndexBean {
|
|||||||
"proxyUsername", "proxyPassword"
|
"proxyUsername", "proxyPassword"
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static final Set _noShowSet = new HashSet(64);
|
protected static final Set _noShowSet = new HashSet(128);
|
||||||
protected static final Set _nonProxyNoShowSet = new HashSet(4);
|
protected static final Set _nonProxyNoShowSet = new HashSet(4);
|
||||||
static {
|
static {
|
||||||
_noShowSet.addAll(Arrays.asList(_noShowOpts));
|
_noShowSet.addAll(Arrays.asList(_noShowOpts));
|
||||||
@@ -1199,6 +1239,7 @@ public class IndexBean {
|
|||||||
_noShowSet.addAll(Arrays.asList(_booleanServerOpts));
|
_noShowSet.addAll(Arrays.asList(_booleanServerOpts));
|
||||||
_noShowSet.addAll(Arrays.asList(_otherClientOpts));
|
_noShowSet.addAll(Arrays.asList(_otherClientOpts));
|
||||||
_noShowSet.addAll(Arrays.asList(_otherServerOpts));
|
_noShowSet.addAll(Arrays.asList(_otherServerOpts));
|
||||||
|
_noShowSet.addAll(Arrays.asList(_httpServerOpts));
|
||||||
_nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts));
|
_nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -425,7 +425,41 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="subdivider">
|
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||||
|
%><div class="rowItem">
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label><%=intl._("POST limits (0=unlimited)")%><br /><%=intl._("Per client")%>:</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label><%=intl._("Per period")%>:</label>
|
||||||
|
<input type="text" id="port" name="postMax" value="<%=editBean.getPostMax(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label><%=intl._("Ban minutes")%>:</label>
|
||||||
|
<input type="text" id="port" name="postBanTime" value="<%=editBean.getPostBanTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rowItem">
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label><%=intl._("Total")%>:</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<input type="text" id="port" name="postTotalMax" value="<%=editBean.getPostTotalMax(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<input type="text" id="port" name="postTotalBanTime" value="<%=editBean.getPostTotalBanTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rowItem">
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label><%=intl._("POST limit period (minutes)")%>:</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<input type="text" id="port" name="postCheckTime" value="<%=editBean.getPostCheckTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } // httpserver
|
||||||
|
%><div class="subdivider">
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
|
2013-10-26 zzz
|
||||||
|
* I2PTunnel HTTP server: New POST limiter
|
||||||
|
|
||||||
2013-10-25 zzz
|
2013-10-25 zzz
|
||||||
|
* Router: Only log ping file error once (ticket #1086)
|
||||||
* Streaming:
|
* Streaming:
|
||||||
- Check blacklist/whitelist before connection limits, so
|
- Check blacklist/whitelist before connection limits, so
|
||||||
a blacklisted peer does not increment the counters
|
a blacklisted peer does not increment the counters
|
||||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 10;
|
public final static long BUILD = 11;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user