Completed
Push — master ( 51beac...801282 )
by Sam
03:19
created

DHCID::setDigestType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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