Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

975 lines
19 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. rrprint.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. Print resource record routines.
  8. Author:
  9. Jim Gilroy (jamesg) February, 1997
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. //
  14. // Private prototypes
  15. //
  16. VOID
  17. printBadDataLength(
  18. IN PRINT_ROUTINE PrintRoutine,
  19. IN OUT PPRINT_CONTEXT pContext,
  20. IN PDNS_RECORD pRecord
  21. );
  22. VOID
  23. ARecordPrint(
  24. IN PRINT_ROUTINE PrintRoutine,
  25. IN OUT PPRINT_CONTEXT pContext,
  26. IN PDNS_RECORD pRecord
  27. )
  28. /*++
  29. Routine Description:
  30. Print A records.
  31. Arguments:
  32. PrintRoutine -- routine to print with
  33. pRecord -- record to print
  34. Return Value:
  35. TRUE if record data equal
  36. FALSE otherwise
  37. --*/
  38. {
  39. WORD dataLength = pRecord->wDataLength;
  40. if ( dataLength == sizeof(IP_ADDRESS) )
  41. {
  42. PrintRoutine(
  43. pContext,
  44. "\tIP address = %s\n",
  45. IP_STRING(pRecord->Data.A.IpAddress) );
  46. }
  47. else if ( dataLength % sizeof(DNS_A_DATA) )
  48. {
  49. printBadDataLength( PrintRoutine, pContext, pRecord );
  50. }
  51. else // multiple records
  52. {
  53. PIP_ADDRESS pip = &pRecord->Data.A.IpAddress;
  54. DnsPrint_Lock();
  55. while ( dataLength )
  56. {
  57. PrintRoutine(
  58. pContext,
  59. "\tIP address = %s\n",
  60. IP_STRING(*pip) );
  61. dataLength -= sizeof(IP_ADDRESS);
  62. pip++;
  63. }
  64. DnsPrint_Unlock();
  65. }
  66. }
  67. VOID
  68. PtrRecordPrint(
  69. IN PRINT_ROUTINE PrintRoutine,
  70. IN OUT PPRINT_CONTEXT pContext,
  71. IN PDNS_RECORD pRecord
  72. )
  73. /*++
  74. Routine Description:
  75. Print PTR compatible record.
  76. Includes: NS, PTR, CNAME, MB, MR, MG, MD, MF
  77. Arguments:
  78. PrintRoutine -- routine to print with
  79. pRecord -- record to print
  80. Return Value:
  81. TRUE if record data equal
  82. FALSE otherwise
  83. --*/
  84. {
  85. PrintRoutine(
  86. pContext,
  87. "\tHostName = %s%S\n",
  88. RECSTRING_UTF8( pRecord, pRecord->Data.PTR.pNameHost ),
  89. RECSTRING_WIDE( pRecord, pRecord->Data.PTR.pNameHost )
  90. );
  91. }
  92. VOID
  93. MxRecordPrint(
  94. IN PRINT_ROUTINE PrintRoutine,
  95. IN OUT PPRINT_CONTEXT pContext,
  96. IN PDNS_RECORD pRecord
  97. )
  98. /*++
  99. Routine Description:
  100. Print MX compatible record.
  101. Includes: MX, RT, AFSDB
  102. Arguments:
  103. PrintRoutine -- routine to print with
  104. pRecord -- record to print
  105. Return Value:
  106. TRUE if record data equal
  107. FALSE otherwise
  108. --*/
  109. {
  110. PrintRoutine(
  111. pContext,
  112. "\tPreference = %d\n"
  113. "\tExchange = %s%S\n",
  114. pRecord->Data.MX.wPreference,
  115. RECSTRING_UTF8( pRecord, pRecord->Data.MX.pNameExchange ),
  116. RECSTRING_WIDE( pRecord, pRecord->Data.MX.pNameExchange )
  117. );
  118. }
  119. VOID
  120. SoaRecordPrint(
  121. IN PRINT_ROUTINE PrintRoutine,
  122. IN OUT PPRINT_CONTEXT pContext,
  123. IN PDNS_RECORD pRecord
  124. )
  125. /*++
  126. Routine Description:
  127. Print SOA record.
  128. Arguments:
  129. PrintRoutine -- routine to print with
  130. pRecord -- record to print
  131. Return Value:
  132. TRUE if record data equal
  133. FALSE otherwise
  134. --*/
  135. {
  136. PrintRoutine(
  137. pContext,
  138. "\tPrimary = %s%S\n"
  139. "\tAdmin = %s%S\n"
  140. "\tSerial = %d\n"
  141. "\tRefresh = %d\n"
  142. "\tRetry = %d\n"
  143. "\tExpire = %d\n"
  144. "\tDefault TTL = %d\n",
  145. RECSTRING_UTF8( pRecord, pRecord->Data.SOA.pNamePrimaryServer ),
  146. RECSTRING_WIDE( pRecord, pRecord->Data.SOA.pNamePrimaryServer ),
  147. RECSTRING_UTF8( pRecord, pRecord->Data.SOA.pNameAdministrator ),
  148. RECSTRING_WIDE( pRecord, pRecord->Data.SOA.pNameAdministrator ),
  149. pRecord->Data.SOA.dwSerialNo,
  150. pRecord->Data.SOA.dwRefresh,
  151. pRecord->Data.SOA.dwRetry,
  152. pRecord->Data.SOA.dwExpire,
  153. pRecord->Data.SOA.dwDefaultTtl );
  154. }
  155. VOID
  156. MinfoRecordPrint(
  157. IN PRINT_ROUTINE PrintRoutine,
  158. IN OUT PPRINT_CONTEXT pContext,
  159. IN PDNS_RECORD pRecord
  160. )
  161. /*++
  162. Routine Description:
  163. Print MINFO and RP records.
  164. Arguments:
  165. PrintRoutine -- routine to print with
  166. pRecord -- record to print
  167. Return Value:
  168. TRUE if record data equal
  169. FALSE otherwise
  170. --*/
  171. {
  172. PrintRoutine(
  173. pContext,
  174. "\tMailbox = %s%S\n"
  175. "\tErrorsMbox = %s%S\n",
  176. RECSTRING_UTF8( pRecord, pRecord->Data.MINFO.pNameMailbox ),
  177. RECSTRING_WIDE( pRecord, pRecord->Data.MINFO.pNameMailbox ),
  178. RECSTRING_UTF8( pRecord, pRecord->Data.MINFO.pNameErrorsMailbox ),
  179. RECSTRING_WIDE( pRecord, pRecord->Data.MINFO.pNameErrorsMailbox )
  180. );
  181. }
  182. VOID
  183. TxtRecordPrint(
  184. IN PRINT_ROUTINE PrintRoutine,
  185. IN OUT PPRINT_CONTEXT pContext,
  186. IN PDNS_RECORD pRecord
  187. )
  188. /*++
  189. Routine Description:
  190. Print TXT compatible records.
  191. Includes: TXT, X25, HINFO, ISDN
  192. Arguments:
  193. PrintRoutine -- routine to print with
  194. pRecord -- record to print
  195. Return Value:
  196. TRUE if record data equal
  197. FALSE otherwise
  198. --*/
  199. {
  200. LPTSTR * ppstring;
  201. INT i;
  202. INT count;
  203. count = pRecord->Data.TXT.dwStringCount;
  204. ppstring = pRecord->Data.TXT.pStringArray;
  205. DnsPrint_Lock();
  206. PrintRoutine(
  207. pContext,
  208. "\tStringCount = %d\n",
  209. count );
  210. for( i=1; i<=count; i++ )
  211. {
  212. PrintRoutine(
  213. pContext,
  214. "\tString[%d] = %s%S\n",
  215. i,
  216. RECSTRING_UTF8( pRecord, *ppstring ),
  217. RECSTRING_WIDE( pRecord, *ppstring )
  218. );
  219. ppstring++;
  220. }
  221. DnsPrint_Unlock();
  222. }
  223. VOID
  224. AaaaRecordPrint(
  225. IN PRINT_ROUTINE PrintRoutine,
  226. IN OUT PPRINT_CONTEXT pContext,
  227. IN PDNS_RECORD pRecord
  228. )
  229. /*++
  230. Routine Description:
  231. Print flat data records.
  232. Includes AAAA type.
  233. Arguments:
  234. PrintRoutine -- routine to print with
  235. pRecord -- record to print
  236. Return Value:
  237. TRUE if record data equal
  238. FALSE otherwise
  239. --*/
  240. {
  241. CHAR ip6String[ IP6_ADDRESS_STRING_LENGTH ];
  242. Dns_Ip6AddressToString_A(
  243. ip6String,
  244. (PDNS_IP6_ADDRESS) &pRecord->Data.AAAA.Ip6Address );
  245. PrintRoutine(
  246. pContext,
  247. "\tIP6 Address = %s\n",
  248. ip6String );
  249. }
  250. VOID
  251. SrvRecordPrint(
  252. IN PRINT_ROUTINE PrintRoutine,
  253. IN OUT PPRINT_CONTEXT pContext,
  254. IN PDNS_RECORD pRecord
  255. )
  256. /*++
  257. Routine Description:
  258. Print SRV record.
  259. Arguments:
  260. PrintRoutine -- routine to print with
  261. pRecord -- record to print
  262. Return Value:
  263. TRUE if record data equal
  264. FALSE otherwise
  265. --*/
  266. {
  267. PrintRoutine(
  268. pContext,
  269. "\tPriority = %d\n"
  270. "\tWeight = %d\n"
  271. "\tPort = %d\n"
  272. "\tTarget Host = %s%S\n",
  273. pRecord->Data.SRV.wPriority,
  274. pRecord->Data.SRV.wWeight,
  275. pRecord->Data.SRV.wPort,
  276. RECSTRING_UTF8( pRecord, pRecord->Data.SRV.pNameTarget ),
  277. RECSTRING_WIDE( pRecord, pRecord->Data.SRV.pNameTarget )
  278. );
  279. }
  280. VOID
  281. AtmaRecordPrint(
  282. IN PRINT_ROUTINE PrintRoutine,
  283. IN OUT PPRINT_CONTEXT pContext,
  284. IN PDNS_RECORD pRecord
  285. )
  286. /*++
  287. Routine Description:
  288. Print ATMA record.
  289. Arguments:
  290. PrintRoutine -- routine to print with
  291. pRecord -- record to print
  292. Return Value:
  293. TRUE if record data equal
  294. FALSE otherwise
  295. --*/
  296. {
  297. PrintRoutine(
  298. pContext,
  299. "\tAddress type = %d\n",
  300. pRecord->Data.ATMA.AddressType );
  301. if ( pRecord->Data.ATMA.Address &&
  302. pRecord->Data.ATMA.AddressType == DNS_ATMA_FORMAT_E164 )
  303. {
  304. PrintRoutine(
  305. pContext,
  306. "\tAddress = %s\n",
  307. pRecord->Data.ATMA.Address );
  308. }
  309. else if ( pRecord->Data.ATMA.Address )
  310. {
  311. DnsPrint_RawOctets(
  312. PrintRoutine,
  313. pContext,
  314. "\tAddress = ",
  315. "\t ", // no line header
  316. pRecord->Data.ATMA.Address,
  317. pRecord->wDataLength - 1
  318. );
  319. }
  320. }
  321. VOID
  322. TsigRecordPrint(
  323. IN PRINT_ROUTINE PrintRoutine,
  324. IN OUT PPRINT_CONTEXT pContext,
  325. IN PDNS_RECORD pRecord
  326. )
  327. /*++
  328. Routine Description:
  329. Print TSIG record.
  330. Arguments:
  331. PrintRoutine -- routine to print with
  332. pRecord -- record to print
  333. Return Value:
  334. TRUE if record data equal
  335. FALSE otherwise
  336. --*/
  337. {
  338. DnsPrint_Lock();
  339. if ( pRecord->Data.TSIG.bPacketPointers )
  340. {
  341. DnsPrint_PacketName(
  342. PrintRoutine,
  343. pContext,
  344. "\tAlgorithm = ",
  345. pRecord->Data.TSIG.pAlgorithmPacket,
  346. NULL, // no packet context
  347. NULL,
  348. "\n" );
  349. }
  350. else
  351. {
  352. PrintRoutine(
  353. pContext,
  354. "\tAlgorithm = %s%S\n",
  355. RECSTRING_UTF8( pRecord, pRecord->Data.TSIG.pNameAlgorithm ),
  356. RECSTRING_WIDE( pRecord, pRecord->Data.TSIG.pNameAlgorithm )
  357. );
  358. }
  359. PrintRoutine(
  360. pContext,
  361. "\tSigned Time = %I64u\n"
  362. "\tFudge Time = %u\n"
  363. "\tSig Length = %u\n"
  364. "\tSig Ptr = %p\n"
  365. "\tXid = %u\n"
  366. "\tError = %u\n"
  367. "\tOtherLength = %u\n"
  368. "\tOther Ptr = %p\n",
  369. pRecord->Data.TSIG.i64CreateTime,
  370. pRecord->Data.TSIG.wFudgeTime,
  371. pRecord->Data.TSIG.wSigLength,
  372. pRecord->Data.TSIG.pSignature,
  373. pRecord->Data.TSIG.wOriginalXid,
  374. pRecord->Data.TSIG.wError,
  375. pRecord->Data.TSIG.wOtherLength,
  376. pRecord->Data.TSIG.pOtherData
  377. );
  378. if ( pRecord->Data.TSIG.pSignature )
  379. {
  380. DnsPrint_RawOctets(
  381. PrintRoutine,
  382. pContext,
  383. "Signature:",
  384. NULL, // no line header
  385. pRecord->Data.TSIG.pSignature,
  386. pRecord->Data.TSIG.wSigLength
  387. );
  388. }
  389. if ( pRecord->Data.TSIG.pOtherData )
  390. {
  391. DnsPrint_RawOctets(
  392. PrintRoutine,
  393. pContext,
  394. "Other Data:",
  395. NULL, // no line header
  396. pRecord->Data.TSIG.pOtherData,
  397. pRecord->Data.TSIG.wOtherLength
  398. );
  399. }
  400. DnsPrint_Unlock();
  401. }
  402. VOID
  403. TkeyRecordPrint(
  404. IN PRINT_ROUTINE PrintRoutine,
  405. IN OUT PPRINT_CONTEXT pContext,
  406. IN PDNS_RECORD pRecord
  407. )
  408. /*++
  409. Routine Description:
  410. Print TKEY record.
  411. Arguments:
  412. PrintRoutine -- routine to print with
  413. pRecord -- record to print
  414. Return Value:
  415. TRUE if record data equal
  416. FALSE otherwise
  417. --*/
  418. {
  419. DnsPrint_Lock();
  420. if ( pRecord->Data.TKEY.bPacketPointers )
  421. {
  422. DnsPrint_PacketName(
  423. PrintRoutine,
  424. pContext,
  425. "\tAlgorithm = ",
  426. pRecord->Data.TKEY.pAlgorithmPacket,
  427. NULL, // no packet context
  428. NULL,
  429. "\n" );
  430. }
  431. else
  432. {
  433. PrintRoutine(
  434. pContext,
  435. "\tAlgorithm = %s%S\n",
  436. RECSTRING_UTF8( pRecord, pRecord->Data.TKEY.pNameAlgorithm ),
  437. RECSTRING_WIDE( pRecord, pRecord->Data.TKEY.pNameAlgorithm )
  438. );
  439. }
  440. PrintRoutine(
  441. pContext,
  442. "\tCreate Time = %d\n"
  443. "\tExpire Time = %d\n"
  444. "\tMode = %d\n"
  445. "\tError = %d\n"
  446. "\tKey Length = %d\n"
  447. "\tKey Ptr = %p\n"
  448. "\tOtherLength = %d\n"
  449. "\tOther Ptr = %p\n",
  450. pRecord->Data.TKEY.dwCreateTime,
  451. pRecord->Data.TKEY.dwExpireTime,
  452. pRecord->Data.TKEY.wMode,
  453. pRecord->Data.TKEY.wError,
  454. pRecord->Data.TKEY.wKeyLength,
  455. pRecord->Data.TKEY.pKey,
  456. pRecord->Data.TKEY.wOtherLength,
  457. pRecord->Data.TKEY.pOtherData
  458. );
  459. if ( pRecord->Data.TKEY.pKey )
  460. {
  461. DnsPrint_RawOctets(
  462. PrintRoutine,
  463. pContext,
  464. "Key:",
  465. NULL, // no line header
  466. pRecord->Data.TKEY.pKey,
  467. pRecord->Data.TKEY.wKeyLength
  468. );
  469. }
  470. if ( pRecord->Data.TKEY.pOtherData )
  471. {
  472. DnsPrint_RawOctets(
  473. PrintRoutine,
  474. pContext,
  475. "Other Data:",
  476. NULL, // no line header
  477. pRecord->Data.TKEY.pOtherData,
  478. pRecord->Data.TKEY.wOtherLength
  479. );
  480. }
  481. DnsPrint_Unlock();
  482. }
  483. //
  484. // RR Print Dispatch Table
  485. //
  486. RR_PRINT_FUNCTION RRPrintTable[] =
  487. {
  488. NULL, // ZERO
  489. ARecordPrint, // A
  490. PtrRecordPrint, // NS
  491. PtrRecordPrint, // MD
  492. PtrRecordPrint, // MF
  493. PtrRecordPrint, // CNAME
  494. SoaRecordPrint, // SOA
  495. PtrRecordPrint, // MB
  496. PtrRecordPrint, // MG
  497. PtrRecordPrint, // MR
  498. NULL, // NULL
  499. NULL, //WksRecordPrint, // WKS
  500. PtrRecordPrint, // PTR
  501. TxtRecordPrint, // HINFO
  502. MinfoRecordPrint, // MINFO
  503. MxRecordPrint, // MX
  504. TxtRecordPrint, // TXT
  505. MinfoRecordPrint, // RP
  506. MxRecordPrint, // AFSDB
  507. TxtRecordPrint, // X25
  508. TxtRecordPrint, // ISDN
  509. MxRecordPrint, // RT
  510. NULL, // NSAP
  511. NULL, // NSAPPTR
  512. NULL, // SIG
  513. NULL, // KEY
  514. NULL, // PX
  515. NULL, // GPOS
  516. AaaaRecordPrint, // AAAA
  517. NULL, // LOC
  518. NULL, // NXT
  519. NULL, // EID
  520. NULL, // NIMLOC
  521. SrvRecordPrint, // SRV
  522. AtmaRecordPrint, // ATMA
  523. NULL, // NAPTR
  524. NULL, // KX
  525. NULL, // CERT
  526. NULL, // A6
  527. NULL, // DNAME
  528. NULL, // SINK
  529. NULL, // OPT
  530. NULL, // 42
  531. NULL, // 43
  532. NULL, // 44
  533. NULL, // 45
  534. NULL, // 46
  535. NULL, // 47
  536. NULL, // 48
  537. //
  538. // NOTE: last type indexed by type ID MUST be set
  539. // as MAX_SELF_INDEXED_TYPE #define in record.h
  540. // (see note above in record info table)
  541. //
  542. // Pseudo record types
  543. //
  544. TkeyRecordPrint, // TKEY
  545. TsigRecordPrint, // TSIG
  546. //
  547. // MS only types
  548. //
  549. NULL, // WINS
  550. NULL, // WINSR
  551. };
  552. //
  553. // Generic print record functions
  554. //
  555. VOID
  556. DnsPrint_Record(
  557. IN PRINT_ROUTINE PrintRoutine,
  558. IN OUT PPRINT_CONTEXT pContext,
  559. IN LPSTR pszHeader,
  560. IN PDNS_RECORD pRecord,
  561. IN PDNS_RECORD pPreviousRecord OPTIONAL
  562. )
  563. /*++
  564. Routine Description:
  565. Print record.
  566. Arguments:
  567. PrintRoutine -- routine to print with
  568. pszHeader -- header message
  569. pRecord -- record to print
  570. pPreviousRecord -- previous record in RR set (if any)
  571. Return Value:
  572. None.
  573. --*/
  574. {
  575. WORD type = pRecord->wType;
  576. WORD dataLength = pRecord->wDataLength;
  577. WORD index;
  578. DnsPrint_Lock();
  579. if ( pszHeader )
  580. {
  581. PrintRoutine(
  582. pContext,
  583. pszHeader );
  584. }
  585. if ( !pRecord )
  586. {
  587. PrintRoutine(
  588. pContext,
  589. "ERROR: Null record ptr to print!\n" );
  590. goto Unlock;
  591. }
  592. //
  593. // print record info
  594. //
  595. // same as previous -- skip duplicated info
  596. // must match
  597. // - name ptr (or have no name)
  598. // - type
  599. // - flags (hence section)
  600. if ( pPreviousRecord &&
  601. (!pRecord->pName || pPreviousRecord->pName == pRecord->pName) &&
  602. pPreviousRecord->wType == type &&
  603. *(PWORD)&pPreviousRecord->Flags.DW == *(PWORD)&pRecord->Flags.DW )
  604. {
  605. PrintRoutine(
  606. pContext,
  607. " Next record in set:\n"
  608. "\tPtr = %p, pNext = %p\n"
  609. "\tTTL = %d\n"
  610. "\tDataLength = %d\n",
  611. pRecord,
  612. pRecord->pNext,
  613. pRecord->dwTtl,
  614. dataLength );
  615. }
  616. //
  617. // different from previous -- full print
  618. //
  619. else
  620. {
  621. PrintRoutine(
  622. pContext,
  623. " Record:\n"
  624. "\tPtr = %p, pNext = %p\n"
  625. "\tOwner = %s%S\n"
  626. "\tType = %s (%d)\n"
  627. "\tFlags = %hx\n"
  628. "\t\tSection = %d\n"
  629. "\t\tDelete = %d\n"
  630. "\t\tCharSet = %d\n"
  631. "\tTTL = %d\n"
  632. "\tDataLength = %d\n",
  633. pRecord,
  634. pRecord->pNext,
  635. RECSTRING_UTF8( pRecord, pRecord->pName ),
  636. RECSTRING_WIDE( pRecord, pRecord->pName ),
  637. Dns_RecordStringForType( type ),
  638. type,
  639. pRecord->Flags.DW,
  640. pRecord->Flags.S.Section,
  641. pRecord->Flags.S.Delete,
  642. pRecord->Flags.S.CharSet,
  643. pRecord->dwTtl,
  644. dataLength );
  645. }
  646. //
  647. // if no data -- done
  648. //
  649. if ( ! dataLength )
  650. {
  651. goto Unlock;
  652. }
  653. //
  654. // print data
  655. //
  656. index = INDEX_FOR_TYPE( type );
  657. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  658. if ( index && RRPrintTable[ index ] )
  659. {
  660. RRPrintTable[ index ](
  661. PrintRoutine,
  662. pContext,
  663. pRecord );
  664. }
  665. else if ( !index )
  666. {
  667. PrintRoutine(
  668. pContext,
  669. "\tUnknown type: can not print data\n" );
  670. }
  671. else
  672. {
  673. // DCR: should do raw bytes print
  674. PrintRoutine(
  675. pContext,
  676. "\tNo print routine for this type\n" );
  677. }
  678. Unlock:
  679. DnsPrint_Unlock();
  680. }
  681. VOID
  682. printBadDataLength(
  683. IN PRINT_ROUTINE PrintRoutine,
  684. IN OUT PPRINT_CONTEXT pContext,
  685. IN PDNS_RECORD pRecord
  686. )
  687. /*++
  688. Routine Description:
  689. Prints waring on bad data in record.
  690. Arguments:
  691. PrintRoutine -- routine to print with
  692. pRecord -- record with bad data
  693. Return Value:
  694. None.
  695. --*/
  696. {
  697. PrintRoutine(
  698. pContext,
  699. "\tERROR: Invalid record data length for this type.\n" );
  700. }
  701. VOID
  702. DnsPrint_RecordSet(
  703. IN PRINT_ROUTINE PrintRoutine,
  704. IN OUT PPRINT_CONTEXT pContext,
  705. IN LPSTR pszHeader,
  706. IN PDNS_RECORD pRecord
  707. )
  708. /*++
  709. Routine Description:
  710. Print record set.
  711. Arguments:
  712. PrintRoutine -- routine to print with
  713. pRecord -- record set to print
  714. Return Value:
  715. None
  716. --*/
  717. {
  718. PDNS_RECORD pprevious;
  719. DnsPrint_Lock();
  720. if ( pszHeader )
  721. {
  722. PrintRoutine(
  723. pContext,
  724. pszHeader );
  725. }
  726. if ( !pRecord )
  727. {
  728. PrintRoutine(
  729. pContext,
  730. " No Records in list.\n" );
  731. goto Unlock;
  732. }
  733. //
  734. // print all records in set
  735. //
  736. pprevious = NULL;
  737. while ( pRecord )
  738. {
  739. DnsPrint_Record(
  740. PrintRoutine,
  741. pContext,
  742. NULL,
  743. pRecord,
  744. pprevious );
  745. pprevious = pRecord;
  746. pRecord = pRecord->pNext;
  747. }
  748. PrintRoutine(
  749. pContext,
  750. "\n" );
  751. Unlock:
  752. DnsPrint_Unlock();
  753. }
  754. //
  755. // End rrprint.c
  756. //