Passed
Push — master ( 1b54b9...ff455f )
by Sam
10:10 queued 11s
created

DHCID   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 49
dl 0
loc 225
ccs 62
cts 62
cp 1
rs 10
c 1
b 0
f 0
wmc 24

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getDigest() 0 3 1
A setFqdn() 0 6 2
A getIdentifierType() 0 3 1
A getFqdn() 0 3 1
A getHtype() 0 3 1
A setIdentifier() 0 4 1
A setIdentifierType() 0 6 2
A fromText() 0 9 2
A setHtype() 0 6 2
A getDigestType() 0 3 1
A getIdentifier() 0 3 1
A setDigest() 0 3 1
A toText() 0 3 1
A toWire() 0 7 2
A fromWire() 0 7 1
A calculateDigest() 0 13 4
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\Message;
17
use Badcow\DNS\Validator;
18
19
/**
20
 * {@link https://tools.ietf.org/html/rfc4701}.
21
 */
22
class DHCID implements RdataInterface
23
{
24
    use RdataTrait;
25
26
    const TYPE = 'DHCID';
27
    const TYPE_CODE = 49;
28
29
    /**
30
     * 16-bit DHCID RR Identifier Type Code specifies what data from the DHCP
31
     * client's request was used as input into the hash function.
32
     *
33
     * @var int
34
     */
35
    private $identifierType;
36
37
    /**
38
     * The 1-octet 'htype' followed by 'hlen' octets of 'chaddr' from a DHCPv4
39
     * client's DHCPREQUEST.
40
     *
41
     * The data octets (i.e., the Type and Client-Identifier fields) from a
42
     * DHCPv4 client's Client Identifier option.
43
     *
44
     * The client's DUID (i.e., the data octets of a DHCPv6 client's Client
45
     * Identifier option or the DUID field from a DHCPv4 client's Client
46
     * Identifier option).
47
     *
48
     * @var string
49
     */
50
    private $identifier;
51
52
    /**
53
     * Hardware Type {@link https://tools.ietf.org/html/rfc2131}.
54
     *
55
     * @var int Hardware type used if identifier is DHCPv4 DHCPREQUEST carrying client hardware address (chaddr or MAC)
56
     */
57
    private $htype = 1;
58
59
    /**
60
     * The Fully Qualified Domain Name of the DHCP client.
61
     *
62
     * @var string
63
     */
64
    private $fqdn;
65
66
    /**
67
     * The digest type. Only one type is defined by IANA, SHA256 with value 1.
68
     *
69
     * @var int
70
     */
71
    private $digestType = 1;
72
73
    /**
74
     * @var string
75
     */
76
    private $digest;
77
78
    /**
79
     * @param int $identifierType
80
     */
81 13
    public function setIdentifierType(int $identifierType): void
82
    {
83 13
        if (!Validator::isUnsignedInteger($identifierType, 16)) {
84 1
            throw new \InvalidArgumentException('Identifier type must be a 16-bit integer.');
85
        }
86 12
        $this->identifierType = $identifierType;
87 12
    }
88
89
    /**
90
     * @return int
91
     */
92 6
    public function getIdentifierType(): int
93
    {
94 6
        return $this->identifierType;
95
    }
96
97
    /**
98
     * @return string
99
     */
100 3
    public function getIdentifier(): string
101
    {
102 3
        return $this->identifier;
103
    }
104
105
    /**
106
     * @return int
107
     */
108 1
    public function getHtype(): int
109
    {
110 1
        return $this->htype;
111
    }
112
113
    /**
114
     * @param int $htype
115
     */
116 1
    public function setHtype(int $htype): void
117
    {
118 1
        if (!Validator::isUnsignedInteger($htype, 8)) {
119 1
            throw new \InvalidArgumentException('HType must be an 8-bit integer.');
120
        }
121 1
        $this->htype = $htype;
122 1
    }
123
124
    /**
125
     * @param int    $identifierType
126
     * @param string $identifier
127
     *
128
     * @throws \InvalidArgumentException
129
     */
130 12
    public function setIdentifier(int $identifierType, string $identifier): void
131
    {
132 12
        $this->setIdentifierType($identifierType);
133 12
        $this->identifier = $identifier;
134 12
    }
135
136
    /**
137
     * @return string
138
     */
139 1
    public function getFqdn(): string
140
    {
141 1
        return $this->fqdn;
142
    }
143
144
    /**
145
     * @param string $fqdn
146
     */
147 14
    public function setFqdn(string $fqdn): void
148
    {
149 14
        if (!Validator::fullyQualifiedDomainName($fqdn)) {
150 1
            throw new \InvalidArgumentException(sprintf('"%s" is not a fully qualified domain name.', $fqdn));
151
        }
152 14
        $this->fqdn = $fqdn;
153 14
    }
154
155
    /**
156
     * @return int
157
     */
158 6
    public function getDigestType(): int
159
    {
160 6
        return $this->digestType;
161
    }
162
163
    /**
164
     * @return string Digest as hexadecimal string
165
     */
166 9
    public function getDigest(): string
167
    {
168 9
        return $this->digest;
169
    }
170
171
    /**
172
     * @param string $digest Digest as hexadecimal string
173
     */
174 9
    public function setDigest(string $digest): void
175
    {
176 9
        $this->digest = $digest;
177 9
    }
178
179
    /**
180
     * Calculate the digest from the identifier and fully qualified domain name already set on the object.
181
     *
182
     * @throws \BadMethodCallException
183
     */
184 13
    public function calculateDigest(): void
185
    {
186 13
        if (null === $this->identifier || null === $this->fqdn) {
187 1
            throw new \BadMethodCallException('Identifier and Fully Qualified Domain Name (FQDN) must both be set on DHCID object before calling calculateDigest().');
188
        }
189
190 12
        $fqdn = Message::encodeName($this->fqdn);
191 12
        $identifier = pack('H*', str_replace(':', '', strtolower($this->identifier)));
192 12
        if (0 === $this->identifierType) {
193 4
            $identifier = chr($this->htype).$identifier;
194
        }
195
196 12
        $this->digest = hash('sha256', $identifier.$fqdn);
197 12
    }
198
199
    /**
200
     * {@inheritdoc}
201
     *
202
     * @throws \BadMethodCallException
203
     */
204 12
    public function toText(): string
205
    {
206 12
        return base64_encode($this->toWire());
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212 12
    public function toWire(): string
213
    {
214 12
        if (null === $this->digest) {
215 9
            $this->calculateDigest();
216
        }
217
218 12
        return pack('nCH*', $this->identifierType, $this->digestType, $this->digest);
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     *
224
     * @throws \Exception
225
     */
226 3
    public function fromText(string $text): void
227
    {
228 3
        if (false === $decoded = base64_decode($text, true)) {
229 3
            throw new \Exception(sprintf('Unable to base64 decode text "%s".', $text));
230
        }
231
232 3
        $rdata = unpack('nIdentifierType/CDigestType/H*Digest', $decoded);
233 3
        $this->setIdentifierType((int) $rdata['IdentifierType']);
234 3
        $this->setDigest((string) $rdata['Digest']);
235 3
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 3
    public function fromWire(string $rdata, int &$offset = 0, ?int $rdLength = null): void
241
    {
242 3
        $rdLength = $rdLength ?? strlen($rdata);
243 3
        $rdata = unpack('nIdentifierType/CDigestType/H*Digest', substr($rdata, $offset, $rdLength));
244 3
        $this->setIdentifierType((int) $rdata['IdentifierType']);
245 3
        $this->setDigest((string) $rdata['Digest']);
246 3
        $offset += $rdLength;
247 3
    }
248
}
249