From 9feabab4eaf9ff057789467b9c505b178bca4040 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Wed, 23 Apr 2025 23:34:05 +0200 Subject: [PATCH 01/15] DnsDriver.php: add --- inc/Data/Driver/Dns/DnsDriver.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 inc/Data/Driver/Dns/DnsDriver.php diff --git a/inc/Data/Driver/Dns/DnsDriver.php b/inc/Data/Driver/Dns/DnsDriver.php new file mode 100644 index 00000000..cbfcc621 --- /dev/null +++ b/inc/Data/Driver/Dns/DnsDriver.php @@ -0,0 +1,21 @@ + Date: Wed, 23 Apr 2025 23:38:02 +0200 Subject: [PATCH 02/15] HostDnsDriver.php: add --- inc/Data/Driver/Dns/HostDnsDriver.php | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 inc/Data/Driver/Dns/HostDnsDriver.php diff --git a/inc/Data/Driver/Dns/HostDnsDriver.php b/inc/Data/Driver/Dns/HostDnsDriver.php new file mode 100644 index 00000000..b860256a --- /dev/null +++ b/inc/Data/Driver/Dns/HostDnsDriver.php @@ -0,0 +1,43 @@ +timeout = $timeout; + } + + public function nameToIPs(string $name): ?array { + $ret = shell_exec_error("host -W {$this->timeout} {$name}"); + if ($ret === false) { + return null; + } + + $ipv4 = self::matchOrEmpty('/has address ([^\s]+)/', $ret); + $ipv6 = self::matchOrEmpty('/has IPv6 address ([^\s]+)/', $ret); + return \array_merge($ipv4, $ipv6); + } + + public function IPToNames(string $ip): ?array { + $ret = shell_exec_error("host -W {$this->timeout} {$ip}"); + if ($ret === false) { + return null; + } + + $names = self::matchOrEmpty('/domain name pointer ([^\s]+)\./', $ret); + return \array_map(fn($n) => \strtolower(\rtrim($n, '.')), $names); + } +} From 4135c2924e6d173632d96f6a22c22433db5f0aeb Mon Sep 17 00:00:00 2001 From: Zankaria Date: Wed, 23 Apr 2025 23:39:22 +0200 Subject: [PATCH 03/15] LibcDnsDriver.php: add --- inc/Data/Driver/Dns/LibcDnsDriver.php | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 inc/Data/Driver/Dns/LibcDnsDriver.php diff --git a/inc/Data/Driver/Dns/LibcDnsDriver.php b/inc/Data/Driver/Dns/LibcDnsDriver.php new file mode 100644 index 00000000..367304e0 --- /dev/null +++ b/inc/Data/Driver/Dns/LibcDnsDriver.php @@ -0,0 +1,40 @@ + Date: Thu, 24 Apr 2025 00:05:11 +0200 Subject: [PATCH 04/15] LibcDnsDriver.php: use something less bad than gethostbynamel --- inc/Data/Driver/Dns/LibcDnsDriver.php | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/inc/Data/Driver/Dns/LibcDnsDriver.php b/inc/Data/Driver/Dns/LibcDnsDriver.php index 367304e0..71e838df 100644 --- a/inc/Data/Driver/Dns/LibcDnsDriver.php +++ b/inc/Data/Driver/Dns/LibcDnsDriver.php @@ -11,18 +11,28 @@ class LibcDnsDriver implements DnsDriver { \putenv("RES_OPTIONS=retrans:1 retry:1 timeout:{$timeout} attempts:1"); } - /** - * For the love of god never use this. - * https://www.php.net/manual/en/function.gethostbynamel.php#119535 - */ public function nameToIPs(string $name): ?array { - // Add a trailing dot to not return the loopback address on failure - // https://www.php.net/manual/en/function.gethostbynamel.php#119535 - $ret = \gethostbynamel("{$name}."); + $ret = \dns_get_record($name, DNS_A | DNS_AAAA); if ($ret === false) { return null; } - return $ret; + + $ips = []; + foreach ($ret as $dns_record) { + if ($dns_record['type'] == 'A') { + $ips[] = $dns_record['ip']; + } elseif ($dns_record['type'] == 'AAAA') { + $ips[] = $dns_record['ipv6']; + } + } + + if (empty($ips)) { + return []; + } else { + // Stable return order. + \sort($ips, \SORT_STRING); + return $ips; + } } /** From 35218b356fc9d5c84dc0fb5495a70ebfee1c9b26 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 00:37:17 +0200 Subject: [PATCH 05/15] IpBlacklistService.php: add --- inc/Service/IpBlacklistService.php | 215 +++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 inc/Service/IpBlacklistService.php diff --git a/inc/Service/IpBlacklistService.php b/inc/Service/IpBlacklistService.php new file mode 100644 index 00000000..9eafbd79 --- /dev/null +++ b/inc/Service/IpBlacklistService.php @@ -0,0 +1,215 @@ +exceptions)) { + return true; + } + + if (\filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE) !== false) { + return true; + } + + return false; + } + + private function isIpBlacklistedImpl(string $ip, string $rip): ?string { + foreach ($this->blacklist_providers as $blacklist) { + $blacklist_host = $blacklist; + if (\is_array($blacklist)) { + $blacklist_host = $blacklist[0]; + } + + // The name that will be looked up. + $name = self::buildEndpoint($blacklist_host, $rip); + + // Do the actual check. + $is_blacklisted = $this->checkNameResolves($name); + + if ($is_blacklisted) { + // Pick the strategy to deal with this blacklisted host. + + if (!isset($blacklist[1])) { + // Just block them. + return $blacklist_host; + } elseif (\is_array($blacklist[1])) { + // Check if the blacklist applies only to some IPs. + foreach ($blacklist[1] as $octet_or_ip) { + if ($ip == $octet_or_ip || $ip == "127.0.0.$octet_or_ip") { + return $blacklist_host; + } + } + } elseif (\is_callable($blacklist[1])) { + // Custom user provided function. + if ($blacklist[1]($ip)) { + return $blacklist_host; + } + } else { + // Check if the blacklist only applies to a specific IP. + if ($ip == $blacklist[1] || $ip == "127.0.0.{$blacklist[1]}") { + return $blacklist_host; + } + } + } + } + return null; + } + + private function checkNameResolves(string $name): bool { + $value = $this->cache->get(self::buildDnsCacheKey($name)); + if ($value === null) { + $value = !empty($this->resolver->nameToIps(self::buildDnsCacheKey($name))); + $this->cache->set(self::buildDnsCacheKey($name), $value, self::DNS_CACHE_TIMEOUT); + } + return $value; + } + + + /** + * Build a DNS accessor. + * + * @param DnsDriver $resolver DNS driver. + * @param CacheDriver $cache Cache driver. + * @param array $blacklists Array of DNS blacklist providers. + * @param array $exceptions Exceptions to the blacklists. + * @param bool $rdns_validate If to validate the Reverse DNS queries results. + */ + public function __construct(DnsDriver $resolver, CacheDriver $cache, array $blacklist_providers, array $exceptions, bool $rdns_validate) { + $this->resolver = $resolver; + $this->cache = $cache; + $this->blacklist_providers = $blacklist_providers; + $this->exceptions = $exceptions; + $this->rdns_validate = $rdns_validate; + } + + /** + * Is the given IP known to a blacklist and not whitelisted? + * Documentation: https://github.com/vichan-devel/vichan/wiki/dnsbl + * + * @param string $ip The ip to lookup. + * @return ?string Returns the hit blacklist if the IP is a in known blacklist. Null if the IP is not blacklisted. + * @throws InvalidArgumentException Throws if $ip is not a valid IPv4 or IPv6 address. + */ + public function isIpBlacklisted(string $ip): ?string { + $rev_ip = false; + $ret = self::reverseIpv4Octets($ip); + if ($ret !== null) { + $rev_ip = $ret; + } + $ret = self::reverseIpv6Octets($ip); + if ($ret !== null) { + $rev_ip = $ret; + } + + if ($rev_ip === false) { + throw new \InvalidArgumentException("'$ip' is not a valid ip address"); + } + + if ($this->isIpWhitelisted($ip)) { + return null; + } + + return $this->isIpBlacklistedImpl($ip, $rev_ip); + } + + /** + * Performs the Reverse DNS lookup (rDNS) of the given IP. + * This function can be slow since may validate the response. + * + * @param string $ip The ip to lookup. + * @return array The hostnames of the given ip. + * @throws InvalidArgumentException Throws if $ip is not a valid IPv4 or IPv6 address. + */ + public function ipToNames(string $ip): ?array { + $ret = self::filterIp($ip); + if ($ret === false) { + throw new \InvalidArgumentException("'$ip' is not a valid ip address"); + } + + $names = $this->cache->get(self::buildRDnsCacheKey($ret)); + if ($names !== null) { + return $names; + } + + $names = $this->resolver->IpToNames($ret); + if ($names === false) { + $this->cache->set(self::buildRDnsCacheKey($ret), [], self::DNS_CACHE_TIMEOUT); + return []; + } + + // Do we bother with validating the result? + if (!$this->rdns_validate) { + $this->cache->set(self::buildRDnsCacheKey($ret), $names, self::DNS_CACHE_TIMEOUT); + return $names; + } + + // Filter out the names that do not resolve to the given ip. + $acc = []; + foreach ($names as $name) { + // Validate the response. + $resolved_ips = $this->resolver->nameToIps($name); + if ($resolved_ips !== null && \is_array($ret, $resolved_ips)) { + $acc[] = $name; + } + } + + $this->cache->set(self::buildRDnsCacheKey($ret), $acc, self::DNS_CACHE_TIMEOUT); + return $acc; + } +} From 038a2f74d6c14db178f059048ae0dc36757871d8 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 00:49:09 +0200 Subject: [PATCH 06/15] context.php: add DnsDriver --- inc/context.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/inc/context.php b/inc/context.php index 8c7eff9c..4f7eea73 100644 --- a/inc/context.php +++ b/inc/context.php @@ -3,10 +3,12 @@ namespace Vichan; use Vichan\Controller\FloodManager; use Vichan\Data\Driver\{CacheDriver, HttpDriver, ErrorLogLogDriver, FileLogDriver, LogDriver, StderrLogDriver, SyslogLogDriver}; +use Vichan\Data\Driver\Dns\{DnsDriver, HostDnsDriver, LibcDnsDriver}; use Vichan\Data\{FloodQueries, IpNoteQueries, UserPostQueries, ReportQueries}; use Vichan\Service\FilterService; use Vichan\Service\FloodService; use Vichan\Service\HCaptchaQuery; +use Vichan\Service\IpBlacklistService; use Vichan\Service\SecureImageCaptchaQuery; use Vichan\Service\ReCaptchaQuery; use Vichan\Service\YandexCaptchaQuery; @@ -71,6 +73,14 @@ function build_context(array $config): Context { ); }, CacheDriver::class => fn(): CacheDriver => \Cache::getCache(), + DnsDriver::class => function(Context $c) { + $config = $c->get('config'); + if ($config['dns_system']) { + return new HostDnsDriver(2); + } else { + return new LibcDnsDriver(2); + } + }, \PDO::class => function(): \PDO { global $pdo; // Ensure the PDO is initialized. @@ -106,7 +116,7 @@ function build_context(array $config): Context { $c->get(FloodService::class), $c->get(IpNoteQueries::class), $c->get(LogDriver::class) - ), + ) ]); } From 9cfcf6e536dcec9fddb996d60179deeea92e4bdd Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 00:53:04 +0200 Subject: [PATCH 07/15] context.php: add IpBlacklistService --- inc/context.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/inc/context.php b/inc/context.php index 4f7eea73..f2fe23a5 100644 --- a/inc/context.php +++ b/inc/context.php @@ -116,7 +116,17 @@ function build_context(array $config): Context { $c->get(FloodService::class), $c->get(IpNoteQueries::class), $c->get(LogDriver::class) - ) + ), + IpBlacklistService::class => function(Context $c): IpBlacklistService { + $config = $c->get('config'); + return new IpBlacklistService( + $c->get(DnsDriver::class), + $c->get(CacheDriver::class), + $config['dnsbl'], + $config['dnsbl_exceptions'], + $config['fcrdns'] + ); + } ]); } From d210f408dc9c93edbd888428364f1973d8fcbbd8 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 00:58:10 +0200 Subject: [PATCH 08/15] post.php: use IpBlacklistService to check for spam ips via DNS --- post.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/post.php b/post.php index fb927987..1478b733 100644 --- a/post.php +++ b/post.php @@ -8,7 +8,7 @@ require_once 'inc/bootstrap.php'; use Vichan\{Context, WebDependencyFactory}; use Vichan\Data\Driver\{LogDriver, HttpDriver}; use Vichan\Data\ReportQueries; -use Vichan\Service\{RemoteCaptchaQuery, SecureImageCaptchaQuery}; +use Vichan\Service\{IpBlacklistService, RemoteCaptchaQuery, SecureImageCaptchaQuery}; use Vichan\Functions\{Format, IP}; /** @@ -373,7 +373,10 @@ if (isset($_POST['delete'])) { } } - checkDNSBL(); + $blacklist = $context->get(IpBlacklistService::class)->isIpBlacklisted($_SERVER['REMOTE_ADDR']); + if ($blacklist !== false) { + error(\sprintf($config['error']['dnsbl'], $blacklist)); + } // Check if board exists if (!openBoard($_POST['board'])) @@ -468,7 +471,10 @@ if (isset($_POST['delete'])) { } } - checkDNSBL(); + $blacklist = $context->get(IpBlacklistService::class)->isIpBlacklisted($_SERVER['REMOTE_ADDR']); + if ($blacklist !== false) { + error(\sprintf($config['error']['dnsbl'], $blacklist)); + } // Check if board exists if (!openBoard($_POST['board'])) @@ -661,7 +667,10 @@ if (isset($_POST['delete'])) { (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) error($config['error']['referer']); - checkDNSBL(); + $blacklist = $context->get(IpBlacklistService::class)->isIpBlacklisted($_SERVER['REMOTE_ADDR']); + if ($blacklist !== false) { + error(\sprintf($config['error']['dnsbl'], $blacklist)); + } if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { From 456356ad9ac19f58d2966009685ef43b7192ba87 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 01:01:55 +0200 Subject: [PATCH 09/15] pages.php: use DnsDriver --- inc/mod/pages.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index f815ab4b..32c7d0ad 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -6,6 +6,7 @@ use Vichan\Context; use Vichan\Data\{IpNoteQueries, UserPostQueries, ReportQueries}; use Vichan\Functions\{Format, Net}; use Vichan\Data\Driver\{CacheDriver, LogDriver}; +use Vichan\Data\Driver\Dns\DnsDriver; defined('TINYBOARD') or exit; @@ -971,7 +972,11 @@ function mod_user_posts_by_ip(Context $ctx, string $cip, ?string $encoded_cursor } if ($config['mod']['dns_lookup'] && empty($config['ipcrypt_key'])) { - $args['hostname'] = rDNS($ip); + $resolver = $ctx->get(DnsDriver::class); + $names = $resolver->IPToNames($ip); + if (!empty($names)) { + $args['hostname'] = $names[0]; + } } if (hasPermission($config['mod']['view_ban'])) { From 4c29e84527d5b1500d41dbf713fac54b678932dc Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 01:07:09 +0200 Subject: [PATCH 10/15] FilterService.php: use DnsDriver --- inc/Service/FilterService.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/inc/Service/FilterService.php b/inc/Service/FilterService.php index b90abfdd..eb1a35d7 100644 --- a/inc/Service/FilterService.php +++ b/inc/Service/FilterService.php @@ -3,6 +3,7 @@ namespace Vichan\Service; use Throwable; +use Vichan\Data\Driver\Dns\DnsDriver; use Vichan\Data\Driver\LogDriver; /** @@ -24,6 +25,11 @@ class FilterService { */ private LogDriver $logger; + /** + * @var DnsDriver Dns driver for reverse DNS lookup. + */ + private DnsDriver $dns_resolver; + /** * Filter service constructor @@ -31,11 +37,13 @@ class FilterService { * @param array> $filters The config filters. * @param FloodService $floodService The FloodService. * @param LogDriver $logger The LogDriver. + * @param DnsDriver $dns_resolver DnsResolver for hostname matching. */ - public function __construct(array $filters, FloodService $floodService, LogDriver $logger) { + public function __construct(array $filters, FloodService $floodService, LogDriver $logger, DnsDriver $dns_resolver) { $this->filters = $filters; $this->floodService = $floodService; $this->logger = $logger; + $this->dns_resolver = $dns_resolver; } /** @@ -260,13 +268,17 @@ class FilterService { return false; } - $hostname = \rDNS($post['ip']); - if ($hostname === $post['ip']) { - $this->logger->log(LogDriver::WARNING, "RDNS lookup failed for IP: {$post['ip']}"); + $hostnames = $this->dns_resolver->IPToNames($post['ip']); + if ($hostnames === null) { return false; } - return $this->checkRegex($value, $hostname, 'RDNS'); + foreach ($hostnames as $name) { + if ($this->checkRegex($value, $name, 'RDNS')) { + return true; + } + } + return false; case 'agent': $this->validateType($value, 'array', 'Agent condition list'); return $this->matchAgentCondition($value); From 08a5e0d3bf0b21b2c6976416ad25be909ca43503 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 00:59:29 +0200 Subject: [PATCH 11/15] functions.php: remove checkDNSBL and DNS functions --- inc/functions.php | 109 ---------------------------------------------- 1 file changed, 109 deletions(-) diff --git a/inc/functions.php b/inc/functions.php index 52a7ec20..e568874f 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -1767,61 +1767,6 @@ function buildJavascript() { file_write($config['file_script'], $script); } -function checkDNSBL() { - global $config; - - if (isIPv6()) - return; // No IPv6 support yet. - - if (!isset($_SERVER['REMOTE_ADDR'])) - return; // Fix your web server configuration - - if (preg_match("/^(::(ffff:)?)?(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|0\.|255\.)/", $_SERVER['REMOTE_ADDR'])) - return; // It's pointless to check for local IP addresses in dnsbls, isn't it? - - if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions'])) - return; - - $ipaddr = ReverseIPOctets($_SERVER['REMOTE_ADDR']); - - foreach ($config['dnsbl'] as $blacklist) { - if (!is_array($blacklist)) - $blacklist = array($blacklist); - - if (($lookup = str_replace('%', $ipaddr, $blacklist[0])) == $blacklist[0]) - $lookup = $ipaddr . '.' . $blacklist[0]; - - if (!$ip = DNS($lookup)) - continue; // not in list - - $blacklist_name = isset($blacklist[2]) ? $blacklist[2] : $blacklist[0]; - - if (!isset($blacklist[1])) { - // If you're listed at all, you're blocked. - error(sprintf($config['error']['dnsbl'], $blacklist_name)); - } elseif (is_array($blacklist[1])) { - foreach ($blacklist[1] as $octet) { - if ($ip == $octet || $ip == '127.0.0.' . $octet) - error(sprintf($config['error']['dnsbl'], $blacklist_name)); - } - } elseif (is_callable($blacklist[1])) { - if ($blacklist[1]($ip)) - error(sprintf($config['error']['dnsbl'], $blacklist_name)); - } else { - if ($ip == $blacklist[1] || $ip == '127.0.0.' . $blacklist[1]) - error(sprintf($config['error']['dnsbl'], $blacklist_name)); - } - } -} - -function isIPv6() { - return strstr($_SERVER['REMOTE_ADDR'], ':') !== false; -} - -function ReverseIPOctets($ip) { - return implode('.', array_reverse(explode('.', $ip))); -} - function wordfilters(&$body) { global $config; @@ -2512,60 +2457,6 @@ function undoImage(array $post) { } } -function rDNS($ip_addr) { - global $config; - - if ($config['cache']['enabled'] && ($host = cache::get('rdns_' . $ip_addr))) { - return $host; - } - - if (!$config['dns_system']) { - $host = gethostbyaddr($ip_addr); - } else { - $resp = shell_exec_error('host -W 3 ' . $ip_addr); - if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m)) - $host = $m[1]; - else - $host = $ip_addr; - } - - $isip = filter_var($host, FILTER_VALIDATE_IP); - - if ($config['fcrdns'] && !$isip && DNS($host) != $ip_addr) { - $host = $ip_addr; - } - - if ($config['cache']['enabled']) - cache::set('rdns_' . $ip_addr, $host); - - return $host; -} - -function DNS($host) { - global $config; - - if ($config['cache']['enabled'] && ($ip_addr = cache::get('dns_' . $host))) { - return $ip_addr != '?' ? $ip_addr : false; - } - - if (!$config['dns_system']) { - $ip_addr = gethostbyname($host); - if ($ip_addr == $host) - $ip_addr = false; - } else { - $resp = shell_exec_error('host -W 1 ' . $host); - if (preg_match('/has address ([^\s]+)$/', $resp, $m)) - $ip_addr = $m[1]; - else - $ip_addr = false; - } - - if ($config['cache']['enabled']) - cache::set('dns_' . $host, $ip_addr !== false ? $ip_addr : '?'); - - return $ip_addr; -} - function shell_exec_error($command, $suppress_stdout = false) { global $config, $debug; From cb3ac5c4f16ce6a311029d7f11516c565688bfa1 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 01:16:17 +0200 Subject: [PATCH 12/15] context.php: provide DnsDriver to FilterService --- inc/context.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inc/context.php b/inc/context.php index f2fe23a5..92a8cc20 100644 --- a/inc/context.php +++ b/inc/context.php @@ -109,7 +109,8 @@ function build_context(array $config): Context { FilterService::class => fn(Context $c): FilterService => new FilterService( $c->get('config')['filters'], $c->get(FloodService::class), - $c->get(LogDriver::class) + $c->get(LogDriver::class), + $c->get(DnsDriver::class) ), FloodManager::class => fn(Context $c): FloodManager => new FloodManager( $c->get(FilterService::class), From 7ea504c7d372beb3d2110ca12cd9166e2070fbd3 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 01:17:18 +0200 Subject: [PATCH 13/15] post.php: remove obsolete imports --- post.php | 1 - 1 file changed, 1 deletion(-) diff --git a/post.php b/post.php index 1478b733..d636b7a6 100644 --- a/post.php +++ b/post.php @@ -5,7 +5,6 @@ require_once 'inc/bootstrap.php'; -use Vichan\{Context, WebDependencyFactory}; use Vichan\Data\Driver\{LogDriver, HttpDriver}; use Vichan\Data\ReportQueries; use Vichan\Service\{IpBlacklistService, RemoteCaptchaQuery, SecureImageCaptchaQuery}; From d10968a6693dfe7b429fd2d3b41e33d812796c9c Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 14:07:47 +0200 Subject: [PATCH 14/15] page.html: add support for multiple subtitles --- templates/page.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/templates/page.html b/templates/page.html index b6203a61..75cb4939 100644 --- a/templates/page.html +++ b/templates/page.html @@ -16,7 +16,13 @@

{{ title }}

{% if subtitle %} - {{ subtitle }} + {% if subtitle is iterable %} + {% for s in subtitle %} + {{ s }}{% if not loop.last %}
{% endif %} + {% endfor %} + {% else %} + {{ subtitle }} + {% endif %} {% endif %} {% if mod and not hide_dashboard_link %}

{% trans %}Return to dashboard{% endtrans %}

{% endif %}
From 843e31ff916f7d9969db99a64afdc7ad948fabae Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 24 Apr 2025 14:08:25 +0200 Subject: [PATCH 15/15] pages.php: supply multiple DNS names to IP page, if present --- inc/mod/pages.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 32c7d0ad..4c860fe7 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -974,8 +974,13 @@ function mod_user_posts_by_ip(Context $ctx, string $cip, ?string $encoded_cursor if ($config['mod']['dns_lookup'] && empty($config['ipcrypt_key'])) { $resolver = $ctx->get(DnsDriver::class); $names = $resolver->IPToNames($ip); + if (!empty($names)) { - $args['hostname'] = $names[0]; + if (count($names) === 1) { + $args['hostname'] = $names[0]; + } else { + $args['hostname'] = $names; + } } }