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.

1012 lines
19 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. rrcomp.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. Compare resource record routines.
  8. Author:
  9. Jim Gilroy (jamesg) February, 1997
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. #include "locale.h" // for setlocale stuff for Win9x
  14. //
  15. // Type specific RR compare routine prototypes
  16. //
  17. BOOL
  18. ARecordCompare(
  19. IN PDNS_RECORD pRR1,
  20. IN PDNS_RECORD pRR2
  21. )
  22. /*++
  23. Routine Description:
  24. Compare A records.
  25. All these routines assume:
  26. - type compare complete
  27. - datalength compare completed
  28. - NO unicode
  29. Arguments:
  30. pRR1 - first record
  31. pRR2 -- second record
  32. Return Value:
  33. TRUE if record data equal
  34. FALSE otherwise
  35. --*/
  36. {
  37. return( pRR1->Data.A.IpAddress == pRR2->Data.A.IpAddress );
  38. }
  39. BOOL
  40. PtrRecordCompare(
  41. IN PDNS_RECORD pRR1,
  42. IN PDNS_RECORD pRR2
  43. )
  44. /*++
  45. Routine Description:
  46. Compare PTR compatible record.
  47. Includes: NS, PTR, CNAME, MB, MR, MG, MD, MF
  48. Arguments:
  49. pRR1 - first record
  50. pRR2 -- second record
  51. Return Value:
  52. TRUE if record data equal
  53. FALSE otherwise
  54. --*/
  55. {
  56. return Dns_NameComparePrivate(
  57. (LPSTR) pRR1->Data.PTR.pNameHost,
  58. (LPSTR) pRR2->Data.PTR.pNameHost,
  59. RECORD_CHARSET(pRR1) );
  60. }
  61. BOOL
  62. MxRecordCompare(
  63. IN PDNS_RECORD pRR1,
  64. IN PDNS_RECORD pRR2
  65. )
  66. /*++
  67. Routine Description:
  68. Compare MX compatible record.
  69. Includes: MX, RT, AFSDB
  70. Arguments:
  71. pRR1 - first record
  72. pRR2 -- second record
  73. Return Value:
  74. TRUE if record data equal
  75. FALSE otherwise
  76. --*/
  77. {
  78. // verify preference match first
  79. if ( pRR1->Data.MX.wPreference != pRR2->Data.MX.wPreference )
  80. {
  81. return( FALSE );
  82. }
  83. // then result is name comparison
  84. return Dns_NameComparePrivate(
  85. (LPSTR) pRR1->Data.MX.pNameExchange,
  86. (LPSTR) pRR2->Data.MX.pNameExchange,
  87. RECORD_CHARSET(pRR1) );
  88. }
  89. BOOL
  90. SoaRecordCompare(
  91. IN PDNS_RECORD pRR1,
  92. IN PDNS_RECORD pRR2
  93. )
  94. /*++
  95. Routine Description:
  96. Compare SOA record.
  97. Arguments:
  98. pRR1 - first record
  99. pRR2 -- second record
  100. Return Value:
  101. TRUE if record data equal
  102. FALSE otherwise
  103. --*/
  104. {
  105. // verify integer data match first
  106. if ( memcmp( & pRR1->Data.SOA.dwSerialNo,
  107. & pRR2->Data.SOA.dwSerialNo,
  108. SIZEOF_SOA_FIXED_DATA ) )
  109. {
  110. return( FALSE );
  111. }
  112. // match check names
  113. // - primary name server
  114. // - admin email name
  115. if ( ! Dns_NameComparePrivate(
  116. (LPSTR) pRR1->Data.SOA.pNamePrimaryServer,
  117. (LPSTR) pRR2->Data.SOA.pNamePrimaryServer,
  118. RECORD_CHARSET(pRR1) ) )
  119. {
  120. return( FALSE );
  121. }
  122. return Dns_NameComparePrivate(
  123. (LPSTR) pRR1->Data.SOA.pNameAdministrator,
  124. (LPSTR) pRR2->Data.SOA.pNameAdministrator,
  125. RECORD_CHARSET(pRR1) );
  126. }
  127. BOOL
  128. MinfoRecordCompare(
  129. IN PDNS_RECORD pRR1,
  130. IN PDNS_RECORD pRR2
  131. )
  132. /*++
  133. Routine Description:
  134. Compare MINFO and RP records.
  135. Arguments:
  136. pRR1 -- first record
  137. pRR2 -- second record
  138. Return Value:
  139. TRUE if record data equal
  140. FALSE otherwise
  141. --*/
  142. {
  143. if ( ! Dns_NameComparePrivate(
  144. (LPSTR) pRR1->Data.MINFO.pNameMailbox,
  145. (LPSTR) pRR2->Data.MINFO.pNameMailbox,
  146. RECORD_CHARSET(pRR1) ) )
  147. {
  148. return( FALSE );
  149. }
  150. return Dns_NameComparePrivate(
  151. (LPSTR) pRR1->Data.MINFO.pNameErrorsMailbox,
  152. (LPSTR) pRR2->Data.MINFO.pNameErrorsMailbox,
  153. RECORD_CHARSET(pRR1) );
  154. }
  155. BOOL
  156. TxtRecordCompare(
  157. IN PDNS_RECORD pRR1,
  158. IN PDNS_RECORD pRR2
  159. )
  160. /*++
  161. Routine Description:
  162. Compare TXT compatible records.
  163. Includes: TXT, X25, HINFO, ISDN
  164. Arguments:
  165. pRR1 -- first record
  166. pRR2 -- second record
  167. Return Value:
  168. TRUE if record data equal
  169. FALSE otherwise
  170. --*/
  171. {
  172. DWORD count;
  173. PCHAR * pstring1;
  174. PCHAR * pstring2;
  175. //
  176. // compare every string
  177. // since string order DOES matter
  178. // - find string count
  179. // - compare each (case-sensitive)
  180. //
  181. count = pRR1->Data.TXT.dwStringCount;
  182. if ( count != pRR2->Data.TXT.dwStringCount )
  183. {
  184. return( FALSE );
  185. }
  186. pstring1 = (PCHAR *) pRR1->Data.TXT.pStringArray;
  187. pstring2 = (PCHAR *) pRR2->Data.TXT.pStringArray;
  188. while ( count-- )
  189. {
  190. if ( IS_UNICODE_RECORD(pRR1) )
  191. {
  192. if ( wcscmp( (LPWSTR)*pstring1++, (LPWSTR)*pstring2++ ) != 0 )
  193. {
  194. return( FALSE );
  195. }
  196. }
  197. else
  198. {
  199. if ( strcmp( *pstring1++, *pstring2++ ) != 0 )
  200. {
  201. return( FALSE );
  202. }
  203. }
  204. }
  205. return( TRUE );
  206. }
  207. BOOL
  208. FlatRecordCompare(
  209. IN PDNS_RECORD pRR1,
  210. IN PDNS_RECORD pRR2
  211. )
  212. /*++
  213. Routine Description:
  214. Compare flat data records.
  215. Includes AAAA type.
  216. Arguments:
  217. pRR1 -- first record
  218. pRR2 -- second record
  219. Return Value:
  220. TRUE if record data equal
  221. FALSE otherwise
  222. --*/
  223. {
  224. if ( pRR1->wDataLength != pRR2->wDataLength )
  225. {
  226. return( FALSE );
  227. }
  228. return( !memcmp( & pRR1->Data,
  229. & pRR2->Data,
  230. pRR1->wDataLength ) );
  231. }
  232. BOOL
  233. SrvRecordCompare(
  234. IN PDNS_RECORD pRR1,
  235. IN PDNS_RECORD pRR2
  236. )
  237. /*++
  238. Routine Description:
  239. Compare SRV record.
  240. Arguments:
  241. pRR1 -- first record
  242. pRR2 -- second record
  243. Return Value:
  244. TRUE if record data equal
  245. FALSE otherwise
  246. --*/
  247. {
  248. // verify integer data match first
  249. if ( memcmp( & pRR1->Data.SRV.wPriority,
  250. & pRR2->Data.SRV.wPriority,
  251. SIZEOF_SRV_FIXED_DATA ) )
  252. {
  253. return( FALSE );
  254. }
  255. // then result is compare on target host
  256. return Dns_NameComparePrivate(
  257. (LPSTR) pRR1->Data.SRV.pNameTarget,
  258. (LPSTR) pRR2->Data.SRV.pNameTarget,
  259. RECORD_CHARSET(pRR1) );
  260. }
  261. BOOL
  262. AtmaRecordCompare(
  263. IN PDNS_RECORD pRR1,
  264. IN PDNS_RECORD pRR2
  265. )
  266. /*++
  267. Routine Description:
  268. Compare ATMA record.
  269. Arguments:
  270. pRR1 -- first record
  271. pRR2 -- second record
  272. Return Value:
  273. TRUE if record data equal
  274. FALSE otherwise
  275. --*/
  276. {
  277. WORD length = pRR1->wDataLength;
  278. if ( length > pRR2->wDataLength )
  279. {
  280. length = pRR2->wDataLength;
  281. }
  282. // verify integer data match first
  283. if ( pRR1->Data.ATMA.AddressType != pRR2->Data.ATMA.AddressType )
  284. {
  285. return( FALSE );
  286. }
  287. if ( memcmp(
  288. pRR1->Data.ATMA.Address,
  289. pRR2->Data.ATMA.Address,
  290. length ) != 0 )
  291. {
  292. return( FALSE );
  293. }
  294. return( TRUE );
  295. }
  296. //
  297. // RR compare routines jump table
  298. //
  299. RR_COMPARE_FUNCTION RRCompareTable[] =
  300. {
  301. NULL, // ZERO
  302. ARecordCompare, // A
  303. PtrRecordCompare, // NS
  304. PtrRecordCompare, // MD
  305. PtrRecordCompare, // MF
  306. PtrRecordCompare, // CNAME
  307. SoaRecordCompare, // SOA
  308. PtrRecordCompare, // MB
  309. PtrRecordCompare, // MG
  310. PtrRecordCompare, // MR
  311. NULL, // NULL
  312. NULL, //WksRecordCompare, // WKS
  313. PtrRecordCompare, // PTR
  314. TxtRecordCompare, // HINFO
  315. MinfoRecordCompare, // MINFO
  316. MxRecordCompare, // MX
  317. TxtRecordCompare, // TXT
  318. MinfoRecordCompare, // RP
  319. MxRecordCompare, // AFSDB
  320. TxtRecordCompare, // X25
  321. TxtRecordCompare, // ISDN
  322. MxRecordCompare, // RT
  323. NULL, // NSAP
  324. NULL, // NSAPPTR
  325. NULL, // SIG
  326. NULL, // KEY
  327. NULL, // PX
  328. NULL, // GPOS
  329. FlatRecordCompare, // AAAA
  330. NULL, // LOC
  331. NULL, // NXT
  332. NULL, // EID
  333. NULL, // NIMLOC
  334. SrvRecordCompare, // SRV
  335. AtmaRecordCompare, // ATMA
  336. NULL, // NAPTR
  337. NULL, // KX
  338. NULL, // CERT
  339. NULL, // A6
  340. NULL, // DNAME
  341. NULL, // SINK
  342. NULL, // OPT
  343. NULL, // 42
  344. NULL, // 43
  345. NULL, // 44
  346. NULL, // 45
  347. NULL, // 46
  348. NULL, // 47
  349. NULL, // 48
  350. //
  351. // NOTE: last type indexed by type ID MUST be set
  352. // as MAX_SELF_INDEXED_TYPE #define in record.h
  353. // (see note above in record info table)
  354. //
  355. // Pseudo record types
  356. //
  357. NULL, // TKEY
  358. NULL, // TSIG
  359. //
  360. // MS only types
  361. //
  362. FlatRecordCompare, // WINS
  363. NULL, // WINSR
  364. };
  365. BOOL
  366. WINAPI
  367. Dns_RecordCompare(
  368. IN PDNS_RECORD pRecord1,
  369. IN PDNS_RECORD pRecord2
  370. )
  371. /*++
  372. Routine Description:
  373. Compare two records.
  374. Record compare ignores TTL and flags and section information.
  375. Arguments:
  376. pRecord1 -- first record
  377. pRecord2 -- second record
  378. Return Value:
  379. TRUE if records equal.
  380. FALSE otherwise.
  381. --*/
  382. {
  383. BOOL fresult;
  384. WORD type = pRecord1->wType;
  385. WORD index;
  386. IF_DNSDBG( UPDATE )
  387. {
  388. DNS_PRINT((
  389. "Dns_RecordCompare()\n"
  390. "\tfirst = %p\n"
  391. "\tfirst = %p\n",
  392. pRecord1,
  393. pRecord2 ));
  394. }
  395. //
  396. // verify that both records have same character set
  397. //
  398. if ( RECORD_CHARSET(pRecord1) != RECORD_CHARSET(pRecord2) )
  399. {
  400. DNS_PRINT(( "ERROR: comparing records with non-matching character sets!\n" ));
  401. //
  402. // If they are different and one of them is undefined, just use
  403. // the defined char set of the other for each.
  404. //
  405. if ( !RECORD_CHARSET(pRecord1) && RECORD_CHARSET(pRecord2) )
  406. {
  407. RECORD_CHARSET(pRecord1) = RECORD_CHARSET(pRecord2);
  408. }
  409. if ( !RECORD_CHARSET(pRecord2) && RECORD_CHARSET(pRecord1) )
  410. {
  411. RECORD_CHARSET(pRecord2) = RECORD_CHARSET(pRecord1);
  412. }
  413. }
  414. //
  415. // compare type
  416. //
  417. if ( type != pRecord2->wType )
  418. {
  419. DNSDBG( UPDATE, (
  420. "record compare failed -- type mismatch\n" ));
  421. return( FALSE );
  422. }
  423. //
  424. // compare names
  425. //
  426. if ( ! Dns_NameComparePrivate(
  427. pRecord1->pName,
  428. pRecord2->pName,
  429. RECORD_CHARSET( pRecord1 )
  430. ) )
  431. {
  432. DNSDBG( UPDATE, (
  433. "record compare failed -- owner name mismatch\n" ));
  434. return( FALSE );
  435. }
  436. //
  437. // compare data
  438. //
  439. index = INDEX_FOR_TYPE( type );
  440. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  441. if ( !index || !RRCompareTable[ index ] )
  442. {
  443. fresult = FlatRecordCompare( pRecord1, pRecord2 );
  444. }
  445. else
  446. {
  447. fresult = RRCompareTable[ index ](
  448. pRecord1,
  449. pRecord2 );
  450. }
  451. IF_DNSDBG( UPDATE )
  452. {
  453. DNS_PRINT((
  454. "Dns_RecordCompare(%p, %p) returns = %d.\n",
  455. pRecord1,
  456. pRecord2,
  457. fresult ));
  458. }
  459. return( fresult );
  460. }
  461. DNS_STATUS
  462. buildUnmatchedRecordSet(
  463. OUT PDNS_RECORD * ppDiffRR,
  464. IN OUT PDNS_RECORD pRR
  465. )
  466. /*++
  467. Routine Description:
  468. Build new list of difference records.
  469. Arguments:
  470. ppDiffRR - address to recieve PTR to new set
  471. pRR - incoming RR set; matched records marked wReserved TRUE
  472. Return Value:
  473. ERROR_SUCCESS if successful.
  474. Error code on failure.
  475. --*/
  476. {
  477. PDNS_RECORD pcur;
  478. PDNS_RECORD pnew;
  479. DNS_RRSET rrset;
  480. // init comparison rrset
  481. DNS_RRSET_INIT( rrset );
  482. //
  483. // loop through RR set, add records to either match or diff sets
  484. //
  485. pcur = pRR;
  486. while ( pcur )
  487. {
  488. if ( ! IS_RR_MATCHED(pcur) )
  489. {
  490. // make copy of record
  491. pnew = Dns_RecordCopyEx(
  492. pcur,
  493. RECORD_CHARSET(pcur),
  494. RECORD_CHARSET(pcur)
  495. );
  496. if ( !pnew )
  497. {
  498. // DCR_FIX1: last error not set on all Dns_RecordCopy() failures
  499. // Charlie Wickham was getting some random win32 error
  500. //
  501. // DNS_STATUS status = GetLastError();
  502. // assume unable to copy because of invalid data
  503. DNS_PRINT((
  504. "ERROR: unable to copy record at %p\n"
  505. "\thence unable to build diff of set at %p\n",
  506. pcur,
  507. pRR ));
  508. Dns_RecordListFree( rrset.pFirstRR );
  509. *ppDiffRR = NULL;
  510. //return( status ? status : ERROR_INVALID_DATA );
  511. return( ERROR_INVALID_DATA );
  512. }
  513. DNS_RRSET_ADD( rrset, pnew );
  514. }
  515. pcur = pcur->pNext;
  516. }
  517. *ppDiffRR = rrset.pFirstRR;
  518. return( ERROR_SUCCESS );
  519. }
  520. DWORD
  521. isUnmatchedRecordInSet(
  522. IN PDNS_RECORD pRR
  523. )
  524. /*++
  525. Routine Description:
  526. Check if unmatched record in set.
  527. Arguments:
  528. pRR - incoming RR set; matched records marked wReserved TRUE
  529. Return Value:
  530. Count of all unmatched records in set.
  531. Zero if all records matched.
  532. --*/
  533. {
  534. PDNS_RECORD pcur;
  535. DWORD countUnmatched = 0;
  536. //
  537. // loop through RR set check for unmatched records
  538. //
  539. pcur = pRR;
  540. while ( pcur )
  541. {
  542. if ( ! IS_RR_MATCHED(pcur) )
  543. {
  544. countUnmatched++;
  545. }
  546. pcur = pcur->pNext;
  547. }
  548. return( countUnmatched );
  549. }
  550. DNS_SET_COMPARE_RESULT
  551. WINAPI
  552. Dns_RecordSetCompareEx(
  553. IN OUT PDNS_RECORD pRR1,
  554. IN OUT PDNS_RECORD pRR2,
  555. OUT PDNS_RECORD * ppDiff1, OPTIONAL
  556. OUT PDNS_RECORD * ppDiff2 OPTIONAL
  557. )
  558. /*++
  559. Routine Description:
  560. Compare two records.
  561. Record compare ignores TTL and flags and section information.
  562. Arguments:
  563. pRR1 - first incoming RR set
  564. pRR2 - second incoming RR set
  565. ppDiff1 - addr to receive ptr to unmatched records from first set
  566. ppDiff2 - addr to receive ptr to unmatched records from second set
  567. Return Value:
  568. Result indicating relationship -- or error on allocation error:
  569. DnsSetCompareError
  570. DnsSetCompareIdentical
  571. DnsSetCompareNoOverlap
  572. DnsSetCompareOneSubsetOfTwo
  573. DnsSetCompareTwoSubsetOfOne
  574. DnsSetCompareIntersection
  575. --*/
  576. {
  577. PDNS_RECORD pcur1;
  578. PDNS_RECORD pcur2;
  579. DWORD count1 = 0;
  580. DWORD count2 = 0;
  581. DNS_STATUS status;
  582. DWORD unmatched1;
  583. DWORD unmatched2;
  584. DNS_SET_COMPARE_RESULT result;
  585. //
  586. // init RR sets for compare
  587. // - clear reserved field used as matched flag in compare
  588. //
  589. pcur1 = pRR1;
  590. while ( pcur1 )
  591. {
  592. CLEAR_RR_MATCHED(pcur1);
  593. pcur1 = pcur1->pNext;
  594. count1++;
  595. }
  596. pcur1 = pRR2;
  597. while ( pcur1 )
  598. {
  599. CLEAR_RR_MATCHED(pcur1);
  600. pcur1 = pcur1->pNext;
  601. count2++;
  602. }
  603. //
  604. // loop through set 1
  605. // attempt match of each record to all records in set 2
  606. // except those already matched
  607. pcur1 = pRR1;
  608. while ( pcur1 )
  609. {
  610. pcur2 = pRR2;
  611. while ( pcur2 )
  612. {
  613. if ( !IS_RR_MATCHED(pcur2) && Dns_RecordCompare( pcur1, pcur2 ) )
  614. {
  615. SET_RR_MATCHED(pcur1);
  616. SET_RR_MATCHED(pcur2);
  617. }
  618. pcur2 = pcur2->pNext;
  619. }
  620. pcur1 = pcur1->pNext;
  621. }
  622. //
  623. // get diff record lists, return
  624. // - if no diffs, then have match
  625. //
  626. // tedious, but do all this error handling because it is easy for
  627. // user to pass in bad records that may fail copy routines, need
  628. // way to easily report info, even if only for debugging apps calling in
  629. //
  630. if ( ppDiff1 )
  631. {
  632. status = buildUnmatchedRecordSet( ppDiff1, pRR1 );
  633. if ( status != ERROR_SUCCESS )
  634. {
  635. goto Failed;
  636. }
  637. }
  638. if ( ppDiff2 )
  639. {
  640. status = buildUnmatchedRecordSet( ppDiff2, pRR2 );
  641. if ( status != ERROR_SUCCESS )
  642. {
  643. if ( ppDiff1 && *ppDiff1 )
  644. {
  645. Dns_RecordListFree( *ppDiff1 );
  646. }
  647. goto Failed;
  648. }
  649. }
  650. //
  651. // determine relationship between sets
  652. //
  653. // impl note: the only better way i could see doing this
  654. // is to map relationships directly to bit flags
  655. // however, our enum type doesn't map to bit flags, so
  656. // then would have to run mapping table to map flags to enum
  657. //
  658. // note, we do compare so that NULL lists comes out the first
  659. // as no-overlap rather than as subset
  660. //
  661. unmatched1 = isUnmatchedRecordInSet( pRR1 );
  662. unmatched2 = isUnmatchedRecordInSet( pRR2 );
  663. if ( unmatched1 == count1 )
  664. {
  665. ASSERT( unmatched2 == count2 );
  666. result = DnsSetCompareNoOverlap;
  667. }
  668. else if ( unmatched1 == 0 )
  669. {
  670. if ( unmatched2 == 0 )
  671. {
  672. result = DnsSetCompareIdentical;
  673. }
  674. else
  675. {
  676. ASSERT( unmatched2 != count2 );
  677. result = DnsSetCompareOneSubsetOfTwo;
  678. }
  679. }
  680. else if ( unmatched2 == 0 )
  681. {
  682. result = DnsSetCompareTwoSubsetOfOne;
  683. }
  684. else
  685. {
  686. ASSERT( unmatched2 != count2 );
  687. result = DnsSetCompareIntersection;
  688. }
  689. return( result );
  690. Failed:
  691. *ppDiff1 = *ppDiff2 = NULL;
  692. SetLastError( status );
  693. return( DnsSetCompareError );
  694. }
  695. BOOL
  696. WINAPI
  697. Dns_RecordSetCompare(
  698. IN OUT PDNS_RECORD pRR1,
  699. IN OUT PDNS_RECORD pRR2,
  700. OUT PDNS_RECORD * ppDiff1, OPTIONAL
  701. OUT PDNS_RECORD * ppDiff2 OPTIONAL
  702. )
  703. /*++
  704. Routine Description:
  705. Compare two records.
  706. Record compare ignores TTL and flags and section information.
  707. Arguments:
  708. pRR1 - first incoming RR set
  709. pRR2 - second incoming RR set
  710. ppDiff1 - addr to receive ptr to unmatched records from first set
  711. ppDiff2 - addr to receive ptr to unmatched records from second set
  712. Return Value:
  713. TRUE if record sets equal.
  714. FALSE otherwise.
  715. --*/
  716. {
  717. DNS_SET_COMPARE_RESULT result;
  718. result = Dns_RecordSetCompareEx(
  719. pRR1,
  720. pRR2,
  721. ppDiff1,
  722. ppDiff2 );
  723. return( result == DnsSetCompareIdentical );
  724. }
  725. BOOL
  726. WINAPI
  727. Dns_RecordSetCompareForIntersection(
  728. IN OUT PDNS_RECORD pRR1,
  729. IN OUT PDNS_RECORD pRR2
  730. )
  731. /*++
  732. Routine Description:
  733. Compare two record sets for intersection.
  734. Arguments:
  735. pRR1 - first incoming RR set
  736. pRR2 - second incoming RR set
  737. Return Value:
  738. TRUE if record sets intersect.
  739. FALSE otherwise.
  740. --*/
  741. {
  742. DNS_SET_COMPARE_RESULT result;
  743. result = Dns_RecordSetCompareEx(
  744. pRR1,
  745. pRR2,
  746. NULL,
  747. NULL );
  748. return( result == DnsSetCompareIdentical ||
  749. result == DnsSetCompareIntersection ||
  750. result == DnsSetCompareOneSubsetOfTwo ||
  751. result == DnsSetCompareTwoSubsetOfOne );
  752. }
  753. //
  754. // End rrcomp.c
  755. //