DHCID   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 4
dl 0
loc 200
ccs 66
cts 66
cp 1
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A setIdentifierType() 0 7 2
A getIdentifierType() 0 4 1
A getIdentifier() 0 4 1
A getHtype() 0 4 1
A setHtype() 0 7 2
A setIdentifier() 0 5 1
A getFqdn() 0 4 1
A setFqdn() 0 7 2
A getDigestType() 0 4 1
A setDigestType() 0 4 1
A getDigest() 0 4 1
A setDigest() 0 4 1
A calculateDigest() 0 14 4
A toText() 0 4 1
A toWire() 0 8 2
A fromText() 0 8 2
A fromWire() 0 13 2
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 1
    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
     * The digest. This is calculated from the other parameters. Stored in raw binary.
75
     *
76
     * @var string
77
     */
78
    private $digest;
79
80 13
    public function setIdentifierType(int $identifierType): void
81
    {
82 13
        if (!Validator::isUnsignedInteger($identifierType, 16)) {
83 1
            throw new \InvalidArgumentException('Identifier type must be a 16-bit integer.');
84
        }
85 12
        $this->identifierType = $identifierType;
86 12
    }
87
88 6
    public function getIdentifierType(): int
89
    {
90 6
        return $this->identifierType;
91
    }
92
93 3
    public function getIdentifier(): string
94
    {
95 3
        return $this->identifier;
96
    }
97
98 1
    public function getHtype(): int
99
    {
100 1
        return $this->htype;
101
    }
102
103 1
    public function setHtype(int $htype): void
104
    {
105 1
        if (!Validator::isUnsignedInteger($htype, 8)) {
106 1
            throw new \InvalidArgumentException('HType must be an 8-bit integer.');
107
        }
108 1
        $this->htype = $htype;
109 1
    }
110
111
    /**
112
     * @throws \InvalidArgumentException
113
     */
114 12
    public function setIdentifier(int $identifierType, string $identifier): void
115
    {
116 12
        $this->setIdentifierType($identifierType);
117 12
        $this->identifier = $identifier;
118 12
    }
119
120 1
    public function getFqdn(): string
121
    {
122 1
        return $this->fqdn;
123
    }
124
125 14
    public function setFqdn(string $fqdn): void
126
    {
127 14
        if (!Validator::fullyQualifiedDomainName($fqdn)) {
128 1
            throw new \InvalidArgumentException(sprintf('"%s" is not a fully qualified domain name.', $fqdn));
129
        }
130 14
        $this->fqdn = $fqdn;
131 14
    }
132
133 6
    public function getDigestType(): int
134
    {
135 6
        return $this->digestType;
136
    }
137
138 6
    public function setDigestType(int $digestType): void
139
    {
140 6
        $this->digestType = $digestType;
141 6
    }
142
143
    /**
144
     * @return string Digest in raw binary
145
     */
146 9
    public function getDigest(): string
147
    {
148 9
        return $this->digest;
149
    }
150
151
    /**
152
     * @param string $digest Digest as raw binary
153
     */
154 9
    public function setDigest(string $digest): void
155
    {
156 9
        $this->digest = $digest;
157 9
    }
158
159
    /**
160
     * Calculate the digest from the identifier and fully qualified domain name already set on the object.
161
     *
162
     * @throws \BadMethodCallException
163
     */
164 13
    public function calculateDigest(): void
165
    {
166 13
        if (null === $this->identifier || null === $this->fqdn) {
167 1
            throw new \BadMethodCallException('Identifier and Fully Qualified Domain Name (FQDN) must both be set on DHCID object before calling calculateDigest().');
168
        }
169
170 12
        $fqdn = Message::encodeName($this->fqdn);
171 12
        $identifier = pack('H*', str_replace(':', '', strtolower($this->identifier)));
172 12
        if (0 === $this->identifierType) {
173 4
            $identifier = chr($this->htype).$identifier;
174
        }
175
176 12
        $this->digest = hash('sha256', $identifier.$fqdn, true);
177 12
    }
178
179
    /**
180
     * @throws \BadMethodCallException
181
     */
182 12
    public function toText(): string
183
    {
184 12
        return base64_encode($this->toWire());
185
    }
186
187 12
    public function toWire(): string
188
    {
189 12
        if (null === $this->digest) {
190 9
            $this->calculateDigest();
191
        }
192
193 12
        return pack('nC', $this->identifierType, $this->digestType).$this->digest;
194
    }
195
196
    /**
197
     * @throws \Exception
198
     */
199 3
    public function fromText(string $text): void
200
    {
201 3
        if (false === $decoded = base64_decode($text, true)) {
202 3
            throw new \Exception(sprintf('Unable to base64 decode text "%s".', $text));
203
        }
204
205 3
        $this->fromWire($decoded);
206 3
    }
207
208 6
    public function fromWire(string $rdata, int &$offset = 0, ?int $rdLength = null): void
209
    {
210 6
        $rdLength = $rdLength ?? strlen($rdata);
211 6
        if (false === $integers = unpack('nIdentifierType/CDigestType', $rdata, $offset)) {
212
            throw new DecodeException(static::TYPE, $rdata);
213 6
        }
214 6
215 6
        $this->setIdentifierType((int) $integers['IdentifierType']);
216
        $this->setDigestType((int) $integers['DigestType']);
217 6
        $this->setDigest(substr($rdata, $offset + 3, $rdLength - 3));
218 6
219
        $offset += $rdLength;
220
    }
221
}
222