Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 76 additions & 24 deletions cleantalk.antispam/lib/Cleantalk/Common/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,17 @@ static function ip__mask__long_to_number($long_mask)
*/
static public function ip__validate($ip)
{
if(!$ip) return false; // NULL || FALSE || '' || so on...
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && $ip != '0.0.0.0') return 'v4'; // IPv4
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && self::ip__v6_reduce($ip) != '0::0') return 'v6'; // IPv6
return false; // Unknown
if ( !$ip ) { // NULL || FALSE || '' || so on...
return false;
}
if ( filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && $ip != '0.0.0.0' ) { // IPv4
return 'v4';
}
if ( filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && self::ip__v6_reduce($ip) != '0::0' ) { // IPv6
return 'v6';
}

return false; // Unknown
}

/**
Expand Down Expand Up @@ -428,10 +435,10 @@ static public function ip__v6_normalize($ip)
*/
static public function ip__v6_reduce($ip)
{
if(strpos($ip, ':') !== false){
if (strpos($ip, ':') !== false){
$ip = preg_replace('/:0{1,4}/', ':', $ip);
$ip = preg_replace('/:{2,}/', '::', $ip);
$ip = strpos($ip, '0') === 0 ? substr($ip, 1) : $ip;
$ip = strpos($ip, '0') === 0 && substr($ip, 1) !== false ? substr($ip, 1) : $ip;
}
return $ip;
}
Expand Down Expand Up @@ -466,29 +473,74 @@ static public function ip__resolve__cleantalks($ip)
if(self::ip__validate($ip)){
$url = array_search($ip, self::$cleantalks_servers);
return $url
? $url
? parse_url($url, PHP_URL_HOST)
: self::ip__resolve($ip);
}else
return $ip;
}

/**
* Get URL form IP
*
* @param $ip
*
* @return string
*/
static public function ip__resolve($ip)
{
if(self::ip__validate($ip)){
$url = gethostbyaddr($ip);
if($url)
return $url;
}
return $ip;
}

* Resolve IP to hostname with FCrDNS (Forward-Confirmed reverse DNS) verification.
* Protects against PTR spoofing by verifying the hostname resolves back to the same IP.
*
* @param string $ip IP address to resolve
*
* @return string|false Verified hostname or false on failure
*/
public static function ip__resolve($ip)
{
// Validate IP first
$ip_version = self::ip__validate($ip);
if (!$ip_version) {
return false;
}

// Reverse DNS lookup (PTR record)
$hostname = gethostbyaddr($ip);

// If gethostbyaddr returns the IP itself, it means no PTR record exists
if (!$hostname || $hostname === $ip) {
return false;
}

// Forward DNS lookup - use dns_get_record() to support both IPv4 (A) and IPv6 (AAAA) records
$record_type = ($ip_version === 'v6') ? DNS_AAAA : DNS_A;
$ip_field = ($ip_version === 'v6') ? 'ipv6' : 'ip';

$records = @dns_get_record($hostname, $record_type);

// If forward lookup fails, we can't verify
if (empty($records)) {
return false;
}

// Extract IPs from DNS records
$forward_ips = array();
foreach ($records as $record) {
if (isset($record[$ip_field])) {
$forward_ips[] = $record[$ip_field];
}
}

if (empty($forward_ips)) {
return false;
}

// Check if the original IP is in the list of IPs the hostname resolves to
if ($ip_version === 'v6') {
$normalized_ip = self::ip__v6_normalize($ip);
foreach ($forward_ips as $forward_ip) {
if (self::ip__v6_normalize($forward_ip) === $normalized_ip) {
return $hostname;
}
}
} elseif (in_array($ip, $forward_ips, true)) {
return $hostname;
}

return false;
}

/**
* Resolve DNS to IP
*
Expand Down
32 changes: 25 additions & 7 deletions cleantalk.antispam/lib/Cleantalk/Common/RemoteCalls.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Cleantalk\Common;

use Cleantalk\Common\Variables\Get;
use Cleantalk\Common\Variables\Request;
use Cleantalk\Common\Helper;

abstract class RemoteCalls
{
Expand Down Expand Up @@ -30,19 +32,35 @@ public function __construct( $api_key )
$this->available_rc_actions = $this->getAvailableRcActions();
}

/**
* @param $name
* @return mixed
* @psalm-taint-source input
*/
public static function getVariable($name)
{
return Request::get($name);
}

/**
* Checking if the current request is the Remote Call
*
* @return bool
*/
public static function check()
public static function check()
{
return
Get::get( 'spbc_remote_call_token' ) &&
Get::get( 'spbc_remote_call_action' ) &&
Get::get( 'plugin_name' ) &&
in_array( Get::get( 'plugin_name' ), array( 'antispam','anti-spam', 'apbct' ) );
}
if (static::getVariable('spbc_remote_call_action')) {
static::getVariable('spbc_remote_call_token')
? self::checkWithToken()
: false;
}
return false;
}

public static function checkWithToken()
{
return in_array(static::getVariable('plugin_name'), array('antispam', 'anti-spam', 'apbct'));
}

/**
* Execute corresponding method of RemoteCalls if exists
Expand Down