VirtualBox

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

Last change on this file was 104010, checked in by vboxsync, 2 months ago

*: Fixed various calloc argument order warnings from gcc 14.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use