Completed
Push — master ( fe5fd9...28190b )
by Sam
07:00 queued 04:19
created

NSEC3   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 84.38%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 25
eloc 77
c 2
b 0
f 0
dl 0
loc 246
ccs 81
cts 96
cp 0.8438
rs 10

20 Methods

Rating   Name   Duplication   Size   Complexity  
A setSalt() 0 6 2
A getIterations() 0 3 1
A base32decode() 0 3 1
A base32encode() 0 3 1
A base32() 0 7 2
A getHashAlgorithm() 0 3 1
A addType() 0 3 1
A getSalt() 0 3 1
A toText() 0 9 1
A toWire() 0 14 1
A setHashAlgorithm() 0 6 2
A clearTypes() 0 3 1
A setUnsignedDelegationsCovered() 0 3 1
A fromWire() 0 21 1
A isUnsignedDelegationsCovered() 0 3 1
A setIterations() 0 6 2
A setNextHashedOwnerName() 0 7 2
A fromText() 0 9 1
A getTypes() 0 3 1
A getNextHashedOwnerName() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Badcow DNS Library.
7
 *
8
 * (c) Samuel Williams <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Badcow\DNS\Rdata;
15
16
use Badcow\DNS\Parser\Tokens;
17
use Badcow\DNS\Validator;
18
19
/**
20
 * {@link https://tools.ietf.org/html/rfc5155}.
21
 */
22
class NSEC3 implements RdataInterface
23
{
24 1
    use RdataTrait;
25
26
    const TYPE = 'NSEC3';
27
    const TYPE_CODE = 50;
28
29
    /**
30
     * @var int
31
     */
32
    private $hashAlgorithm;
33
34
    /**
35
     * @var bool
36
     */
37
    private $unsignedDelegationsCovered = false;
38
39
    /**
40
     * @var int
41
     */
42
    private $iterations;
43
44
    /**
45
     * @var string Binary encoded string
46
     */
47
    private $salt;
48
49
    /**
50
     * @var string Binary encoded hash
51
     */
52
    private $nextHashedOwnerName;
53
54
    /**
55
     * @var array
56
     */
57
    private $types = [];
58
59
    /**
60
     * @var \Base2n
61
     */
62
    private static $base32;
63
64
    /**
65
     * Singleton to instantiate and return \Base2n instance for extended hex.
66
     */
67 4
    private static function base32(): \Base2n
68
    {
69 4
        if (!isset(self::$base32)) {
70 1
            self::$base32 = new \Base2n(5, '0123456789abcdefghijklmnopqrstuv', false, true, true);
71
        }
72
73 4
        return self::$base32;
74
    }
75
76
    public function getHashAlgorithm(): int
77
    {
78
        return $this->hashAlgorithm;
79
    }
80
81
    /**
82
     * @throws \InvalidArgumentException
83
     */
84 4
    public function setHashAlgorithm(int $hashAlgorithm): void
85
    {
86 4
        if (!Validator::isUnsignedInteger($hashAlgorithm, 8)) {
87
            throw new \InvalidArgumentException('Hash algorithm must be 8-bit integer.');
88
        }
89 4
        $this->hashAlgorithm = $hashAlgorithm;
90 4
    }
91
92
    public function isUnsignedDelegationsCovered(): bool
93
    {
94
        return $this->unsignedDelegationsCovered;
95
    }
96
97 4
    public function setUnsignedDelegationsCovered(bool $unsignedDelegationsCovered): void
98
    {
99 4
        $this->unsignedDelegationsCovered = $unsignedDelegationsCovered;
100 4
    }
101
102
    public function getIterations(): int
103
    {
104
        return $this->iterations;
105
    }
106
107
    /**
108
     * @throws \InvalidArgumentException
109
     */
110 4
    public function setIterations(int $iterations): void
111
    {
112 4
        if (!Validator::isUnsignedInteger($iterations, 16)) {
113
            throw new \InvalidArgumentException('Hash algorithm must be 16-bit integer.');
114
        }
115 4
        $this->iterations = $iterations;
116 4
    }
117
118
    /**
119
     * @return string Base16 string
120
     */
121 2
    public function getSalt(): string
122
    {
123 2
        return bin2hex($this->salt);
124
    }
125
126
    /**
127
     * @param string $salt Hexadecimal string
128
     */
129 4
    public function setSalt(string $salt): void
130
    {
131 4
        if (false === $bin = @hex2bin($salt)) {
132
            throw new \InvalidArgumentException('Salt must be a hexadecimal string.');
133
        }
134 4
        $this->salt = $bin;
135 4
    }
136
137
    /**
138
     * @return string Base32 hashed string
139
     */
140 2
    public function getNextHashedOwnerName(): string
141
    {
142 2
        return self::base32encode($this->nextHashedOwnerName);
143
    }
144
145 4
    public function setNextHashedOwnerName(string $nextHashedOwnerName): void
146
    {
147 4
        if (!Validator::isBase32HexEncoded($nextHashedOwnerName)) {
148
            throw new \InvalidArgumentException('Next hashed owner name must be a base32 encoded string.');
149
        }
150
151 4
        $this->nextHashedOwnerName = self::base32decode($nextHashedOwnerName);
152 4
    }
153
154 4
    public function addType(string $type): void
155
    {
156 4
        $this->types[] = $type;
157 4
    }
158
159
    /**
160
     * Clears the types from the RDATA.
161
     */
162
    public function clearTypes(): void
163
    {
164
        $this->types = [];
165
    }
166
167
    public function getTypes(): array
168
    {
169
        return $this->types;
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175 2
    public function toText(): string
176
    {
177 2
        return sprintf('%d %d %d %s %s %s',
178 2
            $this->hashAlgorithm,
179 2
            (int) $this->unsignedDelegationsCovered,
180 2
            $this->iterations,
181 2
            $this->getSalt(),
182 2
            $this->getNextHashedOwnerName(),
183 2
            implode(Tokens::SPACE, $this->types)
184
        );
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     *
190
     * @throws UnsupportedTypeException
191
     */
192 1
    public function toWire(): string
193
    {
194 1
        $wire = pack('CCnC',
195 1
            $this->hashAlgorithm,
196 1
            (int) $this->unsignedDelegationsCovered,
197 1
            $this->iterations,
198 1
            strlen($this->salt)
199
        );
200 1
        $wire .= $this->salt;
201 1
        $wire .= chr(strlen($this->nextHashedOwnerName));
202 1
        $wire .= $this->nextHashedOwnerName;
203 1
        $wire .= NSEC::renderBitmap($this->types);
204
205 1
        return $wire;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 1
    public function fromText(string $text): void
212
    {
213 1
        $rdata = explode(Tokens::SPACE, $text);
214 1
        $this->setHashAlgorithm((int) array_shift($rdata));
215 1
        $this->setUnsignedDelegationsCovered((bool) array_shift($rdata));
216 1
        $this->setIterations((int) array_shift($rdata));
217 1
        $this->setSalt((string) array_shift($rdata));
218 1
        $this->setNextHashedOwnerName((string) array_shift($rdata));
219 1
        array_map([$this, 'addType'], $rdata);
220 1
    }
221
222
    /**
223
     * {@inheritdoc}
224
     *
225
     * @throws UnsupportedTypeException|DecodeException
226
     */
227 1
    public function fromWire(string $rdata, int &$offset = 0, ?int $rdLength = null): void
228
    {
229 1
        $values = unpack('C<hashAlgo>/C<flags>/n<iterations>/C<saltLen>', $rdata, $offset);
230 1
        $offset += 5;
231 1
        $this->setHashAlgorithm((int) $values['<hashAlgo>']);
232 1
        $this->setUnsignedDelegationsCovered((bool) $values['<flags>']);
233 1
        $this->setIterations((int) $values['<iterations>']);
234
235 1
        $saltLen = (int) $values['<saltLen>'];
236 1
        $salt = unpack('H*', substr($rdata, $offset, $saltLen))[1];
237 1
        $this->setSalt($salt);
238 1
        $offset += $saltLen;
239
240 1
        $hashLen = ord(substr($rdata, $offset, 1));
241 1
        ++$offset;
242 1
        $hash = substr($rdata, $offset, $hashLen);
243 1
        $offset += $hashLen;
244 1
        $this->setNextHashedOwnerName(self::base32encode($hash));
245
246 1
        $types = NSEC::parseBitmap($rdata, $offset);
247 1
        array_map([$this, 'addType'], $types);
248 1
    }
249
250
    /**
251
     * Encode data as a base32 string.
252
     *
253
     * @return string base32 string
254
     */
255 3
    public static function base32encode(string $data): string
256
    {
257 3
        return self::base32()->encode($data);
258
    }
259
260
    /**
261
     * Decode a base32 encoded string.
262
     *
263
     * @param string $data base32 string
264
     */
265 4
    public static function base32decode(string $data): string
266
    {
267 4
        return self::base32()->decode($data);
268
    }
269
}
270