diff --git a/rt-config/ip-rewrite.php b/rt-config/ip-rewrite.php new file mode 100644 index 0000000..24723b3 --- /dev/null +++ b/rt-config/ip-rewrite.php @@ -0,0 +1,220 @@ + 1 ) { + return false; + } + + return true; + } + + /** + * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets. + * + * @param string $requestIp IP to check + * @param string|array $ips List of IPs or subnets (can be a string if only a single one) + * + * @return bool Whether the IP is valid + */ + public static function checkIp( $requestIp, $ips ) { + if ( ! is_array( $ips ) ) { + $ips = array( $ips ); + } + + foreach ( $ips as $ip ) { + if ( self::isIpv4( $requestIp ) && self::checkIp4( $requestIp, $ip ) || + ( self::isIpv6( $requestIp ) && self::checkIp6( $requestIp, $ip ) ) ) { + return true; + } + } + + return false; + } + + /** + * Compares two IPv4 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @param string $requestIp IPv4 address to check + * @param string $ip IPv4 address or subnet in CIDR notation + * + * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet. + */ + public static function checkIp4( $requestIp, $ip ) { + if ( false !== strpos( $ip, '/' ) ) { + list( $address, $netmask ) = explode( '/', $ip, 2 ); + + if ( $netmask === '0' ) { + // Ensure IP is valid - using ip2long below implicitly validates, but we need to do it manually here + return filter_var( $address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ); + } + + if ( $netmask < 0 || $netmask > 32 ) { + return false; + } + } else { + $address = $ip; + $netmask = 32; + } + + return 0 === substr_compare( sprintf( '%032b', ip2long( $requestIp ) ), sprintf( '%032b', ip2long( $address ) ), 0, $netmask ); + } + + /** + * Compares two IPv6 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp IPv6 address to check + * @param string $ip IPv6 address or subnet in CIDR notation + * + * @return bool Whether the IP is valid + * + * @throws \RuntimeException When IPV6 support is not enabled + */ + public static function checkIp6( $requestIp, $ip ) { + if ( ! ( ( extension_loaded( 'sockets' ) && defined( 'AF_INET6' ) ) || @inet_pton( '::1' ) ) ) { + throw new \RuntimeException( 'Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".' ); + } + + if ( false !== strpos( $ip, '/' ) ) { + list( $address, $netmask ) = explode( '/', $ip, 2 ); + + if ( $netmask < 1 || $netmask > 128 ) { + return false; + } + } else { + $address = $ip; + $netmask = 128; + } + + $bytesAddr = unpack( 'n*', @inet_pton( $address ) ); + $bytesTest = unpack( 'n*', @inet_pton( $requestIp ) ); + + if ( ! $bytesAddr || ! $bytesTest ) { + return false; + } + + for ( $i = 1, $ceil = ceil( $netmask / 16 ); $i <= $ceil; ++$i ) { + $left = $netmask - 16 * ( $i - 1 ); + $left = ( $left <= 16 ) ? $left : 16; + $mask = ~( 0xffff >> $left ) & 0xffff; + if ( ( $bytesAddr[$i] & $mask ) != ( $bytesTest[ $i ] & $mask ) ) { + return false; + } + } + + return true; + } + +} diff --git a/rt-config/rt-config.php b/rt-config/rt-config.php index 4ebb3ec..adcdaeb 100644 --- a/rt-config/rt-config.php +++ b/rt-config/rt-config.php @@ -14,8 +14,10 @@ // Disable updates for theme and plugin. define( 'DISALLOW_FILE_MODS', true ); +require_once __DIR__ . '/ip-rewrite.php'; + // Set IP from Cloudflare to RealIP in site health. -if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) { +if ( rt_IP_Rewrite::isCloudFlare() ) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; $_SERVER['HTTP_X_REAL_IP'] = $_SERVER['HTTP_CF_CONNECTING_IP']; }