22
33namespace Drupal \turnstile_protect \EventSubscriber ;
44
5+ use Drupal \Core \Cache \CacheBackendInterface ;
56use Drupal \Core \Config \ConfigFactoryInterface ;
67use Drupal \Core \Config \ImmutableConfig ;
78use 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}
0 commit comments