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.

1030 lines
20 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. config.c
  5. Abstract:
  6. Domain Name System (DNS) API
  7. Configuration routines.
  8. Author:
  9. Jim Gilroy (jamesg) September 1999
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. //
  14. // Config mapping table.
  15. //
  16. // Maps config IDs into corresponding registry lookups.
  17. //
  18. typedef struct _ConfigMapping
  19. {
  20. DWORD ConfigId;
  21. DWORD RegId;
  22. BOOLEAN fAdapterAllowed;
  23. BOOLEAN fAdapterRequired;
  24. BYTE CharSet;
  25. //BYTE Reserved;
  26. }
  27. CONFIG_MAPPING, *PCONFIG_MAPPING;
  28. //
  29. // Mapping table
  30. //
  31. CONFIG_MAPPING ConfigMappingArray[] =
  32. {
  33. // In Win2K
  34. DnsConfigPrimaryDomainName_W,
  35. RegIdPrimaryDomainName,
  36. 0,
  37. 0,
  38. DnsCharSetUnicode,
  39. DnsConfigPrimaryDomainName_A,
  40. RegIdPrimaryDomainName,
  41. 0,
  42. 0,
  43. DnsCharSetAnsi,
  44. DnsConfigPrimaryDomainName_UTF8,
  45. RegIdPrimaryDomainName,
  46. 0,
  47. 0,
  48. DnsCharSetUtf8,
  49. // Not available
  50. DnsConfigAdapterDomainName_W,
  51. RegIdAdapterDomainName,
  52. 1,
  53. 1,
  54. DnsCharSetUnicode,
  55. DnsConfigAdapterDomainName_A,
  56. RegIdAdapterDomainName,
  57. 1,
  58. 1,
  59. DnsCharSetAnsi,
  60. DnsConfigAdapterDomainName_UTF8,
  61. RegIdAdapterDomainName,
  62. 1,
  63. 1,
  64. DnsCharSetUtf8,
  65. // In Win2K
  66. DnsConfigDnsServerList,
  67. RegIdDnsServers,
  68. 1, // adapter allowed
  69. 0, // not required
  70. 0,
  71. // Not available
  72. DnsConfigSearchList,
  73. RegIdSearchList,
  74. 0, // adapter allowed
  75. 0, // not required
  76. 0,
  77. DnsConfigAdapterInfo,
  78. 0, // no reg mapping
  79. 0, // adapter allowed
  80. 0, // not required
  81. 0,
  82. // In Win2K
  83. DnsConfigPrimaryHostNameRegistrationEnabled,
  84. RegIdRegisterPrimaryName,
  85. 1, // adapter allowed
  86. 0, // not required
  87. 0,
  88. DnsConfigAdapterHostNameRegistrationEnabled,
  89. RegIdRegisterAdapterName,
  90. 1, // adapter allowed
  91. 0, // adapter note required
  92. 0,
  93. DnsConfigAddressRegistrationMaxCount,
  94. RegIdRegistrationMaxAddressCount,
  95. 1, // adapter allowed
  96. 0, // not required
  97. 0,
  98. // In WindowsXP
  99. DnsConfigHostName_W,
  100. RegIdHostName,
  101. 0,
  102. 0,
  103. DnsCharSetUnicode,
  104. DnsConfigHostName_A,
  105. RegIdHostName,
  106. 0,
  107. 0,
  108. DnsCharSetAnsi,
  109. DnsConfigHostName_UTF8,
  110. RegIdHostName,
  111. 0,
  112. 0,
  113. DnsCharSetUtf8,
  114. // In WindowsXP
  115. //
  116. // System Public -- Windows XP
  117. //
  118. DnsConfigRegistrationEnabled,
  119. RegIdRegistrationEnabled,
  120. 1, // adapter allowed
  121. 0, // not required
  122. 0,
  123. DnsConfigWaitForNameErrorOnAll,
  124. RegIdWaitForNameErrorOnAll,
  125. 0, // no adapter
  126. 0, // not required
  127. 0,
  128. // These exist in system-public space but are
  129. // not DWORDs and table is never used for them
  130. //
  131. // DnsConfigNetworkInformation:
  132. // DnsConfigSearchInformation:
  133. // DnsConfigNetInfo:
  134. };
  135. #define LAST_CONFIG_MAPPED (DnsConfigHostName_UTF8)
  136. #define CONFIG_TABLE_LENGTH (sizeof(ConfigMappingArray) / sizeof(CONFIG_MAPPING))
  137. PCONFIG_MAPPING
  138. GetConfigToRegistryMapping(
  139. IN DNS_CONFIG_TYPE ConfigId,
  140. IN PCWSTR pwsAdapterName,
  141. IN BOOL fCheckAdapter
  142. )
  143. /*++
  144. Routine Description:
  145. Get registry enum type for config enum type.
  146. Purpose of this is to do mapping -- thus hiding internal
  147. registry implemenation -- AND to do check on whether
  148. adapter info is allowed or required for the config type.
  149. Arguments:
  150. ConfigId -- config type
  151. pwsAdapterName -- adapter name
  152. Return Value:
  153. Ptr to config to registry mapping -- if found.
  154. --*/
  155. {
  156. DWORD iter = 0;
  157. PCONFIG_MAPPING pfig;
  158. //
  159. // find config
  160. //
  161. // note, using loop through config IDs; this allows
  162. // use to have gap in config table allowing private
  163. // ids well separated from public id space
  164. //
  165. while ( iter < CONFIG_TABLE_LENGTH )
  166. {
  167. pfig = & ConfigMappingArray[ iter ];
  168. if ( pfig->ConfigId != (DWORD)ConfigId )
  169. {
  170. iter++;
  171. continue;
  172. }
  173. goto Found;
  174. }
  175. goto Invalid;
  176. Found:
  177. //
  178. // verify adapter info is appropriate to config type
  179. //
  180. if ( fCheckAdapter )
  181. {
  182. if ( pwsAdapterName )
  183. {
  184. if ( !pfig->fAdapterAllowed )
  185. {
  186. goto Invalid;
  187. }
  188. }
  189. else
  190. {
  191. if ( pfig->fAdapterRequired )
  192. {
  193. goto Invalid;
  194. }
  195. }
  196. }
  197. return pfig;
  198. Invalid:
  199. DNS_ASSERT( FALSE );
  200. SetLastError( ERROR_INVALID_PARAMETER );
  201. return NULL;
  202. }
  203. DNS_STATUS
  204. LookupDwordConfigValue(
  205. OUT PDWORD pResult,
  206. IN DNS_CONFIG_TYPE ConfigId,
  207. IN PWSTR pwsAdapter
  208. )
  209. /*++
  210. Routine Description:
  211. Get registry enum type for config enum type.
  212. Purpose of this is to do mapping -- thus hiding internal
  213. registry implemenation -- AND to do check on whether
  214. adapter info is allowed or required for the config type.
  215. Arguments:
  216. pResult -- address to recv DWORD result
  217. ConfigId -- config type
  218. pwsAdapter -- adapter name
  219. Return Value:
  220. ERROR_SUCCESS on successful read.
  221. ErrorCode on failure.
  222. --*/
  223. {
  224. PCONFIG_MAPPING pfig;
  225. DNS_STATUS status;
  226. //
  227. // verify config is known and mapped
  228. //
  229. pfig = GetConfigToRegistryMapping(
  230. ConfigId,
  231. pwsAdapter,
  232. TRUE // check adapter validity
  233. );
  234. if ( !pfig )
  235. {
  236. return ERROR_INVALID_PARAMETER;
  237. }
  238. //
  239. // lookup in registry
  240. //
  241. status = Reg_GetDword(
  242. NULL, // no session
  243. NULL, // no key given
  244. pwsAdapter,
  245. pfig->RegId, // reg id for config type
  246. pResult );
  247. #if DBG
  248. if ( status != NO_ERROR )
  249. {
  250. DNSDBG( ANY, (
  251. "Reg_GetDword() failed for config lookup!\n"
  252. "\tstatus = %d\n"
  253. "\tConfigId = %d\n"
  254. "\tRedId = %d\n"
  255. "\tpwsAdapter = %S\n",
  256. status,
  257. ConfigId,
  258. pfig->RegId,
  259. pwsAdapter ));
  260. ASSERT( status == NO_ERROR );
  261. }
  262. #endif
  263. return( status );
  264. }
  265. //
  266. // Public Configuration API
  267. //
  268. DNS_STATUS
  269. DnsQueryConfig(
  270. IN DNS_CONFIG_TYPE ConfigId,
  271. IN DWORD Flag,
  272. IN PWSTR pwsAdapterName,
  273. IN PVOID pReserved,
  274. OUT PVOID pBuffer,
  275. IN OUT PDWORD pBufferLength
  276. )
  277. /*++
  278. Routine Description:
  279. Get DNS configuration info.
  280. Arguments:
  281. ConfigId -- type of config info desired
  282. Flag -- flags to query
  283. pAdapterName -- name of adapter; NULL if no specific adapter
  284. pReserved -- reserved parameter, should be NULL
  285. pBuffer -- buffer to receive config info
  286. pBufferLength -- addr of DWORD containing buffer length; on return
  287. contains length
  288. Return Value:
  289. ERROR_SUCCESS -- if query successful
  290. ERROR_MORE_DATA -- if not enough space in buffer
  291. --*/
  292. {
  293. DNS_STATUS status = ERROR_SUCCESS;
  294. DWORD bufLength = 0;
  295. DWORD resultLength = 0;
  296. PBYTE presult;
  297. PBYTE pallocResult = NULL;
  298. BOOL boolData;
  299. DWORD dwordData;
  300. DNSDBG( TRACE, (
  301. "DnsQueryConfig()\n"
  302. "\tconfig = %d\n"
  303. "\tflag = %08x\n"
  304. "\tadapter = %S\n"
  305. "\tpBuffer = %p\n",
  306. ConfigId,
  307. Flag,
  308. pwsAdapterName,
  309. pBuffer
  310. ));
  311. //
  312. // check out param setup
  313. //
  314. if ( !pBufferLength )
  315. {
  316. return ERROR_INVALID_PARAMETER;
  317. }
  318. if ( pBuffer )
  319. {
  320. bufLength = *pBufferLength;
  321. }
  322. //
  323. // find specific configuration data requested
  324. //
  325. switch( ConfigId )
  326. {
  327. case DnsConfigPrimaryDomainName_W:
  328. presult = (PBYTE) Reg_GetPrimaryDomainName( DnsCharSetUnicode );
  329. goto WideString;
  330. case DnsConfigPrimaryDomainName_A:
  331. presult = (PBYTE) Reg_GetPrimaryDomainName( DnsCharSetAnsi );
  332. goto NarrowString;
  333. case DnsConfigPrimaryDomainName_UTF8:
  334. presult = (PBYTE) Reg_GetPrimaryDomainName( DnsCharSetUtf8 );
  335. goto NarrowString;
  336. case DnsConfigDnsServerList:
  337. presult = (PBYTE) GetDnsServerList(
  338. TRUE // force registry read
  339. );
  340. if ( !presult )
  341. {
  342. status = GetLastError();
  343. if ( status == NO_ERROR )
  344. {
  345. DNS_ASSERT( FALSE );
  346. status = DNS_ERROR_NO_DNS_SERVERS;
  347. }
  348. goto Done;
  349. }
  350. pallocResult = presult;
  351. resultLength = Dns_SizeofIpArray( (PIP_ARRAY)presult );
  352. goto Process;
  353. case DnsConfigPrimaryHostNameRegistrationEnabled:
  354. case DnsConfigAdapterHostNameRegistrationEnabled:
  355. case DnsConfigAddressRegistrationMaxCount:
  356. goto Dword;
  357. //case DnsConfigAdapterDomainName:
  358. //case DnsConfigAdapterInfo:
  359. //case DnsConfigSearchList:
  360. case DnsConfigHostName_W:
  361. presult = (PBYTE) Reg_GetHostName( DnsCharSetUnicode );
  362. goto WideString;
  363. case DnsConfigHostName_A:
  364. presult = (PBYTE) Reg_GetHostName( DnsCharSetAnsi );
  365. goto NarrowString;
  366. case DnsConfigHostName_UTF8:
  367. presult = (PBYTE) Reg_GetHostName( DnsCharSetUtf8 );
  368. goto NarrowString;
  369. case DnsConfigFullHostName_W:
  370. presult = (PBYTE) Reg_GetFullHostName( DnsCharSetUnicode );
  371. goto WideString;
  372. case DnsConfigFullHostName_A:
  373. presult = (PBYTE) Reg_GetFullHostName( DnsCharSetAnsi );
  374. goto NarrowString;
  375. case DnsConfigFullHostName_UTF8:
  376. presult = (PBYTE) Reg_GetFullHostName( DnsCharSetUtf8 );
  377. goto NarrowString;
  378. default:
  379. return ERROR_INVALID_PARAMETER;
  380. }
  381. //
  382. // setup return info for common types
  383. //
  384. // this just avoids code duplication above
  385. //
  386. Dword:
  387. status = LookupDwordConfigValue(
  388. & dwordData,
  389. ConfigId,
  390. pwsAdapterName );
  391. if ( status != NO_ERROR )
  392. {
  393. goto Done;
  394. }
  395. presult = (PBYTE) &dwordData;
  396. resultLength = sizeof(DWORD);
  397. goto Process;
  398. NarrowString:
  399. if ( !presult )
  400. {
  401. status = DNS_ERROR_NO_MEMORY;
  402. goto Done;
  403. }
  404. pallocResult = presult;
  405. resultLength = strlen( (PSTR)presult ) + 1;
  406. goto Process;
  407. WideString:
  408. if ( !presult )
  409. {
  410. status = DNS_ERROR_NO_MEMORY;
  411. goto Done;
  412. }
  413. pallocResult = presult;
  414. resultLength = (wcslen( (PWSTR)presult ) + 1) * sizeof(WCHAR);
  415. goto Process;
  416. Process:
  417. //
  418. // return results -- three basic programs
  419. // - no buffer => only return length required
  420. // - allocate => return allocated result
  421. // - supplied buffer => copy result into buffer
  422. //
  423. // note, this section only handles simple flag datablobs to aVOID
  424. // duplicating code for specific config types above;
  425. // when we add config types that require nested pointers, they must
  426. // roll their own return-results code and jump to Done
  427. //
  428. //
  429. // no buffer
  430. // - no-op, length is set below
  431. if ( !pBuffer )
  432. {
  433. }
  434. //
  435. // allocated result
  436. // - return buffer gets ptr
  437. // - allocate copy of result if not allocated
  438. //
  439. else if ( Flag & DNS_CONFIG_FLAG_ALLOC )
  440. {
  441. PBYTE pheap;
  442. if ( bufLength < sizeof(PVOID) )
  443. {
  444. resultLength = sizeof(PVOID);
  445. status = ERROR_MORE_DATA;
  446. goto Done;
  447. }
  448. // create local alloc buffer
  449. pheap = LocalAlloc( 0, resultLength );
  450. if ( !pheap )
  451. {
  452. status = DNS_ERROR_NO_MEMORY;
  453. goto Done;
  454. }
  455. RtlCopyMemory(
  456. pheap,
  457. presult,
  458. resultLength );
  459. // return ptr to allocated result
  460. * (PVOID*) pBuffer = pheap;
  461. }
  462. //
  463. // allocated result -- but dnsapi alloc
  464. //
  465. else if ( Flag & DNS_CONFIG_FLAG_DNSAPI_ALLOC )
  466. {
  467. if ( bufLength < sizeof(PVOID) )
  468. {
  469. resultLength = sizeof(PVOID);
  470. status = ERROR_MORE_DATA;
  471. goto Done;
  472. }
  473. // if result not allocated, alloc and copy it
  474. if ( ! pallocResult )
  475. {
  476. pallocResult = ALLOCATE_HEAP( resultLength );
  477. if ( !pallocResult )
  478. {
  479. status = DNS_ERROR_NO_MEMORY;
  480. goto Done;
  481. }
  482. RtlCopyMemory(
  483. pallocResult,
  484. presult,
  485. resultLength );
  486. }
  487. // return ptr to allocated result
  488. * (PVOID*) pBuffer = pallocResult;
  489. // clear pallocResult, so not freed in generic cleanup
  490. pallocResult = NULL;
  491. }
  492. //
  493. // copy result to caller buffer
  494. //
  495. else
  496. {
  497. if ( bufLength < resultLength )
  498. {
  499. status = ERROR_MORE_DATA;
  500. goto Done;
  501. }
  502. RtlCopyMemory(
  503. pBuffer,
  504. presult,
  505. resultLength );
  506. }
  507. Done:
  508. //
  509. // set result length
  510. // cleanup any allocated (but not returned) data
  511. //
  512. *pBufferLength = resultLength;
  513. if ( pallocResult )
  514. {
  515. FREE_HEAP( pallocResult );
  516. }
  517. return( status );
  518. }
  519. //
  520. // System Public Configuration API
  521. //
  522. PVOID
  523. WINAPI
  524. DnsQueryConfigAllocEx(
  525. IN DNS_CONFIG_TYPE ConfigId,
  526. IN PWSTR pwsAdapterName,
  527. IN BOOL fLocalAlloc
  528. )
  529. /*++
  530. Routine Description:
  531. Get DNS configuration info.
  532. Allocate DNS configuration info.
  533. This is the cover API both handling the system public API
  534. DnsQueryConfigAlloc() below and the backward compatible
  535. macros for the old hostname and PDN alloc routines (see dnsapi.h)
  536. Arguments:
  537. ConfigId -- type of config info desired
  538. pAdapterName -- name of adapter; NULL if no specific adapter
  539. fLocalAlloc -- allocate with LocalAlloc
  540. Return Value:
  541. ERROR_SUCCESS -- if query successful
  542. ERROR_MORE_DATA -- if not enough space in buffer
  543. --*/
  544. {
  545. DNS_STATUS status = ERROR_SUCCESS;
  546. DWORD bufLength = sizeof(PVOID);
  547. PBYTE presult = NULL;
  548. DNSDBG( TRACE, (
  549. "DnsQueryConfigAllocEx()\n"
  550. "\tconfig = %d\n"
  551. "\tadapter = %S\n"
  552. "\tflocal = %d\n",
  553. ConfigId,
  554. pwsAdapterName,
  555. fLocalAlloc
  556. ));
  557. //
  558. // SDK-public types
  559. //
  560. if ( ConfigId < DnsConfigSystemBase )
  561. {
  562. //
  563. // DCR: could screen here for alloc types
  564. //
  565. // DnsConfigPrimaryDomainName_W:
  566. // DnsConfigPrimaryDomainName_A:
  567. // DnsConfigPrimaryDomainName_UTF8:
  568. // DnsConfigHostname_W:
  569. // DnsConfigHostname_A:
  570. // DnsConfigHostname_UTF8:
  571. // DnsConfigDnsServerList:
  572. //
  573. status = DnsQueryConfig(
  574. ConfigId,
  575. fLocalAlloc
  576. ? DNS_CONFIG_FLAG_LOCAL_ALLOC
  577. : DNS_CONFIG_FLAG_DNSAPI_ALLOC,
  578. pwsAdapterName,
  579. NULL, // reserved
  580. & presult,
  581. & bufLength );
  582. if ( status != NO_ERROR )
  583. {
  584. SetLastError( status );
  585. return NULL;
  586. }
  587. return presult;
  588. }
  589. //
  590. // System public types
  591. //
  592. if ( fLocalAlloc )
  593. {
  594. goto Invalid;
  595. }
  596. switch ( ConfigId )
  597. {
  598. case DnsConfigNetworkInformation:
  599. return GetNetworkInformation();
  600. case DnsConfigSearchInformation:
  601. return GetSearchInformation();
  602. case DnsConfigNetInfo:
  603. return NetInfo_Get(
  604. TRUE, // force
  605. TRUE // include IP addresses
  606. );
  607. case DnsConfigIp4AddressArray:
  608. return LocalIp_GetIp4Array();
  609. // unknown falls through to invalid
  610. }
  611. Invalid:
  612. DNS_ASSERT( FALSE );
  613. SetLastError( ERROR_INVALID_PARAMETER );
  614. return( NULL );
  615. }
  616. //
  617. // DWORD system-public config
  618. //
  619. DWORD
  620. DnsQueryConfigDword(
  621. IN DNS_CONFIG_TYPE ConfigId,
  622. IN PWSTR pwsAdapter
  623. )
  624. /*++
  625. Routine Description:
  626. Get DNS DWORD configuration value.
  627. This is system public routine.
  628. Arguments:
  629. ConfigId -- type of config info desired
  630. pwsAdapter -- name of adapter; NULL if no specific adapter
  631. Return Value:
  632. DWORD config value.
  633. Zero if no such config.
  634. --*/
  635. {
  636. DNS_STATUS status;
  637. DWORD value = 0;
  638. DNSDBG( TRACE, (
  639. "DnsQueryConfigDword()\n"
  640. "\tconfig = %d\n"
  641. "\tadapter = %S\n",
  642. ConfigId,
  643. pwsAdapter
  644. ));
  645. status = LookupDwordConfigValue(
  646. & value,
  647. ConfigId,
  648. pwsAdapter );
  649. #if DBG
  650. if ( status != NO_ERROR )
  651. {
  652. DNSDBG( ANY, (
  653. "LookupDwordConfigValue() failed for config lookup!\n"
  654. "\tstatus = %d\n"
  655. "\tConfigId = %d\n"
  656. "\tpwsAdapter = %S\n",
  657. status,
  658. ConfigId,
  659. pwsAdapter ));
  660. DNS_ASSERT( status == NO_ERROR );
  661. }
  662. #endif
  663. return( value );
  664. }
  665. DNS_STATUS
  666. DnsSetConfigDword(
  667. IN DNS_CONFIG_TYPE ConfigId,
  668. IN PWSTR pwsAdapter,
  669. IN DWORD NewValue
  670. )
  671. /*++
  672. Routine Description:
  673. Set DNS DWORD configuration value.
  674. This is system public routine.
  675. Arguments:
  676. ConfigId -- type of config info desired
  677. pwsAdapter -- name of adapter; NULL if no specific adapter
  678. NewValue -- new value for parameter
  679. Return Value:
  680. DWORD config value.
  681. Zero if no such config.
  682. --*/
  683. {
  684. PCONFIG_MAPPING pfig;
  685. DNSDBG( TRACE, (
  686. "DnsSetConfigDword()\n"
  687. "\tconfig = %d\n"
  688. "\tadapter = %S\n"
  689. "\tvalue = %d (%08x)\n",
  690. ConfigId,
  691. pwsAdapter,
  692. NewValue, NewValue
  693. ));
  694. //
  695. // verify config is known and mapped
  696. //
  697. pfig = GetConfigToRegistryMapping(
  698. ConfigId,
  699. pwsAdapter,
  700. TRUE // check adapter validity
  701. );
  702. if ( !pfig )
  703. {
  704. return ERROR_INVALID_PARAMETER;
  705. }
  706. //
  707. // set in registry
  708. //
  709. return Reg_SetDwordPropertyAndAlertCache(
  710. pwsAdapter, // adapter name key (if any)
  711. pfig->RegId,
  712. NewValue );
  713. }
  714. //
  715. // Config data free
  716. //
  717. VOID
  718. WINAPI
  719. DnsFreeConfigStructure(
  720. IN OUT PVOID pData,
  721. IN DNS_CONFIG_TYPE ConfigId
  722. )
  723. /*++
  724. Routine Description:
  725. Free config data
  726. This routine simply handles the mapping between config IDs
  727. and the free type.
  728. Arguments:
  729. pData -- data to free
  730. ConfigId -- config id
  731. Return Value:
  732. None
  733. --*/
  734. {
  735. DNS_FREE_TYPE freeType = DnsFreeFlat;
  736. DNSDBG( TRACE, (
  737. "DnsFreeConfigStructure( %p, %d )\n",
  738. pData,
  739. ConfigId ));
  740. //
  741. // find any unflat config types
  742. //
  743. // note: currently all config types that are not flat
  744. // are system-public only and the config ID is also
  745. // the free type (for convenience); if we start
  746. // exposing some of these bringing them into the low
  747. // space, then this will change
  748. //
  749. // unfortunately these types can NOT be identical because
  750. // the space conflicts in shipped Win2K (FreeType==1 is
  751. // record list)
  752. //
  753. if ( ConfigId > DnsConfigSystemBase &&
  754. ( ConfigId == DnsConfigNetworkInformation ||
  755. ConfigId == DnsConfigSearchInformation ||
  756. ConfigId == DnsConfigAdapterInformation ||
  757. ConfigId == DnsConfigNetInfo ) )
  758. {
  759. freeType = (DNS_FREE_TYPE) ConfigId;
  760. }
  761. DnsFree(
  762. pData,
  763. freeType );
  764. }
  765. //
  766. // End config.c
  767. //