Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 31 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
CheckUserUtilityService | |
0.00% |
0 / 30 |
|
0.00% |
0 / 2 |
182 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getClientIPfromXFF | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
156 |
1 | <?php |
2 | |
3 | namespace MediaWiki\CheckUser\Services; |
4 | |
5 | use MediaWiki\Request\ProxyLookup; |
6 | use MediaWiki\Request\WebRequest; |
7 | use Wikimedia\IPUtils; |
8 | |
9 | class CheckUserUtilityService { |
10 | |
11 | private ProxyLookup $proxyLookup; |
12 | |
13 | private bool $usePrivateIPs; |
14 | |
15 | /** |
16 | * @param ProxyLookup $proxyLookup |
17 | * @param bool $usePrivateIPs |
18 | */ |
19 | public function __construct( ProxyLookup $proxyLookup, bool $usePrivateIPs ) { |
20 | $this->proxyLookup = $proxyLookup; |
21 | $this->usePrivateIPs = $usePrivateIPs; |
22 | } |
23 | |
24 | /** |
25 | * Locates the client IP within a given XFF string. |
26 | * Unlike the XFF checking to determine a user IP in WebRequest, |
27 | * this simply follows the chain and does not account for server trust. |
28 | * |
29 | * This returns an array containing: |
30 | * - The best guess of the client IP |
31 | * - Whether all the proxies are just squid/varnish |
32 | * - The XFF value, converted to a empty string if false |
33 | * |
34 | * @param string|bool $xff XFF header value |
35 | * @return array (string|null, bool, string) |
36 | */ |
37 | public function getClientIPfromXFF( $xff ) { |
38 | if ( $xff === false || !strlen( $xff ) ) { |
39 | // If the XFF is empty or not a string return with a |
40 | // XFF of the empty string and no results |
41 | return [ null, false, '' ]; |
42 | } |
43 | |
44 | # Get the list in the form of <PROXY N, ... PROXY 1, CLIENT> |
45 | $ipchain = array_map( 'trim', explode( ',', $xff ) ); |
46 | $ipchain = array_reverse( $ipchain ); |
47 | |
48 | // best guess of the client IP |
49 | $client = null; |
50 | |
51 | // all proxy servers where site Squid/Varnish servers? |
52 | $isSquidOnly = false; |
53 | # Step through XFF list and find the last address in the list which is a |
54 | # sensible proxy server. Set $ip to the IP address given by that proxy server, |
55 | # unless the address is not sensible (e.g. private). However, prefer private |
56 | # IP addresses over proxy servers controlled by this site (more sensible). |
57 | foreach ( $ipchain as $i => $curIP ) { |
58 | $curIP = IPUtils::canonicalize( |
59 | WebRequest::canonicalizeIPv6LoopbackAddress( $curIP ) |
60 | ); |
61 | if ( $curIP === null ) { |
62 | // not a valid IP address |
63 | break; |
64 | } |
65 | $curIsSquid = $this->proxyLookup->isConfiguredProxy( $curIP ); |
66 | if ( $client === null ) { |
67 | $client = $curIP; |
68 | $isSquidOnly = $curIsSquid; |
69 | } |
70 | if ( |
71 | isset( $ipchain[$i + 1] ) && |
72 | IPUtils::isIPAddress( $ipchain[$i + 1] ) && |
73 | ( |
74 | IPUtils::isPublic( $ipchain[$i + 1] ) || |
75 | $this->usePrivateIPs || |
76 | // T50919 |
77 | $curIsSquid |
78 | ) |
79 | ) { |
80 | $client = IPUtils::canonicalize( |
81 | WebRequest::canonicalizeIPv6LoopbackAddress( $ipchain[$i + 1] ) |
82 | ); |
83 | $isSquidOnly = ( $isSquidOnly && $curIsSquid ); |
84 | continue; |
85 | } |
86 | break; |
87 | } |
88 | |
89 | return [ $client, $isSquidOnly, $xff ]; |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * Retain the old namespace for backwards compatibility. |
95 | * @deprecated since 1.41 |
96 | */ |
97 | class_alias( CheckUserUtilityService::class, 'MediaWiki\CheckUser\CheckUserUtilityService' ); |