Passed
Push — master ( b07a8f...789bb0 )
by Roeland
15:45 queued 10s
created

LocalAddressChecker::ThrowIfLocalIp()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 9
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 14
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2021, Lukas Reschke <[email protected]>
7
 *
8
 * @author Lukas Reschke <[email protected]>
9
 *
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OC\Http\Client;
28
29
use OCP\ILogger;
30
use OCP\Http\Client\LocalServerException;
31
32
class LocalAddressChecker {
33
	/** @var ILogger */
34
	private $logger;
35
36
	public function __construct(ILogger $logger) {
37
		$this->logger = $logger;
38
	}
39
40
	public function ThrowIfLocalIp(string $ip) : void {
41
		if ((bool)filter_var($ip, FILTER_VALIDATE_IP) && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
42
			$this->logger->warning("Host $ip was not connected to because it violates local access rules");
43
			throw new LocalServerException('Host violates local access rules');
44
		}
45
46
		// Also check for IPv6 IPv4 nesting, because that's not covered by filter_var
47
		if ((bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && substr_count($ip, '.') > 0) {
48
			$delimiter = strrpos($ip, ':'); // Get last colon
49
			$ipv4Address = substr($ip, $delimiter + 1);
50
51
			if (!filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
52
				$this->logger->warning("Host $ip was not connected to because it violates local access rules");
53
				throw new LocalServerException('Host violates local access rules');
54
			}
55
		}
56
	}
57
58
	public function ThrowIfLocalAddress(string $uri) : void {
59
		$host = parse_url($uri, PHP_URL_HOST);
60
		if ($host === false || $host === null) {
61
			$this->logger->warning("Could not detect any host in $uri");
62
			throw new LocalServerException('Could not detect any host');
63
		}
64
65
		$host = strtolower($host);
66
		// Remove brackets from IPv6 addresses
67
		if (strpos($host, '[') === 0 && substr($host, -1) === ']') {
68
			$host = substr($host, 1, -1);
69
		}
70
71
		// Disallow localhost and local network
72
		if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') {
73
			$this->logger->warning("Host $host was not connected to because it violates local access rules");
74
			throw new LocalServerException('Host violates local access rules');
75
		}
76
77
		// Disallow hostname only
78
		if (substr_count($host, '.') === 0 && !(bool)filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
79
			$this->logger->warning("Host $host was not connected to because it violates local access rules");
80
			throw new LocalServerException('Host violates local access rules');
81
		}
82
83
		$this->ThrowIfLocalIp($host);
84
	}
85
}
86