]> git.mxchange.org Git - friendica.git/blob - src/App/Request.php
c211dfe0e100cbf75d8c1c8f332cff7c29b98ab2
[friendica.git] / src / App / Request.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\App;
23
24 use Friendica\Core\Config\Capability\IManageConfigValues;
25
26 /**
27  * Container for the whole request
28  *
29  * @see https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface
30  *
31  * @todo future container class for whole requests, currently it's not :-)
32  */
33 class Request
34 {
35         /** @var string the default possible headers, which could contain the client IP */
36         const ORDERED_FORWARD_FOR_HEADER = 'HTTP_X_FORWARDED_FOR';
37
38         /** @var string The remote IP address of the current request */
39         protected $remoteAddress;
40
41         /**
42          * @return string The remote IP address of the current request
43          */
44         public function getRemoteAddress(): string
45         {
46                 return $this->remoteAddress;
47         }
48
49         public function __construct(IManageConfigValues $config, array $server = [])
50         {
51                 $this->remoteAddress = $this->determineRemoteAddress($config, $server);
52         }
53
54         /**
55          * Checks if given $remoteAddress matches given $trustedProxy.
56          * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
57          * $remoteAddress is an IPv4 address within that IP range.
58          * Otherwise, $remoteAddress will be compared to $trustedProxy literally and the result
59          * will be returned.
60          *
61          * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
62          */
63         protected function matchesTrustedProxy(string $trustedProxy, string $remoteAddress): bool
64         {
65                 $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
66
67                 if (preg_match($cidrre, $trustedProxy, $match)) {
68                         $net       = $match[1];
69                         $shiftbits = min(32, max(0, 32 - intval($match[2])));
70                         $netnum    = ip2long($net) >> $shiftbits;
71                         $ipnum     = ip2long($remoteAddress) >> $shiftbits;
72
73                         return $ipnum === $netnum;
74                 }
75
76                 return $trustedProxy === $remoteAddress;
77         }
78
79         /**
80          * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
81          * For details regarding what "match" means, refer to `matchesTrustedProxy`.
82          *
83          * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
84          */
85         protected function isTrustedProxy(array $trustedProxies, string $remoteAddress): bool
86         {
87                 foreach ($trustedProxies as $tp) {
88                         if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
89                                 return true;
90                         }
91                 }
92
93                 return false;
94         }
95
96         /**
97          * @param IManageConfigValues $config
98          * @param array               $server
99          *
100          * @return string
101          */
102         protected function determineRemoteAddress(IManageConfigValues $config, array $server): string
103         {
104                 $remoteAddress  = $server['REMOTE_ADDR'] ?? '0.0.0.0';
105                 $trustedProxies = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'trusted_proxies', ''));
106
107                 if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
108                         $forwardedForHeaders = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'forwarded_for_headers')) ?? static::ORDERED_FORWARD_FOR_HEADER;
109
110                         foreach ($forwardedForHeaders as $header) {
111                                 if (isset($server[$header])) {
112                                         foreach (explode(',', $server[$header]) as $IP) {
113                                                 $IP = trim($IP);
114
115                                                 // remove brackets from IPv6 addresses
116                                                 if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
117                                                         $IP = substr($IP, 1, -1);
118                                                 }
119
120                                                 // skip trusted proxies in the list itself
121                                                 if ($this->isTrustedProxy($trustedProxies, $IP)) {
122                                                         continue;
123                                                 }
124
125                                                 if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
126                                                         return $IP;
127                                                 }
128                                         }
129                                 }
130                         }
131                 }
132
133                 return $remoteAddress;
134         }
135 }