Plugin Directory

Changeset 3357194


Ignore:
Timestamp:
09/06/2025 06:05:09 PM (6 months ago)
Author:
encodedothost
Message:

Update to 1.4.3

Location:
edh-bad-bots/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • edh-bad-bots/trunk/admin/views/admin-display.php

    r3355590 r3357194  
    66 * It displays blocked bots, allows whitelisting IPs, and manages unblocking.
    77 */
     8declare(strict_types=1);
    89
    910if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     
    112113            <div class="card edhbb-card">
    113114                <h2 class="title"><?php esc_html_e( 'Currently Blocked Bots', 'edh-bad-bots' ); ?></h2>
     115               
     116                <!-- Manual hostname update buttons -->
     117                <div style="margin-bottom: 20px;">
     118                    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display: inline;">
     119                        <input type="hidden" name="action" value="edhbb_update_hostnames">
     120                        <?php wp_nonce_field( 'edhbb_update_hostnames_nonce' ); ?>
     121                        <?php submit_button( __( 'Update Missing Hostnames', 'edh-bad-bots' ), 'secondary', 'submit_update_hostnames', false ); ?>
     122                    </form>
     123                   
     124                    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display: inline; margin-left: 10px;">
     125                        <input type="hidden" name="action" value="edhbb_force_refresh_all_hostnames">
     126                        <?php wp_nonce_field( 'edhbb_force_refresh_all_hostnames_nonce' ); ?>
     127                        <?php submit_button( __( 'Force Refresh All Hostnames', 'edh-bad-bots' ), 'secondary', 'submit_force_refresh_all', false ); ?>
     128                    </form>
     129                   
     130                    <p class="description" style="margin-top: 5px;">
     131                        <?php esc_html_e( 'Update Missing: Resolves hostnames for IPs that show empty or "[No PTR Record]". Force Refresh: Clears cache and re-resolves ALL hostnames.', 'edh-bad-bots' ); ?>
     132                    </p>
     133                </div>
     134
     135                <?php
     136                // Display debug information if available and WP_DEBUG is enabled
     137                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     138                    $debug_info = get_transient( 'edhbb_debug_info' );
     139                    if ( $debug_info ) :
     140                        delete_transient( 'edhbb_debug_info' ); // Delete after displaying
     141                ?>
     142                    <div class="notice notice-info inline">
     143                        <h3><?php esc_html_e( 'Hostname Resolution Debug Info', 'edh-bad-bots' ); ?></h3>
     144                        <p><?php esc_html_e( 'The following information was gathered to help debug hostname resolution issues.', 'edh-bad-bots' ); ?></p>
     145                        <table class="widefat">
     146                            <thead>
     147                                <tr>
     148                                    <th><?php esc_html_e( 'Function', 'edh-bad-bots' ); ?></th>
     149                                    <th><?php esc_html_e( 'Exists?', 'edh-bad-bots' ); ?></th>
     150                                    <th><?php esc_html_e( 'Callable?', 'edh-bad-bots' ); ?></th>
     151                                    <th><?php esc_html_e( 'Result for 8.8.8.8', 'edh-bad-bots' ); ?></th>
     152                                </tr>
     153                            </thead>
     154                            <tbody>
     155                                <tr>
     156                                    <td><code>gethostbyaddr()</code></td>
     157                                    <td><?php echo esc_html( $debug_info['gethostbyaddr']['exists'] ); ?></td>
     158                                    <td><?php echo esc_html( $debug_info['gethostbyaddr']['callable'] ); ?></td>
     159                                    <td><pre><?php echo esc_html( print_r( $debug_info['gethostbyaddr']['result'], true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Debug output only shown when WP_DEBUG is enabled ?></pre></td>
     160                                </tr>
     161                                <tr>
     162                                    <td><code>dns_get_record()</code></td>
     163                                    <td><?php echo esc_html( $debug_info['dns_get_record']['exists'] ); ?></td>
     164                                    <td><?php echo esc_html( $debug_info['dns_get_record']['callable'] ); ?></td>
     165                                    <td><pre><?php echo esc_html( print_r( $debug_info['dns_get_record']['result'], true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Debug output only shown when WP_DEBUG is enabled ?></pre></td>
     166                                </tr>
     167                            </tbody>
     168                        </table>
     169                    </div>
     170                <?php endif;
     171                } ?>
     172
    114173                <?php if ( ! empty( $blocked_bots ) ) : ?>
    115174                    <table class="wp-list-table widefat fixed striped">
     
    117176                            <tr>
    118177                                <th scope="col"><?php esc_html_e( 'IP Address', 'edh-bad-bots' ); ?></th>
     178                                <th scope="col"><?php esc_html_e( 'Hostname', 'edh-bad-bots' ); ?></th>
    119179                                <th scope="col"><?php esc_html_e( 'Blocked At', 'edh-bad-bots' ); ?></th>
    120180                                <th scope="col"><?php esc_html_e( 'Expires At', 'edh-bad-bots' ); ?></th>
     
    126186                                <tr>
    127187                                    <td><?php echo esc_html( $bot['ip_address'] ); ?></td>
     188                                    <td>
     189                                        <?php
     190                                        $hostname = esc_html( $bot['hostname'] );
     191                                        if ( $hostname === '[No PTR Record]' || empty( $hostname ) ) {
     192                                            echo '<em style="color: #666;">[No PTR Record]</em>';
     193                                        } else {
     194                                            echo esc_html( $hostname );
     195                                        }
     196                                        ?>
     197                                    </td>
    128198                                    <td><?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $bot['blocked_at'] ) ) ); ?></td>
    129199                                    <td><?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $bot['expires_at'] ) ) ); ?></td>
     
    190260            $path = !empty($site_url_parts['path']) ? $site_url_parts['path'] : '';
    191261            $trap_url = home_url( $path . '/' . $hash . '/' );
     262           
     263            // Get current block duration setting
     264            $current_block_duration = get_option( 'edhbb_block_duration_days', 30 );
    192265            ?>
    193266            <!-- Section for Help Text -->
     
    195268                <h2 class="title"><?php esc_html_e( 'How EDH Bad Bots Works', 'edh-bad-bots' ); ?></h2>
    196269                <p>
    197                     <?php esc_html_e( 'This plugin helps protect your site from bots that do not respect the `robots.txt` file.', 'edh-bad-bots' ); ?>
     270                    <?php esc_html_e( 'This plugin helps protect your site from bots that do not respect the `robots.txt` file using an advanced honeypot system with intelligent hostname identification.', 'edh-bad-bots' ); ?>
    198271                </p>
    199272                <h3><?php esc_html_e( 'The Blocking Process:', 'edh-bad-bots' ); ?></h3>
     
    212285                    </li>
    213286                    <li>
    214                         <?php esc_html_e( 'When a bot hits the trap URL, its IP address is recorded and added to a **blocklist** for 30 days.', 'edh-bad-bots' ); ?>
     287                        <?php
     288                        echo sprintf(
     289                            /* translators: %d: number of days bots are blocked */
     290                            esc_html__( 'When a bot hits the trap URL, its IP address is recorded and added to a blocklist for %d days (configurable in Options).', 'edh-bad-bots' ),
     291                            $current_block_duration
     292                        );
     293                        ?>
     294                    </li>
     295                    <li>
     296                        <?php esc_html_e( 'The plugin performs reverse DNS lookups (PTR records) to identify the hostname/organization behind the blocked IP for better analysis.', 'edh-bad-bots' ); ?>
    215297                    </li>
    216298                    <li>
     
    218300                    </li>
    219301                </ol>
     302
     303                <h3><?php esc_html_e( 'Hostname Resolution System:', 'edh-bad-bots' ); ?></h3>
     304                <p>
     305                    <?php esc_html_e( 'The plugin includes an advanced DNS lookup system to identify blocked bots:', 'edh-bad-bots' ); ?>
     306                </p>
     307                <ul>
     308                    <li>
     309                        <strong><?php esc_html_e( 'DNS over HTTPS (DoH):', 'edh-bad-bots' ); ?></strong>
     310                        <?php esc_html_e( 'Uses secure, encrypted DNS queries via Cloudflare and Google DNS for enhanced privacy and reliability.', 'edh-bad-bots' ); ?>
     311                    </li>
     312                    <li>
     313                        <strong><?php esc_html_e( 'PTR Record Lookups:', 'edh-bad-bots' ); ?></strong>
     314                        <?php esc_html_e( 'Converts IP addresses to hostnames (e.g., "crawl-66-249-66-1.googlebot.com") for better identification of what\'s being blocked.', 'edh-bad-bots' ); ?>
     315                    </li>
     316                    <li>
     317                        <strong><?php esc_html_e( 'Background Processing:', 'edh-bad-bots' ); ?></strong>
     318                        <?php esc_html_e( 'Hostname resolution runs automatically in the background via WordPress cron to avoid delays.', 'edh-bad-bots' ); ?>
     319                    </li>
     320                    <li>
     321                        <strong><?php esc_html_e( 'Manual Updates:', 'edh-bad-bots' ); ?></strong>
     322                        <?php esc_html_e( 'Use the "Update Missing Hostnames" and "Force Refresh All Hostnames" buttons in the Blocked Bots tab for manual control.', 'edh-bad-bots' ); ?>
     323                    </li>
     324                </ul>
    220325
    221326                <h3><?php esc_html_e( 'Managing IPs:', 'edh-bad-bots' ); ?></h3>
     
    223328                    <li>
    224329                        <strong><?php esc_html_e( 'Whitelisted IPs', 'edh-bad-bots' ); ?></strong>
    225                         <?php esc_html_e( 'IP addresses added to the whitelist will **never** be blocked, even if they hit the bot trap. Use this for your own IP address, trusted services, or known legitimate bots.', 'edh-bad-bots' ); ?>
     330                        <?php esc_html_e( 'IP addresses added to the whitelist will never be blocked, even if they hit the bot trap. Use this for your own IP address, trusted services, or known legitimate bots.', 'edh-bad-bots' ); ?>
    226331                    </li>
    227332                    <li>
    228333                        <strong><?php esc_html_e( 'Blocked Bots', 'edh-bad-bots' ); ?></strong>
    229                         <?php esc_html_e( 'This tab shows all IP addresses currently on the blocklist. IPs are automatically removed after 30 days, but you can manually unblock them here at any time.', 'edh-bad-bots' ); ?>
    230                     </li>
    231                     <li>
    232                         <strong><?php esc_html_e( '.htaccess Blocking Option:', 'edh-bad-bots' ); ?></strong>
    233                         <?php esc_html_e( 'On the "Options" tab, you can choose to enable or disable server-level IP blocking via the .htaccess file. If disabled, blocking will rely solely on PHP, which might be less effective with caching plugins.', 'edh-bad-bots' ); ?>
     334                        <?php
     335                        echo sprintf(
     336                            /* translators: %d: number of days bots are blocked */
     337                            esc_html__( 'This tab shows all IP addresses currently on the blocklist with their resolved hostnames. IPs are automatically removed after %d days, but you can manually unblock them here at any time.', 'edh-bad-bots' ),
     338                            $current_block_duration
     339                        );
     340                        ?>
     341                    </li>
     342                    <li>
     343                        <strong><?php esc_html_e( 'Options Tab Features:', 'edh-bad-bots' ); ?></strong>
     344                        <ul>
     345                            <li><?php esc_html_e( '.htaccess Blocking: Enable or disable server-level IP blocking via the .htaccess file. If disabled, blocking will rely solely on PHP, which might be less effective with caching plugins.', 'edh-bad-bots' ); ?></li>
     346                            <li><?php esc_html_e( 'Block Duration: Configure how many days to block detected bot IPs (default: 30 days).', 'edh-bad-bots' ); ?></li>
     347                        </ul>
    234348                    </li>
    235349                </ul>
    236350
     351                <h3><?php esc_html_e( 'Admin Tools:', 'edh-bad-bots' ); ?></h3>
     352                <ul>
     353                    <li>
     354                        <strong><?php esc_html_e( 'Update Missing Hostnames:', 'edh-bad-bots' ); ?></strong>
     355                        <?php esc_html_e( 'Resolves hostnames for blocked IPs that show empty or "[No PTR Record]" to help identify what type of bots are being blocked.', 'edh-bad-bots' ); ?>
     356                    </li>
     357                    <li>
     358                        <strong><?php esc_html_e( 'Force Refresh All Hostnames:', 'edh-bad-bots' ); ?></strong>
     359                        <?php esc_html_e( 'Clears the DNS cache and re-resolves hostnames for all blocked IPs. Useful for troubleshooting or getting updated information.', 'edh-bad-bots' ); ?>
     360                    </li>
     361                    <li>
     362                        <strong><?php esc_html_e( 'Debug Information:', 'edh-bad-bots' ); ?></strong>
     363                        <?php esc_html_e( 'When WP_DEBUG is enabled, diagnostic information about hostname resolution functions is displayed to help troubleshoot issues.', 'edh-bad-bots' ); ?>
     364                    </li>
     365                </ul>
     366
    237367                <h3><?php esc_html_e( 'Caching Plugin Exclusion:', 'edh-bad-bots' ); ?></h3>
    238368                <p>
    239                     <?php esc_html_e( 'To ensure that the bot trap works correctly, you **must** exclude the following unique URL from your caching plugin. This prevents the trap page from being cached and served to human visitors.', 'edh-bad-bots' ); ?>
     369                    <?php esc_html_e( 'To ensure that the bot trap works correctly, you must exclude the following unique URL from your caching plugin. This prevents the trap page from being cached and served to human visitors.', 'edh-bad-bots' ); ?>
    240370                </p>
    241371                <p>
     
    249379                </p>
    250380
     381                <h3><?php esc_html_e( 'Technical Notes:', 'edh-bad-bots' ); ?></h3>
     382                <ul>
     383                    <li>
     384                        <strong><?php esc_html_e( 'IPv4 and IPv6 Support:', 'edh-bad-bots' ); ?></strong>
     385                        <?php esc_html_e( 'The hostname resolution system supports both IPv4 and IPv6 addresses for comprehensive coverage.', 'edh-bad-bots' ); ?>
     386                    </li>
     387                    <li>
     388                        <strong><?php esc_html_e( 'DNS Caching:', 'edh-bad-bots' ); ?></strong>
     389                        <?php esc_html_e( 'Hostname lookups are cached for 1 hour to improve performance and reduce DNS server load.', 'edh-bad-bots' ); ?>
     390                    </li>
     391                    <li>
     392                        <strong><?php esc_html_e( 'Fallback Methods:', 'edh-bad-bots' ); ?></strong>
     393                        <?php esc_html_e( 'If DNS over HTTPS fails, the system automatically falls back to traditional DNS methods for maximum compatibility.', 'edh-bad-bots' ); ?>
     394                    </li>
     395                </ul>
    251396            </div>
    252397        <?php endif; ?>
  • edh-bad-bots/trunk/assets/js/admin-script.js

    r3355590 r3357194  
     1// TODO: Add JavaScript for the admin page to add client side validation for the whitelist IP address field
     2// TODO: Add JavaScript for the admin page to add client side validation for the block duration days field
     3// TODO: Add JavaScript for the admin page to add client side validation for the .htaccess blocking field
     4// TODO: Add JavaScript for the admin page to add client side validation for the enable server-level IP blocking field
     5// TODO: Add JavaScript for the admin page to add client side validation for the enable server-level IP blocking field
  • edh-bad-bots/trunk/edh-bad-bots.php

    r3355590 r3357194  
    44 * Plugin URI: https://github.com/EncodeDotHost/edh-bad-bots
    55 * Description: This plugin is used to block bots that don't honor the robots.txt file from the site.
    6  * Version: 1.4.2
     6 * Version: 1.4.3
    77 * Requires at least: 6.2
    88 * Requires PHP: 7.4
     
    1717 * @author EncodeDotHost
    1818 * @contributor nbwpuk
    19  * @version 1.4.2
     19 * @version 1.4.3
    2020 * @link https://github.com/EncodeDotHost/edh-bad-bots
    2121 * @license GPL v3 or later
    2222 */
    23 
    24  if(!defined('ABSPATH')) exit;
     23declare(strict_types=1);
     24
     25if(!defined('ABSPATH')) exit;
    2526
    2627/**
     
    3031define( 'EDHBB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    3132define( 'EDHBB_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    32 define( 'EDHBB_VERSION', '1.4.2' );
     33define( 'EDHBB_VERSION', '1.4.3' );
    3334
    3435/**
     
    3839 */
    3940require_once EDHBB_PLUGIN_DIR . 'includes/class-edhbb-database.php';
     41require_once EDHBB_PLUGIN_DIR . 'includes/class-edhbb-dnslookup.php';
    4042require_once EDHBB_PLUGIN_DIR . 'includes/class-edhbb-blocker.php';
    4143require_once EDHBB_PLUGIN_DIR . 'includes/class-edhbb-admin.php';
     
    4749register_activation_hook( __FILE__, 'edhbb_activate_plugin' );
    4850register_deactivation_hook( __FILE__, 'edhbb_deactivate_plugin' );
     51
     52/**
     53 * Schedule hostname update cron job on activation.
     54 */
     55add_action( 'edhbb_update_hostnames_cron', 'edhbb_update_missing_hostnames' );
     56
     57/**
     58 * Schedule the cron event if it's not already scheduled.
     59 */
     60if ( ! wp_next_scheduled( 'edhbb_update_hostnames_cron' ) ) {
     61    wp_schedule_event( time(), 'hourly', 'edhbb_update_hostnames_cron' );
     62}
    4963
    5064/**
     
    6579 */
    6680function edhbb_deactivate_plugin() {
     81    // Clear the scheduled cron job
     82    wp_clear_scheduled_hook( 'edhbb_update_hostnames_cron' );
     83   
    6784    // Optionally, you can drop tables here. Be cautious with this as users might want to reactivate.
    6885    // For now, we'll leave it empty to preserve data on deactivation.
     
    84101add_action( 'plugins_loaded', 'edhbb_init_plugin' );
    85102
     103/**
     104 * Background cron function to update missing hostnames.
     105 * This runs periodically to resolve hostnames for blocked IPs that don't have them.
     106 */
     107function edhbb_update_missing_hostnames() {
     108    $edh_database = new EDHBB_Database();
     109    $edh_blocker = new EDHBB_Blocker( $edh_database );
     110   
     111    // Get IPs without hostnames (limit to 5 per run to avoid timeouts)
     112    $ips_without_hostnames = $edh_database->get_blocked_ips_without_hostnames( 5 );
     113   
     114    if ( empty( $ips_without_hostnames ) ) {
     115        return; // Nothing to do
     116    }
     117   
     118    // Use reflection to access the private method (since we need the improved hostname resolution)
     119    $reflection = new ReflectionClass( $edh_blocker );
     120    $hostname_method = $reflection->getMethod( 'get_hostname_for_ip' );
     121    $hostname_method->setAccessible( true );
     122   
     123    foreach ( $ips_without_hostnames as $ip_address ) {
     124        // Get hostname using the improved method
     125        $hostname = $hostname_method->invoke( $edh_blocker, $ip_address );
     126       
     127        // Update the database with the resolved hostname (even if empty)
     128        $edh_database->update_blocked_bot_hostname( $ip_address, $hostname );
     129       
     130        // Log successful resolution if debug logging is enabled
     131        if ( ! empty( $hostname ) && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
     132            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only logs when WP_DEBUG_LOG is enabled
     133            error_log( '[EDH Bad Bots] Background hostname resolution: ' . $ip_address . ' -> ' . $hostname );
     134        }
     135       
     136        // Small delay to prevent overwhelming DNS servers
     137        usleep( 100000 ); // 100ms delay
     138    }
     139}
    86140
    87141/**
  • edh-bad-bots/trunk/includes/class-edhbb-admin.php

    r3355590 r3357194  
    66 * and displaying blocked bots and whitelisted IPs.
    77 */
     8declare(strict_types=1);
    89
    910if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     
    3536        // Handle form submission for plugin options.
    3637        add_action( 'admin_post_edhbb_save_options', array( $this, 'handle_save_options' ) );
     38       
     39        // Handle manual hostname update trigger.
     40        add_action( 'admin_post_edhbb_update_hostnames', array( $this, 'handle_update_hostnames' ) );
     41       
     42        // Handle force refresh all hostnames trigger.
     43        add_action( 'admin_post_edhbb_force_refresh_all_hostnames', array( $this, 'handle_force_refresh_all_hostnames' ) );
    3744    }
    3845
     
    318325        exit; // Important: Always exit after a redirect to prevent further script execution.
    319326    }
     327
     328    /**
     329     * Handles the manual hostname update trigger from the admin interface.
     330     */
     331    public function handle_update_hostnames() {
     332        // Verify nonce for security.
     333        if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'edhbb_update_hostnames_nonce' ) ) {
     334            wp_die( esc_html__( 'Security check failed.', 'edh-bad-bots' ) );
     335        }
     336
     337        // Check user capabilities.
     338        if ( ! current_user_can( 'manage_options' ) ) {
     339            wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'edh-bad-bots' ) );
     340        }
     341
     342        // Run the debug function if WP_DEBUG is enabled
     343        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     344            $this->debug_hostname_resolution();
     345        }
     346
     347        // Trigger the hostname update function
     348        $updated_count = $this->manual_hostname_update();
     349
     350        // Add a success message to be displayed on the admin page.
     351        add_settings_error(
     352            'edhbb_messages',
     353            'edhbb_hostnames_updated',
     354            sprintf(
     355                /* translators: %d: number of updated hostnames */
     356                _n(
     357                    'Updated %d hostname.',
     358                    'Updated %d hostnames.',
     359                    $updated_count,
     360                    'edh-bad-bots'
     361                ),
     362                $updated_count
     363            ),
     364            'success'
     365        );
     366
     367        // Redirect back to the admin page, specifically the 'blocked' tab.
     368        $redirect_url = admin_url( 'tools.php?page=' . $this->admin_page_slug . '&tab=blocked' );
     369        wp_redirect( esc_url_raw( $redirect_url ) );
     370        exit;
     371    }
     372
     373    /**
     374     * Manually updates hostnames for blocked IPs that don't have them.
     375     *
     376     * @return int Number of hostnames updated.
     377     */
     378    private function manual_hostname_update() {
     379        // Get IPs without hostnames (limit to 10 for manual processing)
     380        $ips_without_hostnames = $this->db->get_blocked_ips_without_hostnames( 10 );
     381       
     382        if ( empty( $ips_without_hostnames ) ) {
     383            return 0; // Nothing to do
     384        }
     385       
     386        // Create a blocker instance to access the hostname resolution method
     387        $blocker = new EDHBB_Blocker( $this->db );
     388       
     389        // Use reflection to access the private method
     390        $reflection = new ReflectionClass( $blocker );
     391        $hostname_method = $reflection->getMethod( 'get_hostname_for_ip' );
     392        $hostname_method->setAccessible( true );
     393       
     394        $updated_count = 0;
     395       
     396        foreach ( $ips_without_hostnames as $ip_address ) {
     397            // Use the DNS lookup if available, otherwise fall back to blocker method
     398            if ( class_exists( 'EDHBB_DNSLookup' ) ) {
     399                $hostname = EDHBB_DNSLookup::get_hostname_for_blocked_ip( $ip_address );
     400            } else {
     401                // Fallback to the original blocker method
     402                $hostname = $hostname_method->invoke( $blocker, $ip_address );
     403            }
     404           
     405            // Update the database with the resolved hostname (even if empty)
     406            if ( $this->db->update_blocked_bot_hostname( $ip_address, $hostname ) ) {
     407                $updated_count++;
     408            }
     409        }
     410       
     411        return $updated_count;
     412    }
     413
     414    /**
     415     * Debugs hostname resolution for a sample IP address.
     416     */
     417    public function debug_hostname_resolution() {
     418        $debug_info = array();
     419        $ip_to_test = '8.8.8.8'; // A reliable IP for testing
     420
     421        // Test gethostbyaddr()
     422        $debug_info['gethostbyaddr']['exists'] = function_exists( 'gethostbyaddr' ) ? 'Yes' : 'No';
     423        $debug_info['gethostbyaddr']['callable'] = is_callable( 'gethostbyaddr' ) ? 'Yes' : 'No';
     424        if ( function_exists( 'gethostbyaddr' ) ) {
     425            $debug_info['gethostbyaddr']['result'] = gethostbyaddr( $ip_to_test );
     426        }
     427
     428        // Test dns_get_record()
     429        $debug_info['dns_get_record']['exists'] = function_exists( 'dns_get_record' ) ? 'Yes' : 'No';
     430        $debug_info['dns_get_record']['callable'] = is_callable( 'dns_get_record' ) ? 'Yes' : 'No';
     431        if ( function_exists( 'dns_get_record' ) ) {
     432            $debug_info['dns_get_record']['result'] = dns_get_record( $ip_to_test, DNS_PTR );
     433        }
     434
     435        set_transient( 'edhbb_debug_info', $debug_info, 60 );
     436    }
     437
     438    /**
     439     * Handles the force refresh all hostnames action.
     440     */
     441    public function handle_force_refresh_all_hostnames() {
     442        // Verify nonce for security.
     443        if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'edhbb_force_refresh_all_hostnames_nonce' ) ) {
     444            wp_die( esc_html__( 'Security check failed.', 'edh-bad-bots' ) );
     445        }
     446
     447        // Check user capabilities.
     448        if ( ! current_user_can( 'manage_options' ) ) {
     449            wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'edh-bad-bots' ) );
     450        }
     451
     452        // Clear all DNS caches
     453        if ( class_exists( 'EDHBB_DNSLookup' ) ) {
     454            // Clear hostname cache
     455            global $wpdb;
     456            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Intentionally clearing transient cache for this plugin, caching not applicable for cache clearing operations
     457            $wpdb->query(
     458                $wpdb->prepare(
     459                    "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
     460                    $wpdb->esc_like( '_transient_edhbb_hostname_' ) . '%'
     461                )
     462            );
     463            $wpdb->query(
     464                $wpdb->prepare(
     465                    "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
     466                    $wpdb->esc_like( '_transient_timeout_edhbb_hostname_' ) . '%'
     467                )
     468            );
     469            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     470        }
     471
     472        // Get ALL blocked IPs (not just ones without hostnames)
     473        $all_blocked_ips = $this->db->get_blocked_bots( 0 ); // 0 = no limit
     474        $updated_count = 0;
     475
     476        foreach ( $all_blocked_ips as $bot ) {
     477            $ip_address = $bot['ip_address'];
     478           
     479            // Force new hostname lookup
     480            if ( class_exists( 'EDHBB_DNSLookup' ) ) {
     481                $hostname = EDHBB_DNSLookup::get_hostname_for_blocked_ip( $ip_address );
     482            } else {
     483                // Fallback method
     484                $blocker = new EDHBB_Blocker( $this->db );
     485                $reflection = new ReflectionClass( $blocker );
     486                $hostname_method = $reflection->getMethod( 'get_hostname_for_ip' );
     487                $hostname_method->setAccessible( true );
     488                $hostname = $hostname_method->invoke( $blocker, $ip_address );
     489                // Ensure we set a clear indicator for empty results in fallback
     490                if ( empty( $hostname ) ) {
     491                    $hostname = '[No PTR Record]';
     492                }
     493            }
     494           
     495            // Update the database
     496            if ( $this->db->update_blocked_bot_hostname( $ip_address, $hostname ) ) {
     497                $updated_count++;
     498            }
     499        }
     500
     501        // Add success message
     502        add_settings_error(
     503            'edhbb_messages',
     504            'edhbb_force_refresh_completed',
     505            sprintf(
     506                /* translators: %d: number of updated hostnames */
     507                _n(
     508                    'Force refreshed %d hostname (cleared cache and re-resolved all).',
     509                    'Force refreshed %d hostnames (cleared cache and re-resolved all).',
     510                    $updated_count,
     511                    'edh-bad-bots'
     512                ),
     513                $updated_count
     514            ),
     515            'success'
     516        );
     517
     518        // Redirect back to the admin page
     519        $redirect_url = admin_url( 'tools.php?page=' . $this->admin_page_slug . '&tab=blocked' );
     520        wp_redirect( esc_url_raw( $redirect_url ) );
     521        exit;
     522    }
    320523}
  • edh-bad-bots/trunk/includes/class-edhbb-blocker.php

    r3355590 r3357194  
    55 * Handles detecting and blocking bad bots that hit the trap URL.
    66 */
     7declare(strict_types=1);
    78
    89if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     
    108109        $current_url = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) );
    109110        $site_path = wp_parse_url( site_url(), PHP_URL_PATH );
     111       
     112        // Handle cases where site_path might be null (when URL has no path component)
     113        $site_path = $site_path ?? '';
    110114        $expected_trap_path = rtrim( $site_path, '/' ) . '/' . $this->trap_url_hash . '/';
    111115
     
    127131
    128132            // Add the bot to the blocked list.
    129             $this->db->add_blocked_bot( $client_ip );
     133            $hostname = $this->get_hostname_for_ip($client_ip);
     134            $this->db->add_blocked_bot( $client_ip, $hostname );
    130135
    131136            // Immediately block the request after adding to the blocklist.
    132137            $this->block_request_action( $client_ip, true ); // Pass true to indicate it's a trap hit block
    133138        }
     139    }
     140
     141    /**
     142     * Attempts to resolve hostname for an IP address using enhanced DNS lookup.
     143     * Uses the new EDHBB_DNSLookup class with DoH support and fallback methods.
     144     *
     145     * @param string $ip_address The IP address to resolve.
     146     * @return string The hostname if resolved, empty string if failed.
     147     */
     148    private function get_hostname_for_ip( $ip_address ) {
     149        // Validate IP address first
     150        if ( ! filter_var( $ip_address, FILTER_VALIDATE_IP ) ) {
     151            return '';
     152        }
     153
     154        // Check if the DNSLookup class is available
     155        if ( ! class_exists( 'EDHBB_DNSLookup' ) ) {
     156            // Fallback to traditional method if class not loaded
     157            return $this->traditional_hostname_lookup( $ip_address );
     158        }
     159
     160        // Use the DNS lookup method for blocked IPs
     161        return EDHBB_DNSLookup::get_hostname_for_blocked_ip( $ip_address );
     162    }
     163
     164    /**
     165     * Traditional hostname lookup method as fallback.
     166     *
     167     * @param string $ip_address The IP address to resolve.
     168     * @return string The hostname if resolved, empty string if failed.
     169     */
     170    private function traditional_hostname_lookup( $ip_address ) {
     171        // Check if dns_get_record is disabled
     172        if ( ! function_exists( 'dns_get_record' ) || ! is_callable( 'dns_get_record' ) ) {
     173            if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
     174                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     175                error_log( '[EDH Bad Bots] Hostname lookup failed: dns_get_record() is disabled.' );
     176            }
     177            return '';
     178        }
     179
     180        $hostname = '';
     181       
     182        try {
     183            // Perform a reverse DNS lookup (PTR record)
     184            $ptr_records = @dns_get_record( $ip_address, DNS_PTR );
     185
     186            if ( $ptr_records && ! empty( $ptr_records[0]['target'] ) ) {
     187                $resolved = $ptr_records[0]['target'];
     188               
     189                // Validate the resolved hostname
     190                if ( filter_var( $resolved, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME ) ||
     191                     preg_match( '/^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/', $resolved ) ) {
     192                    $hostname = $resolved;
     193                }
     194            }
     195        } catch ( Exception $e ) {
     196            // Log the error if debug logging is enabled
     197            if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
     198                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only logs when WP_DEBUG_LOG is enabled
     199                error_log( '[EDH Bad Bots] Hostname lookup failed for IP ' . $ip_address . ': ' . $e->getMessage() );
     200            }
     201        }
     202
     203        return $hostname;
    134204    }
    135205
  • edh-bad-bots/trunk/includes/class-edhbb-database.php

    r3355590 r3357194  
    55 * Handles all database interactions for storing blocked bots and whitelisted IPs.
    66 */
     7declare(strict_types=1);
    78
    89if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
     
    6768         * - id: Primary key.
    6869         * - ip_address: The IP address of the blocked bot.
     70         * - hostname: The hostname of the blocked bot.
    6971         * - blocked_at: Timestamp when the bot was blocked.
    7072         * - expires_at: Timestamp when the block will expire (30 days after blocked_at).
     
    7274        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
    7375        $sql_blocked_bots = $this->wpdb->prepare(
    74             "CREATE TABLE IF NOT EXISTS %i (
     76            "CREATE TABLE %i (
    7577                id bigint(20) NOT NULL AUTO_INCREMENT,
    7678                ip_address varchar(45) NOT NULL,
     79                hostname varchar(255) DEFAULT NULL,
    7780                blocked_at datetime NOT NULL,
    7881                expires_at datetime NOT NULL,
     
    9396        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
    9497        $sql_whitelisted_ips = $this->wpdb->prepare(
    95             "CREATE TABLE IF NOT EXISTS %i (
     98            "CREATE TABLE %i (
    9699                id bigint(20) NOT NULL AUTO_INCREMENT,
    97100                ip_address varchar(45) NOT NULL,
     
    108111        dbDelta( $sql_whitelisted_ips );
    109112
     113        // Run database migrations for existing installations
     114        $this->migrate_database();
     115
    110116        // Ensure .htaccess rules are up-to-date on activation, respecting the option flag
    111117        $this->update_htaccess_block_rules();
     118    }
     119
     120    /**
     121     * Handles database migrations for existing installations.
     122     * Adds missing columns to existing tables.
     123     */
     124    private function migrate_database() {
     125        // Check if hostname column exists in blocked_bots table
     126        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- INFORMATION_SCHEMA queries require literal DB_NAME constant and table names
     127        $column_exists = $this->wpdb->get_results(
     128            $this->wpdb->prepare(
     129                "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
     130                 WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = 'hostname'",
     131                DB_NAME,
     132                $this->blocked_bots_table_name
     133            )
     134        );
     135        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
     136
     137        // If hostname column doesn't exist, add it
     138        if ( empty( $column_exists ) ) {
     139            // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
     140            $result = $this->wpdb->query(
     141                $this->wpdb->prepare(
     142                    "ALTER TABLE %i ADD COLUMN hostname varchar(255) DEFAULT NULL AFTER ip_address",
     143                    $this->blocked_bots_table_name
     144                )
     145            );
     146            // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
     147
     148            // Log any errors if WP_DEBUG_LOG is enabled
     149            if ( $result === false && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
     150                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only logs when WP_DEBUG_LOG is enabled
     151                error_log( '[EDH Bad Bots] Failed to add hostname column to blocked_bots table. Error: ' . $this->wpdb->last_error );
     152            }
     153        }
     154    }
     155
     156    /**
     157     * Public method to trigger database migrations manually.
     158     * This can be called from admin interfaces or other parts of the plugin.
     159     *
     160     * @return bool True if migration was successful or not needed, false on failure.
     161     */
     162    public function run_migrations() {
     163        $this->migrate_database();
     164        return true;
     165    }
     166
     167    /**
     168     * Checks if a column exists in a given table.
     169     *
     170     * @param string $table_name The name of the table to check.
     171     * @param string $column_name The name of the column to check.
     172     * @return bool True if the column exists, false otherwise.
     173     */
     174    private function column_exists( $table_name, $column_name ) {
     175        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- INFORMATION_SCHEMA queries require literal DB_NAME constant and table names
     176        $column_exists = $this->wpdb->get_results(
     177            $this->wpdb->prepare(
     178                "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
     179                 WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
     180                DB_NAME,
     181                $table_name,
     182                $column_name
     183            )
     184        );
     185        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
     186
     187        return ! empty( $column_exists );
    112188    }
    113189
     
    131207     *
    132208     * @param string $ip_address The IP address to block.
     209     * @param string $hostname The hostname of the blocked bot.
    133210     * @return bool True on success, false on failure.
    134211     */
    135     public function add_blocked_bot( $ip_address ) {
     212    public function add_blocked_bot( $ip_address, $hostname = '' ) {
    136213        // Clean up old entries before adding a new one to keep the table lean.
    137214        $this->clean_old_blocked_bots();
     
    142219        $expires_at = gmdate( 'Y-m-d H:i:s', $expires_timestamp );
    143220
     221        // Check if hostname column exists to determine the query format
     222        $hostname_exists = $this->column_exists( $this->blocked_bots_table_name, 'hostname' );
     223
    144224        // Insert the IP address into the blocked bots table.
    145225        // Using `INSERT IGNORE` to prevent errors if the IP is already present due to the UNIQUE KEY constraint.
    146226        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
    147         $result = $this->wpdb->query(
    148             $this->wpdb->prepare(
    149                 "INSERT IGNORE INTO %i (ip_address, blocked_at, expires_at) VALUES (%s, %s, %s)",
    150                 $this->blocked_bots_table_name,
    151                 $ip_address,
    152                 $current_time,
    153                 $expires_at
    154             )
    155         );
     227        if ( $hostname_exists ) {
     228            $result = $this->wpdb->query(
     229                $this->wpdb->prepare(
     230                    "INSERT IGNORE INTO %i (ip_address, hostname, blocked_at, expires_at) VALUES (%s, %s, %s, %s)",
     231                    $this->blocked_bots_table_name,
     232                    $ip_address,
     233                    $hostname,
     234                    $current_time,
     235                    $expires_at
     236                )
     237            );
     238        } else {
     239            // Fallback for tables without hostname column
     240            $result = $this->wpdb->query(
     241                $this->wpdb->prepare(
     242                    "INSERT IGNORE INTO %i (ip_address, blocked_at, expires_at) VALUES (%s, %s, %s)",
     243                    $this->blocked_bots_table_name,
     244                    $ip_address,
     245                    $current_time,
     246                    $expires_at
     247                )
     248            );
     249        }
    156250        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
    157251
     
    194288        $this->clean_old_blocked_bots(); // Ensure only current blocks are considered.
    195289
    196         // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
    197         $sql = "SELECT id, ip_address, blocked_at, expires_at FROM %i WHERE expires_at > %s ORDER BY blocked_at DESC";
     290        // Check if hostname column exists to avoid SQL errors
     291        $hostname_exists = $this->column_exists( $this->blocked_bots_table_name, 'hostname' );
     292        $select_fields = $hostname_exists ?
     293            "id, ip_address, hostname, blocked_at, expires_at" :
     294            "id, ip_address, '' as hostname, blocked_at, expires_at";
     295
     296        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
     297        $sql = "SELECT {$select_fields} FROM %i WHERE expires_at > %s ORDER BY blocked_at DESC";
    198298        $sql_params = array( $this->blocked_bots_table_name, current_time( 'mysql' ) );
    199299
     
    320420
    321421        return ( $result > 0 );
     422    }
     423
     424    /**
     425     * Updates hostname for a specific blocked IP address.
     426     *
     427     * @param string $ip_address The IP address to update.
     428     * @param string $hostname The hostname to set.
     429     * @return bool True on success, false on failure.
     430     */
     431    public function update_blocked_bot_hostname( $ip_address, $hostname ) {
     432        // Check if hostname column exists
     433        if ( ! $this->column_exists( $this->blocked_bots_table_name, 'hostname' ) ) {
     434            return false; // Cannot update if column doesn't exist
     435        }
     436
     437        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
     438        $result = $this->wpdb->update(
     439            $this->blocked_bots_table_name,
     440            array( 'hostname' => $hostname ),
     441            array( 'ip_address' => $ip_address ),
     442            array( '%s' ),
     443            array( '%s' )
     444        );
     445        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
     446
     447        return $result !== false;
     448    }
     449
     450    /**
     451     * Gets blocked IPs that have empty or null hostnames for background processing.
     452     *
     453     * @param int $limit Maximum number of IPs to return.
     454     * @return array Array of IP addresses that need hostname resolution.
     455     */
     456    public function get_blocked_ips_without_hostnames( $limit = 10 ) {
     457        // Check if hostname column exists
     458        if ( ! $this->column_exists( $this->blocked_bots_table_name, 'hostname' ) ) {
     459            return array(); // Return empty array if column doesn't exist
     460        }
     461
     462        // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- %i placeholder is valid since WordPress 6.2
     463        $results = $this->wpdb->get_results(
     464            $this->wpdb->prepare(
     465                "SELECT ip_address FROM %i WHERE (hostname IS NULL OR hostname = '' OR hostname = '[No PTR Record]') AND expires_at > %s LIMIT %d",
     466                $this->blocked_bots_table_name,
     467                current_time( 'mysql' ),
     468                $limit
     469            ),
     470            ARRAY_A
     471        );
     472        // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
     473
     474        return wp_list_pluck( $results, 'ip_address' );
    322475    }
    323476
  • edh-bad-bots/trunk/readme.txt

    r3355590 r3357194  
    11=== EDH Bad Bots ===
    22Contributors: EncodeDotHost, nbwpuk
    3 Tags: Security, Bots
     3Tags: Security, Bots, DNS, PTR, Hostname
    44Requires at least: 6.2
    55Tested up to: 6.8
    6 Stable tag: 1.4.2
     6Stable tag: 1.4.3
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    1818
    1919- **Automatic Bot Detection**: Identifies bad bots using a hidden trap URL technique
    20 - **Smart Blocking System**: Blocks misbehaving bots for 30 days automatically
     20- **Smart Blocking System**: Blocks misbehaving bots with configurable duration (default 30 days)
     21- **Advanced DNS Resolution**: PTR record lookups with DNS over HTTPS (DoH) support for hostname identification
    2122- **Dual-Level Blocking**: Server-level `.htaccess` blocking AND PHP-level blocking for maximum effectiveness
    2223- **Configurable Blocking Methods**: Choose between `.htaccess` blocking (Apache) or PHP-only blocking (Nginx compatible)
    2324- **IP Whitelist Management**: Protect trusted IPs from ever being blocked
    24 - **Clean Admin Interface**: Easy-to-use dashboard with tabbed navigation
     25- **Enhanced Admin Interface**: Clean dashboard with hostname display, manual hostname updates, and debug tools
     26- **Background Processing**: Automated hostname resolution via WordPress cron jobs
    2527- **Zero False Positives**: Legitimate search engine bots that follow robots.txt rules are never affected
    2628- **Database Optimization**: Automatic cleanup of expired blocks to maintain performance
     
    35373. **Hidden Link Placement**: Places an invisible link to the trap URL in your site's footer
    36384. **Bot Detection**: When bad bots ignore robots.txt and follow the hidden link, they're identified
    37 5. **Automatic Blocking**: Detected bot IPs are blocked for 30 days with immediate effect
    38 6. **Legitimate Bot Protection**: Good bots (like Googlebot) respect robots.txt and never trigger the trap
     395. **Automatic Blocking**: Detected bot IPs are blocked with configurable duration and immediate effect
     406. **Hostname Resolution**: PTR record lookups identify the hostname/organization behind blocked IPs
     417. **Legitimate Bot Protection**: Good bots (like Googlebot) respect robots.txt and never trigger the trap
    3942
    4043## Installation
     
    5760
    5861#### Blocked Bots Tab
    59 - View all currently blocked IP addresses
     62- View all currently blocked IP addresses with hostnames
    6063- See when each IP was blocked and when the block expires
     64- Manually update missing hostnames for better identification
     65- Force refresh all hostnames to clear cache and re-resolve
     66- Debug hostname resolution issues (when WP_DEBUG is enabled)
    6167- Manually unblock IPs if needed
    6268
    6369#### Options Tab
    6470- **`.htaccess Blocking`**: Enable/disable server-level IP blocking via `.htaccess` file
     71- **Block Duration**: Configure how many days to block detected bots
    6572- Configure blocking method based on your server setup (Apache vs Nginx)
    6673- Server-level blocking bypasses caching for immediate effect
     
    7077- Best practices for managing IPs
    7178- Information about `.htaccess` blocking options
     79- Unique trap URL for caching plugin exclusion
    7280
    7381### Requirements
    7482
    75 - WordPress 5.0 or higher
     83- WordPress 6.2 or higher
    7684- PHP 7.4 or higher
    7785- MySQL 5.6 or higher
     
    8593The plugin creates two custom database tables:
    8694
    87 - `wp_edhbb_blocked_bots`: Stores blocked IP addresses with expiration dates
     95- `wp_edhbb_blocked_bots`: Stores blocked IP addresses with expiration dates and hostnames
    8896- `wp_edhbb_whitelisted_ips`: Stores permanently whitelisted IP addresses
     97
     98### DNS Resolution System
     99
     100The plugin includes an advanced DNS lookup system:
     101
     102#### DNS over HTTPS (DoH) Support
     103- **Primary providers**: Cloudflare DNS, Google DNS
     104- **Secure queries**: HTTPS-encrypted DNS requests for enhanced privacy
     105- **Fallback system**: Automatic fallback to traditional DNS methods
     106
     107#### PTR Record Lookups
     108- **Reverse DNS**: Converts IP addresses to hostnames for better identification
     109- **IPv4 and IPv6 support**: Full support for both IP versions
     110- **Caching**: Results cached for 1 hour to improve performance
     111- **Background processing**: Automated hostname resolution via WordPress cron
    89112
    90113### Blocking Methods
     
    122145- **Server-Level Blocking**: `.htaccess` blocking prevents blocked requests from reaching PHP
    123146- **Whitelist Filtering**: Whitelisted IPs are excluded from `.htaccess` rules automatically
     147- **DNS Caching**: Hostname lookups cached to reduce DNS query overhead
     148- **Background Processing**: Hostname resolution runs in background to avoid delays
    124149
    125150## API Hooks
     
    131156- `wp_footer`: Hidden link injection
    132157- `admin_menu`: Admin page registration
     158- `edhbb_update_hostnames_cron`: Background hostname resolution
    133159
    134160### Filters
     
    148174│       └── admin-script.js      # Admin page JavaScript
    149175├── includes/
    150 │   ├── class-edh-admin.php      # Admin functionality
    151 │   ├── class-edh-blocker.php    # Bot detection and blocking
    152 │   └── class-edh-database.php  # Database operations
    153 ├── changelog.txt
     176│   ├── class-edhbb-admin.php    # Admin functionality
     177│   ├── class-edhbb-blocker.php  # Bot detection and blocking
     178│   ├── class-edhbb-database.php # Database operations
     179│   └── class-edhbb-dnslookup.php # DNS/PTR lookup system
    154180├── edh-bad-bots.php            # Main plugin file
    155181├── LICENSE
    156 └── README.md
     182└── readme.txt
    157183```
    158184
    159185## Screenshots
    1601861. Allow list management
    161 2. Block list management
    162 3. Options Page
     1872. Block list management with hostname display
     1883. Options Page with configurable settings
    163189
    164190## Contributing
     
    174200## Changelog
    175201
     202### Version 1.4.3
     203- **New**: Advanced DNS lookup system with DNS over HTTPS (DoH) support
     204- **New**: PTR record resolution for hostname identification of blocked IPs
     205- **New**: Background hostname resolution via WordPress cron jobs
     206- **New**: Manual hostname update buttons in admin interface
     207- **New**: Force refresh all hostnames feature with cache clearing
     208- **New**: Configurable block duration (days) in Options tab
     209- **New**: Enhanced admin interface with hostname display in blocked bots table
     210- **New**: Debug information panel for hostname resolution troubleshooting
     211- **New**: Support for both IPv4 and IPv6 PTR lookups
     212- **Enhancement**: Improved blocked bots table with hostname column
     213- **Enhancement**: Better identification of blocked bots through hostname resolution
     214- **Enhancement**: Automatic database migration system for hostname column
     215- **Enhancement**: Fallback DNS resolution methods for better compatibility
     216- **Enhancement**: DNS query caching for improved performance
     217- **Fix**: Updated queries to gracefully handle missing columns during migration
     218- **Fix**: Added backward compatibility for existing installations
     219- **Fix**: Improved error handling and logging for database migrations
     220
    176221### Version 1.4.2
    177  - Fixed Internal.LineEndings.Mixed in class-edhbb-blocker.php
     222- Fixed Internal.LineEndings.Mixed in class-edhbb-blocker.php
    178223
    179224### Version 1.4.1
    180  - Fixed blank admin page
     225- Fixed blank admin page
    181226
    182227### Version 1.4.0
     
    186231
    187232### Version 1.3.0
    188  - Bringing up to WordPress coding standards
     233- Bringing up to WordPress coding standards
    189234
    190235### Version 1.2.3
    191  - Added instructions to the "Help" tab for excluding the unique trap URL from caching plugins.
    192 
     236- Added instructions to the "Help" tab for excluding the unique trap URL from caching plugins.
    193237
    194238### Version 1.2.2
     
    261305
    262306### How long are bots blocked for?
    263 Bots are automatically blocked for 30 days. You can manually unblock them earlier if needed.
     307Bots are blocked for a configurable duration (default 30 days) that you can adjust in the Options tab. You can manually unblock them earlier if needed.
    264308
    265309### Can I protect my own IP address?
     
    269313`.htaccess` blocking (default) blocks bots at the server level before WordPress loads, making it faster and more effective. PHP blocking works during WordPress initialization and is compatible with Nginx servers.
    270314
     315### What is hostname resolution and why is it useful?
     316The plugin performs PTR record lookups to identify the hostname/organization behind blocked IP addresses. This helps you understand what types of bots are being blocked (e.g., "crawl-66-249-66-1.googlebot.com" vs unknown IPs) for better analysis and decision-making.
     317
     318### How does the DNS over HTTPS feature work?
     319The plugin uses secure HTTPS-encrypted DNS queries via providers like Cloudflare and Google DNS for enhanced privacy and reliability when resolving hostnames. It automatically falls back to traditional DNS methods if DoH is unavailable.
     320
    271321### Does this affect site performance?
    272 The plugin is designed for minimal performance impact. Server-level `.htaccess` blocking actually improves performance by stopping blocked requests before they reach PHP. Database operations are optimized with automatic cleanup.
     322The plugin is designed for minimal performance impact. Server-level `.htaccess` blocking actually improves performance by stopping blocked requests before they reach PHP. DNS lookups are cached and processed in the background to avoid delays.
    273323
    274324### Will this work with caching plugins?
     
    280330### Is it safe for my .htaccess file?
    281331Yes! The plugin uses unique markers (`# BEGIN EDH Bad Bots Block` / `# END EDH Bad Bots Block`) to safely manage its rules without affecting other configurations. Rules are automatically removed on deactivation.
     332
     333### Can I manually update hostnames for blocked IPs?
     334Yes! In the "Blocked Bots" tab, you can use the "Update Missing Hostnames" button to resolve hostnames for IPs that don't have them, or "Force Refresh All Hostnames" to clear the cache and re-resolve all hostnames.
Note: See TracChangeset for help on using the changeset viewer.