MythTV master
ParseBinary.cpp
Go to the documentation of this file.
1/* ParseBinary.cpp
2
3 Copyright (C) David C. J. Matthews 2004 dm at prolingua.co.uk
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19
20*/
21
22/*
23Parser for ASN1 binary notation. Does minimal syntax checking, assuming that this will already have
24been done before the binary was produced. Creates a MHParseNode tree structure.
25*/
26#include <cstdlib> // malloc etc.
27
28#include "Engine.h"
29#include "ParseBinary.h"
30#include "ASN1Codes.h"
31#include "ParseNode.h"
32#include "BaseClasses.h"
33#include "Root.h"
34#include "Groups.h"
35#include "Logging.h"
36
37static constexpr int INDEFINITE_LENGTH { -1 };
38
39// Get the next byte. In most all cases it's an error if we reach end-of-file
40// and we throw an exception.
42{
43 if (m_p >= m_data.size())
44 {
45 MHERROR("Unexpected end of file");
46 }
47
48 return m_data[m_p++];
49}
50
51
52// Parse a string argument. ASN1 strings can include nulls as valid characters.
54{
55 // TODO: Don't deal with indefinite length at the moment.
56 if (endStr == INDEFINITE_LENGTH)
57 {
58 MHERROR("Indefinite length strings are not implemented");
59 }
60
61 int nLength = endStr - m_p;
62 std::vector<uint8_t> stringValue;
63 stringValue.reserve(nLength + 1);
64
65 while (m_p < endStr)
66 {
67 stringValue.push_back(GetNextChar());
68 }
69
70 str.Copy(MHOctetString((const char *)stringValue.data(), nLength));
71}
72
73// Parse an integer argument. Also used for bool and enum.
75{
76 int intVal = 0;
77 bool firstByte = true;
78
79 if (endInt == INDEFINITE_LENGTH)
80 {
81 MHERROR("Indefinite length integers are not implemented");
82 }
83
84 while (m_p < endInt)
85 {
86 unsigned char ch = GetNextChar();
87
88 // Integer values are signed so if the top bit is set in the first byte
89 // we need to set the sign bit.
90 if (firstByte && ch >= 128)
91 {
92 intVal = -1;
93 }
94
95 firstByte = false;
96 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
97 intVal = (intVal << 8) | ch;
98 }
99
100 return intVal;
101}
102
103
104// Simple recursive parser for ASN1 BER.
106{
107 // Tag class
108 enum : std::uint8_t { Universal, Context/*, Pseudo*/ } tagClass = Universal;
109 // Byte count of end of this item. Set to INDEFINITE_LENGTH if the length is Indefinite.
110 int endOfItem = 0;
111 unsigned int tagNumber = 0;
112
113 // Read the first character.
114 unsigned char ch = GetNextChar();
115
116 // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class.
117 // 0x00 - Universal, 0x40 - Application, 0x80 - Context-specific, 0xC0 - Private
118 // We only use Universal and Context.
119 switch (ch & 0xC0)
120 {
121 case 0x00: // Universal
122 tagClass = Universal;
123 break;
124 case 0x80:
125 tagClass = Context;
126 break;
127 default:
128 MHERROR(QString("Invalid tag class = %1").arg(ch, 0, 16));
129 }
130
131 // Bit 2 indicates whether it is a simple or compound type. Not used.
132 // Lower bits are the tag number.
133 tagNumber = ch & 0x1f;
134
135 if (tagNumber == 0x1f) // Except that if it is 0x1F then the tag is encoded in the following bytes.
136 {
137 ch = GetNextChar();
138 tagNumber = ch & 0x7f;
139 while ((ch & 0x80) != 0) // Top bit set means there's more to come.
140 {
141 ch = GetNextChar();
142 tagNumber = (tagNumber << 7) | (ch & 0x7f);
143 }
144 }
145
146 // Next byte is the length. If it is less than 128 it is the actual length, otherwise it
147 // gives the number of bytes containing the length, except that if this is zero the item
148 // has an "indefinite" length and is terminated by two zero bytes.
149 ch = GetNextChar();
150
151 if (ch & 0x80)
152 {
153 int lengthOfLength = ch & 0x7f;
154
155 if (lengthOfLength == 0)
156 {
157 endOfItem = INDEFINITE_LENGTH;
158 }
159 else
160 {
161 endOfItem = 0;
162
163 while (lengthOfLength--)
164 {
165 ch = GetNextChar();
166 endOfItem = (endOfItem << 8) | ch;
167 }
168
169 endOfItem += m_p;
170 }
171 }
172 else
173 {
174 endOfItem = ch + m_p;
175 }
176
177 if (tagClass == Context)
178 {
179 auto *pNode = new MHPTagged(tagNumber);
180
181 try
182 {
183 // The argument here depends on the particular tag we're processing.
184 switch (tagNumber)
185 {
187 case C_OBSCURED_INPUT:
189 case C_WRAP_AROUND:
190 case C_TEXT_WRAPPING:
192 case C_MOVING_CURSOR:
193 case C_SHARED:
194 case C_ENGINE_RESP:
195 case C_TILING:
197 {
198 // BOOL
199 // If there is no argument we need to indicate that so that it gets
200 // the correct default value.
201 if (m_p != endOfItem)
202 {
203 int intVal = ParseInt(endOfItem); // May raise an exception
204 pNode->AddArg(new MHPBool(intVal != 0));
205 }
206
207 break;
208 }
209
210 case C_INPUT_TYPE:
211 case C_SLIDER_STYLE:
212 case C_TERMINATION:
213 case C_ORIENTATION:
215 case C_BUTTON_STYLE:
216 case C_START_CORNER:
219 case C_STORAGE:
220 {
221 // ENUM
222 if (m_p != endOfItem)
223 {
224 int intVal = ParseInt(endOfItem); // May raise an exception
225 pNode->AddArg(new MHPEnum(intVal));
226 }
227
228 break;
229 }
230
232 case C_STEP_SIZE:
234 case C_INITIAL_VALUE:
236 case C_MAX_VALUE:
237 case C_MIN_VALUE:
242 case C_MAX_LENGTH:
243 case C_CHARACTER_SET:
246 case C_LOOPING:
250 case C_CONTENT_HOOK:
252 case C_COMPONENT_TAG:
255 case C_CONTENT_SIZE:
256 {
257 // INT
258 if (m_p != endOfItem)
259 {
260 int intVal = ParseInt(endOfItem); // May raise an exception
261 pNode->AddArg(new MHPInt(intVal));
262 }
263
264 break;
265 }
266
270 case C_CHAR_LIST:
271 case C_NAME:
272 case C_ORIGINAL_LABEL:
273 {
274 // STRING
275 // Unlike INT, BOOL and ENUM we can't distinguish an empty string
276 // from a missing string.
277 MHOctetString str;
278 ParseString(endOfItem, str);
279 pNode->AddArg(new MHPString(str));
280 break;
281 }
282
283 default:
284 {
285 // Everything else has either no argument or is self-describing
286 // TODO: Handle indefinite length.
287 if (endOfItem == INDEFINITE_LENGTH)
288 {
289 MHERROR("Indefinite length arguments are not implemented");
290 }
291
292 while (m_p < endOfItem)
293 {
294 pNode->AddArg(DoParse());
295 }
296 }
297 }
298 }
299 catch (...)
300 {
301 // Memory clean-up
302 delete pNode;
303 throw;
304 }
305
306 return pNode;
307 }
308
309 // Universal - i.e. a primitive type.
310 // Tag values
311
312 switch (tagNumber)
313 {
314 case U_BOOL: // Boolean
315 {
316 int intVal = ParseInt(endOfItem);
317 return new MHPBool(intVal != 0);
318 }
319 case U_INT: // Integer
320 {
321 int intVal = ParseInt(endOfItem);
322 return new MHPInt(intVal);
323 }
324 case U_ENUM: // ENUM
325 {
326 int intVal = ParseInt(endOfItem);
327 return new MHPEnum(intVal);
328 }
329 case U_STRING: // String
330 {
331 MHOctetString str;
332 ParseString(endOfItem, str);
333 return new MHPString(str);
334 }
335 case U_NULL: // ASN1 NULL
336 {
337 return new MHPNull;
338 }
339 case U_SEQUENCE: // Sequence
340 {
341 auto *pNode = new MHParseSequence();
342
343 if (endOfItem == INDEFINITE_LENGTH)
344 {
345 MHERROR("Indefinite length sequences are not implemented");
346 }
347
348 try
349 {
350 while (m_p < endOfItem)
351 {
352 pNode->Append(DoParse());
353 }
354 }
355 catch (...)
356 {
357 // Memory clean-up if error.
358 delete pNode;
359 throw;
360 }
361
362 return pNode;
363 }
364 default:
365 MHERROR(QString("Unknown universal %1").arg(tagNumber));
366 }
367}
@ U_STRING
Definition: ASN1Codes.h:28
@ U_BOOL
Definition: ASN1Codes.h:26
@ U_ENUM
Definition: ASN1Codes.h:30
@ U_SEQUENCE
Definition: ASN1Codes.h:31
@ U_INT
Definition: ASN1Codes.h:27
@ U_NULL
Definition: ASN1Codes.h:29
@ C_MULTIPLE_SELECTION
Definition: ASN1Codes.h:111
@ C_MIN_VALUE
Definition: ASN1Codes.h:140
@ C_COMPONENT_TAG
Definition: ASN1Codes.h:134
@ C_TERMINATION
Definition: ASN1Codes.h:136
@ C_STORAGE
Definition: ASN1Codes.h:129
@ C_ORIGINAL_VOLUME
Definition: ASN1Codes.h:135
@ C_SLIDER_STYLE
Definition: ASN1Codes.h:144
@ C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:79
@ C_ORIGINAL_LINE_WIDTH
Definition: ASN1Codes.h:118
@ C_PROGRAM_CONNECTION_TAG
Definition: ASN1Codes.h:102
@ C_WRAP_AROUND
Definition: ASN1Codes.h:110
@ C_INITIALLY_ACTIVE
Definition: ASN1Codes.h:92
@ C_ENGINE_RESP
Definition: ASN1Codes.h:137
@ C_TEXT_CONTENT_HOOK
Definition: ASN1Codes.h:76
@ C_STREAM_CONTENT_HOOK
Definition: ASN1Codes.h:81
@ C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:123
@ C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:101
@ C_STANDARD_VERSION
Definition: ASN1Codes.h:39
@ C_IP_CONTENT_HOOK
Definition: ASN1Codes.h:80
@ C_INPUT_TYPE
Definition: ASN1Codes.h:145
@ C_CHAR_LIST
Definition: ASN1Codes.h:146
@ C_TEXT_WRAPPING
Definition: ASN1Codes.h:127
@ C_ORIGINAL_LINE_STYLE
Definition: ASN1Codes.h:119
@ C_BITMAP_CONTENT_HOOK
Definition: ASN1Codes.h:82
@ C_LOOPING
Definition: ASN1Codes.h:130
@ C_NAME
Definition: ASN1Codes.h:100
@ C_BUTTON_STYLE
Definition: ASN1Codes.h:150
@ C_INITIAL_PORTION
Definition: ASN1Codes.h:142
@ C_INITIAL_VALUE
Definition: ASN1Codes.h:141
@ C_ORIENTATION
Definition: ASN1Codes.h:138
@ C_CONTENT_CACHE_PRIORITY
Definition: ASN1Codes.h:97
@ C_CONTENT_HOOK
Definition: ASN1Codes.h:93
@ C_SHARED
Definition: ASN1Codes.h:95
@ C_ORIGINAL_LABEL
Definition: ASN1Codes.h:149
@ C_MAX_VALUE
Definition: ASN1Codes.h:139
@ C_OBJECT_INFORMATION
Definition: ASN1Codes.h:40
@ C_MAX_LENGTH
Definition: ASN1Codes.h:148
@ C_MOVING_CURSOR
Definition: ASN1Codes.h:90
@ C_START_CORNER
Definition: ASN1Codes.h:126
@ C_STEP_SIZE
Definition: ASN1Codes.h:143
@ C_CHARACTER_SET
Definition: ASN1Codes.h:74
@ C_INPUT_EVENT_REGISTER
Definition: ASN1Codes.h:87
@ C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:124
@ C_LINE_ART_CONTENT_HOOK
Definition: ASN1Codes.h:83
@ C_CONTENT_REFERENCE
Definition: ASN1Codes.h:105
@ C_ORIGINAL_TRANSPARENCY
Definition: ASN1Codes.h:116
@ C_CONTENT_SIZE
Definition: ASN1Codes.h:96
@ C_ORIGINAL_GC_PRIORITY
Definition: ASN1Codes.h:43
@ C_OBSCURED_INPUT
Definition: ASN1Codes.h:147
@ C_LINE_ORIENTATION
Definition: ASN1Codes.h:125
@ C_BORDERED_BOUNDING_BOX
Definition: ASN1Codes.h:117
@ C_TILING
Definition: ASN1Codes.h:115
#define MHERROR(__text)
Definition: Logging.h:42
static constexpr int INDEFINITE_LENGTH
Definition: ParseBinary.cpp:37
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:85
void ParseString(int endStr, MHOctetString &str)
Definition: ParseBinary.cpp:53
MHParseNode * DoParse()
int ParseInt(int endInt)
Definition: ParseBinary.cpp:74
QByteArray m_data
Definition: ParseBinary.h:50
unsigned char GetNextChar()
Definition: ParseBinary.cpp:41