ProxyTools.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. /**
  3. * Functions for dealing with proxies
  4. * @file
  5. */
  6. /**
  7. * Extracts the XFF string from the request header
  8. * Checks first for "X-Forwarded-For", then "Client-ip"
  9. * Note: headers are spoofable
  10. * @return string
  11. */
  12. function wfGetForwardedFor() {
  13. if( function_exists( 'apache_request_headers' ) ) {
  14. // More reliable than $_SERVER due to case and -/_ folding
  15. $set = array ();
  16. foreach ( apache_request_headers() as $tempName => $tempValue ) {
  17. $set[ strtoupper( $tempName ) ] = $tempValue;
  18. }
  19. $index = strtoupper ( 'X-Forwarded-For' );
  20. $index2 = strtoupper ( 'Client-ip' );
  21. } else {
  22. // Subject to spoofing with headers like X_Forwarded_For
  23. $set = $_SERVER;
  24. $index = 'HTTP_X_FORWARDED_FOR';
  25. $index2 = 'CLIENT-IP';
  26. }
  27. #Try a couple of headers
  28. if( isset( $set[$index] ) ) {
  29. return $set[$index];
  30. } else if( isset( $set[$index2] ) ) {
  31. return $set[$index2];
  32. } else {
  33. return null;
  34. }
  35. }
  36. /**
  37. * Returns the browser/OS data from the request header
  38. * Note: headers are spoofable
  39. * @return string
  40. */
  41. function wfGetAgent() {
  42. if( function_exists( 'apache_request_headers' ) ) {
  43. // More reliable than $_SERVER due to case and -/_ folding
  44. $set = array ();
  45. foreach ( apache_request_headers() as $tempName => $tempValue ) {
  46. $set[ strtoupper( $tempName ) ] = $tempValue;
  47. }
  48. $index = strtoupper ( 'User-Agent' );
  49. } else {
  50. // Subject to spoofing with headers like X_Forwarded_For
  51. $set = $_SERVER;
  52. $index = 'HTTP_USER_AGENT';
  53. }
  54. if( isset( $set[$index] ) ) {
  55. return $set[$index];
  56. } else {
  57. return '';
  58. }
  59. }
  60. /**
  61. * Work out the IP address based on various globals
  62. * For trusted proxies, use the XFF client IP (first of the chain)
  63. * @return string
  64. */
  65. function wfGetIP() {
  66. global $wgIP, $wgUsePrivateIPs;
  67. # Return cached result
  68. if ( !empty( $wgIP ) ) {
  69. return $wgIP;
  70. }
  71. /* collect the originating ips */
  72. # Client connecting to this webserver
  73. if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
  74. $ipchain = array( IP::canonicalize( $_SERVER['REMOTE_ADDR'] ) );
  75. } else {
  76. # Running on CLI?
  77. $ipchain = array( '127.0.0.1' );
  78. }
  79. $ip = $ipchain[0];
  80. # Append XFF on to $ipchain
  81. $forwardedFor = wfGetForwardedFor();
  82. if ( isset( $forwardedFor ) ) {
  83. $xff = array_map( 'trim', explode( ',', $forwardedFor ) );
  84. $xff = array_reverse( $xff );
  85. $ipchain = array_merge( $ipchain, $xff );
  86. }
  87. # Step through XFF list and find the last address in the list which is a trusted server
  88. # Set $ip to the IP address given by that trusted server, unless the address is not sensible (e.g. private)
  89. foreach ( $ipchain as $i => $curIP ) {
  90. $curIP = IP::canonicalize( $curIP );
  91. if ( wfIsTrustedProxy( $curIP ) ) {
  92. if ( isset( $ipchain[$i + 1] ) ) {
  93. if( $wgUsePrivateIPs || IP::isPublic( $ipchain[$i + 1 ] ) ) {
  94. $ip = $ipchain[$i + 1];
  95. }
  96. }
  97. } else {
  98. break;
  99. }
  100. }
  101. wfDebug( "IP: $ip\n" );
  102. $wgIP = $ip;
  103. return $ip;
  104. }
  105. /**
  106. * Checks if an IP is a trusted proxy providor
  107. * Useful to tell if X-Fowarded-For data is possibly bogus
  108. * Squid cache servers for the site and AOL are whitelisted
  109. * @param string $ip
  110. * @return bool
  111. */
  112. function wfIsTrustedProxy( $ip ) {
  113. global $wgSquidServers, $wgSquidServersNoPurge;
  114. if ( in_array( $ip, $wgSquidServers ) ||
  115. in_array( $ip, $wgSquidServersNoPurge )
  116. ) {
  117. $trusted = true;
  118. } else {
  119. $trusted = false;
  120. }
  121. wfRunHooks( 'IsTrustedProxy', array( &$ip, &$trusted ) );
  122. return $trusted;
  123. }
  124. /**
  125. * Forks processes to scan the originating IP for an open proxy server
  126. * MemCached can be used to skip IPs that have already been scanned
  127. */
  128. function wfProxyCheck() {
  129. global $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath;
  130. global $wgMemc, $wgProxyMemcExpiry;
  131. global $wgProxyKey;
  132. if ( !$wgBlockOpenProxies ) {
  133. return;
  134. }
  135. $ip = wfGetIP();
  136. # Get MemCached key
  137. $mcKey = wfMemcKey( 'proxy', 'ip', $ip );
  138. $mcValue = $wgMemc->get( $mcKey );
  139. $skip = (bool)$mcValue;
  140. # Fork the processes
  141. if ( !$skip ) {
  142. $title = SpecialPage::getTitleFor( 'Blockme' );
  143. $iphash = md5( $ip . $wgProxyKey );
  144. $url = $title->getFullURL( 'ip='.$iphash );
  145. foreach ( $wgProxyPorts as $port ) {
  146. $params = implode( ' ', array(
  147. escapeshellarg( $wgProxyScriptPath ),
  148. escapeshellarg( $ip ),
  149. escapeshellarg( $port ),
  150. escapeshellarg( $url )
  151. ));
  152. exec( "php $params &>/dev/null &" );
  153. }
  154. # Set MemCached key
  155. $wgMemc->set( $mcKey, 1, $wgProxyMemcExpiry );
  156. }
  157. }
  158. /**
  159. * Convert a network specification in CIDR notation to an integer network and a number of bits
  160. * @return array(string, int)
  161. */
  162. function wfParseCIDR( $range ) {
  163. return IP::parseCIDR( $range );
  164. }
  165. /**
  166. * Check if an IP address is in the local proxy list
  167. * @return bool
  168. */
  169. function wfIsLocallyBlockedProxy( $ip ) {
  170. global $wgProxyList;
  171. $fname = 'wfIsLocallyBlockedProxy';
  172. if ( !$wgProxyList ) {
  173. return false;
  174. }
  175. wfProfileIn( $fname );
  176. if ( !is_array( $wgProxyList ) ) {
  177. # Load from the specified file
  178. $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
  179. }
  180. if ( !is_array( $wgProxyList ) ) {
  181. $ret = false;
  182. } elseif ( array_search( $ip, $wgProxyList ) !== false ) {
  183. $ret = true;
  184. } elseif ( array_key_exists( $ip, $wgProxyList ) ) {
  185. # Old-style flipped proxy list
  186. $ret = true;
  187. } else {
  188. $ret = false;
  189. }
  190. wfProfileOut( $fname );
  191. return $ret;
  192. }