VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-dump.cpp@ 59728

最後變更 在這個檔案從59728是 59728,由 vboxsync 提交於 9 年 前

asn1-dump: Made it a lot less painful to add new OID to the asn.1 dumper. This generalize the string table code used for the USB database in main.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 20.6 KB
 
1/* $Id: asn1-dump.cpp 59728 2016-02-19 00:30:44Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, Structure Dumper.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/asn1.h>
33
34#include <iprt/err.h>
35#include <iprt/log.h>
36#ifdef IN_RING3
37# include <iprt/stream.h>
38#endif
39#include <iprt/string.h>
40
41#include <iprt/formats/asn1.h>
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47/**
48 * Dump data structure.
49 */
50typedef struct RTASN1DUMPDATA
51{
52 /** RTASN1DUMP_F_XXX. */
53 uint32_t fFlags;
54 /** The printfv like output function. */
55 PFNRTDUMPPRINTFV pfnPrintfV;
56 /** PrintfV user argument. */
57 void *pvUser;
58} RTASN1DUMPDATA;
59/** Pointer to a dump data structure. */
60typedef RTASN1DUMPDATA *PRTASN1DUMPDATA;
61
62
63
64/*
65 * Since we're the only user of OIDs, this stuff lives here.
66 * Should that ever change, this code needs to move elsewhere and get it's own public API.
67 */
68#include "oiddb.h"
69
70
71/**
72 * Searches a range in the big table for a key.
73 *
74 * @returns Pointer to the matching entry. NULL if not found.
75 * @param iEntry The start of the range.
76 * @param cEntries The number of entries in the range.
77 * @param uKey The key to find.
78 */
79DECLINLINE(PCRTOIDENTRYBIG) rtOidDbLookupBig(uint32_t iEntry, uint32_t cEntries, uint32_t uKey)
80{
81 /* Not worth doing binary search here, too few entries. */
82 while (cEntries-- > 0)
83 {
84 uint32_t const uThisKey = g_aBigOidTable[iEntry].uKey;
85 if (uThisKey >= uKey)
86 {
87 if (uThisKey == uKey)
88 return &g_aBigOidTable[iEntry];
89 break;
90 }
91 iEntry++;
92 }
93 return NULL;
94}
95
96
97/**
98 * Searches a range in the small table for a key.
99 *
100 * @returns Pointer to the matching entry. NULL if not found.
101 * @param iEntry The start of the range.
102 * @param cEntries The number of entries in the range.
103 * @param uKey The key to find.
104 */
105DECLINLINE(PCRTOIDENTRYSMALL) rtOidDbLookupSmall(uint32_t iEntry, uint32_t cEntries, uint32_t uKey)
106{
107 if (cEntries < 6)
108 {
109 /* Linear search for small ranges. */
110 while (cEntries-- > 0)
111 {
112 uint32_t const uThisKey = g_aSmallOidTable[iEntry].uKey;
113 if (uThisKey >= uKey)
114 {
115 if (uThisKey == uKey)
116 return &g_aSmallOidTable[iEntry];
117 break;
118 }
119 iEntry++;
120 }
121 }
122 else
123 {
124 /* Binary search. */
125 uint32_t iEnd = iEntry + cEntries;
126 for (;;)
127 {
128 uint32_t const i = iEntry + (iEnd - iEntry) / 2;
129 uint32_t const uThisKey = g_aSmallOidTable[i].uKey;
130 if (uThisKey < uKey)
131 {
132 iEntry = i + 1;
133 if (iEntry >= iEnd)
134 break;
135 }
136 else if (uThisKey > uKey)
137 {
138 iEnd = i;
139 if (iEnd <= iEntry)
140 break;
141 }
142 else
143 return &g_aSmallOidTable[iEntry];
144 }
145 }
146 return NULL;
147}
148
149
150
151/**
152 * Queries the name for an object identifier.
153 *
154 * @returns true if found, false if not.
155 * @param pauComponents The components making up the object ID.
156 * @param cComponents The number of components.
157 * @param pszDst Where to store the name if found.
158 * @param cbDst The size of the destination buffer.
159 */
160static bool rtOidDbQueryObjIdName(uint32_t const *pauComponents, uint8_t cComponents, char *pszDst, size_t cbDst)
161{
162 if (cComponents > 0)
163 {
164 /*
165 * The top level is always in the small table as the range is restricted to 0,1,2.
166 */
167 bool fBigTable = false;
168 uint32_t cEntries = RT_MIN(RT_ELEMENTS(g_aSmallOidTable), 3);
169 uint32_t iEntry = 0;
170 for (;;)
171 {
172 uint32_t const uKey = *pauComponents++;
173 if (!fBigTable)
174 {
175 PCRTOIDENTRYSMALL pSmallHit = rtOidDbLookupSmall(iEntry, cEntries, uKey);
176 if (pSmallHit)
177 {
178 if (--cComponents == 0)
179 {
180 if (RTBldProgStrTabQueryString(&g_OidDbStrTab, pSmallHit->offString,
181 pSmallHit->cchString, pszDst, cbDst) >= 0)
182 return true;
183 break;
184 }
185 cEntries = pSmallHit->cChildren;
186 if (cEntries)
187 {
188 iEntry = pSmallHit->idxChildren;
189 fBigTable = pSmallHit->fBigTable;
190 continue;
191 }
192 }
193 }
194 else
195 {
196 PCRTOIDENTRYBIG pBigHit = rtOidDbLookupBig(iEntry, cEntries, uKey);
197 if (pBigHit)
198 {
199 if (--cComponents == 0)
200 {
201 if (RTBldProgStrTabQueryString(&g_OidDbStrTab, pBigHit->offString,
202 pBigHit->cchString, pszDst, cbDst) >= 0)
203 return true;
204 break;
205 }
206 cEntries = pBigHit->cChildren;
207 if (cEntries)
208 {
209 iEntry = pBigHit->idxChildren;
210 fBigTable = pBigHit->fBigTable;
211 continue;
212 }
213 }
214 }
215 break;
216 }
217 }
218
219 return false;
220}
221
222
223
224/**
225 * Wrapper around FNRTASN1DUMPPRINTFV.
226 *
227 * @param pData The dump data structure.
228 * @param pszFormat Format string.
229 * @param ... Format arguments.
230 */
231static void rtAsn1DumpPrintf(PRTASN1DUMPDATA pData, const char *pszFormat, ...)
232{
233 va_list va;
234 va_start(va, pszFormat);
235 pData->pfnPrintfV(pData->pvUser, pszFormat, va);
236 va_end(va);
237}
238
239
240/**
241 * Prints indentation.
242 *
243 * @param pData The dump data structure.
244 * @param uDepth The indentation depth.
245 */
246static void rtAsn1DumpPrintIdent(PRTASN1DUMPDATA pData, uint32_t uDepth)
247{
248 uint32_t cchLeft = uDepth * 2;
249 while (cchLeft > 0)
250 {
251 static char const s_szSpaces[] = " ";
252 uint32_t cch = RT_MIN(cchLeft, sizeof(s_szSpaces) - 1);
253 rtAsn1DumpPrintf(pData, &s_szSpaces[sizeof(s_szSpaces) - 1 - cch]);
254 cchLeft -= cch;
255 }
256}
257
258
259/**
260 * Dumps UTC TIME and GENERALIZED TIME
261 *
262 * @param pData The dump data structure.
263 * @param pAsn1Core The ASN.1 core object representation.
264 * @param pszType The time type name.
265 */
266static void rtAsn1DumpTime(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, const char *pszType)
267{
268 if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
269 {
270 PCRTASN1TIME pTime = (PCRTASN1TIME)pAsn1Core;
271 rtAsn1DumpPrintf(pData, "%s -- %04u-%02u-%02u %02u:%02u:%02.%09Z\n",
272 pszType,
273 pTime->Time.i32Year, pTime->Time.u8Month, pTime->Time.u8MonthDay,
274 pTime->Time.u8Hour, pTime->Time.u8Minute, pTime->Time.u8Second,
275 pTime->Time.u32Nanosecond);
276 }
277 else if (pAsn1Core->cb > 0 && pAsn1Core->cb < 32 && pAsn1Core->uData.pch)
278 rtAsn1DumpPrintf(pData, "%s '%.*s'\n", pszType, (size_t)pAsn1Core->cb, pAsn1Core->uData.pch);
279 else
280 rtAsn1DumpPrintf(pData, "%s -- cb=%u\n", pszType, pAsn1Core->cb);
281}
282
283
284/**
285 * Dumps strings sharing the RTASN1STRING structure.
286 *
287 * @param pData The dump data structure.
288 * @param pAsn1Core The ASN.1 core object representation.
289 * @param pszType The string type name.
290 * @param uDepth The current identation level.
291 */
292static void rtAsn1DumpString(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, const char *pszType, uint32_t uDepth)
293{
294 rtAsn1DumpPrintf(pData, "%s", pszType);
295
296 const char *pszPostfix = "'\n";
297 bool fUtf8 = false;
298 const char *pch = pAsn1Core->uData.pch;
299 uint32_t cch = pAsn1Core->cb;
300 PCRTASN1STRING pString = (PCRTASN1STRING)pAsn1Core;
301 if ( (pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT)
302 && pString->pszUtf8
303 && pString->cchUtf8)
304 {
305 fUtf8 = true;
306 pszPostfix = "' -- utf-8\n";
307 }
308
309 if (cch == 0 || !pch)
310 rtAsn1DumpPrintf(pData, "-- cb=%u\n", pszType, pAsn1Core->cb);
311 else
312 {
313 if (cch >= 48)
314 {
315 rtAsn1DumpPrintf(pData, "\n");
316 rtAsn1DumpPrintIdent(pData, uDepth + 1);
317 }
318 rtAsn1DumpPrintf(pData, " '");
319
320 /** @todo Handle BMP and UNIVERSIAL strings specially. */
321 do
322 {
323 const char *pchStart = pch;
324 while ( cch > 0
325 && (uint8_t)*pch >= 0x20
326 && (!fUtf8 ? (uint8_t)*pch < 0x7f : (uint8_t)*pch != 0x7f)
327 && *pch != '\'')
328 cch--, pch++;
329 if (pchStart != pch)
330 rtAsn1DumpPrintf(pData, "%.*s", pch - pchStart, pchStart);
331
332 while ( cch > 0
333 && ( (uint8_t)*pch < 0x20
334 || (!fUtf8 ? (uint8_t)*pch >= 0x7f : (uint8_t)*pch == 0x7f)
335 || (uint8_t)*pch == '\'') )
336 {
337 rtAsn1DumpPrintf(pData, "\\x%02x", *pch);
338 cch--;
339 pch++;
340 }
341 } while (cch > 0);
342
343 rtAsn1DumpPrintf(pData, pszPostfix);
344 }
345}
346
347
348/**
349 * Dumps the type and value of an universal ASN.1 type.
350 *
351 * @returns True if it opens a child, false if not.
352 * @param pData The dumper data.
353 * @param pAsn1Core The ASN.1 object to dump.
354 * @param uDepth The current depth (for indentation).
355 */
356static bool rtAsn1DumpUniversalTypeAndValue(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, uint32_t uDepth)
357{
358 const char *pszValuePrefix = "-- value:";
359 const char *pszDefault = "";
360 if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
361 {
362 pszValuePrefix = "DEFAULT";
363 pszDefault = "DEFAULT ";
364 }
365
366 bool fOpen = false;
367 switch (pAsn1Core->uRealTag)
368 {
369 case ASN1_TAG_BOOLEAN:
370 if (pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT)
371 rtAsn1DumpPrintf(pData, "BOOLEAN %s %RTbool\n", pszValuePrefix, ((PCRTASN1BOOLEAN)pAsn1Core)->fValue);
372 else if (pAsn1Core->cb == 1 && pAsn1Core->uData.pu8)
373 rtAsn1DumpPrintf(pData, "BOOLEAN %s %u\n", pszValuePrefix, *pAsn1Core->uData.pu8);
374 else
375 rtAsn1DumpPrintf(pData, "BOOLEAN -- cb=%u\n", pAsn1Core->cb);
376 break;
377
378 case ASN1_TAG_INTEGER:
379 if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT) && pAsn1Core->cb <= 8)
380 rtAsn1DumpPrintf(pData, "INTEGER %s %llu / %#llx\n", pszValuePrefix,
381 ((PCRTASN1INTEGER)pAsn1Core)->uValue, ((PCRTASN1INTEGER)pAsn1Core)->uValue);
382 else if (pAsn1Core->cb == 0 || pAsn1Core->cb >= 512 || !pAsn1Core->uData.pu8)
383 rtAsn1DumpPrintf(pData, "INTEGER -- cb=%u\n", pAsn1Core->cb);
384 else if (pAsn1Core->cb <= 32)
385 rtAsn1DumpPrintf(pData, "INTEGER %s %.*Rhxs\n", pszValuePrefix, (size_t)pAsn1Core->cb, pAsn1Core->uData.pu8);
386 else
387 rtAsn1DumpPrintf(pData, "INTEGER %s\n%.*Rhxd\n", pszValuePrefix, (size_t)pAsn1Core->cb, pAsn1Core->uData.pu8);
388 break;
389
390 case ASN1_TAG_BIT_STRING:
391 if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
392 {
393 PCRTASN1BITSTRING pBitString = (PCRTASN1BITSTRING)pAsn1Core;
394 rtAsn1DumpPrintf(pData, "BIT STRING %s-- cb=%u cBits=%#x cMaxBits=%#x",
395 pszDefault, pBitString->Asn1Core.cb, pBitString->cBits, pBitString->cMaxBits);
396 if (pBitString->cBits <= 64)
397 rtAsn1DumpPrintf(pData, " value=%#llx\n", RTAsn1BitString_GetAsUInt64(pBitString));
398 else
399 rtAsn1DumpPrintf(pData, "\n");
400 }
401 else
402 rtAsn1DumpPrintf(pData, "BIT STRING %s-- cb=%u\n", pszDefault, pAsn1Core->cb);
403 fOpen = pAsn1Core->pOps != NULL;
404 break;
405
406 case ASN1_TAG_OCTET_STRING:
407 rtAsn1DumpPrintf(pData, "OCTET STRING %s-- cb=%u\n", pszDefault, pAsn1Core->cb);
408 fOpen = pAsn1Core->pOps != NULL;
409 break;
410
411 case ASN1_TAG_NULL:
412 rtAsn1DumpPrintf(pData, "NULL\n");
413 break;
414
415 case ASN1_TAG_OID:
416 if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
417 {
418 PCRTASN1OBJID pObjId = (PCRTASN1OBJID)pAsn1Core;
419 char szName[64];
420 if (rtOidDbQueryObjIdName(pObjId->pauComponents, pObjId->cComponents, szName, sizeof(szName)))
421 rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s%s ('%s')\n",
422 pszDefault, szName, ((PCRTASN1OBJID)pAsn1Core)->szObjId);
423 else
424 rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s'%s'\n", pszDefault, ((PCRTASN1OBJID)pAsn1Core)->szObjId);
425 }
426 else
427 rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s -- cb=%u\n", pszDefault, pAsn1Core->cb);
428 break;
429
430 case ASN1_TAG_OBJECT_DESCRIPTOR:
431 rtAsn1DumpPrintf(pData, "OBJECT DESCRIPTOR -- cb=%u TODO\n", pAsn1Core->cb);
432 break;
433
434 case ASN1_TAG_EXTERNAL:
435 rtAsn1DumpPrintf(pData, "EXTERNAL -- cb=%u TODO\n", pAsn1Core->cb);
436 break;
437
438 case ASN1_TAG_REAL:
439 rtAsn1DumpPrintf(pData, "REAL -- cb=%u TODO\n", pAsn1Core->cb);
440 break;
441
442 case ASN1_TAG_ENUMERATED:
443 rtAsn1DumpPrintf(pData, "ENUMERATED -- cb=%u TODO\n", pAsn1Core->cb);
444 break;
445
446 case ASN1_TAG_EMBEDDED_PDV:
447 rtAsn1DumpPrintf(pData, "EMBEDDED PDV -- cb=%u TODO\n", pAsn1Core->cb);
448 break;
449
450 case ASN1_TAG_UTF8_STRING:
451 rtAsn1DumpString(pData, pAsn1Core, "UTF8 STRING", uDepth);
452 break;
453
454 case ASN1_TAG_RELATIVE_OID:
455 rtAsn1DumpPrintf(pData, "RELATIVE OBJECT IDENTIFIER -- cb=%u TODO\n", pAsn1Core->cb);
456 break;
457
458 case ASN1_TAG_SEQUENCE:
459 rtAsn1DumpPrintf(pData, "SEQUENCE -- cb=%u\n", pAsn1Core->cb);
460 fOpen = true;
461 break;
462 case ASN1_TAG_SET:
463 rtAsn1DumpPrintf(pData, "SET -- cb=%u\n", pAsn1Core->cb);
464 fOpen = true;
465 break;
466
467 case ASN1_TAG_NUMERIC_STRING:
468 rtAsn1DumpString(pData, pAsn1Core, "NUMERIC STRING", uDepth);
469 break;
470
471 case ASN1_TAG_PRINTABLE_STRING:
472 rtAsn1DumpString(pData, pAsn1Core, "PRINTABLE STRING", uDepth);
473 break;
474
475 case ASN1_TAG_T61_STRING:
476 rtAsn1DumpString(pData, pAsn1Core, "T61 STRING", uDepth);
477 break;
478
479 case ASN1_TAG_VIDEOTEX_STRING:
480 rtAsn1DumpString(pData, pAsn1Core, "VIDEOTEX STRING", uDepth);
481 break;
482
483 case ASN1_TAG_IA5_STRING:
484 rtAsn1DumpString(pData, pAsn1Core, "IA5 STRING", uDepth);
485 break;
486
487 case ASN1_TAG_GRAPHIC_STRING:
488 rtAsn1DumpString(pData, pAsn1Core, "GRAPHIC STRING", uDepth);
489 break;
490
491 case ASN1_TAG_VISIBLE_STRING:
492 rtAsn1DumpString(pData, pAsn1Core, "VISIBLE STRING", uDepth);
493 break;
494
495 case ASN1_TAG_GENERAL_STRING:
496 rtAsn1DumpString(pData, pAsn1Core, "GENERAL STRING", uDepth);
497 break;
498
499 case ASN1_TAG_UNIVERSAL_STRING:
500 rtAsn1DumpString(pData, pAsn1Core, "UNIVERSAL STRING", uDepth);
501 break;
502
503 case ASN1_TAG_BMP_STRING:
504 rtAsn1DumpString(pData, pAsn1Core, "BMP STRING", uDepth);
505 break;
506
507 case ASN1_TAG_UTC_TIME:
508 rtAsn1DumpTime(pData, pAsn1Core, "UTC TIME");
509 break;
510
511 case ASN1_TAG_GENERALIZED_TIME:
512 rtAsn1DumpTime(pData, pAsn1Core, "GENERALIZED TIME");
513 break;
514
515 case ASN1_TAG_CHARACTER_STRING:
516 rtAsn1DumpPrintf(pData, "CHARACTER STRING -- cb=%u TODO\n", pAsn1Core->cb);
517 break;
518
519 default:
520 rtAsn1DumpPrintf(pData, "[UNIVERSAL %u]\n", pAsn1Core->uTag);
521 break;
522 }
523 return fOpen;
524}
525
526
527/** @callback_method_impl{FNRTASN1ENUMCALLBACK} */
528static DECLCALLBACK(int) rtAsn1DumpEnumCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
529{
530 PRTASN1DUMPDATA pData = (PRTASN1DUMPDATA)pvUser;
531 if (!pAsn1Core->fFlags)
532 return VINF_SUCCESS;
533
534 bool fOpen = false;
535 rtAsn1DumpPrintIdent(pData, uDepth);
536 switch (pAsn1Core->fClass & ASN1_TAGCLASS_MASK)
537 {
538 case ASN1_TAGCLASS_UNIVERSAL:
539 rtAsn1DumpPrintf(pData, "%-16s ", pszName);
540 fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
541 break;
542
543 case ASN1_TAGCLASS_CONTEXT:
544 if ((pAsn1Core->fRealClass & ASN1_TAGCLASS_MASK) == ASN1_TAGCLASS_UNIVERSAL)
545 {
546 rtAsn1DumpPrintf(pData, "%-16s [%u] ", pszName, pAsn1Core->uTag);
547 fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
548 }
549 else
550 {
551 rtAsn1DumpPrintf(pData, "%-16s [%u]\n", pszName, pAsn1Core->uTag);
552 fOpen = true;
553 }
554 break;
555
556 case ASN1_TAGCLASS_APPLICATION:
557 if ((pAsn1Core->fRealClass & ASN1_TAGCLASS_MASK) == ASN1_TAGCLASS_UNIVERSAL)
558 {
559 rtAsn1DumpPrintf(pData, "%-16s [APPLICATION %u] ", pszName, pAsn1Core->uTag);
560 fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
561 }
562 else
563 {
564 rtAsn1DumpPrintf(pData, "%-16s [APPLICATION %u]\n", pszName, pAsn1Core->uTag);
565 fOpen = true;
566 }
567 break;
568
569 case ASN1_TAGCLASS_PRIVATE:
570 if (RTASN1CORE_IS_DUMMY(pAsn1Core))
571 rtAsn1DumpPrintf(pData, "%-16s DUMMY\n", pszName);
572 else
573 {
574 rtAsn1DumpPrintf(pData, "%-16s [PRIVATE %u]\n", pszName, pAsn1Core->uTag);
575 fOpen = true;
576 }
577 break;
578 }
579 /** @todo {} */
580
581 /*
582 * Recurse.
583 */
584 if ( pAsn1Core->pOps
585 && pAsn1Core->pOps->pfnEnum)
586 pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1DumpEnumCallback, uDepth, pData);
587 return VINF_SUCCESS;
588}
589
590
591RTDECL(int) RTAsn1Dump(PCRTASN1CORE pAsn1Core, uint32_t fFlags, uint32_t uLevel, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
592{
593 if ( pAsn1Core->pOps
594 && pAsn1Core->pOps->pfnEnum)
595 {
596 RTASN1DUMPDATA Data;
597 Data.fFlags = fFlags;
598 Data.pfnPrintfV = pfnPrintfV;
599 Data.pvUser = pvUser;
600
601 return pAsn1Core->pOps->pfnEnum((PRTASN1CORE)pAsn1Core, rtAsn1DumpEnumCallback, uLevel, &Data);
602 }
603 return VINF_SUCCESS;
604}
605
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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