Completed
Branch Message (fe4ba6)
by Sam
13:54
created

NSEC::parseBitmap()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5.0061

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 25
ccs 15
cts 16
cp 0.9375
crap 5.0061
rs 9.4555
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
18
class NSEC implements RdataInterface
19
{
20
    use RdataTrait;
21
22
    const TYPE = 'NSEC';
23
    const TYPE_CODE = 47;
24
25
    /**
26
     * The Next Domain field contains the next owner name (in the canonical
27
     * ordering of the zone) that has authoritative data or contains a
28
     * delegation point NS RR set.
29
     * {@link https://tools.ietf.org/html/rfc4034#section-4.1.1}.
30
     *
31
     * @var string
32
     */
33
    private $nextDomainName;
34
35
    /**
36
     * @var array
37
     */
38
    private $types = [];
39
40
    /**
41
     * @return string
42
     */
43 2
    public function getNextDomainName(): string
44
    {
45 2
        return $this->nextDomainName;
46
    }
47
48
    /**
49
     * @param string $nextDomainName
50
     */
51 4
    public function setNextDomainName(string $nextDomainName): void
52
    {
53 4
        $this->nextDomainName = $nextDomainName;
54 4
    }
55
56
    /**
57
     * @param string $type
58
     */
59 5
    public function addType(string $type): void
60
    {
61 5
        $this->types[] = $type;
62 5
    }
63
64
    /**
65
     * Clears the types from the RDATA.
66
     */
67 1
    public function clearTypes(): void
68
    {
69 1
        $this->types = [];
70 1
    }
71
72
    /**
73
     * @return array
74
     */
75 3
    public function getTypes(): array
76
    {
77 3
        return $this->types;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 2
    public function toText(): string
84
    {
85 2
        return sprintf(
86 2
            '%s %s',
87 2
            $this->nextDomainName,
88 2
            implode(' ', $this->types)
89
        );
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     *
95
     * @throws UnsupportedTypeException
96
     */
97 1
    public function toWire(): string
98
    {
99 1
        return self::encodeName($this->nextDomainName).self::renderBitmap($this->types);
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 2
    public static function fromText(string $text): RdataInterface
106
    {
107 2
        $iterator = new \ArrayIterator(explode(Tokens::SPACE, $text));
108 2
        $nsec = new self();
109 2
        $nsec->setNextDomainName($iterator->current());
110 2
        $iterator->next();
111 2
        while ($iterator->valid()) {
112 2
            $nsec->addType($iterator->current());
113 2
            $iterator->next();
114
        }
115
116 2
        return $nsec;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     *
122
     * @throws UnsupportedTypeException
123
     */
124 1
    public static function fromWire(string $rdata, int &$offset = 0, ?int $rdLength = null): RdataInterface
125
    {
126 1
        $nsec = new self();
127 1
        $nsec->setNextDomainName(self::decodeName($rdata, $offset));
128 1
        $types = self::parseBitmap($rdata, $offset);
129 1
        array_map([$nsec, 'addType'], $types);
130
131 1
        return $nsec;
132
    }
133
134
    /**
135
     * @param string $rdata
136
     * @param int    $offset
137
     *
138
     * @return string[]
139
     *
140
     * @throws UnsupportedTypeException
141
     * @throws DecodeException
142
     */
143 3
    public static function parseBitmap(string $rdata, int &$offset): array
144
    {
145 3
        $bytes = unpack('C*', $rdata, $offset);
146
147 3
        if (!is_array($bytes)) {
148
            throw new DecodeException(static::TYPE, $rdata);
149
        }
150
151 3
        $types = [];
152
153 3
        while (count($bytes) > 0) {
154 3
            $mask = '';
155 3
            $window = array_shift($bytes);
156 3
            $len = array_shift($bytes);
157 3
            for ($i = 0; $i < $len; ++$i) {
158 3
                $mask .= str_pad(decbin(array_shift($bytes)), 8, '0', STR_PAD_LEFT);
159
            }
160 3
            $offset = 0;
161 3
            while (false !== $pos = strpos($mask, '1', $offset)) {
162 3
                $types[] = Types::getName((int) $window * 256 + $pos);
163 3
                $offset = $pos + 1;
164
            }
165
        }
166
167 3
        return $types;
168
    }
169
170
    /**
171
     * @param string[] $types
172
     *
173
     * @return string
174
     *
175
     * @throws UnsupportedTypeException
176
     */
177 3
    public static function renderBitmap(array $types): string
178
    {
179 3
        $blocks = [];
180
181 3
        foreach ($types as $type) {
182 3
            $int = Types::getTypeCode($type);
183 3
            $window = $int >> 8;
184 3
            $int = $int & 0b11111111;
185 3
            $mod = $int % 8;
186 3
            $mask = $blocks[$window] ?? str_repeat("\0", 32);
187 3
            $byteNum = ($int - $mod) / 8;
188 3
            $byte = ord($mask[$byteNum]) | (128 >> $mod);
189 3
            $mask[$byteNum] = chr($byte);
190 3
            $blocks[$window] = $mask;
191
        }
192
193 3
        $encoded = '';
194 3
        foreach ($blocks as $n => $mask) {
195 3
            $mask = rtrim($mask, "\0");
196 3
            $encoded .= chr($n).chr(strlen($mask)).$mask;
197
        }
198
199 3
        return $encoded;
200
    }
201
}
202