#!/usr/bin/perl ### # # simple proxy server to observe and control HTTP connections. # version 1.2 - 07.01.2009 # # http://www.megapanzer.com - carrumba # ### use HTTP::Daemon; use HTTP::Status; use LWP::UserAgent; use DBI; # DB connection details $gDBDatabaseName = "Megapanzer"; $gDBTableName = "Connection"; $gDBUsername = "mega"; $gDBPassword = "yourPanzerPW"; # redirect requests to blocked destinations to this site instead. my($gErrorURL) = "http://www.megapanzer.com/proxy/test.html"; my($gRedirectOnError) = 1; # log requests that match the following patterns my($gGETRegex) = "(pass=|passwd=|pwd=|password=)"; my($gPOSTRegex) = $gGETRegex; my($gCookieRegex) = "(pass=|passwd=|pwd=|password=|session|sid)"; my($gDstRegex) = "\.(facebook|linkedin|skype|xing|myspqce|amazon|ebay|hi5|flickr|youtube|craiglist|skyrock|blogger|wordpress|netlog)\."; # general settings my($gLogFile) = "./proxy.log"; my($gMaxRequestTime) = 20; my($gClientConnection) = 0; my($gPID) = 0; my($gDEBUG) = 2; my($gHTTPDaemon) = new HTTP::Daemon(LocalPort => 8000, LocalAddr => '0.0.0.0', ReuseAddr => 1, Listen => 30); my(%gACCEPTEDPorts) = (80 => 1, # http 7000 => 1, # sub7 ); my(@gBLOCKDestinations) = ('\.rapidshare\.com', '\.blogspot\.com', 'www\.google\.', '\.yahoo\.', '\.live\.', '\.gov', ); my(@gBLOCKURI) = ('viagra', 'cialis', 'reply', 'gbook', 'guestbook', 'iframe', '\/\/ads?\.', 'http:\/\/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}', 'comment' ); $SIG{CHLD} = 'IGNORE'; $SIG{PIPE} = 'IGNORE'; while ($gClientConnection = $gHTTPDaemon->accept) { ### # child process ### if (($gPID = fork()) == 0) { my($gDstIP) = ""; my($gHostName) = ""; my($gPort) = ""; my($gReferer) = ""; my($gFile) = ""; my($gURI) = ""; my($gClientIP) = ""; my($gUserAgent) = ""; my($gUA) = ""; my($gMethod) = ""; my($gRequestTitle) = ""; my($gContentLength) = 0; my($gCookies) = ""; my $gObfuscatedClientIP = ""; my($gIsIntresting) = 0; my($gDBH) = connectDB($gDBDatabaseName, $gDBUsername, $gDBPassword); eval { $SIG{ALRM} = sub { die "timeout" }; alarm($gMaxRequestTime); while (my $gRequest = $gClientConnection->get_request()) { $gDstIP = $gRequest->uri->host ? $gRequest->uri->host : $gRequest->header("Host"); $gPort = (split(/:/, $gRequest->uri->host_port, 2))[1]; $gFile = $gRequest->url->path; $gURI = $gRequest->uri; $gClientIP = $gClientConnection->peerhost; $gUserAgent = $gRequest->user_agent; $gMethod = $gRequest->method; $gContentLength = ($gRequest->header("Content-Length")>0)?$gRequest->header("Content-Length"):0; $gCookies = $gRequest->header("Cookie"); $gHostName = $gRequest->header("Host"); $gReferer = $gRequest->header("Referer"); $gUA = ""; ### # port block check ### unless (defined($gACCEPTEDPorts{$gPort})) { logger("[ BLOCK port ] $gClientIP -> $gDstIP $gPort\"\n", 0); $gClientConnection->send_redirect($gErrorURL) if $gRedirectOnError; goto END; } ### # host block check ### foreach my $i (@gBLOCKDestinations) { if ($gDstIP =~ /$i/i) { $gClientConnection->send_redirect($gErrorURL) if $gRedirectOnError; logger("[ BLOCK address ] $gClientIP -> $gDstIP", 0); goto END; } } ### # uri block check ### foreach my $i (@gBLOCKURI) { if ($gURI =~ /$i/i) { $gClientConnection->send_redirect($gErrorURL) if $gRedirectOnError; logger("[ BLOCK URI ] $gClientIP -> $gDstIP -> $gURI", 0); goto END; } } ### # URI block check ### if ($gRequest->header("Content-Length") > 8192) { $gClientConnection->send_redirect($gErrorURL) if $gRedirectOnError; logger("[ BLOCK data size ] $gClientIP -> $gDstIP : Content-Length: " . $gRequest->header("Content-Length"), 0); goto END; } ### # Request method block check ### if (!$gMethod eq "GET" && $gMethod eq "POST" && $gMethod eq "HEAD") { logger("[ BLOCK method ] $gClientIP -> $gDstIP : method $gMethod", 0); goto END; } ### # Log intresting requests ### my($gIsIntresting) = 0; $gIsIntresting++ if ($gRequest->header("Authorization")); $gIsIntresting++ if ($gURI =~ /$gGETRegex/i); $gIsIntresting++ if ($gRequest->content =~ /$gPOSTRegex/i); $gIsIntresting++ if ($gRequest->header("Cookie") =~ /$gCookieRegex/i); if ($gIsIntresting > 0 && $gDstIP =~ /$gDstRegex/i) { my($lStat) = 0; if (($lStat = newConnRec($gDBH, $gDBTableName, "Request", $gClientIP, $gDstIP, $gHostName, $gPort, $gReferer, $gURI, $gMethod, $gContentLength, $gCookies)) == 0) { } elsif ($lStat < 0) { disconnectDB($gDBH); $gDBH = connectDB($gDBDatabaseName, $gDBUsername, $gDBPassword); } } ### # Prepare the new connection object to the real destination server ### $gObfuscatedClientIP = $gClientIP; $gRequest->remove_header("Proxy-Connection"); $gRequest->remove_header("Connection"); $gRequest->header("Connection", "Close"); $gRequest->header("X-Forwarded-For", $gClientIP); $gUA = LWP::UserAgent->new; $gUA->agent($gUserAgent); ### # fetch and send back data to user ### if ($gResponse = $gUA->simple_request($gRequest)) { $gClientConnection->send_response($gResponse); } $gRequestTitle = (length($gRequest->header("Cookie")) > 0)?"REQUEST W/ COOKIE":"REQUEST"; logger("[ $gRequestTitle $gIsIntresting ] - $gClientIP -> $gMethod $gURI - " . length($gResponse->content) . " bytes ", 0) if ($gDEBUG > 1); } alarm(0); }; if ($@ =~ /timeout/) { logger("[ TIMEOUT ] - $gClientIP -> $gHost - $gMethod $gURI", 0); alarm(0); } ### # free allocated resources and exit the child process. ### END: $gClientConnection->close; undef($gClientConnection); disconnectDB($gDBH); exit(0); } # if (($gPID = fork()) == 0) ### # free allocated resources in the parent process. they are useless here. ### $gClientConnection->close; undef($gClientConnection); } exit(0); ########################################################################## sub logger ########################################################################## { my($lLogMessage) = shift; my($lExitStatus) = shift; my($lFH); chomp($lLogMessage); if (open($lFH, ">>$gLogFile")) { flock($lFH, 2); print $lFH localtime(time()) . " - $lLogMessage\n"; flock($lFH, 8); close($lFH); } else { print "[ error logfile ] : $!\n"; } print localtime(time()) . " - $lLogMessage ($lExitStatus)\n"; exit($lExitStatus) if ($lExitStatus); } ########################################################################## sub connectDB ########################################################################## { my($lDBDatabaseName) = shift; my($lDBUsername) = shift; my($lDBPassword) = shift; my($lDBH); my($lSTH); my($lRetVal) = 0; if ($lDBH = DBI->connect("dbi:mysql:$lDBDatabaseName", $lDBUsername, $lDBPassword)) { $lRetVal = $lDBH; } else { ### # unable to establish DB connection ### print "connectDB() : unable to connect to DB : $DBI::errstr\n" if ($gDEBUG > 0); } return($lRetVal); } ########################################################################## sub disconnectDB ########################################################################## { my($lDBH) = shift; $lDBH->disconnect; } ########################################################################## sub newConnRec ########################################################################## { my($lRetVal) = 1; my($lSTH) = 0; my($lDBH) = shift; my($lDBTableName) = shift; my($lAction) = shift; my($lSrcIP) = shift; my($lDstIP) = shift; my($lDstHostName) = shift; my($lDstPort) = shift; my($lReferer) = shift; my($lURI) = shift; my($lReqMethod) = shift; my($lReqSize) = shift; my($lCookies) = shift; $lDBQuery = qq{INSERT INTO $lDBTableName (Action, SrcIP, DstIP, DstPort, DstHostName, Referer, URI, RequestMethod, RequestSize, Cookies) VALUES ("$lAction", "$lSrcIP", "$lDstIP", $lDstPort, "$lDstHostName", "$lReferer", "$lURI", "$lReqMethod", $lReqSize, "$lCookies")}; $lSTH = $lDBH->do($lDBQuery); return($lRetVal); } __END__ MySQL table create statement ---------------------------- CREATE TABLE Connection ( ConnectionID int(16) unsigned NOT NULL auto_increment, Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, Action varchar(32) NOT NULL, SrcIP varchar(64) NOT NULL, DstIP varchar(64) NOT NULL, DstPort int(4) NOT NULL, DstHostName varchar(128) DEFAULT NULL, Referer varchar(256) DEFAULT NULL, URI varchar(512) NOT NULL, RequestMethod varchar(16) NOT NULL, RequestSize int(16) DEFAULT 0, Cookies varchar(1024), PRIMARY KEY (ConnectionID) );