You are here

httpbl.module in http:BL 6.2

Same filename and directory in other branches
  1. 8 httpbl.module
  2. 5 httpbl.module
  3. 6 httpbl.module
  4. 7 httpbl.module

Implementation of http:BL for Drupal. It provides IP-based blacklisting through http:BL and allows linking to a honeypot.

@author Mark Janssen (praseodym) @link http://drupal.org/project/httpbl @link http://httpbl.org/

File

httpbl.module
View source
<?php

/**
 * @file
 * Implementation of http:BL for Drupal. It provides IP-based
 * blacklisting through http:BL and allows linking to a honeypot.
 *
 * @author Mark Janssen (praseodym)
 * @link http://drupal.org/project/httpbl
 * @link http://httpbl.org/
 */

/**
 *
 * Version 6.x-2.x-dev
 * Contact: Bryan Lewellen (bryrock) (http://drupal.org/user/346823)
 *
 */

/**
 *
 * Version 6.x-dev
 * Contact: praseodym (at) gmail (dot) com
 *
 * Feel free to improve this module, but please contact the author with any
 * changes you make so they can be implemented into the 'official' version.
 *
 */

/**
 * Cache levels.
 */
define('HTTPBL_CACHE_OFF', 0);

// Cache is off
define('HTTPBL_CACHE_DB', 1);

// Only database cache
define('HTTPBL_CACHE_DBDRUPAL', 2);

// Both database cache and Drupal access table

/**
 * Blacklist levels.
 */
define('HTTPBL_LIST_SAFE', 0);

// Not listed (or very low threat)
define('HTTPBL_LIST_GREY', 2);

// Greylisted: session whitelist request permitted
define('HTTPBL_LIST_BLACK', 1);

// Blacklisted: all requests blocked.

/**
 * Threshold levels.
 */
define('HTTPBL_THRESHOLD_GREY', 1);

// Default threshold under which a user is allowed, above which a user is greylisted
define('HTTPBL_THRESHOLD_BLACK', 50);

// Default threshold, above which a user is blacklisted

/**
 * Check levels.
 */
define('HTTPBL_CHECK_NONE', 0);

// No Checking
define('HTTPBL_CHECK_COMMENTS', 1);

// Check only comments
define('HTTPBL_CHECK_ALL', 2);

// Check all requests

/**
 * Implementation of hook_menu().
 */
function httpbl_menu() {
  $items['httpbl/whitelist'] = array(
    'title' => 'Request whitelisting',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'httpbl_request_whitelist',
    ),
    'access callback' => 'httpbl_whitelist_access',
    'access arguments' => array(),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/httpbl'] = array(
    'title' => 'http:BL',
    'description' => 'Manage http:BL settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'httpbl_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  return $items;
}

/**
 * Implementation of hook_boot().
 */
function httpbl_boot() {

  // Only continue when checks are enabled
  if (variable_get('httpbl_check', HTTPBL_CHECK_NONE) != HTTPBL_CHECK_ALL) {
    return;
  }
  $result = httpbl_check();
  if ($result) {
    if ($result == HTTPBL_LIST_GREY) {
      if (isset($_GET['q']) && $_GET['q'] == 'httpbl/whitelist') {
        return;
      }
      $message = variable_get('httpbl_message_grey', 'Sorry, %ip has been greylisted by <a href="%ipurl">http:BL</a>.<br>You may try whitelisting on <a href="%whitelisturl">%whitelisturl</a>.%honeypot');
    }
    else {
      if ($result == HTTPBL_LIST_BLACK) {
        $message = variable_get('httpbl_message_black', 'Sorry, %ip has been blacklisted by <a href="%ipurl">http:BL</a>.%honeypot');
      }
    }
    if ($link = variable_get('httpbl_link', NULL)) {
      $word = variable_get('httpbl_word', 'randomness');
      $link = httpbl_honeylink($link, $word);
    }
    $message = strtr($message, array(
      '%ip' => ip_address(),
      '%ipurl' => _httpbl_ipdata(ip_address(), FALSE),
      '%honeypot' => $link,
      '%whitelisturl' => _httpbl_url('/httpbl/whitelist'),
    ));
    header('HTTP/1.1 403 Forbidden');
    echo $message;
    exit;
  }
}

/**
 * Implementation of hook_comment().
 */
function httpbl_comment($comment, $op) {
  if (variable_get('httpbl_check', HTTPBL_CHECK_NONE) != HTTPBL_CHECK_COMMENTS) {
    return;
  }
  switch ($op) {
    case 'insert':
    case 'update':
      $comment = (object) $comment;
      if (httpbl_check()) {

        // Unpublish and inform the user.
        $operation = comment_operations('unpublish');
        $query = $operation['unpublish'][1];
        db_query($query, $comment->cid);
        drupal_set_message(t('Your comment has been queued for moderation by site administrators and will be published after approval.'));

        // Add to our statistics
        if (variable_get('httpbl_stats', TRUE)) {
          variable_set('httpbl_stat_comment', variable_get('httpbl_stat_comment', 0) + 1);
        }
      }
      return;
  }
}

/**
 * Check if an IP should be banned
 *
 * @return constant: HTTP_LIST_*
 */
function httpbl_check() {
  static $result;

  // Result was already calculated -- return.
  if (is_int($result)) {
    return $result;
  }
  $ip = ip_address();

  // $ip = '127.1.40.1'; // simulate greylist response for testing
  // $ip = '127.1.80.1'; // simulate blacklist response for testing
  // Check if user is whitelisted in any way
  if (_httpbl_whitelisted($ip)) {
    $result = HTTPBL_LIST_SAFE;
  }
  else {
    if ($cache = variable_get('httpbl_cache', HTTPBL_CACHE_DBDRUPAL)) {
      $result = _httpbl_cache_get($ip);
    }
  }
  if (!is_numeric($result)) {

    // Do a DNS lookup, and continue if lookup was succesful
    if ($response = httpbl_dnslookup($ip)) {
      $stats = variable_get('httpbl_stats', TRUE);

      // Blacklist?
      if ($response['threat'] > variable_get('httpbl_black_threshold', HTTPBL_THRESHOLD_BLACK) && $response['type']) {
        if (variable_get('httpbl_log', FALSE)) {
          include_once "includes/bootstrap.inc";
          drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

          // This is needed for logs to work
          watchdog('httpbl', '%ip was blacklisted (%response)', array(
            '%ip' => $ip,
            '%response' => $response['raw'],
          ), WATCHDOG_WARNING, _httpbl_ipdata($ip));
        }
        if ($stats) {
          variable_set('httpbl_stat_black', variable_get('httpbl_stat_black', 0) + 1);
        }
        $result = HTTPBL_LIST_BLACK;
      }
      else {
        if ($response['threat'] > variable_get('httpbl_grey_threshold', HTTPBL_THRESHOLD_GREY) && $response['type']) {
          include_once "includes/bootstrap.inc";
          drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

          // This is needed for whitelisting and logs
          if (variable_get('httpbl_log', FALSE)) {
            watchdog('httpbl', '%ip was greylisted (%response)', array(
              '%ip' => $ip,
              '%response' => $response['raw'],
            ), WATCHDOG_WARNING, _httpbl_ipdata($ip));
          }
          if ($stats) {
            variable_set('httpbl_stat_grey', variable_get('httpbl_stat_grey', 0) + 1);
          }
          $result = HTTPBL_LIST_GREY;
        }
        else {
          $result = HTTPBL_LIST_SAFE;
        }
      }

      // Cache results - Use Blacklist offset settings (default 1 year)
      //             or Greylist offset settings (default 1 day)
      if ($cache) {
        if ($result == HTTPBL_LIST_BLACK) {
          _httpbl_cache_set($ip, $result, variable_get('httpbl_blacklist_offset', 31536000));
        }
        else {
          if ($result == HTTPBL_LIST_GREY) {
            _httpbl_cache_set($ip, $result, variable_get('httpbl_greylist_offset', 86400));
          }
        }
      }
    }
    else {
      if ($cache) {
        _httpbl_cache_set($ip, HTTPBL_LIST_SAFE, variable_get('httpbl_safe_offset', 10800));
      }
      $result = HTTPBL_LIST_SAFE;
    }
  }
  return $result;
}

/**
 * Implementation of hook_settings().
 */
function httpbl_admin_settings() {
  if (!$_POST && (!variable_get('httpbl_accesskey', NULL) || !variable_get('httpbl_check', HTTPBL_CHECK_NONE))) {
    drupal_set_message(t('Blacklist lookups are currently disabled; enter your access key below and enable checks to enable blacklist lookups.'), 'error');
  }
  $form['core'] = array(
    '#type' => 'fieldset',
    '#title' => t('http:BL'),
    '#description' => t('For more information about http:BL, see the <a href="http://www.projecthoneypot.org/httpbl.php">http:BL homepage</a>.'),
    '#collapsible' => TRUE,
  );
  $form['core']['httpbl_accesskey'] = array(
    '#type' => 'textfield',
    '#title' => t('http:BL Access Key'),
    '#default_value' => variable_get('httpbl_accesskey', NULL),
    '#description' => t('Your http:BL <a href="http://www.projecthoneypot.org/httpbl_configure.php">Access Key</a>.'),
    '#size' => 20,
    '#maxlength' => 12,
  );
  $form['core']['httpbl_check'] = array(
    '#type' => 'radios',
    '#title' => t('Blacklist checks'),
    '#default_value' => variable_get('httpbl_check', HTTPBL_CHECK_NONE),
    '#options' => array(
      t('Disabled'),
      t('Only on comment submissions'),
      t('On all page requests'),
    ),
    '#description' => t('At what times the blacklist should be checked.'),
  );
  $form['honeypot'] = array(
    '#type' => 'fieldset',
    '#title' => t('Honeypot'),
    '#description' => t('Your Honeypot (spam trap) settings. For more information, see the <a href="http://www.projecthoneypot.org/">Project Honey Pot homepage</a>.'),
    '#collapsible' => TRUE,
  );
  $form['honeypot']['httpbl_footer'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add link to footer'),
    '#default_value' => variable_get('httpbl_footer', FALSE),
    '#description' => t('Whether to add your honeypot link to the footer of every page.'),
  );
  $form['honeypot']['httpbl_link'] = array(
    '#type' => 'textfield',
    '#title' => t('Honeypot link'),
    '#default_value' => variable_get('httpbl_link', NULL),
    '#description' => t('Your Honeypot (spam trap) link. This can be one of your <a href="http://www.projecthoneypot.org/manage_honey_pots.php">own Honey Pots</a> or a <a href="http://www.projecthoneypot.org/manage_quicklink.php">QuickLink</a>.'),
  );
  $form['honeypot']['httpbl_word'] = array(
    '#type' => 'textfield',
    '#title' => t('Link word'),
    '#default_value' => variable_get('httpbl_word', 'randomness'),
    '#description' => t('A random word which will be used as a link.'),
  );
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['advanced']['httpbl_log'] = array(
    '#type' => 'checkbox',
    '#title' => t('Positive watchdog logging'),
    // Not really all that verbose
    '#default_value' => variable_get('httpbl_log', FALSE),
    '#description' => t('Logs all positive lookups in the watchdog.'),
  );
  $form['advanced']['httpbl_stats'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable statistics'),
    '#default_value' => variable_get('httpbl_stats', TRUE),
    '#description' => t('Whether to enable counting of positive lookups. Statistics are show on the Drupal <a href="@statusreport">Status report page</a>.', array(
      '@statusreport' => url('admin/reports/status'),
    )),
  );
  $form['advanced']['httpbl_cache'] = array(
    '#type' => 'radios',
    '#title' => t('Caching'),
    '#default_value' => variable_get('httpbl_cache', HTTPBL_CACHE_DBDRUPAL),
    '#options' => array(
      t('Off'),
      t('Database cache'),
      t('Database cache and Drupal access table'),
    ),
    '#description' => t('Whether to enable database-based caching. Note that when this is disabled, IPs that fail the session whitelist test cannot be banned. The Drupal access table allows visitors to be blocked in the early stages of the bootstrap.'),
  );
  $blacklist_threshold_options = drupal_map_assoc(array(
    50,
    55,
    60,
    65,
    70,
    75,
    80,
    85,
    90,
    95,
    100,
    200,
    255,
  ));
  $form['advanced']['httpbl_black_threshold'] = array(
    '#type' => 'select',
    '#title' => t('Blacklisting Threshhold'),
    '#default_value' => variable_get('httpbl_black_threshold', HTTPBL_THRESHOLD_BLACK),
    '#options' => $blacklist_threshold_options,
    '#description' => t('Threshold above which a user is blacklisted.  Use this to fine-tune the Honeypot threat-levels for Black-listing. Note that threat levels at 100 or more effectively turns off Blacklisting (they are extremely rare).  If you want to stop blacklisting altogether, set this to 255.'),
  );
  $form['advanced']['httpbl_message_black'] = array(
    '#type' => 'textarea',
    '#title' => t('Blacklist message'),
    '#default_value' => variable_get('httpbl_message_black', "Sorry, %ip has been blacklisted by <a href=\"%ipurl\">http:BL</a>.\n%honeypot"),
    '#description' => t("The message visitors will see when their IP is blacklisted. <em>%ip</em> will be replaced with the visitor's IP, <em>%ipurl</em> with a link to the Project Honeypot information page for that IP, <em>%honeypot</em> with your Honeypot link."),
  );
  $greylist_threshold_options = drupal_map_assoc(array(
    1,
    2,
    3,
    4,
    5,
    10,
    15,
    20,
    25,
    30,
    35,
    40,
    45,
  ));
  $form['advanced']['httpbl_grey_threshold'] = array(
    '#type' => 'select',
    '#title' => t('Greylisting Threshhold'),
    '#default_value' => variable_get('httpbl_grey_threshold', HTTPBL_THRESHOLD_GREY),
    '#options' => $greylist_threshold_options,
    '#description' => t('Threshold at which a user is allowed, above which a user is greylisted.  Use this to fine-tune the Honeypot threat-levels that you are willing to permit for access to your site.'),
  );
  $form['advanced']['httpbl_message_grey'] = array(
    '#type' => 'textarea',
    '#title' => t('Greylist message'),
    '#default_value' => variable_get('httpbl_message_grey', "Sorry, %ip has been greylisted by <a href=\"%ipurl\">http:BL</a>.\nYou may try whitelisting on <a href=\"%whitelisturl\">%whitelisturl</a>.\n%honeypot"),
    '#description' => t("The message visitors will see when their IP is greylisted. <em>%ip</em> will be replaced with the visitor's IP, <em>%ipurl</em> with a link to the Project Honeypot information page for that IP, <em>%honeypot</em> with your Honeypot link, <em>%whitelisturl</em> with the internal whitelist request URL."),
  );
  $cache_period = drupal_map_assoc(array(
    10800,
    21600,
    32400,
    43200,
    86400,
    172800,
    259200,
    604800,
    1209600,
    2419200,
  ), 'format_interval');
  $form['advanced']['httpbl_safe_offset'] = array(
    '#type' => 'select',
    '#title' => t('Safe Visitor Cache Expires in...'),
    '#default_value' => variable_get('httpbl_safe_offset', 10800),
    '#options' => $cache_period,
    '#description' => t('How long to keep safe or white-listed IPs in cache.'),
  );
  $cache_period2 = drupal_map_assoc(array(
    43200,
    86400,
    172800,
    259200,
    604800,
    1209600,
    1814400,
    2419200,
  ), 'format_interval');
  $form['advanced']['httpbl_greylist_offset'] = array(
    '#type' => 'select',
    '#title' => t('Grey Listed Visitor Cache Expires in...'),
    '#default_value' => variable_get('httpbl_greylist_offset', 86400),
    '#options' => $cache_period2,
    '#description' => t('How long to keep grey-listed IPs in cache.'),
  );
  $cache_period3 = drupal_map_assoc(array(
    604800,
    1209600,
    1814400,
    2419200,
    7257600,
    15724800,
    31536000,
    63072000,
    94608000,
  ), 'format_interval');
  $form['advanced']['httpbl_blacklist_offset'] = array(
    '#type' => 'select',
    '#title' => t('Black Listed Visitor Cache Expires in...'),
    '#default_value' => variable_get('httpbl_blacklist_offset', 31536000),
    '#options' => $cache_period3,
    '#description' => t('How long to keep black-listed IPs in cache (and also banned in Access table, when applicable).'),
  );
  return system_settings_form($form);
}

/**
 * Form API callback to validate the httpbl settings form.
 */
function httpbl_admin_settings_validate($form, &$form_state) {
  $key = $form_state['values']['httpbl_accesskey'];
  if ($form_state['values']['httpbl_check'] && !$key) {
    form_set_error('httpbl_accesskey', t('You must enter an access key to enable blacklist checks.'));
  }
  if ($form_state['values']['httpbl_footer'] && !$form_state['values']['httpbl_link']) {
    form_set_error('httpbl_link', t('You must enter a link to be able to add it to the footer.'));
  }
  if ($key) {

    // Key should be 12 lowercase alpha characters.
    // There's no unicode allowed, so we're not using drupal_strlen().
    if (ereg('[^a-z]', $key) || strlen($key) != 12) {
      form_set_error('httpbl_accesskey', t('Your access key is formatted incorrectly.'));
    }
    else {
      if (!count(form_get_errors())) {

        // Do a test lookup (with known result).
        $lookup = httpbl_dnslookup('127.1.80.1', $key);
        if (!$lookup || $lookup['threat'] != 80) {
          form_set_error('httpbl_accesskey', t('Testcase failed. This either means that your access key is incorrect or that there is a problem in your DNS system.'));
        }
        else {
          drupal_set_message('http:BL test completed successfully.');
        }
      }
    }
  }
}

/**
 * Implementation of hook_footer().
 *
 * Adds a Project Honeypot link to the footer.
 */
function httpbl_footer($main = 0) {
  if (variable_get('httpbl_footer', FALSE)) {
    $link = variable_get('httpbl_link', NULL);
    $word = variable_get('httpbl_word', 'randomness');
    return httpbl_honeylink($link, $word);
  }
}

/**
 * Implementation of hook_requirements().
 *
 * Shows some basic usage statistics for the module.
 */
function httpbl_requirements($phase) {
  $requirements = array();
  if ($phase == 'runtime') {
    if (!variable_get('httpbl_accesskey', NULL) || !variable_get('httpbl_check', HTTPBL_CHECK_NONE)) {
      $requirements['httpbl'] = array(
        'description' => t('IP blacklist lookups are currently disabled; enter your access key <a href="@settings">on the settings page</a> and enable checks to enable blacklist lookups.', array(
          '@settings' => url('admin/settings/httpbl'),
        )),
        'severity' => REQUIREMENT_ERROR,
        'value' => t('Disabled'),
      );
      if (variable_get('httpbl_footer', FALSE)) {
        $requirements['httpbl']['severity'] = REQUIREMENT_WARNING;
      }
    }
    else {
      $stat_black = variable_get('httpbl_stat_black', 0);
      $stat_comment = variable_get('httpbl_stat_comment', 0);
      $stat_grey = variable_get('httpbl_stat_grey', 0);
      if (!variable_get('httpbl_stats', TRUE)) {
        $requirements['httpbl'] = array(
          'description' => t('http:BL is enabled.'),
          'severity' => REQUIREMENT_OK,
          'value' => t('Enabled'),
        );
      }
      else {
        if (variable_get('httpbl_check', HTTPBL_CHECK_NONE) == HTTPBL_CHECK_COMMENTS) {
          $requirements['httpbl'] = array(
            'description' => t('http:BL is enabled and has blocked @c comments.', array(
              '@c' => $stat_comment,
            )),
            'severity' => REQUIREMENT_OK,
            'value' => t('Enabled'),
          );
        }
        else {
          if (variable_get('httpbl_check', HTTPBL_CHECK_NONE) == HTTPBL_CHECK_ALL) {
            $requirements['httpbl'] = array(
              'description' => t('http:BL is enabled and has blocked @t visits (@b blacklisted and @g greylisted).', array(
                '@t' => $stat_grey + $stat_black,
                '@b' => $stat_black,
                '@g' => $stat_grey,
              )),
              'severity' => REQUIREMENT_OK,
              'value' => t('Enabled'),
            );
          }
        }
      }
      if (!variable_get('httpbl_footer', FALSE)) {
        $requirements['httpbl']['severity'] = REQUIREMENT_WARNING;
        $requirements['httpbl']['description'] .= ' ' . t('However, the honeypot link is not enabled.');
      }
    }
    $requirements['httpbl']['title'] = t('http:BL');
  }
  return $requirements;
}

/**
 * Implementation of hook_cron().
 *
 * Cleans old results from the cache table and also Drupal access table if appropriate.
 */
function httpbl_cron() {

  // Only continue when caching is enabled
  if (variable_get('httpbl_cache', HTTPBL_CACHE_OFF)) {
    if (variable_get('httpbl_cache', HTTPBL_CACHE_OFF) == HTTPBL_CACHE_DBDRUPAL) {

      // Also check status so that we do not accidentally delete the site's custom access rules.
      db_query("DELETE FROM {access} WHERE mask IN (SELECT hostname FROM {httpbl} WHERE status > 0 AND expire <= %d)", time());
    }
    db_query("DELETE FROM {httpbl} WHERE expire <= %d", time());
  }
}

/**
 * Return HTML code with hidden Honeypot link
 * in one of the many styles.
 *
 * @param string $link
 * @param string $word
 * @return string
 */
function httpbl_honeylink($link, $word) {
  if (!$link) {
    return;
  }
  switch (mt_rand(0, 6)) {
    case 0:
      return '<div><a rel="nofollow" href="' . $link . '"><!-- ' . $word . ' --></a></div>';
    case 1:
      return '<div><a rel="nofollow" href="' . $link . '" style="display: none;">' . $word . '</a></div>';
    case 2:
      return '<div style="display: none;"><a rel="nofollow" href="' . $link . '">' . $word . '</a></div>';
    case 3:
      return '<div><a rel="nofollow" href="' . $link . '"></a></div>';
    case 4:
      return '<!-- <a href="' . $link . '">' . $word . '</a> -->';
    case 5:
      return '<div style="position: absolute; top: -250px; left: -250px;"><a rel="nofollow" href="' . $link . '">' . $word . '</a></div>';
    case 6:
      return '<div><a rel="nofollow" href="' . $link . '"><span style="display: none;">' . $word . '</span></a></div>';
  }
}

/**
 * Check if an IP is known to belong to a search engine
 *
 * @param string $ip
 * @return bool
 */
function httpbl_se($ip = NULL) {
  if ($response = httpbl_dnslookup($ip)) {
    $iplink = _httpbl_ipdata($ip);
    return $response['search_engine'] === TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Reverse IP octets
 *
 * @param string $ip
 * @return string
 */
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];
}

/**
 * Determine whether a user has access to the session whitelist functionality.
 */
function httpbl_whitelist_access() {
  return httpbl_check() == HTTPBL_LIST_GREY;
}

/**
 * Form to request a session whitelist.
 */
function httpbl_request_whitelist() {
  $form['#redirect'] = 'node';
  $form['reason'] = array(
    '#type' => 'textarea',
    '#title' => t('Reason you were blocked (any answer accepted)'),
    '#size' => 60,
    '#required' => TRUE,
  );
  $form['block'] = array(
    '#type' => 'textfield',
    '#title' => t('Leave this blank'),
    '#size' => 15,
  );
  $form['leave'] = array(
    '#type' => 'textfield',
    '#size' => 30,
    '#attributes' => array(
      'style' => 'display: none',
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Whitelist request'),
  );
  return $form;
}

/**
 * Validate session whitelist request.
 * Ban the user if one of the 'forbidden' fields were filled.
 */
function httpbl_request_whitelist_validate($form, &$form_state) {
  $ip = ip_address();
  $iplink = _httpbl_ipdata($ip);
  if ($form_state['values']['block'] || $form_state['values']['leave']) {
    if (variable_get('httpbl_cache', HTTPBL_CACHE_OFF)) {
      _httpbl_cache_update($ip, HTTPBL_LIST_BLACK, variable_get('httpbl_blacklist_offset', 31536000));
      $return_date = format_interval(variable_get('httpbl_blacklist_offset', 31536000), $granularity = 2, $langcode = NULL);
      watchdog('httpbl', '%ip failed session whitelist request, blacklisted for %return_date.', array(
        '%ip' => $ip,
        '%return_date' => $return_date,
      ), WATCHDOG_WARNING, $iplink);
      print t('Whitelist request failed; your IP has been blacklisted for ' . $return_date . '.');
      exit;
    }
    else {
      watchdog('httpbl', '%ip failed session whitelist request.', array(
        '%ip' => $ip,
      ), WATCHDOG_WARNING, $iplink);
      print t('Whitelist request failed.');
      exit;
    }
  }
  if (variable_get('httpbl_log', FALSE)) {
    watchdog('httpbl', '%ip tried a whitelist request', array(
      '%ip' => $ip,
    ), WATCHDOG_NOTICE, $iplink);
  }
}

/**
 * Grant the session whitelist.
 */
function httpbl_request_whitelist_submit($form, &$form_state) {
  $ip = ip_address();
  $iplink = _httpbl_ipdata($ip);
  watchdog('httpbl', 'Session from %ip whitelisted. Reason for block: @reason', array(
    '%ip' => $ip,
    '@reason' => check_plain($form_state['values']['reason']),
  ), WATCHDOG_WARNING, $iplink);
  drupal_set_message(t('The current session has been whitelisted.'));
  $_SESSION['httpbl_status'] = 'white';
  drupal_goto('/');
}

/**
 * Do http:BL DNS lookup
 *
 * @param string $ip
 * @param string $key
 * @return array
 */
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 = _httpbl_reverse_ip($ip))) {
    return FALSE;
  }
  if (!$key && !($key = variable_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
    include_once "includes/bootstrap.inc";
    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

    // Critically needed for logging, or else we won't know this failed!
    watchdog('httpbl', 'Lookup failed for %ip, response was %response', array(
      '%ip' => $ip,
      '%response' => $values['raw'],
    ), WATCHDOG_ERROR);
    return FALSE;
  }
  $values['last_activity'] = $response[1];
  $values['threat'] = $response[2];
  $values['type'] = $response[3];
  if ($response[3] == 0) {

    //if it's 0 then there's only one thing it can be
    $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;
}

/**
 * Check if an IP is whitelisted through the access table or a session whitelist.
 */
function _httpbl_whitelisted($ip) {
  return isset($_SESSION['httpbl_status']) && $_SESSION['httpbl_status'] == 'white' || db_result(db_query_range("SELECT status FROM {access} WHERE type = 'host' AND LOWER('%s') LIKE LOWER(mask) ORDER BY status DESC", $ip, 0, 1)) == '1';
}

/**
 * Write status value into cache table
 */
function _httpbl_cache_set($ip, $status, $offset = 0) {

  // http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-postgresql/6527838#6527838
  db_query("UPDATE {httpbl} SET hostname= '%s', status= %d, expire= %d WHERE hostname= '%s'", $ip, $status, time() + $offset);
  db_query("INSERT INTO {httpbl} (hostname, status, expire) SELECT '%s', %d, %d FROM (SELECT 1) AS foo WHERE NOT EXISTS (SELECT 1 FROM {httpbl} WHERE hostname = '%s')", $ip, $status, time() + $offset, $ip);
  if ($status == HTTPBL_LIST_BLACK && variable_get('httpbl_cache', HTTPBL_CACHE_DBDRUPAL)) {
    db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $ip, 'host', 0);
  }
}

/**
 * Update cache table
 */
function _httpbl_cache_update($ip, $status, $offset = 0) {
  db_query("UPDATE {httpbl} SET status = %d, expire = %d WHERE hostname = '%s'", $status, time() + $offset, $ip);

  // Note that IP addresses will not be deleted from the {access} table, but this is currently not needed.
  // TODO: check for duplicates
  if ($status == HTTPBL_LIST_BLACK && variable_get('httpbl_cache', HTTPBL_CACHE_DBDRUPAL)) {
    db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $ip, 'host', 0);
  }
}

/**
 * Get status value from cache table
 */
function _httpbl_cache_get($ip) {
  return db_result(db_query("SELECT status FROM {httpbl} WHERE hostname = '%s'", $ip));
}

/**
 * Generate a link with Project Honeypot information for a given IP address.
 */
function _httpbl_ipdata($ip, $anchor = TRUE) {
  if ($anchor) {
    return '<a href="http://www.projecthoneypot.org/search_ip.php?ip=' . $ip . '">IP data</a>';
  }
  else {
    return 'http://www.projecthoneypot.org/search_ip.php?ip=' . $ip;
  }
}

/**
 * Lightweight url function, since url() isn't available yet.
 */
function _httpbl_url($path = NULL) {
  global $base_url;
  $script = strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE ? 'index.php' : '';
  if (variable_get('clean_url', '0')) {
    return $base_url . $path;
  }
  else {
    return $base_url . $script . '?q=' . $path;
  }
}

/**
 * Implementation of hook_views_api().
 */
function httpbl_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'httpbl') . '/views',
  );
}

Functions

Namesort descending Description
httpbl_admin_settings Implementation of hook_settings().
httpbl_admin_settings_validate Form API callback to validate the httpbl settings form.
httpbl_boot Implementation of hook_boot().
httpbl_check Check if an IP should be banned
httpbl_comment Implementation of hook_comment().
httpbl_cron Implementation of hook_cron().
httpbl_dnslookup Do http:BL DNS lookup
httpbl_footer Implementation of hook_footer().
httpbl_honeylink Return HTML code with hidden Honeypot link in one of the many styles.
httpbl_menu Implementation of hook_menu().
httpbl_request_whitelist Form to request a session whitelist.
httpbl_request_whitelist_submit Grant the session whitelist.
httpbl_request_whitelist_validate Validate session whitelist request. Ban the user if one of the 'forbidden' fields were filled.
httpbl_requirements Implementation of hook_requirements().
httpbl_se Check if an IP is known to belong to a search engine
httpbl_views_api Implementation of hook_views_api().
httpbl_whitelist_access Determine whether a user has access to the session whitelist functionality.
_httpbl_cache_get Get status value from cache table
_httpbl_cache_set Write status value into cache table
_httpbl_cache_update Update cache table
_httpbl_ipdata Generate a link with Project Honeypot information for a given IP address.
_httpbl_reverse_ip Reverse IP octets
_httpbl_url Lightweight url function, since url() isn't available yet.
_httpbl_whitelisted Check if an IP is whitelisted through the access table or a session whitelist.

Constants