Test Setup Failed
Push — master ( 46ef65...ecf71f )
by Sam
05:21
created

AbstractResolver::extractRdata()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 40
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 12.9492

Importance

Changes 0
Metric Value
cc 12
eloc 35
c 0
b 0
f 0
nc 12
nop 3
dl 0
loc 40
ccs 13
cts 16
cp 0.8125
crap 12.9492
rs 6.9666

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
/*
4
 * This file is part of PHP DNS Server.
5
 *
6
 * (c) Yif Swery <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace yswery\DNS\Resolver;
13
14
use yswery\DNS\ResourceRecord;
15
use yswery\DNS\RecordTypeEnum;
16
use yswery\DNS\UnsupportedTypeException;
17
18
abstract class AbstractResolver implements ResolverInterface
19
{
20
    /**
21
     * @var bool
22
     */
23
    protected $allowRecursion;
24
25
    /**
26
     * @var bool
27
     */
28
    protected $isAuthoritative;
29
30
    /**
31
     * @var bool
32
     */
33
    protected $supportsSaving = false;
34
35
    /**
36
     * @var ResourceRecord[]
37
     */
38
    protected $resourceRecords = [];
39
40
    /**
41
     * Wildcard records are stored as an associative array of labels in reverse. E.g.
42
     * ResourceRecord for "*.example.com." is stored as ['com']['example']['*'][<CLASS>][<TYPE>][].
43
     *
44
     * @var ResourceRecord[]
45
     */
46
    protected $wildcardRecords = [];
47
48 28
    /**
49
     * @param ResourceRecord[] $queries
50 28
     *
51 28
     * @return array
52 27
     */
53 27
    public function getAnswer(array $queries, ?string $client = null): array
54 11
    {
55
        $answers = [];
56
        foreach ($queries as $query) {
57 27
            $answer = $this->resourceRecords[$query->getName()][$query->getType()][$query->getClass()] ?? [];
58
            if (empty($answer)) {
59
                $answer = $this->findWildcardEntry($query);
60 28
            }
61
62
            $answers = array_merge($answers, $answer);
63
        }
64
65
        return $answers;
66 10
    }
67
68 10
    /**
69
     * {@inheritdoc}
70
     */
71
    public function allowsRecursion(): bool
72
    {
73
        return $this->allowRecursion;
74 9
    }
75
76 9
    /**
77
     * {@inheritdoc}
78
     */
79
    public function isAuthority($domain): bool
80
    {
81
        return $this->isAuthoritative;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86 42
     */
87
    public function supportsSaving()
88 42
    {
89 42
        return $this->supportsSaving;
90
    }
91 42
92
    /**
93
     * Determine if a domain is a wildcard domain.
94
     *
95
     * @param string $domain
96
     *
97 42
     * @return bool
98
     */
99 42
    public function isWildcardDomain(string $domain): bool
100 42
    {
101 42
        $domain = rtrim($domain, '.').'.';
102 42
        $pattern = '/^\*\.(?:[a-zA-Z0-9\-\_]+\.)*$/';
103
104 42
        return (bool) preg_match($pattern, $domain);
105
    }
106 42
107
    /**
108
     * @param ResourceRecord[] $resourceRecords
109
     */
110
    protected function addZone(array $resourceRecords): void
111
    {
112
        foreach ($resourceRecords as $resourceRecord) {
113 42
            if ($this->isWildcardDomain($resourceRecord->getName())) {
114
                $this->addWildcardRecord($resourceRecord);
115 42
                continue;
116 42
            }
117
            $this->resourceRecords[$resourceRecord->getName()][$resourceRecord->getType()][$resourceRecord->getClass()][] = $resourceRecord;
118 42
        }
119 42
    }
120 42
121 42
    /**
122 42
     * Add a wildcard ResourceRecord.
123
     *
124
     * @param ResourceRecord $resourceRecord
125 42
     */
126
    protected function addWildcardRecord(ResourceRecord $resourceRecord): void
127 42
    {
128
        $labels = explode('.', rtrim($resourceRecord->getName(), '.'));
129
        $labels = array_reverse($labels);
130
131
        $array = &$this->wildcardRecords;
132
        foreach ($labels as $label) {
133
            if ('*' === $label) {
134 11
                $array[$label][$resourceRecord->getClass()][$resourceRecord->getType()][] = $resourceRecord;
135
                break;
136 11
            }
137 11
138
            $array = &$array[$label];
139
        }
140 11
    }
141 11
142 11
    /**
143 11
     * @param ResourceRecord $query
144 11
     *
145 11
     * @return array
146
     */
147
    protected function findWildcardEntry(ResourceRecord $query): array
148 11
    {
149 4
        $labels = explode('.', rtrim($query->getName(), '.'));
150
        $labels = array_reverse($labels);
151
152
        /** @var ResourceRecord[] $wildcards */
153 11
        $wildcards = [];
154 11
        $array = &$this->wildcardRecords;
155 4
        foreach ($labels as $label) {
156 4
            if (array_key_exists($label, $array)) {
157 4
                $array = &$array[$label];
158
                continue;
159
            }
160 11
161
            if (array_key_exists('*', $array)) {
162
                $wildcards = $array['*'][$query->getClass()][$query->getType()] ?? [];
163
            }
164
        }
165
166
        $answers = [];
167
        foreach ($wildcards as $wildcard) {
168
            $rr = clone $wildcard;
169
            $rr->setName($query->getName());
170
            $answers[] = $rr;
171
        }
172
173
        return $answers;
174
    }
175 42
176
    /**
177 42
     * Add the parent domain to names that are not fully qualified.
178 42
     *
179
     * AbstractResolver::handleName('www', 'example.com.') //Outputs 'www.example.com.'
180
     * AbstractResolver::handleName('@', 'example.com.') //Outputs 'example.com.'
181 42
     * AbstractResolver::handleName('ns1.example.com.', 'example.com.') //Outputs 'ns1.example.com.'
182 42
     *
183
     * @param $name
184
     * @param $parent
185 42
     *
186
     * @return string
187
     */
188
    protected function handleName(string $name, string $parent)
189
    {
190
        if ('@' === $name || '' === $name) {
191
            return $parent;
192
        }
193
194
        if ('.' !== substr($name, -1, 1)) {
195
            return $name.'.'.$parent;
196
        }
197 42
198
        return $name;
199 42
    }
200
201
    /**
202 42
     * @param array  $resourceRecord
203
     * @param int    $type
204
     * @param string $parent
205
     *
206 42
     * @return mixed
207
     *
208
     * @throws UnsupportedTypeException
209 42
     */
210 42
    protected function extractRdata(array $resourceRecord, int $type, string $parent)
211 42
    {
212 42
        switch ($type) {
213 42
            case RecordTypeEnum::TYPE_A:
214 42
            case RecordTypeEnum::TYPE_AAAA:
215 42
                return $resourceRecord['address'];
216
            case RecordTypeEnum::TYPE_NS:
217
            case RecordTypeEnum::TYPE_CNAME:
218
            case RecordTypeEnum::TYPE_PTR:
219 42
                return $this->handleName($resourceRecord['target'], $parent);
220 42
            case RecordTypeEnum::TYPE_SOA:
221
                return [
222
                    'mname' => $this->handleName($resourceRecord['mname'], $parent),
223 23
                    'rname' => $this->handleName($resourceRecord['rname'], $parent),
224
                    'serial' => $resourceRecord['serial'],
225
                    'refresh' => $resourceRecord['refresh'],
226 42
                    'retry' => $resourceRecord['retry'],
227 42
                    'expire' => $resourceRecord['expire'],
228 42
                    'minimum' => $resourceRecord['minimum'],
229 42
                ];
230
            case RecordTypeEnum::TYPE_MX:
231
                return [
232
                    'preference' => $resourceRecord['preference'],
233
                    'exchange' => $this->handleName($resourceRecord['exchange'], $parent),
234
                ];
235
            case RecordTypeEnum::TYPE_TXT:
236
                return $resourceRecord['text'];
237
            case RecordTypeEnum::TYPE_SRV:
238
                return [
239
                    'priority' => (int) $resourceRecord['priority'],
240
                    'weight' => (int) $resourceRecord['weight'],
241
                    'port' => (int) $resourceRecord['port'],
242
                    'target' => $this->handleName($resourceRecord['target'], $parent),
243
                ];
244
            case RecordTypeEnum::TYPE_AXFR:
245
            case RecordTypeEnum::TYPE_ANY:
246
                return '';
247
            default:
248
                throw new UnsupportedTypeException(
249
                    sprintf('Resource Record type "%s" is not a supported type.', RecordTypeEnum::getName($type))
250
                );
251
        }
252
    }
253
}
254