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

DnsPinMiddleware::dnsResolve()   C

Complexity

Conditions 13
Paths 11

Size

Total Lines 45
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 31
c 1
b 0
f 0
nc 11
nop 2
dl 0
loc 45
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Psr\Http\Message\RequestInterface;
30
31
class DnsPinMiddleware {
32
	/** @var NegativeDnsCache */
33
	private $negativeDnsCache;
34
	/** @var LocalAddressChecker */
35
	private $localAddressChecker;
36
37
	public function __construct(
38
		NegativeDnsCache $negativeDnsCache,
39
		LocalAddressChecker $localAddressChecker
40
	) {
41
		$this->negativeDnsCache = $negativeDnsCache;
42
		$this->localAddressChecker = $localAddressChecker;
43
	}
44
45
	private function dnsResolve(string $target, int $recursionCount) : array {
46
		if ($recursionCount >= 10) {
47
			return [];
48
		}
49
50
		$recursionCount = $recursionCount++;
51
		$targetIps = [];
52
53
		$soaDnsEntry = dns_get_record($target, DNS_SOA);
54
		if (isset($soaDnsEntry[0]) && isset($soaDnsEntry[0]['minimum-ttl'])) {
55
			$dnsNegativeTtl = $soaDnsEntry[0]['minimum-ttl'];
56
		} else {
57
			$dnsNegativeTtl = null;
58
		}
59
60
		$dnsTypes = [DNS_A, DNS_AAAA, DNS_CNAME];
61
		foreach ($dnsTypes as $key => $dnsType) {
62
			if ($this->negativeDnsCache->isNegativeCached($target, $dnsType)) {
63
				unset($dnsTypes[$key]);
64
				continue;
65
			}
66
67
			$dnsResponses = dns_get_record($target, $dnsType);
68
			$canHaveCnameRecord = true;
69
			if (count($dnsResponses) > 0) {
70
				foreach ($dnsResponses as $key => $dnsResponse) {
0 ignored issues
show
Comprehensibility Bug introduced by
$key is overwriting a variable from outer foreach loop.
Loading history...
71
					if (isset($dnsResponse['ip'])) {
72
						$targetIps[] = $dnsResponse['ip'];
73
						$canHaveCnameRecord = false;
74
					} elseif (isset($dnsResponse['ipv6'])) {
75
						$targetIps[] = $dnsResponse['ipv6'];
76
						$canHaveCnameRecord = false;
77
					} elseif (isset($dnsResponse['target']) && $canHaveCnameRecord) {
78
						$targetIps = array_merge($targetIps, $this->dnsResolve($dnsResponse['target'], $recursionCount));
79
						$canHaveCnameRecord = true;
80
					}
81
				}
82
			} else {
83
				if ($dnsNegativeTtl !== null) {
84
					$this->negativeDnsCache->setNegativeCacheForDnsType($target, $dnsType, $dnsNegativeTtl);
85
				}
86
			}
87
		}
88
89
		return $targetIps;
90
	}
91
92
	public function addDnsPinning() {
93
		return function (callable $handler) {
94
			return function (
95
				RequestInterface $request,
96
				array $options
97
			) use ($handler) {
98
				if ($options['nextcloud']['allow_local_address'] === true) {
99
					return $handler($request, $options);
100
				}
101
102
				$hostName = (string)$request->getUri()->getHost();
103
				$port = $request->getUri()->getPort();
104
105
				$ports = [
106
					'80',
107
					'443',
108
				];
109
110
				if ($port !== null) {
111
					$ports[] = (string)$port;
112
				}
113
114
				$targetIps = $this->dnsResolve($hostName, 0);
115
116
				foreach ($targetIps as $ip) {
117
					$this->localAddressChecker->ThrowIfLocalIp($ip);
118
119
					foreach ($ports as $port) {
120
						$curlEntry = $hostName . ':' . $port . ':' . $ip;
121
						$options['curl'][CURLOPT_RESOLVE][] = $curlEntry;
122
					}
123
				}
124
125
				return $handler($request, $options);
126
			};
127
		};
128
	}
129
}
130