forked from GithubBackups/vichan
Merge pull request #933 from Zankaria/dep-inj-dns-2
Dependecy injected DNS
This commit is contained in:
commit
2a9f47105d
21
inc/Data/Driver/Dns/DnsDriver.php
Normal file
21
inc/Data/Driver/Dns/DnsDriver.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
namespace Vichan\Data\Driver\Dns;
|
||||||
|
|
||||||
|
|
||||||
|
interface DnsDriver {
|
||||||
|
/**
|
||||||
|
* Resolve a domain name to 1 or more ips.
|
||||||
|
*
|
||||||
|
* @param string $name Domain name.
|
||||||
|
* @return ?array Returns an array of IPv4 and IPv6 addresses or null on error.
|
||||||
|
*/
|
||||||
|
public function nameToIPs(string $name): ?array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve an ip address to a domain name.
|
||||||
|
*
|
||||||
|
* @param string $ip Ip address.
|
||||||
|
* @return ?array Returns the domain names or null on error.
|
||||||
|
*/
|
||||||
|
public function IPToNames(string $ip): ?array;
|
||||||
|
}
|
43
inc/Data/Driver/Dns/HostDnsDriver.php
Normal file
43
inc/Data/Driver/Dns/HostDnsDriver.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
namespace Vichan\Data\Driver\Dns;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relies on the `host` command line executable.
|
||||||
|
*/
|
||||||
|
class HostDnsDriver implements DnsDriver {
|
||||||
|
private int $timeout;
|
||||||
|
|
||||||
|
private static function matchOrEmpty(string $pattern, string $subject): array {
|
||||||
|
$ret = \preg_match_all($pattern, $subject, $out);
|
||||||
|
if ($ret === false || $ret === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return $out[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(int $timeout) {
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
50
inc/Data/Driver/Dns/LibcDnsDriver.php
Normal file
50
inc/Data/Driver/Dns/LibcDnsDriver.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace Vichan\Data\Driver\Dns;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the love of god never use this implementation if you can.
|
||||||
|
*/
|
||||||
|
class LibcDnsDriver implements DnsDriver {
|
||||||
|
public function __construct(int $timeout) {
|
||||||
|
// Try to impose a very frail timeout https://www.php.net/manual/en/function.gethostbyname.php#118841
|
||||||
|
\putenv("RES_OPTIONS=retrans:1 retry:1 timeout:{$timeout} attempts:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nameToIPs(string $name): ?array {
|
||||||
|
$ret = \dns_get_record($name, DNS_A | DNS_AAAA);
|
||||||
|
if ($ret === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the love of god never use this.
|
||||||
|
* https://www.php.net/manual/en/function.gethostbyaddr.php#57553
|
||||||
|
*/
|
||||||
|
public function IPToNames(string $ip): ?array {
|
||||||
|
$ret = \gethostbyaddr($ip);
|
||||||
|
if ($ret === $ip || $ret === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Case extravaganza: https://www.php.net/manual/en/function.gethostbyaddr.php#123563
|
||||||
|
return [ \strtolower($ret) ];
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace Vichan\Service;
|
namespace Vichan\Service;
|
||||||
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
use Vichan\Data\Driver\Dns\DnsDriver;
|
||||||
use Vichan\Data\Driver\LogDriver;
|
use Vichan\Data\Driver\LogDriver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +25,11 @@ class FilterService {
|
|||||||
*/
|
*/
|
||||||
private LogDriver $logger;
|
private LogDriver $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DnsDriver Dns driver for reverse DNS lookup.
|
||||||
|
*/
|
||||||
|
private DnsDriver $dns_resolver;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter service constructor
|
* Filter service constructor
|
||||||
@ -31,11 +37,13 @@ class FilterService {
|
|||||||
* @param array<int, array<string, mixed>> $filters The config filters.
|
* @param array<int, array<string, mixed>> $filters The config filters.
|
||||||
* @param FloodService $floodService The FloodService.
|
* @param FloodService $floodService The FloodService.
|
||||||
* @param LogDriver $logger The LogDriver.
|
* @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->filters = $filters;
|
||||||
$this->floodService = $floodService;
|
$this->floodService = $floodService;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
$this->dns_resolver = $dns_resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,13 +268,17 @@ class FilterService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hostname = \rDNS($post['ip']);
|
$hostnames = $this->dns_resolver->IPToNames($post['ip']);
|
||||||
if ($hostname === $post['ip']) {
|
if ($hostnames === null) {
|
||||||
$this->logger->log(LogDriver::WARNING, "RDNS lookup failed for IP: {$post['ip']}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->checkRegex($value, $hostname, 'RDNS');
|
foreach ($hostnames as $name) {
|
||||||
|
if ($this->checkRegex($value, $name, 'RDNS')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
case 'agent':
|
case 'agent':
|
||||||
$this->validateType($value, 'array', 'Agent condition list');
|
$this->validateType($value, 'array', 'Agent condition list');
|
||||||
return $this->matchAgentCondition($value);
|
return $this->matchAgentCondition($value);
|
||||||
|
215
inc/Service/IpBlacklistService.php
Normal file
215
inc/Service/IpBlacklistService.php
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
namespace Vichan\Service;
|
||||||
|
|
||||||
|
use Vichan\Data\Driver\CacheDriver;
|
||||||
|
use Vichan\Data\Driver\Dns\DnsDriver;
|
||||||
|
use Lifo\IP\IP;
|
||||||
|
|
||||||
|
|
||||||
|
class IpBlacklistService {
|
||||||
|
private const DNS_CACHE_TIMEOUT = 3600; // 1 hour.
|
||||||
|
|
||||||
|
private DnsDriver $resolver;
|
||||||
|
private CacheDriver $cache;
|
||||||
|
private array $blacklist_providers;
|
||||||
|
private array $exceptions;
|
||||||
|
private bool $rdns_validate;
|
||||||
|
|
||||||
|
|
||||||
|
private static function buildDnsCacheKey(string $host) {
|
||||||
|
return 'dns_queries_dns_' . \strtolower($host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function buildRDnsCacheKey(string $ip) {
|
||||||
|
return "dns_queries_rdns_$ip";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function reverseIpv4Octets(string $ip): ?string {
|
||||||
|
$ret = \filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
|
||||||
|
if ($ret === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return \implode('.', \array_reverse(\explode('.', $ip)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function reverseIpv6Octets(string $ip): ?string {
|
||||||
|
$ret = \filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6);
|
||||||
|
if ($ret === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return \strrev(\implode(".", \str_split(\str_replace(':', '', IP::inet_expand($ip)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the name/host to resolve to discover if an ip is the host via DNS blacklists.
|
||||||
|
*/
|
||||||
|
private static function buildEndpoint(string $host, string $ip) {
|
||||||
|
$replaced = 0;
|
||||||
|
// See inc/config.php for the meaning of '%'.
|
||||||
|
$lookup = \str_replace('%', $ip, $host, $replaced);
|
||||||
|
if ($replaced !== 0) {
|
||||||
|
return $lookup;
|
||||||
|
}
|
||||||
|
return "$ip.$host";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function filterIp(string $str): string|false {
|
||||||
|
return \filter_var($str, \FILTER_VALIDATE_IP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isIpWhitelisted(string $ip): bool {
|
||||||
|
if (\in_array($ip, $this->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;
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,12 @@ namespace Vichan;
|
|||||||
|
|
||||||
use Vichan\Controller\FloodManager;
|
use Vichan\Controller\FloodManager;
|
||||||
use Vichan\Data\Driver\{CacheDriver, HttpDriver, ErrorLogLogDriver, FileLogDriver, LogDriver, StderrLogDriver, SyslogLogDriver};
|
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\Data\{FloodQueries, IpNoteQueries, UserPostQueries, ReportQueries};
|
||||||
use Vichan\Service\FilterService;
|
use Vichan\Service\FilterService;
|
||||||
use Vichan\Service\FloodService;
|
use Vichan\Service\FloodService;
|
||||||
use Vichan\Service\HCaptchaQuery;
|
use Vichan\Service\HCaptchaQuery;
|
||||||
|
use Vichan\Service\IpBlacklistService;
|
||||||
use Vichan\Service\SecureImageCaptchaQuery;
|
use Vichan\Service\SecureImageCaptchaQuery;
|
||||||
use Vichan\Service\ReCaptchaQuery;
|
use Vichan\Service\ReCaptchaQuery;
|
||||||
use Vichan\Service\YandexCaptchaQuery;
|
use Vichan\Service\YandexCaptchaQuery;
|
||||||
@ -71,6 +73,14 @@ function build_context(array $config): Context {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
CacheDriver::class => fn(): CacheDriver => \Cache::getCache(),
|
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 {
|
\PDO::class => function(): \PDO {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
// Ensure the PDO is initialized.
|
// Ensure the PDO is initialized.
|
||||||
@ -99,7 +109,8 @@ function build_context(array $config): Context {
|
|||||||
FilterService::class => fn(Context $c): FilterService => new FilterService(
|
FilterService::class => fn(Context $c): FilterService => new FilterService(
|
||||||
$c->get('config')['filters'],
|
$c->get('config')['filters'],
|
||||||
$c->get(FloodService::class),
|
$c->get(FloodService::class),
|
||||||
$c->get(LogDriver::class)
|
$c->get(LogDriver::class),
|
||||||
|
$c->get(DnsDriver::class)
|
||||||
),
|
),
|
||||||
FloodManager::class => fn(Context $c): FloodManager => new FloodManager(
|
FloodManager::class => fn(Context $c): FloodManager => new FloodManager(
|
||||||
$c->get(FilterService::class),
|
$c->get(FilterService::class),
|
||||||
@ -107,6 +118,16 @@ function build_context(array $config): Context {
|
|||||||
$c->get(IpNoteQueries::class),
|
$c->get(IpNoteQueries::class),
|
||||||
$c->get(LogDriver::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']
|
||||||
|
);
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1767,61 +1767,6 @@ function buildJavascript() {
|
|||||||
file_write($config['file_script'], $script);
|
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) {
|
function wordfilters(&$body) {
|
||||||
global $config;
|
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) {
|
function shell_exec_error($command, $suppress_stdout = false) {
|
||||||
global $config, $debug;
|
global $config, $debug;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use Vichan\Context;
|
|||||||
use Vichan\Data\{IpNoteQueries, UserPostQueries, ReportQueries};
|
use Vichan\Data\{IpNoteQueries, UserPostQueries, ReportQueries};
|
||||||
use Vichan\Functions\{Format, Net};
|
use Vichan\Functions\{Format, Net};
|
||||||
use Vichan\Data\Driver\{CacheDriver, LogDriver};
|
use Vichan\Data\Driver\{CacheDriver, LogDriver};
|
||||||
|
use Vichan\Data\Driver\Dns\DnsDriver;
|
||||||
|
|
||||||
defined('TINYBOARD') or exit;
|
defined('TINYBOARD') or exit;
|
||||||
|
|
||||||
@ -971,7 +972,16 @@ function mod_user_posts_by_ip(Context $ctx, string $cip, ?string $encoded_cursor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($config['mod']['dns_lookup'] && empty($config['ipcrypt_key'])) {
|
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)) {
|
||||||
|
if (count($names) === 1) {
|
||||||
|
$args['hostname'] = $names[0];
|
||||||
|
} else {
|
||||||
|
$args['hostname'] = $names;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPermission($config['mod']['view_ban'])) {
|
if (hasPermission($config['mod']['view_ban'])) {
|
||||||
|
18
post.php
18
post.php
@ -5,10 +5,9 @@
|
|||||||
|
|
||||||
require_once 'inc/bootstrap.php';
|
require_once 'inc/bootstrap.php';
|
||||||
|
|
||||||
use Vichan\{Context, WebDependencyFactory};
|
|
||||||
use Vichan\Data\Driver\{LogDriver, HttpDriver};
|
use Vichan\Data\Driver\{LogDriver, HttpDriver};
|
||||||
use Vichan\Data\ReportQueries;
|
use Vichan\Data\ReportQueries;
|
||||||
use Vichan\Service\{RemoteCaptchaQuery, SecureImageCaptchaQuery};
|
use Vichan\Service\{IpBlacklistService, RemoteCaptchaQuery, SecureImageCaptchaQuery};
|
||||||
use Vichan\Functions\{Format, IP};
|
use Vichan\Functions\{Format, IP};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -373,7 +372,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
|
// Check if board exists
|
||||||
if (!openBoard($_POST['board']))
|
if (!openBoard($_POST['board']))
|
||||||
@ -468,7 +470,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
|
// Check if board exists
|
||||||
if (!openBoard($_POST['board']))
|
if (!openBoard($_POST['board']))
|
||||||
@ -661,7 +666,10 @@ if (isset($_POST['delete'])) {
|
|||||||
(!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER']))))
|
(!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER']))))
|
||||||
error($config['error']['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']) {
|
if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
|
||||||
|
@ -16,7 +16,13 @@
|
|||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<div class="subtitle">
|
<div class="subtitle">
|
||||||
{% if subtitle %}
|
{% if subtitle %}
|
||||||
{{ subtitle }}
|
{% if subtitle is iterable %}
|
||||||
|
{% for s in subtitle %}
|
||||||
|
{{ s }}{% if not loop.last %}<br>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{ subtitle }}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if mod and not hide_dashboard_link %}<p><a href="?/">{% trans %}Return to dashboard{% endtrans %}</a></p>{% endif %}
|
{% if mod and not hide_dashboard_link %}<p><a href="?/">{% trans %}Return to dashboard{% endtrans %}</a></p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user