1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author: Viskov Sergey |
4
|
|
|
* @date : 14.04.16 |
5
|
|
|
* @time : 2:33 |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace LTDBeget\dns\record; |
9
|
|
|
|
10
|
|
|
use LTDBeget\ascii\AsciiChar; |
11
|
|
|
use LTDBeget\dns\SyntaxErrorException; |
12
|
|
|
use LTDBeget\stringstream\StringStream; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Class Record |
16
|
|
|
* |
17
|
|
|
* @package LTDBeget\dns\record |
18
|
|
|
*/ |
19
|
|
|
class Record |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var StringStream |
23
|
|
|
*/ |
24
|
|
|
private $stream; |
25
|
|
|
/** |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
private $globalOrigin; |
29
|
|
|
/** |
30
|
|
|
* @var string |
31
|
|
|
*/ |
32
|
|
|
private $globalTtl; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var array |
36
|
|
|
*/ |
37
|
|
|
private $tokens = []; |
38
|
|
|
/** |
39
|
|
|
* @var bool |
40
|
|
|
*/ |
41
|
|
|
private $isFirst; |
42
|
|
|
/** |
43
|
|
|
* @var string |
44
|
|
|
*/ |
45
|
|
|
private $previousName; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Record constructor. |
49
|
|
|
* |
50
|
|
|
* @param StringStream $stream |
51
|
|
|
* @param string|NULL $globalOrigin |
52
|
|
|
* @param string|NULL $globalTtl |
53
|
|
|
* @param bool $isFirst |
54
|
|
|
* @param string $previousName |
55
|
|
|
*/ |
56
|
7 |
|
public function __construct |
57
|
|
|
( |
58
|
|
|
StringStream $stream, |
59
|
|
|
string $globalOrigin = NULL, |
60
|
|
|
string $globalTtl = NULL, |
61
|
|
|
bool $isFirst = false, |
62
|
|
|
string $previousName = NULL |
63
|
|
|
) |
64
|
|
|
{ |
65
|
7 |
|
$this->stream = $stream; |
66
|
7 |
|
$this->globalOrigin = $globalOrigin; |
67
|
7 |
|
$this->globalTtl = $globalTtl; |
68
|
7 |
|
$this->isFirst = $isFirst; |
69
|
7 |
|
$this->previousName = $previousName; |
70
|
7 |
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @return array |
74
|
|
|
*/ |
75
|
7 |
|
public function tokenize() : array |
76
|
|
|
{ |
77
|
7 |
|
if ($this->isRecordClass()) { |
78
|
1 |
View Code Duplication |
if (!empty($this->previousName)) { |
|
|
|
|
79
|
1 |
|
$this->tokens['NAME'] = $this->previousName; |
80
|
|
|
} else { |
81
|
|
|
throw new SyntaxErrorException($this->stream); |
82
|
|
|
} |
83
|
|
|
|
84
|
1 |
View Code Duplication |
if (!empty($this->globalTtl)) { |
|
|
|
|
85
|
1 |
|
$this->tokens['TTL'] = $this->globalTtl; |
86
|
|
|
} else { |
87
|
|
|
throw new SyntaxErrorException($this->stream); |
88
|
|
|
} |
89
|
1 |
|
goto in; |
90
|
|
|
} |
91
|
|
|
|
92
|
7 |
|
$this->defaultExtractor('NAME'); |
93
|
7 |
|
$this->stream->ignoreHorizontalSpace(); |
94
|
|
|
|
95
|
7 |
|
if ($this->isRecordClass()) { |
96
|
3 |
|
if (!empty($this->globalTtl)) { |
97
|
3 |
|
$this->tokens['TTL'] = $this->globalTtl; |
98
|
|
|
} elseif (!empty($this->previousName)) { |
99
|
|
|
$this->tokens['TTL'] = $this->tokens['NAME']; |
100
|
|
|
$this->tokens['NAME'] = $this->previousName; |
101
|
|
|
} else { |
102
|
|
|
throw new SyntaxErrorException($this->stream); |
103
|
|
|
} |
104
|
3 |
|
goto in; |
105
|
|
|
} |
106
|
|
|
|
107
|
5 |
|
$this->defaultExtractor('TTL'); |
108
|
5 |
|
$this->stream->ignoreHorizontalSpace(); |
109
|
|
|
in: |
110
|
7 |
|
$this->extractClass(); |
111
|
|
|
|
112
|
6 |
|
$this->stream->ignoreHorizontalSpace(); |
113
|
6 |
|
$this->extractRData(); |
114
|
|
|
|
115
|
6 |
|
if($this->globalOrigin && substr($this->tokens['NAME'], -1) !== '.' && $this->tokens['NAME'] !== '@') { |
116
|
2 |
|
if($this->globalOrigin === '.') { |
117
|
1 |
|
$this->tokens['NAME'] .= $this->globalOrigin; |
118
|
|
|
} else { |
119
|
2 |
|
$this->tokens['NAME'] .= '.'.$this->globalOrigin; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
6 |
|
return $this->tokens; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @return bool |
128
|
|
|
*/ |
129
|
7 |
|
private function isRecordClass() : bool |
130
|
|
|
{ |
131
|
7 |
|
if ($this->stream->currentAscii()->is(AsciiChar::I_UPPERCASE())) { |
132
|
3 |
|
$this->stream->next(); |
133
|
3 |
|
if ($this->stream->currentAscii()->is(AsciiChar::N_UPPERCASE())) { |
134
|
3 |
|
$this->stream->next(); |
135
|
3 |
|
if ($this->stream->currentAscii()->isHorizontalSpace()) { |
136
|
3 |
|
$this->stream->previous(); |
137
|
3 |
|
$this->stream->previous(); |
138
|
|
|
|
139
|
3 |
|
return true; |
140
|
|
|
} |
141
|
|
|
} else { |
142
|
|
|
$this->stream->previous(); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
146
|
7 |
|
return false; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param string $tokenName |
151
|
|
|
*/ |
152
|
7 |
|
private function defaultExtractor(string $tokenName) |
153
|
|
|
{ |
154
|
7 |
|
if (!array_key_exists($tokenName, $this->tokens)) { |
155
|
7 |
|
$this->tokens[$tokenName] = ''; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
start: |
159
|
7 |
|
$char = $this->stream->currentAscii(); |
160
|
7 |
|
if ($char->isPrintableChar() && !$char->isWhiteSpace()) { |
161
|
7 |
|
$this->tokens[$tokenName] .= $this->stream->current(); |
162
|
7 |
|
$this->stream->next(); |
163
|
7 |
|
goto start; |
164
|
|
|
} |
165
|
7 |
|
} |
166
|
|
|
|
167
|
7 |
|
private function extractClass() |
168
|
|
|
{ |
169
|
7 |
|
if ($this->stream->currentAscii()->is(AsciiChar::I_UPPERCASE)) { |
170
|
6 |
|
$this->stream->next(); |
171
|
6 |
|
if ($this->stream->currentAscii()->is(AsciiChar::N_UPPERCASE)) { |
172
|
6 |
|
$this->stream->next(); |
173
|
|
|
|
174
|
6 |
|
$this->stream->ignoreHorizontalSpace(); |
175
|
6 |
|
$this->defaultExtractor('TYPE'); |
176
|
6 |
|
$this->stream->ignoreHorizontalSpace(); |
177
|
|
|
|
178
|
|
|
} else { |
179
|
6 |
|
throw new SyntaxErrorException($this->stream); |
180
|
|
|
} |
181
|
|
|
} else { |
182
|
2 |
|
if($this->isFirst) { |
183
|
1 |
|
throw new SyntaxErrorException($this->stream); |
184
|
|
|
} else { |
185
|
1 |
|
if(RData::isKnownType($this->tokens['NAME']) && ! RData::isKnownType($this->tokens['TTL'])) { |
186
|
|
|
// no ttl and no origin in record, and in TTL Rdata |
187
|
|
|
last_chance: |
188
|
1 |
|
if($this->previousName && $this->globalTtl) { |
189
|
1 |
|
$this->tokens['TYPE'] = $this->tokens['NAME']; |
190
|
1 |
|
$this->tokens['NAME'] = $this->previousName; |
191
|
1 |
|
$this->tokens['TTL'] = $this->globalTtl; |
192
|
|
|
} else { |
193
|
1 |
|
throw new SyntaxErrorException($this->stream); |
194
|
|
|
} |
195
|
1 |
|
} elseif(!RData::isKnownType($this->tokens['NAME']) && RData::isKnownType($this->tokens['TTL'])) { |
196
|
1 |
|
$this->tokens['TYPE'] = $this->tokens['TTL']; |
197
|
1 |
|
if($this->previousName && ! $this->globalTtl) { |
198
|
|
|
$this->tokens['TTL'] = $this->tokens['NAME']; |
199
|
|
|
$this->tokens['NAME'] = $this->previousName; |
200
|
1 |
|
} elseif(! $this->previousName && $this->globalTtl) { |
201
|
|
|
$this->tokens['TTL'] = $this->globalTtl; |
202
|
1 |
|
} elseif($this->previousName && $this->globalTtl) { |
203
|
1 |
|
$this->tokens['TTL'] = $this->globalTtl; |
204
|
|
|
} else { |
205
|
1 |
|
throw new SyntaxErrorException($this->stream); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
} elseif(RData::isKnownType($this->tokens['NAME']) && RData::isKnownType($this->tokens['TTL'])) { |
209
|
|
|
goto last_chance; |
210
|
|
|
} else { |
211
|
|
|
throw new SyntaxErrorException($this->stream); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
do { |
215
|
1 |
|
$char = $this->stream->currentAscii(); |
216
|
1 |
|
$this->stream->previous(); |
217
|
1 |
|
} while($char->isWhiteSpace()); |
218
|
|
|
|
219
|
|
|
do { |
220
|
1 |
|
$char = $this->stream->currentAscii(); |
221
|
1 |
|
$this->stream->previous(); |
222
|
1 |
|
} while($char->isPrintableChar() && ! $char->isHorizontalSpace()); |
223
|
|
|
} |
224
|
|
|
} |
225
|
6 |
|
} |
226
|
|
|
|
227
|
6 |
|
private function extractRData() |
228
|
|
|
{ |
229
|
6 |
|
$this->tokens['RDATA'] = (new RData($this->stream, $this->tokens['TYPE']))->tokenize(); |
230
|
|
|
} |
231
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.