= $cip && $cip >= $low ) {
return true;
}
return false;
}
/**
* @param $ip
* @param $first_in_range
* @param $last_in_range
*
* @return bool
*/
private function compare_v6_in_range( $ip, $first_in_range, $last_in_range ) {
$first_in_range = inet_pton( $this->expand_ip_v6( $first_in_range ) );
$last_in_range = inet_pton( $this->expand_ip_v6( $last_in_range ) );
$ip = inet_pton( $this->expand_ip_v6( $ip ) );
if ( ( strlen( $ip ) === strlen( $first_in_range ) )
&& ( $ip >= $first_in_range && $ip <= $last_in_range ) ) {
return true;
} else {
return false;
}
}
/**
* @param $ip
* @param $block
*
* @src http://stackoverflow.com/a/594134
* @return bool
*/
private function compare_cidrv4( $ip, $block ) {
[$subnet, $bits] = explode( '/', $block );
if ( is_null( $bits ) ) {
$bits = 32;
}
$ip = ip2long( $ip );
$subnet = ip2long( $subnet );
$mask = - 1 << ( 32 - $bits );
$subnet &= $mask;// nb: in case the supplied subnet wasn't correctly aligned
return ( $ip & $mask ) == $subnet;// phpcs:ignore
}
/**
* @param $ip
* @param $block
*
* @return bool
*/
private function compare_cidrv6( $ip, $block ) {
$ip = $this->expand_ip_v6( $ip );
$ip = inet_pton( $ip );
$b_ip = $this->ine_to_bits( $ip );
[$subnet, $bits] = explode( '/', $block );
$subnet = $this->expand_ip_v6( $subnet );
$subnet = inet_pton( $subnet );
$b_subnet = $this->ine_to_bits( $subnet );
$ip_net_bits = substr( $b_ip, 0, $bits );
$subnet_bits = substr( $b_subnet, 0, $bits );
return $ip_net_bits === $subnet_bits;
}
/**
* Compare ip2 to ip1, true if ip2>ip1, false if not.
*
* @param $ip1
* @param $ip2
*
* @return bool
*/
public function compare_ip( $ip1, $ip2 ) {
if ( $this->is_v4( $ip1 ) && $this->is_v4( $ip2 ) ) {
if ( sprintf( '%u', ip2long( $ip2 ) ) - sprintf( '%u', ip2long( $ip1 ) ) > 0 ) {
return true;
}
} elseif ( $this->is_v6( $ip1 ) && $this->is_v6( $ip2 ) && $this->is_v6_support() ) {
$ip1 = inet_pton( $this->expand_ip_v6( $ip1 ) );
$ip2 = inet_pton( $this->expand_ip_v6( $ip2 ) );
return $ip2 > $ip1;
}
return false;
}
/**
* @param $ip
* @param $first_in_range
* @param $last_in_range
*
* @return bool
*/
public function compare_in_range( $ip, $first_in_range, $last_in_range ) {
if ( $this->is_v4( $first_in_range ) && $this->is_v4( $last_in_range ) ) {
return $this->compare_v4_in_range( $ip, $first_in_range, $last_in_range );
} elseif ( $this->is_v6( $first_in_range ) && $this->is_v6( $last_in_range ) && $this->is_v6_support() ) {
$this->compare_v6_in_range( $ip, $first_in_range, $last_in_range );
}
return false;
}
public function compare_cidr( $ip, $block ) {
[$subnet, $bits] = explode( '/', $block );
if ( $this->is_v4( $ip ) && $this->is_v4( $subnet ) ) {
return $this->compare_cidrv4( $ip, $block );
} elseif ( $this->is_v6( $ip ) && $this->is_v6( $subnet ) && $this->is_v6_support() ) {
return $this->compare_cidrv6( $ip, $block );
}
return false;
}
/**
* $ip an be single ip, or a range like xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx or CIDR.
* @param $ip
*
* @return bool
*/
public function validate_ip( $ip ) {
if (
! stristr( $ip, '-' )
&& ! stristr( $ip, '/' )
&& filter_var( $ip, FILTER_VALIDATE_IP ) ) {
// Only ip, no '-', '/' symbols.
return true;
} elseif ( stristr( $ip, '-' ) ) {
$ips = explode( '-', $ip );
foreach ( $ips as $ip_key ) {
if ( ! filter_var( $ip_key, FILTER_VALIDATE_IP ) ) {
return false;
}
}
if ( $this->compare_ip( $ips[0], $ips[1] ) ) {
return true;
}
} elseif ( stristr( $ip, '/' ) ) {
[$ip, $bits] = explode( '/', $ip );
if ( filter_var( $ip, FILTER_VALIDATE_IP ) && filter_var( $bits, FILTER_VALIDATE_INT ) ) {
if ( $this->is_v4( $ip ) && 0 <= $bits && $bits <= 32 ) {
return true;
} elseif ( $this->is_v6( $ip ) && 0 <= $bits && $bits <= 128 && $this->is_v6_support() ) {
return true;
}
}
}
return false;
}
/**
* Display a message if IP is non-valid. The cases:
* 1) single IP (no '-', '/' symbols),
* 2) IP range,
* 3) CIDR.
* Ignore cases with private, reserved ranges.
* @src https://en.wikipedia.org/wiki/IPv4#Special-use_addresses
* @src https://en.wikipedia.org/wiki/IPv6#Special-use_addresses
* @param $ip
*
* @return array
*/
public function display_validation_message( $ip ) {
$errors = array();
// Case1: single IP.
if (
! stristr( $ip, '-' )
&& ! stristr( $ip, '/' )
&& ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
$errors[] = sprintf(
/* translators: ... */
__( '%s – invalid format', 'wpdef' ),
$ip
);
// Case2: IP range.
} elseif ( stristr( $ip, '-' ) ) {
$ips = explode( '-', $ip );
foreach ( $ips as $ip_key ) {
if ( ! filter_var( $ip_key, FILTER_VALIDATE_IP ) ) {
$errors[] = sprintf(
/* translators: ... */
__( '%s – invalid format', 'wpdef' ),
$ip_key
);
}
}
if ( ! $this->compare_ip( $ips[0], $ips[1] ) ) {
$errors[] = sprintf(
/* translators: ... */
__( "Can't compare %s with %s.", 'wpdef' ),
$ips[1],
$ips[0]
);
}
// Case3: CIDR.
} elseif ( stristr( $ip, '/' ) ) {
[$ip, $bits] = explode( '/', $ip );
if ( filter_var( $ip, FILTER_VALIDATE_IP ) && filter_var( $bits, FILTER_VALIDATE_INT ) ) {
if ( $this->is_v4( $ip ) && 0 <= $bits && $bits <= 32 ) {
// IPv4 is correct.
} elseif ( $this->is_v6( $ip ) && 0 <= $bits && $bits <= 128 && $this->is_v6_support() ) {
// IPv6 is correct.
} else {
$errors[] = sprintf(
/* translators: ... */
__( '%s – address out of range', 'wpdef' ),
$ip
);
}
} else {
$errors[] = sprintf(
/* translators: ... */
__( '%s – invalid format', 'wpdef' ),
$ip
);
}
}
// @since 2.6.3
return apply_filters( 'wd_display_ip_validations', $errors );
}
/**
* Check if there are any cloudflare headers in the request.
*
* @return bool
*/
private function is_cloudflare_request() {
if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
return true;
}
if ( isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
return true;
}
if ( isset( $_SERVER['HTTP_CF_RAY'] ) ) {
return true;
}
if ( isset( $_SERVER['HTTP_CF_VISITOR'] ) ) {
return true;
}
return false;
}
/**
* We fetch the ip range here.
* https://www.cloudflare.com/ips/
*
* @since 2.5.0 Update the ip range.
*
* @return array
*/
private function cloudflare_ip_ranges() {
return array(
array(
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'172.64.0.0/13',
'131.0.72.0/22',
'104.16.0.0/13',
'104.24.0.0/14',
),
array(
'2400:cb00::/32',
'2606:4700::/32',
'2803:f800::/32',
'2405:b500::/32',
'2405:8100::/32',
'2a06:98c0::/29',
'2c0f:f248::/32',
),
);
}
/**
* Check if the request is from cloudflare. If it is, we get the IP.
*
* @return mixed
*/
private function cloudflare_ip() {
if ( $this->is_cloudflare_request() ) {
// This looks like it come from cloudflare, so this should contain the actual IP,
// and REMOTE_ADDR contains cloudflare IP.
[$cloudflare_ipv4_range, $cloudflare_ipv6_range] = $this->cloudflare_ip_ranges();
$ip_helper = new IP_Helper();
$remote_addr = isset( $_SERVER['REMOTE_ADDR'] ) ?
filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP ) :
false;
$cf_con_ip = isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ?
filter_var( $_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP ) :
false;
if ( $remote_addr === $cf_con_ip ) {
return $cf_con_ip;
} else if ( false === $remote_addr || false === $cf_con_ip ) {
return false;
}
$is_cloudflare_proxy_ip = false;
if ( $this->is_v4( $remote_addr ) ) {
foreach ( $cloudflare_ipv4_range as $cf_ip ) {
if ( $ip_helper->ipv4_in_range( $remote_addr, $cf_ip ) ) {
$is_cloudflare_proxy_ip = true;
break;
}
}
} elseif ( $this->is_v6( $remote_addr ) ) {
foreach ( $cloudflare_ipv6_range as $cf_ip ) {
if ( $ip_helper->ipv6_in_range( $remote_addr, $cf_ip ) ) {
$is_cloudflare_proxy_ip = true;
break;
}
}
}
if ( true === $is_cloudflare_proxy_ip ) {
return $cf_con_ip;
}
}
return false;
}
/**
* Validate IP.
* @param $ip
*
* @return bool
*/
public function check_validate_ip( $ip ) {
// Validate the localhost IP address.
if ( in_array( $ip, array( '127.0.0.1', '::1' ), true ) ) {
return true;
}
$filter_flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
// @since 2.4.7
if ( apply_filters( 'wp_defender_filtered_internal_ip', false ) ) {
// Todo: improve display of IP log when filtering reserved or private IPv4/IPv6 ranges.
$filter_flags = $filter_flags | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
}
if ( false === filter_var( $ip, FILTER_VALIDATE_IP, $filter_flags ) ) {
return false;
}
return true;
}
/**
* Get user IP.
*
* @return string
*/
public function get_user_ip() {
// Check if it's any cloudflare IP.
$cf_ip = $this->cloudflare_ip();
if ( ! empty( $cf_ip ) && filter_var( $cf_ip, FILTER_VALIDATE_IP ) ) {
return apply_filters( 'defender_user_ip', $cf_ip );
}
$headers = array(
'HTTP_CLIENT_IP',
'HTTP_X_REAL_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
);
$ip = '';
foreach ( $headers as $key ) {
if ( array_key_exists( $key, $_SERVER ) && ! empty( $_SERVER[ $key ] ) ) {
$ip_array = explode( ',', $_SERVER[ $key ] );
$tmp_ip = array_shift( $ip_array );
$tmp_ip = trim( $tmp_ip );
if ( $this->check_validate_ip( $tmp_ip ) ) {
$ip = $tmp_ip;
break;
}
}
}
return apply_filters( 'defender_user_ip', $ip );
}
}