VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/oiddb2c.cpp@ 83743

Last change on this file since 83743 was 83728, checked in by vboxsync, 5 years ago

IPRT/oiddb2c.cpp: VC++ 141 isn't too smart wrt printf validation, so simplify the code. bugref:8489

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.1 KB
Line 
1/* $Id: oiddb2c.cpp 83728 2020-04-17 02:00:27Z vboxsync $ */
2/** @file
3 * IPRT - OID text database to C converter.
4 *
5 * The output is used by asn1-dump.cpp.
6 */
7
8/*
9 * Copyright (C) 2006-2020 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * The contents of this file may alternatively be used under the terms
20 * of the Common Development and Distribution License Version 1.0
21 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
22 * VirtualBox OSE distribution, in which case the provisions of the
23 * CDDL are applicable instead of those of the GPL.
24 *
25 * You may elect to license modified versions of this file under the
26 * terms and conditions of either the GPL or the CDDL or both.
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#include <iprt/assert.h>
34#include <iprt/types.h>
35#include <iprt/ctype.h>
36#include <iprt/stdarg.h>
37#include <stdio.h>
38#include <string.h>
39#include <stdlib.h>
40
41/*
42 * Include the string table code.
43 */
44#define BLDPROG_STRTAB_MAX_STRLEN 48
45#define BLDPROG_STRTAB_WITH_COMPRESSION
46#define BLDPROG_STRTAB_PURE_ASCII
47#define BLDPROG_STRTAB_WITH_CAMEL_WORDS
48#include <iprt/bldprog-strtab-template.cpp.h>
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54#define OID2C_MAX_COMP_VALUE _1G
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Raw OID tree node.
62 *
63 * This is what we produce while loading OID input files.
64 */
65typedef struct RAWOIDNODE
66{
67 /** The component value. */
68 uint32_t uKey;
69 /** Number of children. */
70 uint32_t cChildren;
71 /** Pointer to the children pointers (sorted by key). */
72 struct RAWOIDNODE **papChildren;
73 /** Pointer to the parent. */
74 struct RAWOIDNODE *pParent;
75 /** The string table entry for this node. */
76 BLDPROGSTRING StrTabEntry;
77 /** The table index of the children. */
78 uint32_t idxChildren;
79 /** Set if we've got one or more children with large keys. */
80 bool fChildrenInBigTable;
81} RAWOIDNODE;
82/** Pointer to a raw OID node. */
83typedef RAWOIDNODE *PRAWOIDNODE;
84
85
86/*********************************************************************************************************************************
87* Global Variables *
88*********************************************************************************************************************************/
89/** What to prefix errors with. */
90static const char *g_pszProgName = "oiddb2c";
91
92/** The OID tree. */
93static PRAWOIDNODE g_pOidRoot = NULL;
94/** Number of nodes in the OID tree. */
95static uint32_t g_cOidNodes = 0;
96/** Number of nodes in the OID tree that has strings (for the string table). */
97static uint32_t g_cOidNodesWithStrings = 0;
98/** Max number of children of a node in the OID tree. */
99static uint32_t g_cMaxOidChildren = 0;
100/** Number of nodes which key fits within 6-bits. */
101static uint32_t g_cOidNodiesWith6bitKeys = 0;
102
103
104static RTEXITCODE error(const char *pszFormat, ...)
105{
106 va_list va;
107 va_start(va, pszFormat);
108 fprintf(stderr, "%s: error: ", g_pszProgName);
109 vfprintf(stderr, pszFormat, va);
110 va_end(va);
111 return RTEXITCODE_FAILURE;
112}
113
114static RTEXITCODE warning(const char *pszFormat, ...)
115{
116 va_list va;
117 va_start(va, pszFormat);
118 fprintf(stderr, "%s: warning: ", g_pszProgName);
119 vfprintf(stderr, pszFormat, va);
120 va_end(va);
121 return RTEXITCODE_FAILURE;
122}
123
124
125static void writeDottedOidForNode(PRAWOIDNODE pCurNode, FILE *pOut)
126{
127 if (pCurNode->pParent)
128 {
129 writeDottedOidForNode(pCurNode->pParent, pOut);
130 fprintf(pOut, ".%u", pCurNode->uKey);
131 }
132 else
133 fprintf(pOut, "%u", pCurNode->uKey);
134}
135
136
137static void writeOidTree(PRAWOIDNODE pCurNode, FILE *pOut, bool fBigTable, PBLDPROGSTRTAB pStrTab)
138{
139 /*
140 * First we produce the entries for our children.
141 */
142 if (pCurNode->fChildrenInBigTable == fBigTable)
143 {
144 for (unsigned i = 0; i < pCurNode->cChildren; i++)
145 {
146 PRAWOIDNODE pChild = pCurNode->papChildren[i];
147 fprintf(pOut, " { %*u, %2u, %u, %2u, %4u, %#06x }, /* ",
148 fBigTable ? 7 : 2,
149 pChild->uKey,
150 (unsigned)pChild->StrTabEntry.cchString,
151 pChild->fChildrenInBigTable,
152 pChild->cChildren,
153 pChild->idxChildren,
154 pChild->StrTabEntry.offStrTab);
155 writeDottedOidForNode(pChild, pOut);
156 if (pChild->StrTabEntry.pszString)
157 {
158 fputs(" = \"", pOut);
159 BldProgStrTab_PrintCStringLitteral(pStrTab, &pChild->StrTabEntry, pOut);
160 fputs("\" */\n", pOut);
161 }
162 else
163 fputs(" */\n", pOut);
164 }
165 }
166
167 /*
168 * Then we decend and let our children do the same.
169 */
170 for (unsigned i = 0; i < pCurNode->cChildren; i++)
171 writeOidTree(pCurNode->papChildren[i], pOut, fBigTable, pStrTab);
172}
173
174
175static uint32_t prepareOidTreeForWriting(PRAWOIDNODE pCurNode, uint32_t idxCur, bool fBigTable)
176{
177 if (pCurNode->fChildrenInBigTable == fBigTable)
178 {
179 pCurNode->idxChildren = pCurNode->cChildren ? idxCur : 0;
180 idxCur += pCurNode->cChildren;
181 }
182
183 for (unsigned i = 0; i < pCurNode->cChildren; i++)
184 idxCur = prepareOidTreeForWriting(pCurNode->papChildren[i], idxCur, fBigTable);
185
186 return idxCur;
187}
188
189
190static void addStringFromOidTree(PRAWOIDNODE pCurNode, PBLDPROGSTRTAB pStrTab)
191{
192 /* Do self. */
193 if (pCurNode->StrTabEntry.pszString)
194 BldProgStrTab_AddString(pStrTab, &pCurNode->StrTabEntry);
195
196 /* Recurse into children. */
197 unsigned i = pCurNode->cChildren;
198 while (i-- > 0)
199 addStringFromOidTree(pCurNode->papChildren[i], pStrTab);
200}
201
202
203static bool isNiceAsciiString(const char *psz)
204{
205 unsigned uch;
206 while ((uch = *psz) != '\0')
207 if ( !(uch & 0x80)
208 && ( uch >= 0x20
209 || uch == '\t') )
210 psz++;
211 else
212 return false;
213 return true;
214}
215
216
217static RTEXITCODE addOidToTree(uint32_t const *pauComponents, unsigned cComponents, const char *pszName,
218 const char *pszFile, unsigned iLineNo)
219{
220 /*
221 * Check preconditions.
222 */
223 size_t cchName = strlen(pszName);
224 if (cchName == '\0')
225 return warning("%s(%d): Empty OID name!\n", pszFile, iLineNo);
226 if (cchName >= BLDPROG_STRTAB_MAX_STRLEN)
227 return warning("%s(%d): OID name is too long (%u)!\n", pszFile, iLineNo, (unsigned)cchName);
228 if (cComponents == 0)
229 return warning("%s(%d): 'Description' without valid OID preceeding it!\n", pszFile, iLineNo);
230 if (!isNiceAsciiString(pszName))
231 return warning("%s(%d): Contains unwanted characters!\n", pszFile, iLineNo);
232
233 /*
234 * Make sure we've got a root node (it has no actual OID componet value,
235 * it's just a place to put the top level children).
236 */
237 if (!g_pOidRoot)
238 {
239 g_pOidRoot = (PRAWOIDNODE)calloc(sizeof(*g_pOidRoot), 1);
240 if (!g_pOidRoot)
241 return error("Out of memory!\n");
242 }
243
244 /*
245 * Decend into the tree, adding any missing nodes as we go along.
246 * We'll end up with the node which is being named.
247 */
248 PRAWOIDNODE pCur = g_pOidRoot;
249 while (cComponents-- > 0)
250 {
251 uint32_t const uKey = *pauComponents++;
252 uint32_t i = pCur->cChildren;
253 while ( i > 0
254 && pCur->papChildren[i - 1]->uKey >= uKey)
255 i--;
256 if ( i < pCur->cChildren
257 && pCur->papChildren[i]->uKey == uKey)
258 pCur = pCur->papChildren[i];
259 else
260 {
261 /* Resize the child pointer array? */
262 if ((pCur->cChildren % 16) == 0)
263 {
264 void *pvNew = realloc(pCur->papChildren, sizeof(pCur->papChildren[0]) * (pCur->cChildren + 16));
265 if (!pvNew)
266 return error("Out of memory!\n");
267 pCur->papChildren = (PRAWOIDNODE *)pvNew;
268 }
269
270 /* Allocate and initialize the node. */
271 PRAWOIDNODE pNew = (PRAWOIDNODE)malloc(sizeof(*pNew));
272 if (!pNew)
273 return error("Out of memory!\n");
274 pNew->uKey = uKey;
275 pNew->pParent = pCur;
276 pNew->papChildren = NULL;
277 pNew->cChildren = 0;
278 pNew->fChildrenInBigTable = false;
279 memset(&pNew->StrTabEntry, 0, sizeof(pNew->StrTabEntry));
280
281 /* Insert it. */
282 if (i < pCur->cChildren)
283 memmove(&pCur->papChildren[i + 1], &pCur->papChildren[i], (pCur->cChildren - i) * sizeof(pCur->papChildren[0]));
284 pCur->papChildren[i] = pNew;
285 pCur->cChildren++;
286
287 if (pCur->cChildren > g_cMaxOidChildren)
288 g_cMaxOidChildren = pCur->cChildren;
289 g_cOidNodes++;
290 if (uKey < 64)
291 g_cOidNodiesWith6bitKeys++;
292 else
293 {
294 pCur->fChildrenInBigTable = true;
295 if (!pCur->pParent)
296 return error("Invalid OID! Top level componet value is out of range: %u (max 2)\n", uKey);
297 }
298
299 /* Decend (could optimize insertion of the remaining nodes, but
300 too much work for very little gain). */
301 pCur = pNew;
302 }
303 }
304
305 /*
306 * Update the node.
307 */
308 if (!pCur->StrTabEntry.pszString)
309 {
310 pCur->StrTabEntry.pszString = (char *)malloc(cchName + 1);
311 if (pCur->StrTabEntry.pszString)
312 memcpy(pCur->StrTabEntry.pszString, pszName, cchName + 1);
313 else
314 return error("Out of memory!\n");
315 pCur->StrTabEntry.cchString = cchName;
316 if (cchName >= 64)
317 pCur->fChildrenInBigTable = true;
318 g_cOidNodesWithStrings++;
319 }
320 /* Ignore duplicates, but warn if different name. */
321 else if ( pCur->StrTabEntry.cchString != cchName
322 || strcmp(pszName, pCur->StrTabEntry.pszString) != 0)
323 warning("%s(%d): Duplicate OID, name differs: '%s' vs '%s'\n", pszFile, iLineNo, pCur->StrTabEntry.pszString, pszName);
324
325 return RTEXITCODE_SUCCESS;
326}
327
328
329static RTEXITCODE parseOid(uint32_t *pauComponents, unsigned *pcComponents, unsigned cMaxComponents, char const *pszOid,
330 const char *pszFile, unsigned iLine)
331{
332 const char *pszInput = pszOid;
333 unsigned i = 0;
334 char ch;
335 for (;;)
336 {
337 /*
338 * Parse the value.
339 */
340 unsigned uValue = 0;
341 if (RT_C_IS_DIGIT((ch = *pszOid)))
342 {
343 do
344 {
345 uValue *= 10;
346 uValue += ch - '0';
347 if (uValue < OID2C_MAX_COMP_VALUE)
348 pszOid++;
349 else
350 return warning("%s(%d): Component %u in OID attribute value '%s' is out side the supported!\n",
351 pszFile, iLine, i, pszInput);
352 } while (RT_C_IS_DIGIT((ch = *pszOid)));
353 if ( ch == '\0'
354 || ch == '.'
355 || RT_C_IS_BLANK(ch))
356 {
357 if (i < cMaxComponents)
358 {
359 pauComponents[i] = uValue;
360 i++;
361 if (ch != '\0')
362 pszOid++;
363 else
364 {
365 *pcComponents = i;
366 return RTEXITCODE_SUCCESS;
367 }
368 }
369 else
370 return warning("%s(%d): Too many OID components in '%s'!\n", pszFile, iLine, pszInput);
371 }
372 else
373 return warning("%s(%d): Invalid OID attribute value '%s' (ch=%c)!\n", pszFile, iLine, pszInput, ch);
374 }
375 else
376 return warning("%s(%d): Invalid OID attribute value '%s' (ch=%c)!\n", pszFile, iLine, pszInput, ch);
377 }
378}
379
380
381static RTEXITCODE loadOidFile(FILE *pIn, const char *pszFile)
382{
383 /*
384 * We share the format used by dumpasn1.cfg, except that we accept
385 * dotted OIDs.
386 *
387 * An OID entry starts with a 'OID = <space or dot separated OID>'.
388 * It is usually followed by an 'Comment = ', which we ignore, and a
389 * 'Description = <name>' which we keep. We save the entry once we
390 * see the description attribute.
391 */
392 unsigned cOidComponents = 0;
393 uint32_t auOidComponents[16];
394 unsigned iLineNo = 0;
395 char szLine[16384];
396 char *pszLine;
397 szLine[sizeof(szLine) - 1] = '\0';
398 while ((pszLine = fgets(szLine, sizeof(szLine) - 1, pIn)) != NULL)
399 {
400 iLineNo++;
401
402 /* Strip leading spaces.*/
403 char ch;
404 while (RT_C_IS_SPACE((ch = *pszLine)) )
405 pszLine++;
406
407 /* We only care about lines starting with 'OID =', 'Description =' or
408 a numbered OID. */
409 if ( ch == 'O' || ch == 'o'
410 || ch == 'D' || ch == 'd'
411 || ch == '0' || ch == '1' || ch == '2')
412 {
413 /* Right strip the line. */
414 size_t cchLine = strlen(pszLine);
415 while (cchLine > 0 && RT_C_IS_SPACE(pszLine[cchLine - 1]))
416 cchLine--;
417 pszLine[cchLine] = '\0';
418
419 /* Separate the attribute name from the value. */
420 char *pszValue = (char *)memchr(pszLine, '=', cchLine);
421 if (pszValue)
422 {
423 size_t cchName = pszValue - pszLine;
424
425 /* Right strip the name. */
426 while (cchName > 0 && RT_C_IS_SPACE(pszLine[cchName - 1]))
427 cchName--;
428 pszLine[cchName] = '\0';
429
430 /* Left strip the value. */
431 do
432 pszValue++;
433 while (RT_C_IS_SPACE(*pszValue));
434
435 /* Attribute switch */
436 if ( cchName == 3
437 && (pszLine[0] == 'O' || pszLine[0] == 'o')
438 && (pszLine[1] == 'I' || pszLine[1] == 'i')
439 && (pszLine[2] == 'D' || pszLine[2] == 'd'))
440 {
441 cOidComponents = 0;
442 parseOid(auOidComponents, &cOidComponents, RT_ELEMENTS(auOidComponents), pszValue, pszFile, iLineNo);
443 }
444 else if ( cchName == 11
445 && (pszLine[0] == 'D' || pszLine[0] == 'd')
446 && (pszLine[1] == 'e' || pszLine[1] == 'E')
447 && (pszLine[2] == 's' || pszLine[2] == 'S')
448 && (pszLine[3] == 'c' || pszLine[3] == 'C')
449 && (pszLine[4] == 'r' || pszLine[4] == 'R')
450 && (pszLine[5] == 'i' || pszLine[5] == 'I')
451 && (pszLine[6] == 'p' || pszLine[6] == 'P')
452 && (pszLine[7] == 't' || pszLine[7] == 'T')
453 && (pszLine[8] == 'i' || pszLine[8] == 'I')
454 && (pszLine[9] == 'o' || pszLine[9] == 'O')
455 && (pszLine[10] == 'n' || pszLine[10] == 'N'))
456 {
457 if ( addOidToTree(auOidComponents, cOidComponents, pszValue, pszFile, iLineNo)
458 != RTEXITCODE_SUCCESS)
459 return RTEXITCODE_FAILURE;
460 cOidComponents = 0;
461 }
462 else
463 {
464 /* <OID> = <Value> */
465 cOidComponents = 0;
466 if ( parseOid(auOidComponents, &cOidComponents, RT_ELEMENTS(auOidComponents), pszLine, pszLine, iLineNo)
467 == RTEXITCODE_SUCCESS)
468 {
469 if ( addOidToTree(auOidComponents, cOidComponents, pszValue, pszFile, iLineNo)
470 != RTEXITCODE_SUCCESS)
471 return RTEXITCODE_FAILURE;
472 }
473 cOidComponents = 0;
474 }
475 }
476 }
477
478 }
479 if (feof(pIn))
480 return RTEXITCODE_SUCCESS;
481 return error("error or something reading '%s'.\n", pszFile);
482}
483
484
485
486static RTEXITCODE usage(FILE *pOut, const char *argv0, RTEXITCODE rcExit)
487{
488 fprintf(pOut, "usage: %s <out-file.c> <oid-file> [oid-file2 [...]]\n", argv0);
489 return rcExit;
490}
491
492int main(int argc, char **argv)
493{
494 /*
495 * Process arguments and input files.
496 */
497 bool fVerbose = false;
498 unsigned cInFiles = 0;
499 const char *pszOutFile = NULL;
500 for (int i = 1; i < argc; i++)
501 {
502 const char *pszFile = NULL;
503 if (argv[i][0] != '-')
504 pszFile = argv[i];
505 else if (!strcmp(argv[i], "-"))
506 pszFile = argv[i];
507 else
508 return usage(stderr, argv[0], RTEXITCODE_SYNTAX);
509
510 if (!pszOutFile)
511 pszOutFile = pszFile;
512 else
513 {
514 cInFiles++;
515 FILE *pInFile = fopen(pszFile, "r");
516 if (!pInFile)
517 return error("opening '%s' for reading.\n", pszFile);
518 RTEXITCODE rcExit = loadOidFile(pInFile, pszFile);
519 fclose(pInFile);
520 if (rcExit != RTEXITCODE_SUCCESS)
521 return rcExit;
522 }
523 }
524
525 /*
526 * Check that the user specified at least one input and an output file.
527 */
528 if (!pszOutFile)
529 return error("No output file specified specified!\n");
530 if (!cInFiles)
531 return error("No input files specified!\n");
532 if (!g_cOidNodes)
533 return error("No OID found!\n");
534 if (fVerbose)
535 printf("debug: %u nodes with strings; %u nodes without strings; %u nodes in total;\n"
536 "debug: max %u children; %u nodes with 6-bit keys (%u others)\n",
537 g_cOidNodesWithStrings, g_cOidNodes - g_cOidNodesWithStrings, g_cOidNodes,
538 g_cMaxOidChildren, g_cOidNodiesWith6bitKeys, g_cOidNodes - g_cOidNodiesWith6bitKeys);
539
540 /*
541 * Compile the string table.
542 */
543 BLDPROGSTRTAB StrTab;
544 if (!BldProgStrTab_Init(&StrTab, g_cOidNodesWithStrings))
545 return error("Out of memory!\n");
546
547 addStringFromOidTree(g_pOidRoot, &StrTab);
548
549 if (!BldProgStrTab_CompileIt(&StrTab, fVerbose))
550 return error("BldProgStrTab_CompileIt failed!\n");
551
552 /*
553 * Open the output file and write out the stuff.
554 */
555 FILE *pOut;
556 if (!strcmp(pszOutFile, "-"))
557 pOut = stdout;
558 else
559 pOut = fopen(pszOutFile, "w");
560 if (!pOut)
561 return error("opening '%s' for writing.\n", pszOutFile);
562
563 /* Write the string table. */
564 BldProgStrTab_WriteStringTable(&StrTab, pOut, "static ", "g_", "OidDbStrTab");
565
566 prepareOidTreeForWriting(g_pOidRoot, 0, false /*fBigTable*/);
567 prepareOidTreeForWriting(g_pOidRoot, 0, true /*fBigTable*/);
568
569 fprintf(pOut,
570 "\n"
571 "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
572 "# pragma pack(2)\n"
573 "#endif\n"
574 "typedef struct RTOIDENTRYSMALL\n"
575 "{\n"
576 " uint32_t uKey : 6;\n"
577 " uint32_t cchString : 6;\n"
578 " uint32_t fBigTable : 1;\n"
579 " uint32_t cChildren : 7;\n"
580 " uint32_t idxChildren : 12;\n"
581 " uint16_t offString;\n"
582 "} RTOIDENTRYSMALL;\n"
583 "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
584 "# pragma pack()\n"
585 "AssertCompileSize(RTOIDENTRYSMALL, 6);\n"
586 "#endif\n"
587 "typedef RTOIDENTRYSMALL const *PCRTOIDENTRYSMALL;\n"
588 "\n"
589 "static const RTOIDENTRYSMALL g_aSmallOidTable[] = \n{\n");
590 writeOidTree(g_pOidRoot, pOut, false /*fBigTable*/, &StrTab);
591 fprintf(pOut, "};\n");
592
593 fprintf(pOut,
594 "\n"
595 "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
596 "# pragma pack(2)\n"
597 "#endif\n"
598 "typedef struct RTOIDENTRYBIG\n"
599 "{\n"
600 " uint32_t uKey;\n"
601 " uint8_t cchString;\n"
602 " uint8_t fBigTable : 1;\n"
603 " uint8_t cChildren : 7;\n"
604 " uint16_t idxChildren;\n"
605 " uint16_t offString;\n"
606 "} RTOIDENTRYBIG;\n"
607 "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
608 "# pragma pack()\n"
609 "AssertCompileSize(RTOIDENTRYBIG, 10);\n"
610 "#endif\n"
611 "typedef RTOIDENTRYBIG const *PCRTOIDENTRYBIG;\n"
612 "\n"
613 "static const RTOIDENTRYBIG g_aBigOidTable[] = \n{\n");
614 writeOidTree(g_pOidRoot, pOut, true /*fBigTable*/, &StrTab);
615 fprintf(pOut, "};\n");
616
617 /* Carefully close the output file. */
618 if (ferror(pOut))
619 return error("problem writing '%s'!\n", pszOutFile);
620 if (fclose(pOut) != 0)
621 return error("closing '%s' after writing it.\n", pszOutFile);
622
623 return RTEXITCODE_SUCCESS;
624}
625
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette