views:

185

answers:

2

Hello!

I'm wondering how to limit the TCP requests per client (per specific IP) in Java. For example, I would like to allow a maximum of X requests per Y seconds for each client IP. I thought of using static Timer/TimerTask in combination with a HashSet of temporary restricted IPs.

private static final Set<InetAddress> restrictedIPs = Collections.synchronizedSet(new HashSet<InetAddress>());

private static final Timer restrictTimer = new Timer();

So when a user connects to the server, I add his IP to the restricted list, and start a task to unrestrict him in X seconds.

restrictedIPs.add(socket.getInetAddress());

restrictTimer.schedule(new TimerTask()
{
  public void run()
  {
    restrictedIPs.remove(socket.getInetAddress());
  }

},  MIN_REQUEST_INTERVAL);

My problem is that at the time the task will run, the socket object may be closed, and the remote IP address won't be accessible anymore...

Any ideas welcomed! Also, if someone knows a Java-framework-built-in way to achieve this, I'd really like to hear it.

+1  A: 

A simple solution for this would be:

final InetAddress ip = socket.getInetAddress();
restrictedIPs.add(ip);

restrictTimer.schedule(new TimerTask()
{
  public void run()
  {
    restrictedIPs.remove(ip);
  }

},  MIN_REQUEST_INTERVAL);

The problem with this method is that it requires extra threads to remove the restricted IP addresses. Maybe it is easier to keep a Map of InetAddress to a timestamp, to keep track of the last time they accessed a page. This way, you can simply check this timestamp each time a client starts a request.

Marc
Thanks for your answer! I'll probably do a Map as you said.
asmo
Just on problem with this solution: how to determine when to remove the IPs from the map? Because if the server run for a long time, it will end up with a map of thousands of IPs.
asmo
A couple of thousand should not be a problem, but I understand your concern. You could schedule a cleanup to be performed every now hour or so, to delete all IPs that are old (say older than an hour).
Marc
+2  A: 

One option is to use netfilter to accomplish this. Not 'pure' java, but probably the solution which will be most robust and error-free. This example taken from debian-administration

iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  --set

iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  --update --seconds 60 --hitcount 4 -j DROP

EDIT:

To just quick-fix your code, save the IP address and add a count before you block, i.e (pseudo):

IPaddress addr = socket.getAddress();
int hitcount = hitcountMap.get(addr).value();
if (hitcount <= HIT_MAX) {
    //only increase if person hasn't reached roof, prevents 'overblocking'
    hitcountMap.get(addr).increase();
    unblockTimer.schedule(hitcountMap.get(addr).decrease(), BLOCK_TIMEOUT);
}
if (hitcount > HIT_MAX) {
    connection.drop();
}

This should make the block last at a maximum BLOCK_TIMEOUT (not guaranteed bug-free). You should consider using a semaphore for the counting, since you will (probably) run this code from many threads, depending on your threading model of course.

disown
hitcount.get? Isn't hitcount a int?
asmo
oh, I used hitcount to denote a map<addr, int> AND the int which I got from it. Confusing, will change.
disown
there changed it.
disown
Thank you! Unfortunately, I won't be able to use iptables since my server application needs cross-platform support.The hitcount idea is not bad though.
asmo