GITLAB

Snippets

Sign in
  • Sign in

get_first_wan_ip.php
Add new snippet


#78 by 72f60816e60779e1cff35b00d383cb94?s=40&d=identicon doekia
← discover snippets
get_first_wan_ip.php Buy Me a Coffee at ko-fi.com
raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
<?php

trait get_first_wan_ip {
	/**
	 * Recover the first wild (wan) ip of the client if proxies get traversed
	 * Return the first ip skipping all combination of LAN<->Trusted
	 *
	 * @copyright (c)2017 doekia Enter-Solutions
	 * @licence GPL
	 * If you like it, you can support the author https://ko-fi.com/A153227G 
	 *
	 * @param array $trusted the list of trusted proxies (i.e: the information they passthru is guaranteed not to have be tampered)
	 * @return string the first non trusted ip
	 */

	public function get_first_wan_ip($trusted = array()) {
	
		$wan_ip = $_SERVER['REMOTE_ADDR'];

		// Filter out non routable ips. They are treated separatedly
		foreach($trusted as &$t) {
			if ( filter_var($t, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE|FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) === false ) {
				$t = null;
			}
		}
		unset($t);
		$trusted = array_filter($trusted);
	
		// Always trust the current local ip if this is not already a loopback/lan (alternate ip should be in the trusted list)
		if (filter_var($_SERVER['SERVER_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE|FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) !== false) {
			$trusted[] = $_SERVER['SERVER_ADDR'];
		}

		$ip_fields = array(
			'HTTP_CLIENT_IP',
			'HTTP_X_FORWARDED_FOR',
			'HTTP_X_FORWARDED',
			'HTTP_FORWARDED_FOR',
			'HTTP_FORWARDED',
			'HTTP_X_CLUSTER_CLIENT_IP',
			'HTTP_CF_CONNECTING_IP',
		);
	
		foreach ( $ip_fields as $key ) {
			if ( array_key_exists( $key, $_SERVER ) === true ) {
				$proxy_list = explode( ',', $_SERVER[$key]);
		
				// Reverse the list so we start from the closest proxy
				$proxy_list = array_reverse($proxy_list);
	
				$last = null;
				$lan = false;
				// Check loopback / LAN based forwarder
				if(filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) !== false) {
					// valid ip ?
					if ( filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) === false ) {
						// valid but private ? this is LAN or LOOPBACK, let's trust
						$last = $_SERVER['REMOTE_ADDR'];
						$lan = true;
					}
				}

				foreach ( $proxy_list as $k => &$ip ) {
				
					$ip = trim( $ip );
					if(is_null($last) || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) === false) {
						// There is no trusted last or this is an invalid ip, bailout
						break;
					}
					
					if ($lan && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) === false ) {
						// Seems we are in the LAN still, continue to trust
						$last = $ip;
						continue;
					}
					
					(in_array($last, $trusted) || $lan) &&	$wan_ip = $ip;
					!in_array($ip, $trusted) && $lan = false;
					
					if (in_array($ip, $trusted)) {
						$last = $ip;
					}
					else {
						$last = null;
					}
				}
			}
		}

		return $wan_ip;
	}

}