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 /*
23 Parser for ASN1 binary notation. Does minimal syntax checking, assuming that this will already have
24 been 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 
37 static 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.
80 int MHParseBinary::ParseInt(int endInt)
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  tagNumber = 0;
144 
145  do
146  {
147  ch = GetNextChar();
148  tagNumber = (tagNumber << 7) | (ch & 0x7f);
149  }
150  while ((ch & 0x80) != 0); // Top bit set means there's more to come.
151  }
152 
153  // Next byte is the length. If it is less than 128 it is the actual length, otherwise it
154  // gives the number of bytes containing the length, except that if this is zero the item
155  // has an "indefinite" length and is terminated by two zero bytes.
156  ch = GetNextChar();
157 
158  if (ch & 0x80)
159  {
160  int lengthOfLength = ch & 0x7f;
161 
162  if (lengthOfLength == 0)
163  {
164  endOfItem = INDEFINITE_LENGTH;
165  }
166  else
167  {
168  endOfItem = 0;
169 
170  while (lengthOfLength--)
171  {
172  ch = GetNextChar();
173  endOfItem = (endOfItem << 8) | ch;
174  }
175 
176  endOfItem += m_p;
177  }
178  }
179  else
180  {
181  endOfItem = ch + m_p;
182  }
183 
184  if (tagClass == Context)
185  {
186  auto *pNode = new MHPTagged(tagNumber);
187 
188  try
189  {
190  // The argument here depends on the particular tag we're processing.
191  switch (tagNumber)
192  {
194  case C_OBSCURED_INPUT:
196  case C_WRAP_AROUND:
197  case C_TEXT_WRAPPING:
198  case C_INITIALLY_ACTIVE:
199  case C_MOVING_CURSOR:
200  case C_SHARED:
201  case C_ENGINE_RESP:
202  case C_TILING:
204  {
205  // BOOL
206  // If there is no argument we need to indicate that so that it gets
207  // the correct default value.
208  if (m_p != endOfItem)
209  {
210  int intVal = ParseInt(endOfItem); // May raise an exception
211  pNode->AddArg(new MHPBool(intVal != 0));
212  }
213 
214  break;
215  }
216 
217  case C_INPUT_TYPE:
218  case C_SLIDER_STYLE:
219  case C_TERMINATION:
220  case C_ORIENTATION:
222  case C_BUTTON_STYLE:
223  case C_START_CORNER:
224  case C_LINE_ORIENTATION:
226  case C_STORAGE:
227  {
228  // ENUM
229  if (m_p != endOfItem)
230  {
231  int intVal = ParseInt(endOfItem); // May raise an exception
232  pNode->AddArg(new MHPEnum(intVal));
233  }
234 
235  break;
236  }
237 
238  case C_INITIAL_PORTION:
239  case C_STEP_SIZE:
241  case C_INITIAL_VALUE:
242  case C_IP_CONTENT_HOOK:
243  case C_MAX_VALUE:
244  case C_MIN_VALUE:
247  case C_TEXT_CONTENT_HOOK:
249  case C_MAX_LENGTH:
250  case C_CHARACTER_SET:
253  case C_LOOPING:
255  case C_STANDARD_VERSION:
257  case C_CONTENT_HOOK:
259  case C_COMPONENT_TAG:
260  case C_ORIGINAL_VOLUME:
262  case C_CONTENT_SIZE:
263  {
264  // INT
265  if (m_p != endOfItem)
266  {
267  int intVal = ParseInt(endOfItem); // May raise an exception
268  pNode->AddArg(new MHPInt(intVal));
269  }
270 
271  break;
272  }
273 
275  case C_CONTENT_REFERENCE:
276  case C_FONT_ATTRIBUTES:
277  case C_CHAR_LIST:
278  case C_NAME:
279  case C_ORIGINAL_LABEL:
280  {
281  // STRING
282  // Unlike INT, BOOL and ENUM we can't distinguish an empty string
283  // from a missing string.
284  MHOctetString str;
285  ParseString(endOfItem, str);
286  pNode->AddArg(new MHPString(str));
287  break;
288  }
289 
290  default:
291  {
292  // Everything else has either no argument or is self-describing
293  // TODO: Handle indefinite length.
294  if (endOfItem == INDEFINITE_LENGTH)
295  {
296  MHERROR("Indefinite length arguments are not implemented");
297  }
298 
299  while (m_p < endOfItem)
300  {
301  pNode->AddArg(DoParse());
302  }
303  }
304  }
305  }
306  catch (...)
307  {
308  // Memory clean-up
309  delete pNode;
310  throw;
311  }
312 
313  return pNode;
314  }
315 
316  // Universal - i.e. a primitive type.
317  // Tag values
318 
319  switch (tagNumber)
320  {
321  case U_BOOL: // Boolean
322  {
323  int intVal = ParseInt(endOfItem);
324  return new MHPBool(intVal != 0);
325  }
326  case U_INT: // Integer
327  {
328  int intVal = ParseInt(endOfItem);
329  return new MHPInt(intVal);
330  }
331  case U_ENUM: // ENUM
332  {
333  int intVal = ParseInt(endOfItem);
334  return new MHPEnum(intVal);
335  }
336  case U_STRING: // String
337  {
338  MHOctetString str;
339  ParseString(endOfItem, str);
340  return new MHPString(str);
341  }
342  case U_NULL: // ASN1 NULL
343  {
344  return new MHPNull;
345  }
346  case U_SEQUENCE: // Sequence
347  {
348  auto *pNode = new MHParseSequence();
349 
350  if (endOfItem == INDEFINITE_LENGTH)
351  {
352  MHERROR("Indefinite length sequences are not implemented");
353  }
354 
355  try
356  {
357  while (m_p < endOfItem)
358  {
359  pNode->Append(DoParse());
360  }
361  }
362  catch (...)
363  {
364  // Memory clean-up if error.
365  delete pNode;
366  throw;
367  }
368 
369  return pNode;
370  }
371  default:
372  MHERROR(QString("Unknown universal %1").arg(tagNumber));
373  }
374 }
U_NULL
@ U_NULL
Definition: ASN1Codes.h:29
C_ORIENTATION
@ C_ORIENTATION
Definition: ASN1Codes.h:138
INDEFINITE_LENGTH
static constexpr int INDEFINITE_LENGTH
Definition: ParseBinary.cpp:37
C_STEP_SIZE
@ C_STEP_SIZE
Definition: ASN1Codes.h:143
C_ORIGINAL_GC_PRIORITY
@ C_ORIGINAL_GC_PRIORITY
Definition: ASN1Codes.h:43
Groups.h
C_MAX_LENGTH
@ C_MAX_LENGTH
Definition: ASN1Codes.h:148
C_BUTTON_STYLE
@ C_BUTTON_STYLE
Definition: ASN1Codes.h:150
U_BOOL
@ U_BOOL
Definition: ASN1Codes.h:26
C_MOVING_CURSOR
@ C_MOVING_CURSOR
Definition: ASN1Codes.h:90
C_CHARACTER_SET
@ C_CHARACTER_SET
Definition: ASN1Codes.h:74
ASN1Codes.h
C_ORIGINAL_LABEL
@ C_ORIGINAL_LABEL
Definition: ASN1Codes.h:149
C_MULTIPLE_SELECTION
@ C_MULTIPLE_SELECTION
Definition: ASN1Codes.h:111
MHPTagged
Definition: ParseNode.h:84
C_TERMINATION
@ C_TERMINATION
Definition: ASN1Codes.h:136
MHParseBinary::ParseInt
int ParseInt(int endInt)
Definition: ParseBinary.cpp:80
MHParseBinary::DoParse
MHParseNode * DoParse()
Definition: ParseBinary.cpp:111
C_ORIGINAL_LINE_STYLE
@ C_ORIGINAL_LINE_STYLE
Definition: ASN1Codes.h:119
C_BORDERED_BOUNDING_BOX
@ C_BORDERED_BOUNDING_BOX
Definition: ASN1Codes.h:117
MHOctetString
Definition: BaseClasses.h:107
C_TILING
@ C_TILING
Definition: ASN1Codes.h:115
C_TEXT_CONTENT_HOOK
@ C_TEXT_CONTENT_HOOK
Definition: ASN1Codes.h:76
ParseBinary.h
C_ENGINE_RESP
@ C_ENGINE_RESP
Definition: ASN1Codes.h:137
C_INITIAL_PORTION
@ C_INITIAL_PORTION
Definition: ASN1Codes.h:142
C_FONT_ATTRIBUTES
@ C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:79
C_IP_CONTENT_HOOK
@ C_IP_CONTENT_HOOK
Definition: ASN1Codes.h:80
U_INT
@ U_INT
Definition: ASN1Codes.h:27
C_PROGRAM_CONNECTION_TAG
@ C_PROGRAM_CONNECTION_TAG
Definition: ASN1Codes.h:102
MHPNull
Definition: ParseNode.h:135
hardwareprofile.config.p
p
Definition: config.py:33
C_TEXT_WRAPPING
@ C_TEXT_WRAPPING
Definition: ASN1Codes.h:127
ParseNode.h
C_HORIZONTAL_JUSTIFICATION
@ C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:123
C_LOOPING
@ C_LOOPING
Definition: ASN1Codes.h:130
C_VERTICAL_JUSTIFICATION
@ C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:124
C_WRAP_AROUND
@ C_WRAP_AROUND
Definition: ASN1Codes.h:110
U_STRING
@ U_STRING
Definition: ASN1Codes.h:28
C_STORAGE
@ C_STORAGE
Definition: ASN1Codes.h:129
C_CONTENT_REFERENCE
@ C_CONTENT_REFERENCE
Definition: ASN1Codes.h:105
MHParseSequence
Definition: ParseNode.h:75
MHParseBinary::m_p
int m_p
Definition: ParseBinary.h:49
Engine.h
MHPEnum
Definition: ParseNode.h:106
C_INITIAL_VALUE
@ C_INITIAL_VALUE
Definition: ASN1Codes.h:141
MHParseBinary::GetNextChar
unsigned char GetNextChar()
Definition: ParseBinary.cpp:41
C_LINE_ORIENTATION
@ C_LINE_ORIENTATION
Definition: ASN1Codes.h:125
U_SEQUENCE
@ U_SEQUENCE
Definition: ASN1Codes.h:31
C_MIN_VALUE
@ C_MIN_VALUE
Definition: ASN1Codes.h:140
C_CONTENT_CACHE_PRIORITY
@ C_CONTENT_CACHE_PRIORITY
Definition: ASN1Codes.h:97
C_NAME
@ C_NAME
Definition: ASN1Codes.h:100
MHPString
Definition: ParseNode.h:125
Root.h
MHPBool
Definition: ParseNode.h:115
C_INITIALLY_AVAILABLE
@ C_INITIALLY_AVAILABLE
Definition: ASN1Codes.h:101
C_COMPONENT_TAG
@ C_COMPONENT_TAG
Definition: ASN1Codes.h:134
BaseClasses.h
MHParseNode
Definition: ParseNode.h:38
C_START_CORNER
@ C_START_CORNER
Definition: ASN1Codes.h:126
C_SLIDER_STYLE
@ C_SLIDER_STYLE
Definition: ASN1Codes.h:144
C_CHAR_LIST
@ C_CHAR_LIST
Definition: ASN1Codes.h:146
MHERROR
#define MHERROR(__text)
Definition: Logging.h:42
C_LINE_ART_CONTENT_HOOK
@ C_LINE_ART_CONTENT_HOOK
Definition: ASN1Codes.h:83
Logging.h
C_ORIGINAL_VOLUME
@ C_ORIGINAL_VOLUME
Definition: ASN1Codes.h:135
C_CONTENT_SIZE
@ C_CONTENT_SIZE
Definition: ASN1Codes.h:96
C_ORIGINAL_LINE_WIDTH
@ C_ORIGINAL_LINE_WIDTH
Definition: ASN1Codes.h:118
C_STREAM_CONTENT_HOOK
@ C_STREAM_CONTENT_HOOK
Definition: ASN1Codes.h:81
C_BITMAP_CONTENT_HOOK
@ C_BITMAP_CONTENT_HOOK
Definition: ASN1Codes.h:82
C_OBJECT_INFORMATION
@ C_OBJECT_INFORMATION
Definition: ASN1Codes.h:40
C_CONTENT_HOOK
@ C_CONTENT_HOOK
Definition: ASN1Codes.h:93
U_ENUM
@ U_ENUM
Definition: ASN1Codes.h:30
MHParseBinary::ParseString
void ParseString(int endStr, MHOctetString &str)
Definition: ParseBinary.cpp:53
C_ORIGINAL_TRANSPARENCY
@ C_ORIGINAL_TRANSPARENCY
Definition: ASN1Codes.h:116
C_INPUT_TYPE
@ C_INPUT_TYPE
Definition: ASN1Codes.h:145
MHPInt
Definition: ParseNode.h:96
C_SHARED
@ C_SHARED
Definition: ASN1Codes.h:95
C_MAX_VALUE
@ C_MAX_VALUE
Definition: ASN1Codes.h:139
MHParseBinary::m_data
QByteArray m_data
Definition: ParseBinary.h:50
C_OBSCURED_INPUT
@ C_OBSCURED_INPUT
Definition: ASN1Codes.h:147
C_STANDARD_VERSION
@ C_STANDARD_VERSION
Definition: ASN1Codes.h:39
MHOctetString::Copy
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:119
C_INITIALLY_ACTIVE
@ C_INITIALLY_ACTIVE
Definition: ASN1Codes.h:92
C_INPUT_EVENT_REGISTER
@ C_INPUT_EVENT_REGISTER
Definition: ASN1Codes.h:87