Skip to content

Commit 2ed16ad

Browse files
committed
Issue #3515373: cache dns lookups per IP
1 parent ce5a27f commit 2ed16ad

File tree

2 files changed

+45
-15
lines changed

2 files changed

+45
-15
lines changed

src/EventSubscriber/Challenge.php

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\turnstile_protect\EventSubscriber;
44

5+
use Drupal\Core\Cache\CacheBackendInterface;
56
use Drupal\Core\Config\ConfigFactoryInterface;
67
use Drupal\Core\Config\ImmutableConfig;
78
use Drupal\Core\Flood\FloodInterface;
@@ -47,6 +48,13 @@ class Challenge implements EventSubscriberInterface {
4748
*/
4849
protected $logger;
4950

51+
/**
52+
* The Drupal cache service.
53+
*
54+
* @var \Drupal\Core\Cache\CacheBackendInterface
55+
*/
56+
protected $cache;
57+
5058
/**
5159
* Constructs the event subscriber.
5260
*
@@ -58,12 +66,15 @@ class Challenge implements EventSubscriberInterface {
5866
* The config factory service.
5967
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
6068
* The current user.
69+
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
70+
* The Drupal cache service.
6171
*/
62-
public function __construct(LoggerChannelFactoryInterface $logger_factory, FloodInterface $flood, ConfigFactoryInterface $config_factory, AccountProxyInterface $current_user) {
72+
public function __construct(LoggerChannelFactoryInterface $logger_factory, FloodInterface $flood, ConfigFactoryInterface $config_factory, AccountProxyInterface $current_user, CacheBackendInterface $cache) {
6373
$this->logger = $logger_factory->get('turnstile_protect');
6474
$this->flood = $flood;
6575
$this->configFactory = $config_factory;
6676
$this->currentUser = $current_user;
77+
$this->cache = $cache;
6778
}
6879

6980
/**
@@ -100,21 +111,14 @@ protected function applies(RequestEvent $event, ImmutableConfig $config): bool {
100111
if (captcha_whitelist_ip_whitelisted($clientIp)) {
101112
return FALSE;
102113
}
103-
// See if the client IP resolves to a good bot.
104-
$hostname = gethostbyaddr($clientIp);
105-
// Being sure to lookup the domain to avoid spoofing.
106-
$resolved_ip = gethostbyname($hostname);
107-
if ($clientIp !== $resolved_ip) {
108-
if ($clientIp !== '127.0.0.1') {
109-
return TRUE;
110-
}
114+
115+
$hostname = self::getHostname($clientIp, $hostname);
116+
// Need at least a second level domain.
117+
if (strpos($hostname, ".") === FALSE) {
118+
return TRUE;
111119
}
120+
112121
$parts = explode(".", $hostname);
113-
if (count($parts) < 2) {
114-
if ($clientIp !== '127.0.0.1') {
115-
return TRUE;
116-
}
117-
}
118122
$tld = array_pop($parts);
119123
$hostname = array_pop($parts) . '.' . $tld;
120124
if (in_array($hostname, $config->get('bots'))) {
@@ -233,4 +237,25 @@ public static function expandIpv6($ip) {
233237
return $expanded;
234238
}
235239

240+
/**
241+
* Helper function to cache DNS lookups on an IP.
242+
*/
243+
public static function getHostName($clientIp) {
244+
$cid = 'turnstile_protect:' . $clientIp;
245+
$cache = $this->cache->get($cid);
246+
if ($cache) {
247+
return $cache->data;
248+
}
249+
250+
$hostname = gethostbyaddr($clientIp);
251+
$resolvedIp = gethostbyname($hostname);
252+
// Ensure the hostname isn't a reverse DNS spoof.
253+
if ($clientIp !== $resolvedIp && $clientIp !== '127.0.0.1') {
254+
$hostname = "";
255+
}
256+
$this->cache->set($cid, $hostname);
257+
258+
return $hostname;
259+
}
260+
236261
}

turnstile_protect.services.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
services:
22
turnstile_protect.challenge:
33
class: Drupal\turnstile_protect\EventSubscriber\Challenge
4-
arguments: ['@logger.factory', '@flood', '@config.factory', '@current_user']
4+
arguments:
5+
- "@logger.factory"
6+
- "@flood"
7+
- "@config.factory"
8+
- "@current_user"
9+
- "@cache.default"
510
tags:
611
- { name: event_subscriber }

0 commit comments

Comments
 (0)