<?php
/****************************************************************************************
* Class : firewall
* Ver. : 1.0
* Author : Temperini Mirko <hide@address.com>
* Date : 03-29-2010
* License : GPL License
*
* IMPORTANT!!!
*
* The firewall must be EVER started at the top of the script ( think to session_start() as condition rules)
*
* ONLY ONE ISTANCE IS ALLOWED IN A PAGE!!!
*
*
* calling costructor, you can pass directly the rules table to parse
* es: $myFirewall = new firewall(fielname.ext);
* and the table is directly loaded
*
*
*
* you can set 2 different types of output on deny ip:
* default: send an header with 403 Status (Forbidden)
* redirect: it redirect by a single javascript line to a link specified.
* why javascript? because when a server is not propelry setted or the page have already sended the header, it not fail.
* $myFirewall->setAction('redirect','http://www.google.com');
*
* show: simply output the code you pass in
* $myFirewall->setAction( 'show',file_get_contents('forbidden.tpl') );
*
*
* if no action is specified, the default is a blank page whit 403 Status Header (Forbidden)
*
*
* ok, we turn On our firewall
* $myFirewall->start();
*
* ----------------------------------------
* |CAUTION! |
* ----------------------------------------------------------------------------------------------------
* the forceHostname() method try to resolve an IP from ah hipotetic hostname, when it isn't a valid |
* this feature can make your firewall slow! Depends from the necessary time to resolve the IP! |
* ----------------------------------------------------------------------------------------------------
*
*this class is successfull tested on PHP 5.3
*
* please report bugs at : <hide@address.com>
*
****************************************************************************************/
class firewall{
private $rules_table =""; //path to rules table
private $code_rules =array('allow'=>array(),'deny'=>array());
private $action ="";
private $source ="";
private $ipClient =null;
private $force_hostname =false ;
private $onDenied ='';
private $onAllowed ='';
final public function __construct($rules_table=""){
$this->rules_table=$rules_table;
$this->loadTable($this->rules_table);
}
final public function start(){
$this->ipClient =$this->clientIp();
$this->onDenied =trim((string)$this->onDenied);
$this->onAllowed =trim((string)$this->onAllowed);
//var_dump($this->onAllowed);
if( !$this->isAllowed($this->ipClient) ){
switch($this->action){
case 'redirect':
echo '<script type="text/javascript">location.href="'.$this->source.'"</script>';
break;
case 'show':
echo $this->source;
break;
default:
header('HTTP/1.1 403 Forbidden');
break;
}
if($this->onDenied != '')
call_user_func($this->onDenied,$this->ipClient);
exit();
}
else{
if(trim($this->onAllowed) != '')
call_user_func($this->onAllowed,$this->ipClient);
}
}
final private function storeRule($type,$values){
$clientIp=$this->ip2int($this->clientIp());
switch($type){
case 'range':
$action =$values[1];
$from =( str_replace('.','',$values[3]) > str_replace('.','',$values[2]) )?$values[2]:$values[3];
$from =$this->ip2int($from);
$to =( str_replace('.','',$values[3]) > str_replace('.','',$values[2]) )?$values[3]:$values[2];
$to =$this->ip2int($to);
$this->code_rules[$action][]="if($clientIp >= $from && $clientIp <= $to) \$result=true;";
break;
case 'simple':
$action =$values[1];
$ip=$this->ip2int($values[2]);
$this->code_rules[$action][]="if ($clientIp == $ip) \$result = true;";
break;
}
}
final public function isAllowed($ip){
// first check allowed arrays. if not results, check denied array. if not results return true;
$rules=$this->code_rules;
if(!empty($rules['allow'])){
foreach($rules['allow'] as $allow){
@eval($allow);
if(isset($result)) return true;
}
}
if(!empty($rules['deny'])){
foreach($rules['deny'] as $deny){
@eval($deny);
if(isset($result)) return false;
}
}
return true;
}
final private function ip2int($ip){
$tmp=explode('.',$ip);
foreach($tmp as $t_key=>$t_val) $tmp[$t_key]=str_pad($t_val,3, "0", STR_PAD_LEFT);
return implode('',$tmp);
}
final private function isValidIp($ip){
return preg_match("^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}^", $ip);
}
final private function clientIp(){
if (!empty($_SERVER['HTTP_CLIENT_IP'])) $ip=$_SERVER['HTTP_CLIENT_IP'];
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) $ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
else $ip=$_SERVER['REMOTE_ADDR'];
return $ip;
}
final public function loadTable($rule_table=''){
$this->rules_table=$rule_table;
$rulesFile=$this->rules_table;
if(is_file($rulesFile) && is_readable($rulesFile)){
$rules=file($rulesFile,FILE_IGNORE_NEW_LINES);
if(!$rules) $rules=array();
if(!empty($rules)){
foreach($rules as $ruleLine){
$ruleLine=trim($ruleLine);
if($ruleLine !=""){
// check syntax allow/deny n.n.n.n to n.n.n.n
preg_match('/^(deny|allow)[\s](.*)[\s]to[\s](.*)$/i',$ruleLine,$values);
if(!empty($values) && $this->isValidIp($values[2]) && $this->isValidIp($values[3])){
$this->storeRule('range',$values);
continue;
}
//check syntax allow/deny simple n.n.n.n
preg_match('/^(deny|allow)[\s](.*)$/i',$ruleLine,$values);
if(!empty($values) && !empty($values[2])){
$checkIp=$this->isValidIp($values[2]);
if( !(bool)$checkIp ){
//try to resolve ip by the given string...
if($this->force_hostname)
$checkRemoteIp=gethostbyname($checkIp);
$values[2]=(isset($checkRemoteIp) && (bool)$checkRemoteIp !== false )?$checkRemoteIp:$values[2];
}
$this->storeRule('simple',$values);
continue;
}
}
}
}
}
}
final public function onAllow($callback=''){
$this->onAllowed=(string)$callback;
}
final public function onDeny($callback=''){
$this->onDenied=(string)$callback;
}
//CAUTION!
//this feature can make your firewall slow! Depends from the necessary time to resolve the IP!
final public function forceHostname($value=true){
$this->force_hostname=(bool)$value;
}
final public function setAction($type,$source){
$this->action=$type;
$this->source=$source;
}
}