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 auto *stringValue = (unsigned char *)malloc(nLength + 1);
63 if (stringValue == nullptr)
64 {
65 MHERROR("Out of memory");
66 }
67
68 unsigned char *p = stringValue;
69
70 while (m_p < endStr)
71 {
72 *p++ = GetNextChar();
73 }
74
75 str.Copy(MHOctetString((const char *)stringValue, nLength));
76 free(stringValue);
77}
78
79// Parse an integer argument. Also used for bool and enum.
81{
82 int intVal = 0;
83 bool firstByte = true;
84
85 if (endInt == INDEFINITE_LENGTH)
86 {
87 MHERROR("Indefinite length integers are not implemented");
88 }
89
90 while (m_p < endInt)
91 {
92 unsigned char ch = GetNextChar();
93
94 // Integer values are signed so if the top bit is set in the first byte
95 // we need to set the sign bit.
96 if (firstByte && ch >= 128)
97 {
98 intVal = -1;
99 }
100
101 firstByte = false;
102 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
103 intVal = (intVal << 8) | ch;
104 }
105
106 return intVal;
107}
108
109
110// Simple recursive parser for ASN1 BER.
112{
113 // Tag class
114 enum : std::uint8_t { Universal, Context/*, Pseudo*/ } tagClass = Universal;
115 // Byte count of end of this item. Set to INDEFINITE_LENGTH if the length is Indefinite.
116 int endOfItem = 0;
117 unsigned int tagNumber = 0;
118
119 // Read the first character.
120 unsigned char ch = GetNextChar();
121
122 // ASN1 Coding rules: Top two bits (0 and 1) indicate the tag class.
123 // 0x00 - Universal, 0x40 - Application, 0x80 - Context-specific, 0xC0 - Private
124 // We only use Universal and Context.
125 switch (ch & 0xC0)
126 {
127 case 0x00: // Universal
128 tagClass = Universal;
129 break;
130 case 0x80:
131 tagClass = Context;
132 break;
133 default:
134 MHERROR(QString("Invalid tag class = %1").arg(ch, 0, 16));
135 }
136
137 // Bit 2 indicates whether it is a simple or compound type. Not used.
138 // Lower bits are the tag number.
139 tagNumber = ch & 0x1f;
140
141 if (tagNumber == 0x1f) // Except that if it is 0x1F then the tag is encoded in the following bytes.
142 {
143 ch = GetNextChar();
144 tagNumber = ch & 0x7f;
145 while ((ch & 0x80) != 0) // Top bit set means there's more to come.
146 {
147 ch = GetNextChar();
148 tagNumber = (tagNumber << 7) | (ch & 0x7f);
149 }
150 }
151
152 // Next byte is the length. If it is less than 128 it is the actual length, otherwise it
153 // gives the number of bytes containing the length, except that if this is zero the item
154 // has an "indefinite" length and is terminated by two zero bytes.
155 ch = GetNextChar();
156
157 if (ch & 0x80)
158 {
159 int lengthOfLength = ch & 0x7f;
160
161 if (lengthOfLength == 0)
162 {
163 endOfItem = INDEFINITE_LENGTH;
164 }
165 else
166 {
167 endOfItem = 0;
168
169 while (lengthOfLength--)
170 {
171 ch = GetNextChar();
172 endOfItem = (endOfItem << 8) | ch;
173 }
174
175 endOfItem += m_p;
176 }
177 }
178 else
179 {
180 endOfItem = ch + m_p;
181 }
182
183 if (tagClass == Context)
184 {
185 auto *pNode = new MHPTagged(tagNumber);
186
187 try
188 {
189 // The argument here depends on the particular tag we're processing.
190 switch (tagNumber)
191 {
193 case C_OBSCURED_INPUT:
195 case C_WRAP_AROUND:
196 case C_TEXT_WRAPPING:
198 case C_MOVING_CURSOR:
199 case C_SHARED:
200 case C_ENGINE_RESP:
201 case C_TILING:
203 {
204 // BOOL
205 // If there is no argument we need to indicate that so that it gets
206 // the correct default value.
207 if (m_p != endOfItem)
208 {
209 int intVal = ParseInt(endOfItem); // May raise an exception
210 pNode->AddArg(new MHPBool(intVal != 0));
211 }
212
213 break;
214 }
215
216 case C_INPUT_TYPE:
217 case C_SLIDER_STYLE:
218 case C_TERMINATION:
219 case C_ORIENTATION:
221 case C_BUTTON_STYLE:
222 case C_START_CORNER:
225 case C_STORAGE:
226 {
227 // ENUM
228 if (m_p != endOfItem)
229 {
230 int intVal = ParseInt(endOfItem); // May raise an exception
231 pNode->AddArg(new MHPEnum(intVal));
232 }
233
234 break;
235 }
236
238 case C_STEP_SIZE:
240 case C_INITIAL_VALUE:
242 case C_MAX_VALUE:
243 case C_MIN_VALUE:
248 case C_MAX_LENGTH:
249 case C_CHARACTER_SET:
252 case C_LOOPING:
256 case C_CONTENT_HOOK:
258 case C_COMPONENT_TAG:
261 case C_CONTENT_SIZE:
262 {
263 // INT
264 if (m_p != endOfItem)
265 {
266 int intVal = ParseInt(endOfItem); // May raise an exception
267 pNode->AddArg(new MHPInt(intVal));
268 }
269
270 break;
271 }
272
276 case C_CHAR_LIST:
277 case C_NAME:
278 case C_ORIGINAL_LABEL:
279 {
280 // STRING
281 // Unlike INT, BOOL and ENUM we can't distinguish an empty string
282 // from a missing string.
283 MHOctetString str;
284 ParseString(endOfItem, str);
285 pNode->AddArg(new MHPString(str));
286 break;
287 }
288
289 default:
290 {
291 // Everything else has either no argument or is self-describing
292 // TODO: Handle indefinite length.
293 if (endOfItem == INDEFINITE_LENGTH)
294 {
295 MHERROR("Indefinite length arguments are not implemented");
296 }
297
298 while (m_p < endOfItem)
299 {
300 pNode->AddArg(DoParse());
301 }
302 }
303 }
304 }
305 catch (...)
306 {
307 // Memory clean-up
308 delete pNode;
309 throw;
310 }
311
312 return pNode;
313 }
314
315 // Universal - i.e. a primitive type.
316 // Tag values
317
318 switch (tagNumber)
319 {
320 case U_BOOL: // Boolean
321 {
322 int intVal = ParseInt(endOfItem);
323 return new MHPBool(intVal != 0);
324 }
325 case U_INT: // Integer
326 {
327 int intVal = ParseInt(endOfItem);
328 return new MHPInt(intVal);
329 }
330 case U_ENUM: // ENUM
331 {
332 int intVal = ParseInt(endOfItem);
333 return new MHPEnum(intVal);
334 }
335 case U_STRING: // String
336 {
337 MHOctetString str;
338 ParseString(endOfItem, str);
339 return new MHPString(str);
340 }
341 case U_NULL: // ASN1 NULL
342 {
343 return new MHPNull;
344 }
345 case U_SEQUENCE: // Sequence
346 {
347 auto *pNode = new MHParseSequence();
348
349 if (endOfItem == INDEFINITE_LENGTH)
350 {
351 MHERROR("Indefinite length sequences are not implemented");
352 }
353
354 try
355 {
356 while (m_p < endOfItem)
357 {
358 pNode->Append(DoParse());
359 }
360 }
361 catch (...)
362 {
363 // Memory clean-up if error.
364 delete pNode;
365 throw;
366 }
367
368 return pNode;
369 }
370 default:
371 MHERROR(QString("Unknown universal %1").arg(tagNumber));
372 }
373}
@ 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)
void ParseString(int endStr, MHOctetString &str)
Definition: ParseBinary.cpp:53
MHParseNode * DoParse()
int ParseInt(int endInt)
Definition: ParseBinary.cpp:80
QByteArray m_data
Definition: ParseBinary.h:50
unsigned char GetNextChar()
Definition: ParseBinary.cpp:41