class HttpblEvaluator in http:BL 8
HttpblEvaluator evaluates visitor/host page requests.
Hierarchy
- class \Drupal\httpbl\HttpblEvaluator implements HttpblEvaluatorInterface
Expanded class hierarchy of HttpblEvaluator
1 string reference to 'HttpblEvaluator'
1 service uses HttpblEvaluator
File
- src/
HttpblEvaluator.php, line 99
Namespace
Drupal\httpblView source
class HttpblEvaluator implements HttpblEvaluatorInterface {
/**
* The ban IP manager.
*
* @var \Drupal\ban\BanIpManagerInterface
*/
protected $banManager;
/**
* A logger arbitration instance.
*
* @var \Drupal\httpbl\Logger\HttpblLogTrapperInterface
*/
protected $logTrapper;
/**
* Construct HttpblEvaluator.
*
* @param \Drupal\ban\BanIpManagerInterface $banManager
* Core Drupal Ban manager.
* @param \Drupal\httpbl\Logger\HttpblLogTrapperInterface $logTrapper
* A logger arbitration instance.
*/
public function __construct(BanIpManagerInterface $banManager, HttpblLogTrapperInterface $logTrapper) {
$this->banManager = $banManager;
$this->logTrapper = $logTrapper;
}
/**
* {@inheritdoc}
*/
public function getPageRequestOption() {
$check_option = (int) \Drupal::state()
->get('httpbl.check');
if ($check_option == HTTPBL_CHECK_ALL) {
return TRUE;
}
return FALSE;
}
/**
* {@inheritdoc}
*
* Manages remote and local lookups on visiting host IPs, evaluates their
* remote status as safe or suspicious and determines a locally stored status
* (safe / white-listed, grey-listed, or blacklisted) which is used (by other
* functions) to determine an appropriate, subsequent response to a request.
*/
public function evaluateVisitor($ip, $request, $project_supported) {
// Evaluated status that was already locally stored or was calculated based on
// a score retrieved from Project Honeypot.
/** @var integer $evaluated_status */
static $evaluated_status;
// If not a supported lookup, mark "safe" and log notice.
// This will avoid any further processing or storage.
if (!$project_supported) {
$evaluated_status = HTTPBL_LIST_SAFE;
$this->logTrapper
->trapNotice('HttpBL evaluation not supported for IPv6 @ip.', [
'@ip' => $ip,
]);
}
// Evaluated status was already calculated -- return.
if (is_int($evaluated_status)) {
$evaluated = [
'evaluated',
$evaluated_status,
];
/** @var array $evaluated */
return $evaluated;
}
// Check if visitor already has a white-listed session (granted by the white-list challenge).
if (self::visitor_whitelisted_session($ip)) {
$evaluated_status = HTTPBL_LIST_SAFE;
$this->logTrapper
->trapDebug('@ip already session white-listed for request @request.', [
'@ip' => $ip,
'@request' => $request
->getRequestUri(),
]);
$evaluated = [
'evaluated',
$evaluated_status,
];
return $evaluated;
}
elseif (\Drupal::state()
->get('httpbl.storage') > HTTPBL_DB_OFF) {
$evaluated_status = $this
->getIpLocalStatus($ip);
//Humanize the status results for any verbose logging.
$human = 'Not found';
$status = $evaluated_status;
if (is_string($evaluated_status)) {
switch ($evaluated_status) {
case '0':
$human = 'white-listed';
break;
case '1':
$human = 'blacklisted';
break;
case '2':
$human = 'greylisted';
// Prepare to set up a challenge response.
$_SESSION['httpbl_ip'] = $ip;
$_SESSION['httpbl_challenge'] = TRUE;
break;
}
}
$this->logTrapper
->trapDebug('Local query for @ip: @human (status = @status).', [
'@ip' => $ip,
'@status' => $status,
'@human' => $human,
]);
}
// Visitor is not already white listed and not found in Httpbl table, so we'll do a DNS Lookup.
if (!is_numeric($evaluated_status)) {
$this->logTrapper
->trapDebug('Honeypot DNS Lookup for IP @ip.', [
'@ip' => $ip,
]);
// Do a Project Honeypot DNS lookup, and continue if lookup was succesful
if ($response = $this
->httpbl_dnslookup($ip)) {
$stats = \Drupal::state()
->get('httpbl.stats') ?: TRUE;
$black_threshold = \Drupal::state()
->get('httpbl.black_threshold') ?: HTTPBL_THRESHOLD_BLACK;
$grey_threshold = \Drupal::state()
->get('httpbl.grey_threshold') ?: HTTPBL_THRESHOLD_GREY;
$score = $response['threat'];
//@todo Someday we'll do something with the 'type' response from P.H.
//$type = $response['type'];
// Blacklisted?
// (Is the threat score at Project Honeypot above our threshold?)
if ($score > $black_threshold && $response['type']) {
$this->logTrapper
->trapWarning('@ip ranked: blacklisted (Threat Score = @score).', [
'@ip' => $ip,
'@score' => $score,
'link' => self::projectLink($ip),
]);
// If settings indicate we are storing results...
if (\Drupal::state()
->get('httpbl.storage') > HTTPBL_DB_OFF) {
// Store this blacklisted IP.
$this
->setIpLocalStatus($ip, HTTPBL_LIST_BLACK, \Drupal::state()
->get('httpbl.blacklist_offset') ?: 31536000);
// Increment the stats if configured to do so.
if ($stats) {
\Drupal::state()
->set('httpbl.stat_black', \Drupal::state()
->get('httpbl.stat_black') + 1);
}
}
$evaluated_status = HTTPBL_LIST_BLACK;
$evaluated = [
'evaluated',
$evaluated_status,
];
return $evaluated;
}
elseif ($score > $grey_threshold && $response['type']) {
// Prepare to set up a challenge response.
$_SESSION['httpbl_ip'] = $ip;
$_SESSION['httpbl_challenge'] = TRUE;
$this->logTrapper
->trapNotice('@ip ranked: grey-listed (Threat Score = @score).', [
'@ip' => $ip,
'@score' => $score,
'link' => self::projectLink($ip),
]);
// Store the results if configured to do so.
if (\Drupal::state()
->get('httpbl.storage') > HTTPBL_DB_OFF) {
$this
->setIpLocalStatus($ip, HTTPBL_LIST_GREY, \Drupal::state()
->get('httpbl.greylist_offset') ?: 86400);
// Increment the stats if configured to do so.
if ($stats) {
\Drupal::state()
->set('httpbl.stat_grey', \Drupal::state()
->get('httpbl.stat_grey') + 1);
}
}
$evaluated_status = HTTPBL_LIST_GREY;
$evaluated = [
'evaluated',
$evaluated_status,
];
return $evaluated;
}
}
else {
// No result from Project Honeypot, so log and then...
$this->logTrapper
->trapInfo('No Honeypot profile for @ip. ("safe").', [
'@ip' => $ip,
'link' => self::projectLink($ip),
]);
// If settings indicate we are storing results,
if (\Drupal::state()
->get('httpbl.storage') > HTTPBL_DB_OFF) {
// White-list locally - with configured offset settings (default is 3 hours).
$this
->setIpLocalStatus($ip, HTTPBL_LIST_SAFE, \Drupal::state()
->get('httpbl.safe_offset') ?: 10800);
}
// Evaluated (assumed) Safe.
$evaluated_status = HTTPBL_LIST_SAFE;
}
$evaluated = [
'evaluated',
$evaluated_status,
];
return $evaluated;
}
elseif (!($evaluated_status == HTTPBL_LIST_SAFE)) {
// This line will show when only blocking comment submissions.
drupal_set_message(t('Your IP address (@ip) is restricted on this site.', [
'@ip' => $ip,
]), 'error', FALSE);
}
// Fini!
$evaluated = [
'evaluated',
$evaluated_status,
];
return $evaluated;
}
/**
* {@inheritdoc}
*
* Check if an IP is already white-listed via a session white-list challenge.
*/
public static function visitor_whitelisted_session($ip) {
return isset($_SESSION['httpbl_status']) && $_SESSION['httpbl_status'] == 'session_whitelisted';
}
/**
* Do http:BL DNS lookup at Project Honeypot Org
*
* @param string $ip
* The IP address to be checked.
* @param string $key
* The administrative access key.
*
* @return array $values | FALSE
*
* @todo Don't think anything is really capturing the response type
* values to store with the hosts. Use these?
*/
public function httpbl_dnslookup($ip, $key = NULL) {
// Thanks to J.Wesley2 at
// http://www.projecthoneypot.org/board/read.php?f=10&i=1&t=1
if (!($ip = self::_httpbl_reverse_ip($ip))) {
return FALSE;
}
// Make sure there is a valid access key before we proceed.
if (!$key && !($key = \Drupal::state()
->get('httpbl.accesskey') ?: NULL)) {
return FALSE;
}
$query = $key . '.' . $ip . '.dnsbl.httpbl.org.';
$response = gethostbyname($query);
if ($response == $query) {
// if the domain does not resolve then it will be the same thing we passed to gethostbyname.
return FALSE;
}
$values = array();
$values['raw'] = $response;
$response = explode('.', $response);
if ($response[0] != '127') {
// if the first octet is not 127, the response should be considered invalid
$this->logTrapper
->trapWarning('DNS Lookup failed for @ip, response was @response', array(
'@ip' => $ip,
'@response' => $values['raw'],
));
return FALSE;
}
// Lookup at Project Honey Pot was successful.
$this->logTrapper
->trapDebug('DNS lookup results for @ip, response was @response', array(
'@ip' => $ip,
'@response' => $values['raw'],
));
$values['last_activity'] = $response[1];
$values['threat'] = $response[2];
$values['type'] = $response[3];
if ($response[3] == 0) {
//if it's 0 then it's only a Search Engine
$values['search_engine'] = TRUE;
}
if ($response[3] & 1) {
//does it have the same bits as 1 set
$values['suspicious'] = TRUE;
}
if ($response[3] & 2) {
//does it have the same bits as 2 set
$values['harvester'] = TRUE;
}
if ($response[3] & 4) {
//does it have the same bits as 4 set
$values['comment_spammer'] = TRUE;
}
return $values;
}
/**
* Reverse IP octets
*
* @param string $ip
* @return string
*/
public static function _httpbl_reverse_ip($ip) {
if (!is_numeric(str_replace('.', '', $ip))) {
return NULL;
}
$ip = explode('.', $ip);
if (count($ip) != 4) {
return NULL;
}
return $ip[3] . '.' . $ip[2] . '.' . $ip[1] . '.' . $ip[0];
}
/**
* {@inheritdoc}
*
* Get status of IP in httpbl_host table of stored hosts.
*
* (legacy name was "_httpbl_cache_get".)
*/
public function getIpLocalStatus($ip) {
// Gather all hosts with this IP.
$hosts = HostQuery::loadHostsByIp($ip);
// If we have some, count them.
if (isset($hosts) && !empty($hosts)) {
$count = count($hosts);
// As long as there's more than one...
while ($count > 1) {
// Sort them in order by index.
ksort($hosts);
// Get that host and delete it.
$id = key($hosts);
$host = Host::load($id);
$host
->delete();
// Reverse sort the array and remove the last one.
arsort($hosts);
array_pop($hosts);
// Rinse and repeat.
$count--;
}
// Get the status of the last IP found.
$id = key($hosts);
$host = Host::load($id);
$status = $host
->getHostStatus();
}
else {
$status = NULL;
}
return $status;
}
/**
* {@inheritdoc}
*
* Create and store new evaluated hosts to httpbl_host table.
*
* (legacy name was "_httpbl_cache_set")
*/
public function setIpLocalStatus($ip, $status, $offset = 0) {
$hosts = HostQuery::loadHostsByIp($ip);
if (isset($hosts) && empty($hosts)) {
$host = Host::create([
'host_ip' => $ip,
'host_status' => $status,
'expire' => \Drupal::time()
->getRequestTime() + $offset,
'source' => HTTPBL_ORIGINAL_SOURCE,
]);
$host
->save();
$project_link = $host
->projectLink();
$source = $host
->getSource();
// If configured to also ban blacklisted IPs via Drupal Core Ban module...
if ($status == HTTPBL_LIST_BLACK && \Drupal::state()
->get('httpbl.storage') == HTTPBL_DB_HH_DRUPAL && \Drupal::moduleHandler()
->moduleExists('ban')) {
// Ban this IP!
$this->banManager
->banIp($ip);
$this->logTrapper
->trapNotice('Host: new blacklisted and banned @title. Source: @source.', array(
'@title' => $host
->label(),
'@source' => $source,
'link' => $project_link,
));
}
elseif ($status == HTTPBL_LIST_BLACK && \Drupal::state()
->get('httpbl.storage') == HTTPBL_DB_HH) {
$this->logTrapper
->trapNotice('Host: new blacklisted @title. Source: @source.', array(
'@title' => $host
->label(),
'@source' => $source,
'link' => $project_link,
));
}
elseif ($status == HTTPBL_LIST_GREY) {
$this->logTrapper
->trapNotice('Host: new grey-listed @title. Source: @source.', array(
'@title' => $host
->label(),
'@source' => $source,
'link' => $project_link,
));
}
elseif ($status == HTTPBL_LIST_SAFE) {
// Most IPs should be safe, so only log this as Info.
$this->logTrapper
->trapInfo('Host: new white-listed @title. Source: @source.', array(
'@title' => $host
->label(),
'@source' => $source,
'link' => $project_link,
));
}
return;
}
else {
$this->logTrapper
->trapError('Attempt to add host @ip, but it already exists!', [
'@ip' => $ip,
]);
}
return FALSE;
}
/**
* {@inheritdoc}
*
* Emergency White-list Update of Host IP.
*/
public function drushWhitelist($ip) {
// Set a default of 48 hours.
$offset = 172800;
// Take the higher of the default or the configured offset for safe hosts.
$offsetConfig = \Drupal::state()
->get('httpbl.safe_offset');
//$max <= $limit ?: $max = $limit;
$offset >= $offsetConfig ?: ($offset = $offsetConfig);
$hosts = HostQuery::loadHostsByIp($ip);
if (isset($hosts) && !empty($hosts)) {
foreach ($hosts as $host) {
$host
->setHostStatus(0);
$host
->setExpiry(\Drupal::time()
->getRequestTime() + $offset);
$host
->setSource(HTTPBL_DRUSH_SOS_SOURCE);
$host
->save();
}
}
else {
// Warning to identify any abuse.
$this->logTrapper
->trapWarning('Drush whitelist did not find IP @ip.', [
'@ip' => $ip,
]);
}
}
/**
* {@inheritdoc}
*/
public function getHumanStatus($status) {
switch ($status) {
case '0':
$human = t('White-listed');
break;
case '1':
$human = t('Blacklisted');
break;
case '2':
$human = t('Grey-listed');
break;
}
return $human;
}
/**
* Update stored status of Host IP.
*
* (legacy name was "_httpbl_cache_update".)
*/
public static function updateIpLocalStatus($ip, $status, $offset = 0) {
// Collect needed services.
$banManager = \Drupal::service('ban.ip_manager');
$logTrapper = \Drupal::service('httpbl.logtrapper');
$hosts = HostQuery::loadHostsByIp($ip);
if (isset($hosts) && !empty($hosts)) {
foreach ($hosts as $host) {
$host
->setHostStatus($status);
$host
->setExpiry(\Drupal::time()
->getRequestTime() + $offset);
$host
->setSource(HTTPBL_CHALLENGE_FAILURE);
$host
->save();
}
}
else {
// Error. Something could be broken.
$logTrapper
->trapError('Cannot blacklist non-existing IP (@ip).', [
'@ip' => $ip,
]);
}
// If blacklisted host and using "Auto-banning"...
if ($status == HTTPBL_LIST_BLACK && \Drupal::state()
->get('httpbl.storage') == HTTPBL_DB_HH_DRUPAL) {
// Check if host is already banned.
if ($banManager
->isBanned($ip)) {
// Warning. This shouldn't be happening.
$logTrapper
->trapWarning('This host (@ip) is already banned', [
'@ip' => $ip,
]);
// This message should never be seen by anyone who has really been banned.
drupal_set_message(t('IP address ( @ip ) currently banned from this site.', array(
'@ip' => $ip,
)), 'error', FALSE);
}
else {
$banManager
->banIp($ip);
// Warning. Most likely a white-list challenge failure.
$logTrapper
->trapWarning('Host (@ip) has been banned from this site.', [
'@ip' => $ip,
]);
// Possible to see this message once after failing challenge, but never
// again after a page refresh.
drupal_set_message(t('Your IP address ( @ip ) has been banned from this site.', array(
'@ip' => $ip,
)), 'error', FALSE);
}
}
}
/**
* Quickly make up to 255 evaluated hosts of a certain status and expire time.
*
* @param int $max The number of hosts to be generated.
* @param int $status The status (0=safe, 1=blackisted, 2=grey-listed)
* @param int $offset The time from now the host should expire.
* @param string $pattern The pattern used for IP addresses.
*
* @internal $count
* ---------------------------------------------------------------------------
*
* Example uses:
* Execute in devel/php to make dummy hosts.
*
* Also executable in drush as "drush mho".
*
* use Drupal\httpbl\Utility\Makehosts;
* Makehosts::makeHosts(); // Use defaults and create 255 safe hosts that will
* expire in 5 minutes. Useful for testing Cron.
*
* use Drupal\httpbl\Utility\Makehosts;
* Makehosts::makeHosts(50,2, 60, '129.0.1.'); Make some grey-listed and edit
* them.
*
* use Drupal\httpbl\Utility\Makehosts;
* Makehosts::makeHosts(1,0, 120, '127.0.0.' ); Make a localhost that lasts
* 2 minutes. Then delete it (it will come right back, from Project Honeypot!)
*
*/
public static function makeHosts($max = 255, $status = 0, $offset = 300, $pattern = '127.0.1.') {
$limit = 255;
$max <= $limit ?: ($max = $limit);
$count = 1;
$max = $max + 1;
while ($count < $max) {
$ip = $pattern . $count;
$host = Host::create([
'host_ip' => $ip,
'host_status' => $status,
'expire' => \Drupal::time()
->getRequestTime() + $offset,
'source' => t(HTTPBL_DRUSH_CREATED),
]);
$host
->save();
$logTrapper = \Drupal::service('httpbl.logtrapper');
$logTrapper
->trapDebug('@ip test host created with makeHosts().', [
'@ip' => $ip,
]);
$count++;
}
}
/**
* Quickly make up to 255 evaluated and banned hosts of a certain expire time.
*
* @param int $max The number of hosts to be generated.
* @param int $offset The time from now the host should expire.
* @param string $pattern The pattern used for IP addresses.
*
* @internal $count
* ---------------------------------------------------------------------------
*
* Example uses:
* Execute in devel/php to make dummy hosts.
*
* Also executable in drush as "drush mbb".
*
* use Drupal\httpbl\Utility\Makehosts;
* Makehosts::makeBannedHosts(); // Use defaults to create 255 blacklisted and
* banned hosts that will expire in 5 minutes. Useful for testing Cron.
*
* use Drupal\httpbl\Utility\Makehosts;
* Makehosts::makeBannedHosts(50,60, '129.0.8.'); Make 50 blacklisted and
* banned hosts that will last one minute.
*
*/
public static function makeBannedHosts($max = 255, $offset = 300, $pattern = '127.1.8.') {
$limit = 255;
$max <= $limit ?: ($max = $limit);
$count = 1;
$max = $max + 1;
$status = 1;
while ($count < $max) {
$ip = $pattern . $count;
$host = Host::create([
'host_ip' => $ip,
'host_status' => $status,
'expire' => \Drupal::time()
->getRequestTime() + $offset,
'source' => t(HTTPBL_DRUSH_CREATED_BANNED),
]);
$host
->save();
$banManager = \Drupal::service('ban.ip_manager');
$banManager
->banIp($host
->label());
$logTrapper = \Drupal::service('httpbl.logtrapper');
$logTrapper
->trapDebug('@ip test banned host created with makeBannedHosts().', [
'@ip' => $ip,
]);
$count++;
}
}
/**
* Creates a link to Project Honey Pot IP Address Inspector.
*
* This function is used after a lookup, before a host entity has been
* created, to enable an operations link in the log entry.
*
* @param string $ip
* The IP address that was looked up.
* @param string $text
* The link text.
* @return string
* The formatted link.
*/
public static function projectLink($ip, $text = 'Project Honeypot') {
$url = \Drupal\Core\Url::fromUri('http://www.projecthoneypot.org/search_ip.php?ip=' . $ip);
$url_options = [
'attributes' => [
'target' => '_blank',
'title' => t('Project Honey Pot IP Address Inspector.'),
],
];
$url
->setOptions($url_options);
// Break this line up for debugging.
//$operations = \Drupal\Core\Link::fromTextAndUrl(t($text), $url )->toString();
$operations = \Drupal\Core\Link::fromTextAndUrl(t($text), $url);
// Below fails (intermittently) in core url_generator, when page_cache
// is enabled.
$operations = $operations
->toString();
return $operations;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
HttpblEvaluator:: |
protected | property | The ban IP manager. | |
HttpblEvaluator:: |
protected | property | A logger arbitration instance. | |
HttpblEvaluator:: |
public | function |
Emergency White-list Update of Host IP. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public | function |
Manages remote and local lookups on visiting host IPs, evaluates their
remote status as safe or suspicious and determines a locally stored status
(safe / white-listed, grey-listed, or blacklisted) which is used (by other
functions) to determine an… Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public | function |
Translates status codes to Mnemonic. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public | function |
Get status of IP in httpbl_host table of stored hosts. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public | function |
Examine config option for checking all page requests. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public | function | Do http:BL DNS lookup at Project Honeypot Org | |
HttpblEvaluator:: |
public static | function | Quickly make up to 255 evaluated and banned hosts of a certain expire time. | |
HttpblEvaluator:: |
public static | function | Quickly make up to 255 evaluated hosts of a certain status and expire time. | |
HttpblEvaluator:: |
public static | function | Creates a link to Project Honey Pot IP Address Inspector. | |
HttpblEvaluator:: |
public | function |
Create and store new evaluated hosts to httpbl_host table. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public static | function |
Update stored status of Host IP. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public static | function |
Check if an IP is already white-listed via a session white-list challenge. Overrides HttpblEvaluatorInterface:: |
|
HttpblEvaluator:: |
public static | function | Reverse IP octets | |
HttpblEvaluator:: |
public | function | Construct HttpblEvaluator. |