#!/usr/bin/perl -w


=head1 NAME

DropClients - Drop agents that make bad requests to our site.

=cut

=head1 ABOUT

  This module will ban clients that make bogus requests to our
 server.

  We only care about one kind of bogus request - and that
 is requests that contain the "#" or fragment character.  This shouldn't
 be sent to our server, instead it should be processed client side.

=cut

=head1 INFORMATION

  This module was written for a simple article on mod_perl available
 here:

    http://debian-administration.org/tag/mod_perl

=cut

=head1 AUTHOR

 Steve
 --
 http://www.steve.org.uk/

=cut

=head1 LICENSE

Copyright (c) 2009 by Steve Kemp.  All rights reserved.

This module is free software;
you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut


use strict;
use warnings;


#
#  Modules we use.
#
use APR::Table;
use Apache2::Connection;
use Apache2::Const;
use Apache2::Log;



#
#  Our package.
#
package DropClients;
our $VERSION = '0.01';




=begin doc

  Called when a request comes in.

=end doc

=cut

sub handler
{

    #
    #  We only care about the initial request.
    #
    my $r = shift;
    return Apache2::Const::DECLINED unless $r->is_initial_req;


    #
    #  Create an identifier for the remote client,
    # which is based upon their IP, and their user-agent
    #
    my $ip     = $r->connection->remote_ip();
    my $agent  = $r->headers_in->{ 'User-Agent' } || "unknown-agent";
    my $client = $ip . $agent;


    #
    #  Sanitize the client a little
    #
    $client =~ s/[^a-zA-Z0-9_-]//g;


    #
    #  If the request contains the hash then mark the client as bad
    #
    my $uri = $r->unparsed_uri();

    if ( $uri =~ /#/ )
    {

        #
        #  Prevent the client from making further connections
        #
        blockRemote($client);


        #
        #  Deny
        #
        $r->log_reason("Blocking IP:$ip [$agent] for request: $uri");
        return Apache2::Const::FORBIDDEN;
    }

    #
    # Is the visitor currently blocked?
    #
    if ( isBlocked($client) )
    {
        $r->log_reason("Blocked IP:$ip [$agent] for request: $uri");
        return Apache2::Const::FORBIDDEN;
    }

    #
    #  OK access allowed
    #
    return Apache2::Const::DECLINED;

}



=begin doc

  Test if the given client is blocked.

=end doc

=cut

sub isBlocked
{
    my ($remote) = (@_);

    return ( -e "/tmp/blah/$remote" );
}



=begin doc

  Block the given client.

=end doc

=cut

sub blockRemote
{
    my ($remote) = (@_);

    #
    #  Make sure we have the block directory
    #
    if ( !-d "/tmp/blah" )
    {
        system("mkdir -p /tmp/blah");
    }

    #
    #  Record the block
    #
    system( "touch", "/tmp/blah/$remote" );

}

#
#  The end of the module
#
1;
