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.

13381 lines
397 KiB

  1. //depot/Lab02_N/DS/netapi/svcdlls/logonsrv/idl/netpdc.c#6 - integrate change 5756 (text)
  2. /*++
  3. Copyright (c) 1987-1992 Microsoft Corporation
  4. Module Name:
  5. NetpDc.c
  6. Abstract:
  7. Routines shared by logonsrv\server and logonsrv\common
  8. Author:
  9. Cliff Van Dyke (cliffv) 20-July-1996
  10. Environment:
  11. User mode only.
  12. Contains NT-specific code.
  13. Requires ANSI C extensions: slash-slash comments, long external names.
  14. Revision History:
  15. --*/
  16. //
  17. // Common include files.
  18. //
  19. #ifndef _NETLOGON_SERVER
  20. #include <nt.h>
  21. #include <ntrtl.h>
  22. #include <nturtl.h>
  23. #include <ntsam.h> // Needed by netlogon.h
  24. #include <ntlsa.h>
  25. #include <rpc.h> // RPC_STATUS
  26. #include <windef.h>
  27. #include <winbase.h>
  28. #include <winsock2.h>
  29. #include <lmcons.h> // General net defines
  30. #include <dsgetdc.h> // DsGetDcName()
  31. #include <dsgetdcp.h>
  32. #include <align.h> // ROUND_UP_COUNT()
  33. #include <config.h> // NetConfig
  34. #include <lmsname.h> // SERVICE_TCPIP
  35. #include <lmerr.h> // System Error Log definitions
  36. #include <icanon.h> // NetpNameValidate()
  37. #include <lmapibuf.h> // NetapipBufferAllocate
  38. #include <lmaccess.h> // UF_*
  39. #include <names.h> // NetpIsDomainNameValid()
  40. #include <netlib.h> // NetpMemoryAllcate(
  41. #include <netlibnt.h> // NetpApiStatusToNtStatus();
  42. #include <netlogon.h> // Definition of mailslot messages
  43. #include <ntddbrow.h> // Needed by nlcommon.h
  44. #include <ntrpcp.h>
  45. #include <logonp.h> // NetpLogon.. routines
  46. #include <tstring.h> // NetpCopyStrToWStr()
  47. #include <time.h> // time() function from C runtime
  48. #if DBG
  49. #define NETLOGONDBG 1
  50. #endif // DBG
  51. #include <nldebug.h> // NlPrint()
  52. #include <nlbind.h> // Definitions shared with netlogon
  53. #include <nlcommon.h> // Definitions shared with netlogon
  54. #ifdef WIN32_CHICAGO
  55. #include "ntcalls.h"
  56. BOOLEAN CodePage = TRUE; // Alway be DBCS
  57. BOOLEAN *NlsMbOemCodePageTag = &CodePage;
  58. #endif // WIN32_CHICAGO
  59. #include <iniparm.h>
  60. #endif // _NETLOGON_SERVER
  61. #include <svcguid.h> // SVCID_INET_HOSTADDRBYNAME
  62. #define OLD_DNS_RECORD 1 // Needed for dnsapi.h
  63. #include <dnsapi.h> // DnsNameCompare_W
  64. #include <dnssrv.h> // NetpSrv...
  65. #include <winldap.h> // ldap_...
  66. //
  67. // Include nlcommon.h again allocating the actual variables
  68. // this time around.
  69. //
  70. #define NLCOMMON_ALLOCATE
  71. #include "nlcommon.h"
  72. #undef NLCOMMON_ALLOCATE
  73. //
  74. // Context describing the SRV records for a DNS name.
  75. //
  76. typedef struct _DSGETDC_CONTEXT {
  77. //
  78. // Original parameters passed by the caller.
  79. //
  80. LPSTR QueriedDnsName;
  81. LPWSTR QueriedSiteName;
  82. GUID QueriedDomainGuid;
  83. LPSTR QueriedDnsForestName;
  84. DWORD QueriedInternalFlags;
  85. // NL_DNS_NAME_TYPE QueriedNlDnsNameType;
  86. //
  87. // Type of this DNS name being queried.
  88. //
  89. NL_DNS_NAME_TYPE NlDnsNameType;
  90. //
  91. // Context of the current DNS name.
  92. //
  93. HANDLE SrvContextHandle;
  94. //
  95. // Flags
  96. //
  97. ULONG QueriedFlags; // Flags passed to DsGetDcOpen
  98. BOOLEAN FirstTime; // This is the first DnsGetDcNext call
  99. } DSGETDC_CONTEXT, *PDSGETDC_CONTEXT;
  100. //
  101. // List of previously cached responses.
  102. //
  103. CRITICAL_SECTION NlDcCritSect;
  104. LIST_ENTRY NlDcDomainList;
  105. ULONG NlDcDomainCount;
  106. #define NL_DC_MAX_DOMAINS 2000 // Avoid infinite domains.
  107. GUID NlDcZeroGuid;
  108. DWORD NlDcDnsFailureTime;
  109. //
  110. // Determine if the passed in DWORD has precisely one bit set.
  111. //
  112. #define JUST_ONE_BIT( _x ) (((_x) != 0 ) && ( ( (~(_x) + 1) & (_x) ) == (_x) ))
  113. // If the caller passes ANY of these flags,
  114. // only an NT 5.0 (or newer) DC should respond.
  115. #define DS_NT50_REQUIRED (DS_DIRECTORY_SERVICE_REQUIRED | \
  116. DS_GC_SERVER_REQUIRED | \
  117. DS_IP_REQUIRED | \
  118. DS_RETURN_DNS_NAME | \
  119. DS_KDC_REQUIRED | \
  120. DS_TIMESERV_REQUIRED | \
  121. DS_IS_DNS_NAME )
  122. // If the caller passes ANY of these flags,
  123. // an NT 5.0 (or newer) DC should respond.
  124. #define DS_NT50_WANTED (DS_NT50_REQUIRED | \
  125. DS_DIRECTORY_SERVICE_PREFERRED )
  126. //
  127. // Define an exception filter to improve debuggin capabilities.
  128. //
  129. #ifdef _NETLOGON_SERVER
  130. #define NL_EXCEPTION NlExceptionFilter(GetExceptionInformation())
  131. LONG
  132. NlExceptionFilter( EXCEPTION_POINTERS * pException)
  133. {
  134. #if DBG
  135. DbgPrint("[Netlogon] exception in DsGetDcName.\n" );
  136. DbgBreakPoint();
  137. #endif // DBG
  138. return EXCEPTION_EXECUTE_HANDLER;
  139. UNREFERENCED_PARAMETER( pException );
  140. }
  141. #endif // _NETLOGON_SERVER
  142. /*++
  143. Routine Description:
  144. This macro clears all of the negative cache fields for a particular DC entry.
  145. Arguments:
  146. _DcEntry -- Address of the entry to flush
  147. Return Value:
  148. None
  149. --*/
  150. #ifdef _NETLOGON_SERVER
  151. #define NlFlushNegativeCacheEntry( _DcEntry ) \
  152. (_DcEntry)->NegativeCacheTime = 0; \
  153. (_DcEntry)->ExpBackoffPeriod = 0; \
  154. (_DcEntry)->BackgroundRetryInitTime.QuadPart = 0; \
  155. (_DcEntry)->PermanentNegativeCache = FALSE;
  156. #else // _NETLOGON_SERVER
  157. #define NlFlushNegativeCacheEntry( _DcEntry )
  158. #endif // _NETLOGON_SERVER
  159. #if NETLOGONDBG
  160. LPSTR
  161. NlMailslotOpcode(
  162. IN WORD Opcode
  163. )
  164. /*++
  165. Routine Description:
  166. Return string describing mailslot message.
  167. Arguments:
  168. Opcode: Opcode of message
  169. Return Value:
  170. String corresponding to opcode
  171. --*/
  172. {
  173. switch ( Opcode ) {
  174. case LOGON_REQUEST:
  175. return "UAS Logon";
  176. case LOGON_RESPONSE:
  177. return "UAS Logon Response <old>";
  178. case LOGON_CENTRAL_QUERY:
  179. return "CENTRAL_QUERY";
  180. case LOGON_DISTRIB_QUERY:
  181. return "DISTRIB_QUERY";
  182. case LOGON_CENTRAL_RESPONSE:
  183. return "CENTRAL_RESPONSE";
  184. case LOGON_DISTRIB_RESPONSE:
  185. return "DISTRIB_RESPONSE";
  186. case LOGON_RESPONSE2:
  187. return "Uas Logon Response";
  188. case LOGON_PRIMARY_QUERY:
  189. return "Primary Query";
  190. case LOGON_START_PRIMARY:
  191. return "Start Primary";
  192. case LOGON_FAIL_PRIMARY:
  193. return "Fail Primary";
  194. case LOGON_UAS_CHANGE:
  195. return "Uas Change";
  196. case LOGON_NO_USER:
  197. return "Uas No User <old>";
  198. case LOGON_PRIMARY_RESPONSE:
  199. return "Primary Response";
  200. case LOGON_RELOGON_RESPONSE:
  201. return "RELOGON_RESPONSE";
  202. case LOGON_WKSTINFO_RESPONSE:
  203. return "WKSTINFO_RESPONSE";
  204. case LOGON_PAUSE_RESPONSE:
  205. return "Uas Pause Response";
  206. case LOGON_USER_UNKNOWN:
  207. return "Uas No User";
  208. case LOGON_UPDATE_ACCOUNT:
  209. return "UPDATE_ACCOUNT";
  210. case LOGON_SAM_LOGON_REQUEST:
  211. return "Sam Logon";
  212. case LOGON_SAM_LOGON_RESPONSE:
  213. return "Sam Logon Response";
  214. case LOGON_SAM_PAUSE_RESPONSE:
  215. return "Sam Pause Response";
  216. case LOGON_SAM_USER_UNKNOWN:
  217. return "Sam User Unknown";
  218. case LOGON_SAM_LOGON_RESPONSE_EX:
  219. return "Sam Logon Response Ex";
  220. case LOGON_SAM_PAUSE_RESPONSE_EX:
  221. return "Sam Pause Response Ex";
  222. case LOGON_SAM_USER_UNKNOWN_EX:
  223. return "Sam User Unknown Ex";
  224. default:
  225. return "<Unknown>";
  226. }
  227. }
  228. LPSTR
  229. NlDgrNameType(
  230. IN DGRECEIVER_NAME_TYPE NameType
  231. )
  232. /*++
  233. Routine Description:
  234. Return string describing datagram receiver name type.
  235. Arguments:
  236. NameType: Name type of interest.
  237. Return Value:
  238. String corresponding to name type
  239. --*/
  240. {
  241. switch ( NameType ) {
  242. case ComputerName:
  243. return "00";
  244. case PrimaryDomain:
  245. return "00";
  246. case LogonDomain:
  247. return "LogonDomain";
  248. case OtherDomain:
  249. return "OtherDomain";
  250. case DomainAnnouncement:
  251. return "__MSBROWSE__";
  252. case MasterBrowser:
  253. return "1D";
  254. case BrowserElection:
  255. return "1E";
  256. case BrowserServer:
  257. return "20";
  258. case DomainName:
  259. return "1C";
  260. case PrimaryDomainBrowser:
  261. return "1B";
  262. case AlternateComputerName:
  263. return "Alternate";
  264. default:
  265. return "<Unknown>";
  266. }
  267. }
  268. #endif // NETLOGONDBG
  269. BOOLEAN
  270. NlReadDwordHklmRegValue(
  271. IN LPCSTR SubKey,
  272. IN LPCSTR ValueName,
  273. OUT PDWORD ValueRead
  274. )
  275. /*++
  276. Routine Description:
  277. Reads a DWORD from the specified registry location.
  278. Arguments:
  279. SubKey - Subkey of the value to read.
  280. ValueName - The name of the value to read.
  281. ValueRead - Returns the value read from the registry.
  282. Return Status:
  283. TRUE - We've successfully read the data.
  284. FALSE - We've not been able to read the data successfully.
  285. --*/
  286. {
  287. LONG RegStatus;
  288. HKEY KeyHandle = NULL;
  289. DWORD ValueType;
  290. DWORD Value;
  291. DWORD ValueSize;
  292. //
  293. // Open the key
  294. //
  295. RegStatus = RegOpenKeyExA(
  296. HKEY_LOCAL_MACHINE,
  297. SubKey,
  298. 0, //Reserved
  299. KEY_QUERY_VALUE,
  300. &KeyHandle );
  301. if ( RegStatus != ERROR_SUCCESS ) {
  302. NlPrint(( 0,
  303. "NlReadDwordHklmRegValue: Cannot open registy key 'HKLM\\%s' %ld.\n",
  304. SubKey,
  305. RegStatus ));
  306. return FALSE;
  307. }
  308. //
  309. // Get the value
  310. //
  311. ValueSize = sizeof(Value);
  312. RegStatus = RegQueryValueExA(
  313. KeyHandle,
  314. ValueName,
  315. 0,
  316. &ValueType,
  317. (LPBYTE)&Value,
  318. &ValueSize );
  319. RegCloseKey( KeyHandle );
  320. if ( RegStatus != ERROR_SUCCESS ) {
  321. NlPrint(( 0,
  322. "NlReadDwordHklmRegValue: Cannot query value of 'HKLM\\%s\\%s' %ld.\n",
  323. SubKey,
  324. ValueName,
  325. RegStatus ));
  326. return FALSE;
  327. }
  328. if ( ValueType != REG_DWORD ) {
  329. NlPrint(( 0,
  330. "NlReadDwordHklmRegValue: value of 'HKLM\\%s\\%s'is not a REG_DWORD %ld.\n",
  331. SubKey,
  332. ValueName,
  333. ValueType ));
  334. return FALSE;
  335. }
  336. if ( ValueSize != sizeof(Value) ) {
  337. NlPrint(( 0,
  338. "NlReadDwordHklmRegValue: value size of 'HKLM\\%s\\%s'is not 4 %ld.\n",
  339. SubKey,
  340. ValueName,
  341. ValueSize ));
  342. return FALSE;
  343. }
  344. //
  345. // We've successfully read the data
  346. //
  347. *ValueRead = Value;
  348. return TRUE;
  349. }
  350. BOOLEAN
  351. NlReadDwordNetlogonRegValue(
  352. IN LPCSTR ValueName,
  353. OUT PDWORD Value
  354. )
  355. /*++
  356. Routine Description:
  357. This is common code (i.e. not netlogon specific) that reads
  358. a DWORD from Netlogon specific locations in registry. It
  359. first reads the value from the Netlogon Group Policy section.
  360. If the value is not specified in teh GP section, this routine
  361. reads the value from the Netlogon PArameters section.
  362. Arguments:
  363. ValueName - The name of the value to read.
  364. Value - Returns the value read from the registry.
  365. Return Status:
  366. TRUE - We've successfully read the data.
  367. FALSE - We've not been able to read the data successfully.
  368. --*/
  369. {
  370. BOOLEAN Result = FALSE;
  371. DWORD LocalValue = 0;
  372. //
  373. // The value given in Netlogon GP section takes precedence.
  374. //
  375. Result = NlReadDwordHklmRegValue( NL_GPPARAM_KEY, // GP section
  376. ValueName,
  377. &LocalValue );
  378. //
  379. // If the value is not specified in the netlogon GP section,
  380. // see if it is in the Netlogon Parameters section.
  381. //
  382. if ( !Result ) {
  383. Result = NlReadDwordHklmRegValue( NL_PARAM_KEY, // Netlogon Parameters section
  384. ValueName,
  385. &LocalValue );
  386. }
  387. if ( Result ) {
  388. *Value = LocalValue;
  389. return TRUE;
  390. }
  391. return FALSE;
  392. }
  393. VOID
  394. NetpIpAddressToStr(
  395. ULONG IpAddress,
  396. CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
  397. )
  398. /*++
  399. Routine Description:
  400. Convert an IP address to a string.
  401. Arguments:
  402. IpAddress - IP Address to convert
  403. IpAddressString - resultant string.
  404. Return Value:
  405. None.
  406. --*/
  407. {
  408. struct in_addr InetAddr;
  409. char * InetAddrString;
  410. //
  411. // Convert the address to ascii
  412. //
  413. InetAddr.s_addr = IpAddress;
  414. InetAddrString = inet_ntoa( InetAddr );
  415. //
  416. // Copy the string our to the caller.
  417. //
  418. if ( InetAddrString == NULL ||
  419. strlen(InetAddrString) > NL_IP_ADDRESS_LENGTH ) {
  420. *IpAddressString = L'\0';
  421. } else {
  422. strcpy( IpAddressString, InetAddrString );
  423. }
  424. return;
  425. }
  426. VOID
  427. NetpIpAddressToWStr(
  428. ULONG IpAddress,
  429. WCHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
  430. )
  431. /*++
  432. Routine Description:
  433. Convert an IP address to a string.
  434. Arguments:
  435. IpAddress - IP Address to convert
  436. IpAddressString - resultant string.
  437. Return Value:
  438. None.
  439. --*/
  440. {
  441. CHAR IpAddressStr[NL_IP_ADDRESS_LENGTH+1];
  442. NetpIpAddressToStr( IpAddress, IpAddressStr );
  443. NetpCopyStrToWStr( IpAddressString, IpAddressStr );
  444. }
  445. NET_API_STATUS
  446. NetpSockAddrToStr(
  447. PSOCKADDR SockAddr,
  448. ULONG SockAddrSize,
  449. CHAR SockAddrString[NL_SOCK_ADDRESS_LENGTH+1]
  450. )
  451. /*++
  452. Routine Description:
  453. Convert an socket address to a string.
  454. Arguments:
  455. SockAddr - Socket Address to convert
  456. SockAddrSize - Size (in bytes) of SockAddr
  457. SockAddrString - resultant string.
  458. Return Value:
  459. NO_ERROR: if translation was successful
  460. --*/
  461. {
  462. int WsaError;
  463. ULONG AddressLength;
  464. #ifdef WIN32_CHICAGO
  465. LPSTR pTemp;
  466. #endif // WIN32_CHICAGO
  467. //
  468. // Convert the address to text.
  469. //
  470. AddressLength = NL_SOCK_ADDRESS_LENGTH+1;
  471. #ifndef WIN32_CHICAGO // Needs Winsock2
  472. WsaError = WSAAddressToStringA( SockAddr,
  473. SockAddrSize,
  474. NULL,
  475. SockAddrString,
  476. &AddressLength );
  477. if ( WsaError != 0 ) {
  478. *SockAddrString = '\0';
  479. WsaError = WSAGetLastError();
  480. NlPrint(( NL_CRITICAL,
  481. "NetpSockAddrToStr: Cannot convert socket address %ld\n",
  482. WsaError ));
  483. return WsaError;
  484. }
  485. #else // WIN32_CHICAGO
  486. // cast the PSOCKADDR to a sockaddr_in and access sin_addr
  487. pTemp = inet_ntoa(((SOCKADDR_IN *) SockAddr)->sin_addr);
  488. if ( pTemp != NULL ) {
  489. strcpy(SockAddrString, pTemp);
  490. } else {
  491. *SockAddrString = '\0';
  492. return ERROR_INTERNAL_ERROR;
  493. }
  494. #endif // WIN32_CHICAGO
  495. return NO_ERROR;
  496. }
  497. NET_API_STATUS
  498. NetpSockAddrToWStr(
  499. PSOCKADDR SockAddr,
  500. ULONG SockAddrSize,
  501. WCHAR SockAddrString[NL_SOCK_ADDRESS_LENGTH+1]
  502. )
  503. /*++
  504. Routine Description:
  505. Convert an socket address to a string.
  506. Arguments:
  507. SockAddr - Socket Address to convert
  508. SockAddrSize - Size (in bytes) of SockAddr
  509. SockAddrString - resultant string.
  510. Return Value:
  511. TRUE if translation was successful
  512. --*/
  513. {
  514. int WsaError;
  515. ULONG AddressLength;
  516. #ifdef WIN32_CHICAGO
  517. CHAR OemSockAddrString[NL_SOCK_ADDRESS_LENGTH+1];
  518. OEM_STRING OemString;
  519. UNICODE_STRING UnicodeString;
  520. #endif // WIN32_CHICAGO
  521. //
  522. // Convert the address to text.
  523. //
  524. AddressLength = NL_SOCK_ADDRESS_LENGTH+1;
  525. #ifndef WIN32_CHICAGO
  526. WsaError = WSAAddressToStringW( SockAddr,
  527. SockAddrSize,
  528. NULL,
  529. SockAddrString,
  530. &AddressLength );
  531. if ( WsaError != 0 ) {
  532. *SockAddrString = '\0';
  533. WsaError = WSAGetLastError();
  534. NlPrint(( NL_CRITICAL,
  535. "NetpSockAddrToWStr: Cannot convert socket address %ld\n",
  536. WsaError ));
  537. return WsaError;
  538. }
  539. #else // WIN32_CHICAGO
  540. // cast the PSOCKADDR to a sockaddr_in and access sin_addr
  541. WsaError = NetpSockAddrToStr( SockAddr,
  542. SockAddrSize,
  543. OemSockAddrString);
  544. RtlInitString(&OemString, OemSockAddrString);
  545. UnicodeString.MaximumLength = ((USHORT)AddressLength) * sizeof(WCHAR);
  546. UnicodeString.Buffer = SockAddrString;
  547. RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE);
  548. #endif // WIN32_CHICAGO
  549. return WsaError;
  550. }
  551. LPWSTR
  552. NetpAllocWStrFromUtf8Str(
  553. IN LPSTR Utf8String
  554. )
  555. /*++
  556. Routine Description:
  557. Convert a UTF8 (zero terminated) string to the corresponding UNICODE
  558. string.
  559. Arguments:
  560. Utf8String - Specifies the UTF8 zero terminated string to convert.
  561. Return Value:
  562. NULL - There was some error in the conversion.
  563. Otherwise, it returns a pointer to the zero terminated UNICODE string in
  564. an allocated buffer. The buffer must be freed using NetApiBufferFree.
  565. --*/
  566. {
  567. return NetpAllocWStrFromUtf8StrEx( Utf8String, -1 );
  568. }
  569. ULONG
  570. NetpUtf8ToUnicodeLen(
  571. IN LPSTR Utf8String
  572. )
  573. /*++
  574. Routine Description:
  575. Returns the number of UNICODE characters that will result if the
  576. specified UTF8 (zero terminated) string is converted to UNICODE.
  577. The resultant character count does not include the trailing zero terminator.
  578. Arguments:
  579. Utf8String - Specifies the UTF8 zero terminated string to convert.
  580. Return Value:
  581. Number of characters.
  582. --*/
  583. {
  584. ULONG UnicodeLen;
  585. //
  586. // Determine the length of the Unicode string.
  587. //
  588. #ifndef WIN32_CHICAGO
  589. // No support for UTF8/7 char on Win95. Use the entry points
  590. // exported in wldap32.dll
  591. UnicodeLen = MultiByteToWideChar(
  592. CP_UTF8,
  593. 0, // All characters can be mapped.
  594. Utf8String,
  595. -1, // NULL terminated.
  596. NULL,
  597. 0 );
  598. if ( UnicodeLen == 0 ) {
  599. return 0;
  600. }
  601. return UnicodeLen - 1;
  602. #else // WIN32_CHICAGO
  603. UnicodeLen = LdapUTF8ToUnicode(
  604. Utf8String,
  605. strlen(Utf8String),
  606. NULL,
  607. 0 );
  608. return UnicodeLen ;
  609. #endif // WIN32_CHICAGO
  610. }
  611. VOID
  612. NetpCopyUtf8StrToWStr(
  613. OUT LPWSTR UnicodeString,
  614. IN LPSTR Utf8String
  615. )
  616. /*++
  617. Routine Description:
  618. Convert a UTF8 (zero terminated) string to the corresponding UNICODE
  619. string.
  620. Arguments:
  621. UnicodeString - Specifies the buffer the UTF8 string is to be copied to.
  622. Utf8String - Specifies the UTF8 zero terminated string to convert.
  623. Return Value:
  624. None.
  625. --*/
  626. {
  627. int UnicodeStringLen;
  628. //
  629. // Translate the string to Unicode.
  630. //
  631. #ifndef WIN32_CHICAGO
  632. // No support for UTF8/7 char on Win95. Use the entry points
  633. // exported in wldap32.dll
  634. UnicodeStringLen = MultiByteToWideChar(
  635. CP_UTF8,
  636. 0, // All characters can be mapped.
  637. Utf8String,
  638. -1, // NULL terminated.
  639. UnicodeString,
  640. 0x7FFFFFFF );
  641. #else // WIN32_CHICAGO
  642. UnicodeStringLen = LdapUTF8ToUnicode(
  643. Utf8String,
  644. strlen(Utf8String),
  645. UnicodeString,
  646. 0x7FFFFFFF );
  647. #endif // WIN32_CHICAGO
  648. if ( UnicodeStringLen == 0 ) {
  649. *UnicodeString = L'\0';
  650. }
  651. }
  652. NET_API_STATUS
  653. NetpAllocWStrFromUtf8StrAsRequired(
  654. IN LPSTR Utf8String,
  655. IN ULONG Utf8StringLength,
  656. IN ULONG UnicodeStringBufferSize,
  657. OUT LPWSTR UnicodeStringBuffer OPTIONAL,
  658. OUT LPWSTR *AllocatedUnicodeString OPTIONAL
  659. )
  660. /*++
  661. Routine Description:
  662. Convert a UTF8 (zero terminated) string to the corresponding UNICODE
  663. string. Allocate memory as required.
  664. Arguments:
  665. Utf8String - Specifies the UTF8 zero terminated string to convert.
  666. Utf8StringLength - Length in bytes of Utf8String excluding the NULL
  667. terminator. (-1 for zero terminated)
  668. UnicodeStringBuffer -- Buffer to copy the covnverted string to. If NULL,
  669. the function will allocate the needed memory and return it in
  670. AllocatedUnicodeString.
  671. UnicodeStringBufferSize - Size in wide charactes of UnicodeStringBuffer.
  672. If this size is less than what's needed to store the resulting
  673. NULL terminated unicode string, the function will allocate the
  674. needed memory and return it in AllocatedUnicodeString.
  675. AllocatedUnicodeString - If the passed in buffer for the resulting
  676. unicode string isn't large enough, the function will allocate
  677. the needed memory and the pointer to the allocated memory will
  678. be returned in this parameter. If NULL and the passed in buffer
  679. isn't large enough to store the resulting NULl terminated string,
  680. the function returns ERROR_INSUFFICIENT_BUFFER. The allocated buffer
  681. must be freed using NetApiBufferFree.
  682. Return Value:
  683. NO_ERROR - The strinf has been successfully converted.
  684. ERROR_INVALID_PARAMETER - The paramer combination is invalid.
  685. ERROR_INSUFFICIENT_BUFFER - The passed in buffer isn't large enough
  686. and the caller doesn't want this fi=unction to allocate needed
  687. memory (i.e. AllocatedUnicodeString is NULL).
  688. ERROR_NOT_ENOUGH_MEMORY - Couldn't allocate the needed memory.
  689. --*/
  690. {
  691. NET_API_STATUS NetStatus = NO_ERROR;
  692. LPWSTR UnicodeString = NULL;
  693. int UnicodeStringLen = 0;
  694. //
  695. // Sanity check the parameters
  696. //
  697. if ( (UnicodeStringBuffer == NULL || UnicodeStringBufferSize == 0) &&
  698. AllocatedUnicodeString == NULL ) {
  699. return ERROR_INVALID_PARAMETER;
  700. }
  701. //
  702. // Initilization
  703. //
  704. if ( AllocatedUnicodeString != NULL ) {
  705. *AllocatedUnicodeString = NULL;
  706. }
  707. //
  708. // Determine the length of the Unicode string.
  709. //
  710. #ifndef WIN32_CHICAGO
  711. // No support for UTF8/7 char on Win95. Use the entry points
  712. // exported in wldap32.dll
  713. UnicodeStringLen = MultiByteToWideChar(
  714. CP_UTF8,
  715. 0, // All characters can be mapped.
  716. Utf8String,
  717. Utf8StringLength,
  718. UnicodeString,
  719. 0 );
  720. #else // WIN32_CHICAGO
  721. UnicodeStringLen = LdapUTF8ToUnicode(
  722. Utf8String,
  723. Utf8StringLength,
  724. UnicodeString,
  725. 0 );
  726. #endif // WIN32_CHICAGO
  727. if ( UnicodeStringLen == 0 ) {
  728. return ERROR_INVALID_PARAMETER;
  729. }
  730. //
  731. // Allocate a buffer for the Unicode string,
  732. // if the passed buffer isn't large enough
  733. //
  734. if ( UnicodeStringBuffer == NULL ||
  735. ((ULONG)UnicodeStringLen+1 > UnicodeStringBufferSize) ) {
  736. //
  737. // If the caller doesn't want us to allocate the
  738. // space needed, tell him his buffer isn't large enough
  739. //
  740. if ( AllocatedUnicodeString == NULL ) {
  741. return ERROR_INSUFFICIENT_BUFFER;
  742. }
  743. NetStatus = NetApiBufferAllocate( (UnicodeStringLen+1)*sizeof(WCHAR),
  744. AllocatedUnicodeString );
  745. if ( NetStatus != NO_ERROR ) {
  746. return NetStatus;
  747. }
  748. UnicodeString = *AllocatedUnicodeString;
  749. } else {
  750. UnicodeString = UnicodeStringBuffer;
  751. }
  752. //
  753. // Translate the string to Unicode.
  754. //
  755. #ifndef WIN32_CHICAGO
  756. // No support for UTF8/7 char on Win95. Use the entry points
  757. // exported in wldap32.dll
  758. UnicodeStringLen = MultiByteToWideChar(
  759. CP_UTF8,
  760. 0, // All characters can be mapped.
  761. Utf8String,
  762. Utf8StringLength,
  763. UnicodeString,
  764. UnicodeStringLen );
  765. #else // WIN32_CHICAGO
  766. UnicodeStringLen = LdapUTF8ToUnicode(
  767. Utf8String,
  768. Utf8StringLength,
  769. UnicodeString,
  770. UnicodeStringLen );
  771. #endif // WIN32_CHICAGO
  772. if ( UnicodeStringLen == 0 ) {
  773. //
  774. // If we have allocated the memory, free it
  775. //
  776. if ( AllocatedUnicodeString != NULL &&
  777. *AllocatedUnicodeString != NULL ) {
  778. NetApiBufferFree( *AllocatedUnicodeString );
  779. *AllocatedUnicodeString = NULL;
  780. }
  781. return ERROR_INVALID_PARAMETER;
  782. }
  783. UnicodeString[UnicodeStringLen] = L'\0';
  784. return NO_ERROR;
  785. }
  786. LPWSTR
  787. NetpAllocWStrFromUtf8StrEx(
  788. IN LPSTR Utf8String,
  789. IN ULONG Length
  790. )
  791. /*++
  792. Routine Description:
  793. Convert a UTF8 (zero terminated) string to the corresponding UNICODE
  794. string.
  795. Arguments:
  796. Utf8String - Specifies the UTF8 zero terminated string to convert.
  797. Length - Length in bytes of Utf8String. (-1 for zero terminated).
  798. Return Value:
  799. NULL - There was some error in the conversion.
  800. Otherwise, it returns a pointer to the zero terminated UNICODE string in
  801. an allocated buffer. The buffer must be freed using NetApiBufferFree.
  802. --*/
  803. {
  804. NET_API_STATUS NetStatus;
  805. LPWSTR UnicodeString = NULL;
  806. NetStatus = NetpAllocWStrFromUtf8StrAsRequired( Utf8String,
  807. Length,
  808. 0,
  809. NULL,
  810. &UnicodeString );
  811. if ( NetStatus == NO_ERROR ) {
  812. return UnicodeString;
  813. } else {
  814. return NULL;
  815. }
  816. }
  817. LPSTR
  818. NetpCreateUtf8StrFromWStr(
  819. IN LPCWSTR UnicodeString,
  820. IN LPSTR TargetDestination OPTIONAL,
  821. IN int TargetDestinationBufferSize
  822. )
  823. /*++
  824. Routine Description:
  825. Convert a Unicode (zero terminated) string to the corresponding
  826. zero terminated UTF8 string.
  827. Arguments:
  828. UnicodeString - Specifies the Unicode zero terminated string to convert.
  829. TargetDestination - Specifies the address in the preallocated buffer to
  830. which to copy the converted string. If NULL, memory is allocated
  831. by this routine.
  832. TargetDestinationBufferSize - The size of the preallocated destination
  833. buffer in bytes. If TargetDestination isn't NULL, TargetDestinationBufferSize
  834. will be used to make sure that the routine does not write beyond the
  835. preallocated buffer limit.
  836. Return Value:
  837. NULL - There was some error in the conversion.
  838. Otherwise: if TargetDestination is NULL, it returns a pointer to the
  839. zero terminated UTF8 string in an allocated buffer. The buffer must be
  840. freed using NetpMemoryFree. If TargetDestination isn't NULL, it
  841. returns a pointer whose value is equal to TargetDestination.
  842. --*/
  843. {
  844. LPSTR Utf8String = NULL;
  845. int Utf8StringLen;
  846. //
  847. // Determine the length of the Unicode string.
  848. //
  849. #ifndef WIN32_CHICAGO
  850. // No support for UTF8/7 char on Win95. Use the entry points
  851. // exported in wldap32.dll
  852. Utf8StringLen = WideCharToMultiByte(
  853. CP_UTF8,
  854. 0, // All characters can be mapped.
  855. UnicodeString,
  856. -1, // Zero terminated
  857. Utf8String,
  858. 0,
  859. NULL,
  860. NULL );
  861. #else // WIN32_CHICAGO
  862. Utf8StringLen = LdapUnicodeToUTF8(
  863. UnicodeString,
  864. wcslen(UnicodeString),
  865. Utf8String,
  866. 0
  867. );
  868. #endif // WIN32_CHICAGO
  869. if ( Utf8StringLen == 0 ||
  870. (TargetDestination != NULL && (Utf8StringLen+1 > TargetDestinationBufferSize)) ) {
  871. return NULL;
  872. }
  873. //
  874. // Allocate a buffer for the UTF8 string as needed.
  875. //
  876. if ( TargetDestination == NULL ) {
  877. Utf8String = NetpMemoryAllocate( Utf8StringLen+1 );
  878. } else {
  879. Utf8String = TargetDestination;
  880. }
  881. if ( Utf8String == NULL ) {
  882. return NULL;
  883. }
  884. //
  885. // Translate the string to Unicode.
  886. //
  887. #ifndef WIN32_CHICAGO
  888. // No support for UTF8/7 char on Win95. Use the entry points
  889. // exported in wldap32.dll
  890. Utf8StringLen = WideCharToMultiByte(
  891. CP_UTF8,
  892. 0, // All characters can be mapped.
  893. UnicodeString,
  894. -1, // Zero terminated
  895. Utf8String,
  896. Utf8StringLen,
  897. NULL,
  898. NULL );
  899. #else // WIN32_CHICAGO
  900. Utf8StringLen = LdapUnicodeToUTF8(
  901. UnicodeString,
  902. wcslen(UnicodeString),
  903. Utf8String,
  904. Utf8StringLen
  905. );
  906. #endif // WIN32_CHICAGO
  907. if ( Utf8StringLen == 0 ) {
  908. if ( TargetDestination == NULL ) {
  909. NetpMemoryFree( Utf8String );
  910. }
  911. return NULL;
  912. }
  913. Utf8String[Utf8StringLen] = '\0';
  914. return Utf8String;
  915. }
  916. LPSTR
  917. NetpAllocUtf8StrFromWStr(
  918. IN LPCWSTR UnicodeString
  919. )
  920. /*++
  921. Routine Description:
  922. Convert a Unicode (zero terminated) string to the corresponding UTF8
  923. string.
  924. Arguments:
  925. UnicodeString - Specifies the Unicode zero terminated string to convert.
  926. Return Value:
  927. NULL - There was some error in the conversion.
  928. Otherwise, it returns a pointer to the zero terminated UTF8 string in
  929. an allocated buffer. The buffer must be freed using NetApiBufferFree.
  930. --*/
  931. {
  932. return NetpCreateUtf8StrFromWStr( UnicodeString, NULL, 0 );
  933. }
  934. LPSTR
  935. NetpAllocUtf8StrFromUnicodeString(
  936. IN PUNICODE_STRING UnicodeString
  937. )
  938. /*++
  939. Routine Description:
  940. Convert a Unicode string to the corresponding UTF8
  941. string.
  942. Arguments:
  943. UnicodeString - Specifies the Unicode string to convert.
  944. Return Value:
  945. NULL - There was some error in the conversion.
  946. Otherwise, it returns a pointer to the zero terminated UTF8 string in
  947. an allocated buffer. The buffer must be freed using NetApiBufferFree.
  948. --*/
  949. {
  950. LPSTR Utf8String = NULL;
  951. int Utf8StringLen;
  952. //
  953. // Sanity check.
  954. //
  955. if ( UnicodeString == NULL || UnicodeString->Buffer == NULL ) {
  956. return NULL;
  957. }
  958. //
  959. // Determine the length of the Unicode string.
  960. //
  961. Utf8StringLen = WideCharToMultiByte(
  962. CP_UTF8,
  963. 0, // All characters can be mapped.
  964. UnicodeString->Buffer,
  965. UnicodeString->Length/sizeof(WCHAR),
  966. Utf8String,
  967. 0,
  968. NULL,
  969. NULL );
  970. if ( Utf8StringLen == 0 ) {
  971. return NULL;
  972. }
  973. //
  974. // Allocate a buffer for the Unicode string.
  975. //
  976. Utf8String = NetpMemoryAllocate( Utf8StringLen+1 );
  977. if ( Utf8String == NULL ) {
  978. return NULL;
  979. }
  980. //
  981. // Translate the string to Unicode.
  982. //
  983. Utf8StringLen = WideCharToMultiByte(
  984. CP_UTF8,
  985. 0, // All characters can be mapped.
  986. UnicodeString->Buffer,
  987. UnicodeString->Length/sizeof(WCHAR),
  988. Utf8String,
  989. Utf8StringLen,
  990. NULL,
  991. NULL );
  992. if ( Utf8StringLen == 0 ) {
  993. NetpMemoryFree( Utf8String );
  994. return NULL;
  995. }
  996. Utf8String[Utf8StringLen] = '\0';
  997. return Utf8String;
  998. }
  999. BOOLEAN
  1000. NlpCompareUtf8(
  1001. IN LPCSTR Utf8String1,
  1002. IN ULONG Utf8String1Size,
  1003. IN LPCSTR Utf8String2,
  1004. IN ULONG Utf8String2Size
  1005. )
  1006. /*++
  1007. Routine Description:
  1008. Compare if two UTF8 strings are equal. The comparison is case insensitive.
  1009. Arguments:
  1010. Utf8String1 - First string of Utf8 characters to compare.
  1011. Utf8String1Size - Size (in bytes) of Utf8String1
  1012. Utf8String2 - Second string of Utf8 characters to compare.
  1013. Utf8String2Size - Size (in bytes) of Utf8String2
  1014. Return Value:
  1015. TRUE - if the strings are equal
  1016. --*/
  1017. {
  1018. WCHAR UnicodeString1[NL_MAX_DNS_LABEL_LENGTH];
  1019. WCHAR UnicodeString2[NL_MAX_DNS_LABEL_LENGTH];
  1020. int UnicodeString1Len;
  1021. int UnicodeString2Len;
  1022. //
  1023. // If the strings are bit for bit identical
  1024. // return so.
  1025. //
  1026. if ( Utf8String1Size == Utf8String2Size &&
  1027. RtlEqualMemory( Utf8String1, Utf8String2, Utf8String1Size ) ) {
  1028. return TRUE;
  1029. }
  1030. //
  1031. // Convert the strings to UNICODE
  1032. //
  1033. #ifndef WIN32_CHICAGO
  1034. // No support for UTF8/7 char on Win95. Use the entry points
  1035. // exported in wldap32.dll
  1036. UnicodeString1Len = MultiByteToWideChar(
  1037. CP_UTF8,
  1038. 0, // All characters can be mapped.
  1039. Utf8String1,
  1040. Utf8String1Size, // Zero terminated
  1041. UnicodeString1,
  1042. sizeof(UnicodeString1)/sizeof(WCHAR) );
  1043. #else // WIN32_CHICAGO
  1044. UnicodeString1Len = LdapUTF8ToUnicode(
  1045. Utf8String1,
  1046. Utf8String1Size, // Zero terminated
  1047. UnicodeString1,
  1048. sizeof(UnicodeString1)/sizeof(WCHAR) );
  1049. #endif// WIN32_CHICAGO
  1050. if ( UnicodeString1Len == 0 ) {
  1051. return FALSE;
  1052. }
  1053. #ifndef WIN32_CHICAGO
  1054. // No support for UTF8/7 char on Win95. Use the entry points
  1055. // exported in wldap32.dll
  1056. UnicodeString2Len = MultiByteToWideChar(
  1057. CP_UTF8,
  1058. 0, // All characters can be mapped.
  1059. Utf8String2,
  1060. Utf8String2Size, // Zero terminated
  1061. UnicodeString2,
  1062. sizeof(UnicodeString2)/sizeof(WCHAR) );
  1063. #else // WIN32_CHICAGO
  1064. UnicodeString2Len = LdapUTF8ToUnicode(
  1065. Utf8String2,
  1066. Utf8String2Size, // Zero terminated
  1067. UnicodeString2,
  1068. sizeof(UnicodeString2)/sizeof(WCHAR) );
  1069. #endif// WIN32_CHICAGO
  1070. if ( UnicodeString2Len == 0 ) {
  1071. return FALSE;
  1072. }
  1073. //
  1074. // Compare the Unicode strings
  1075. //
  1076. return CompareStringW( LOCALE_SYSTEM_DEFAULT,
  1077. NORM_IGNORECASE,
  1078. UnicodeString1,
  1079. UnicodeString1Len,
  1080. UnicodeString2,
  1081. UnicodeString2Len ) == 2;
  1082. }
  1083. NET_API_STATUS
  1084. NlpUnicodeToCutf8(
  1085. IN LPBYTE MessageBuffer,
  1086. IN LPCWSTR OrigUnicodeString,
  1087. IN BOOLEAN IgnoreDots,
  1088. IN OUT LPBYTE *Utf8String,
  1089. IN OUT PULONG Utf8StringSize,
  1090. IN OUT PULONG CompressCount,
  1091. IN OUT LPWORD CompressOffset,
  1092. IN OUT CHAR **CompressUtf8String
  1093. )
  1094. /*++
  1095. Routine Description:
  1096. Same as NlpUtf8ToCutf8 except the input string is in Unicode.
  1097. Arguments:
  1098. Same as NlpUtf8ToCutf8 except the input string is in Unicode.
  1099. Return Value:
  1100. Same as NlpUtf8ToCutf8 except the input string is in Unicode.
  1101. --*/
  1102. {
  1103. NET_API_STATUS NetStatus;
  1104. LPSTR LocalUtf8String;
  1105. //
  1106. // Convert the string to Utf8.
  1107. //
  1108. //
  1109. // Default to an empty string.
  1110. //
  1111. if ( !ARGUMENT_PRESENT(OrigUnicodeString) || *OrigUnicodeString == '\0' ) {
  1112. LocalUtf8String = NULL;
  1113. } else {
  1114. LocalUtf8String = NetpAllocUtf8StrFromWStr( OrigUnicodeString );
  1115. if ( LocalUtf8String == NULL ) {
  1116. return ERROR_NOT_ENOUGH_MEMORY;
  1117. }
  1118. }
  1119. //
  1120. // Pack it.
  1121. //
  1122. NetStatus = NlpUtf8ToCutf8( MessageBuffer,
  1123. LocalUtf8String,
  1124. IgnoreDots,
  1125. Utf8String,
  1126. Utf8StringSize,
  1127. CompressCount,
  1128. CompressOffset,
  1129. CompressUtf8String );
  1130. NetApiBufferFree( LocalUtf8String );
  1131. return NetStatus;
  1132. }
  1133. NET_API_STATUS
  1134. NlpUtf8ToCutf8(
  1135. IN LPBYTE MessageBuffer,
  1136. IN LPCSTR OrigUtf8String,
  1137. IN BOOLEAN IgnoreDots,
  1138. IN OUT LPBYTE *Utf8String,
  1139. IN OUT PULONG Utf8StringSize,
  1140. IN OUT PULONG CompressCount,
  1141. IN OUT LPWORD CompressOffset,
  1142. IN OUT CHAR **CompressUtf8String
  1143. )
  1144. /*++
  1145. Routine Description:
  1146. Convert the passed in OrigUtf8String into a counted UTF-8 string. The
  1147. resultant string is actually a series of counted strings in RFC 1035 DNS
  1148. format. Each label (up to 63 bytes terminated in a '.') is preceeded
  1149. by a byte count byte. The final byte count byte is a zero byte.
  1150. This routine also support RFC 1035 compression. In that format, the
  1151. terminating 'byte count' byte might have the high two bits set. In that
  1152. case, that byte and the byte following it represent an "offset" to the
  1153. actual remainder of the string. This routine inputs an array of strings
  1154. that will be matched for compression purposes.
  1155. RFC 1035 limits the character set to A-Z, a-z, 0-9, - and .. This routine
  1156. returns RFC compatible results if the input is limited to that character set.
  1157. The author expects DNS to be extended to include other characters and
  1158. to encode those characters using UTF-8.
  1159. Arguments:
  1160. Buffer - Pointer to the beginning of the buffer that all strings are
  1161. being packed into.
  1162. OrigUtf8String - Zero terminated Utf8 string to be converted.
  1163. IgnoreDots - TRUE if .'s are to be treated as any other character.
  1164. Utf8String - Address of pointer to buffer to copy counted Utf8 string as described above.
  1165. Return a pointer to the byte immediately beyond the copied string.
  1166. Utf8StringSize - On input, specifies the size of the Utf8String buffer.
  1167. Returns the size (in bytes) of the space remaining in the buffer.
  1168. CompressCount - Specifies the number of strings that are candidates for
  1169. compressing the input string.
  1170. Upon successful completion, this count is incremented by one and
  1171. the newly packed string
  1172. CompressOffset - Array of CompressCount offsets that represent the offset
  1173. of the compression string. This offset will be returned at the end of
  1174. Utf8String if the string can indeed be compressed.
  1175. This offset is in host-order and should not include any
  1176. NL_DNS_COMPRESS_WORD_MASK.
  1177. CompressUtf8String - Array of CompressCount strings that are already packed
  1178. in the current message.
  1179. Return Value:
  1180. NO_ERROR - String was coverted successfully.
  1181. ERROR_INVALID_DOMAINNAME - The passed in unicode string contains one
  1182. or more labels longer than 63 bytes (in UTF-8) or short than 1 byte.
  1183. ERROR_INSUFFICIENT_BUFFER - The resultant UTF-8 string was longer than
  1184. 255 bytes.
  1185. --*/
  1186. {
  1187. NET_API_STATUS NetStatus;
  1188. ULONG CharCount;
  1189. char *Period;
  1190. char *Current;
  1191. LPBYTE *AllocatedLabelPointer = NULL;
  1192. LPBYTE *LabelPointer;
  1193. ULONG LabelCount = 0;
  1194. LPBYTE *CompressLabelPointer;
  1195. ULONG CompressLabelCount;
  1196. ULONG Index;
  1197. //
  1198. // Default to an empty string.
  1199. //
  1200. if ( !ARGUMENT_PRESENT(OrigUtf8String) || *OrigUtf8String == '\0' ) {
  1201. if ( *Utf8StringSize < 1 ) {
  1202. return ERROR_INSUFFICIENT_BUFFER;
  1203. }
  1204. **Utf8String = '\0';
  1205. *Utf8StringSize -= 1;
  1206. *Utf8String += 1;
  1207. return NO_ERROR;
  1208. }
  1209. //
  1210. // Copy the zero terminated utf8 string to the buffer.
  1211. // (Leave room for the initial character count.)
  1212. //
  1213. CharCount = strlen( OrigUtf8String ) + 1;
  1214. if ( (*Utf8StringSize) < CharCount + 1 ) {
  1215. return ERROR_INSUFFICIENT_BUFFER;
  1216. }
  1217. RtlCopyMemory( (*Utf8String)+1, OrigUtf8String, CharCount );
  1218. //
  1219. // Allocate a temporary array to keep track of the compression.
  1220. // (At most every second character can be a .)
  1221. // (Allocate two arrays with a single call to LocalAlloc.)
  1222. //
  1223. AllocatedLabelPointer =
  1224. LocalAlloc( 0, sizeof(LPBYTE) * (CharCount / 2) +
  1225. sizeof(LPBYTE) * (NL_MAX_DNS_LENGTH/2) );
  1226. if ( AllocatedLabelPointer == NULL ) {
  1227. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1228. goto Cleanup;
  1229. }
  1230. LabelPointer = AllocatedLabelPointer;
  1231. CompressLabelPointer = &AllocatedLabelPointer[CharCount/2];
  1232. //
  1233. // Convert the string to a counted string.
  1234. // Simply replace '.'s with character counts
  1235. //
  1236. Current = (*Utf8String)+1;
  1237. while ( *Current != '\0' ) {
  1238. ULONG LabelSize;
  1239. //
  1240. // Find the end of the current label.
  1241. // use strchr not lstrchr to avoid DBCS semantics.
  1242. //
  1243. Period = strchr( Current, '.' );
  1244. //
  1245. // Special case ignoring dots.
  1246. //
  1247. // We can't totally ignore dots since we want to take advantage of
  1248. // RFC 1035 compression. But we have to overcome syntax limitations
  1249. // imposed by the compression.
  1250. //
  1251. if ( IgnoreDots ) {
  1252. //
  1253. // When ignoring dots, two adjacent dots are legal.
  1254. // But they confuse RFC 1035 compression. So, put the second dot
  1255. // into the following label
  1256. //
  1257. if ( Period == Current ) {
  1258. Period = strchr( Current+1, '.' );
  1259. }
  1260. //
  1261. // If the last character is a dot,
  1262. // include it in the last label.
  1263. //
  1264. if ( Period != NULL && *(Period+1) == '\0' ) {
  1265. Period++;
  1266. }
  1267. }
  1268. if ( Period == NULL ) {
  1269. Period = strchr( Current, '\0' );
  1270. }
  1271. //
  1272. // Compute the length of the label.
  1273. //
  1274. LabelSize = (ULONG)(Period - Current);
  1275. if ( LabelSize > NL_MAX_DNS_LABEL_LENGTH || LabelSize < 1 ) {
  1276. //
  1277. // Enforce this even for IgnoreDots. This is a restriction of
  1278. // RFC 1035 compression.
  1279. //
  1280. NetStatus = ERROR_INVALID_DOMAINNAME;
  1281. goto Cleanup;
  1282. }
  1283. //
  1284. // Save a pointer to the current label;
  1285. //
  1286. LabelPointer[LabelCount] = Current - 1;
  1287. LabelCount ++;
  1288. //
  1289. // Save the size of the current label and move to the next label.
  1290. //
  1291. *(Current-1) = (char) LabelSize;
  1292. Current += LabelSize;
  1293. if ( *Current == '\0' ) {
  1294. break;
  1295. }
  1296. if ( *Current == '.' && *(Current+1) == '\0' ) {
  1297. // And ignore trailing .'s
  1298. *Current = '\0';
  1299. CharCount --;
  1300. break;
  1301. }
  1302. Current += 1;
  1303. }
  1304. LabelPointer[LabelCount] = Current;
  1305. NlAssert( ((ULONG)(Current - (*Utf8String))) == CharCount );
  1306. //
  1307. // Loop through the compression strings seeing if we can compress this string.
  1308. //
  1309. if ( CompressCount != NULL ) {
  1310. for ( Index=0; Index<*CompressCount; Index++ ) {
  1311. LPBYTE CurrentCompressString = CompressUtf8String[Index];
  1312. LONG LabelIndex;
  1313. LONG CompressLabelIndex;
  1314. //
  1315. // If we're already compressed as much as we can be,
  1316. // exit.
  1317. //
  1318. if ( LabelCount == 0 ) {
  1319. break;
  1320. }
  1321. //
  1322. // Compute label pointers for the next compress string.
  1323. //
  1324. Current = CurrentCompressString;
  1325. CompressLabelCount = 0;
  1326. while ( *Current != '\0' &&
  1327. ((*Current) & NL_DNS_COMPRESS_BYTE_MASK) != NL_DNS_COMPRESS_BYTE_MASK ) {
  1328. CompressLabelPointer[CompressLabelCount] = Current;
  1329. CompressLabelCount++;
  1330. Current += *Current + 1;
  1331. }
  1332. CompressLabelPointer[CompressLabelCount] = Current;
  1333. //
  1334. // Skip this string if there are no labels
  1335. //
  1336. if ( CompressLabelCount == 0 ) {
  1337. continue;
  1338. }
  1339. //
  1340. // Skip this string if it is compressed to a different degree than
  1341. // we are now.
  1342. //
  1343. // If we compress with this string, upon decompressesion we'll
  1344. // append to our string anything that is appended to this string.
  1345. // So, we have to make sure the postfixes match.
  1346. //
  1347. if ( *CompressLabelPointer[CompressLabelCount] != *LabelPointer[LabelCount] ) {
  1348. continue;
  1349. }
  1350. // Compare both bytes if there really was compression.
  1351. if ( ((*LabelPointer[LabelCount]) & NL_DNS_COMPRESS_BYTE_MASK) == NL_DNS_COMPRESS_BYTE_MASK &&
  1352. *(CompressLabelPointer[CompressLabelCount]+1) != *(LabelPointer[LabelCount]+1) ) {
  1353. continue;
  1354. }
  1355. //
  1356. // Walk backward through the labels comparing them.
  1357. // While they continue to match,
  1358. // keep lobbing bytes off the end of our return string.
  1359. //
  1360. LabelIndex = LabelCount-1;
  1361. CompressLabelIndex = CompressLabelCount-1;
  1362. while ( LabelIndex >= 0 &&
  1363. CompressLabelIndex >= 0 &&
  1364. NlpCompareUtf8( LabelPointer[LabelIndex]+1,
  1365. *(LabelPointer[LabelIndex]),
  1366. CompressLabelPointer[CompressLabelIndex]+1,
  1367. *(CompressLabelPointer[CompressLabelIndex]) )) {
  1368. //
  1369. // Put the offset onto the end of the current buffer.
  1370. //
  1371. SmbPutUshort( LabelPointer[LabelIndex],
  1372. htons((WORD)(NL_DNS_COMPRESS_WORD_MASK |
  1373. (CompressOffset[Index] +
  1374. CompressLabelPointer[CompressLabelIndex] -
  1375. CurrentCompressString))) );
  1376. //
  1377. // Adjust the total number of bytes returned.
  1378. //
  1379. CharCount = (ULONG)(LabelPointer[LabelIndex] - (*Utf8String)) + sizeof(WORD) - 1;
  1380. //
  1381. // Indicate we've ditched yet another label from the string.
  1382. //
  1383. LabelCount --;
  1384. //
  1385. // Adjust Index to next label.
  1386. //
  1387. LabelIndex --;
  1388. CompressLabelIndex --;
  1389. }
  1390. }
  1391. //
  1392. // Save a pointer to this string so the next caller can compress
  1393. // into it.
  1394. //
  1395. CompressUtf8String[*CompressCount] = *Utf8String;
  1396. CompressOffset[*CompressCount] = (USHORT)((*Utf8String) - MessageBuffer);
  1397. *CompressCount += 1;
  1398. }
  1399. //
  1400. // Return the character count.
  1401. // (Include the leading label length byte.)
  1402. *Utf8StringSize -= CharCount+1;
  1403. *Utf8String += CharCount+1;
  1404. NetStatus = NO_ERROR;
  1405. //
  1406. // Done
  1407. //
  1408. Cleanup:
  1409. if ( AllocatedLabelPointer != NULL ) {
  1410. LocalFree( AllocatedLabelPointer );
  1411. }
  1412. return NetStatus;
  1413. }
  1414. BOOL
  1415. NlEqualDnsNameU(
  1416. IN PUNICODE_STRING Name1,
  1417. IN PUNICODE_STRING Name2
  1418. )
  1419. /*++
  1420. Routine Description:
  1421. This routine compares two DNS names for equality.
  1422. Case is ignored. A single trailing . is ignored.
  1423. Null is compared equal to a zero length string.
  1424. Arguments:
  1425. Name1 - First DNS name to compare
  1426. Name2 - Second DNS name to compare
  1427. Return Value:
  1428. TRUE: DNS names are equal.
  1429. --*/
  1430. {
  1431. BOOL Result = FALSE;
  1432. LPWSTR String1 = NULL;
  1433. LPWSTR String2 = NULL;
  1434. //
  1435. // Sanity check
  1436. //
  1437. if ( Name1 == NULL ) {
  1438. return (Name2 == NULL);
  1439. } else if ( Name2 == NULL ) {
  1440. return FALSE;
  1441. }
  1442. //
  1443. // Do the work
  1444. //
  1445. String1 = LocalAlloc( 0, Name1->Length + sizeof(WCHAR) );
  1446. if ( String1 == NULL ) {
  1447. goto Cleanup;
  1448. }
  1449. String2 = LocalAlloc( 0, Name2->Length + sizeof(WCHAR) );
  1450. if ( String2 == NULL ) {
  1451. goto Cleanup;
  1452. }
  1453. RtlCopyMemory( String1, Name1->Buffer, Name1->Length );
  1454. String1[ Name1->Length/sizeof(WCHAR) ] = L'\0';
  1455. RtlCopyMemory( String2, Name2->Buffer, Name2->Length );
  1456. String2[ Name2->Length/sizeof(WCHAR) ] = L'\0';
  1457. Result = NlEqualDnsName( (LPCWSTR) String1, (LPCWSTR) String2 );
  1458. Cleanup:
  1459. if ( String1 != NULL ) {
  1460. LocalFree( String1 );
  1461. }
  1462. if ( String2 != NULL ) {
  1463. LocalFree( String2 );
  1464. }
  1465. return Result;
  1466. }
  1467. BOOL
  1468. NlEqualDnsName(
  1469. IN LPCWSTR Name1,
  1470. IN LPCWSTR Name2
  1471. )
  1472. /*++
  1473. Routine Description:
  1474. This routine compares two DNS names for equality.
  1475. Case is ignored. A single trailing . is ignored.
  1476. Null is compared equal to a zero length string.
  1477. Arguments:
  1478. Name1 - First DNS name to compare
  1479. Name2 - Second DNS name to compare
  1480. Return Value:
  1481. TRUE: DNS names are equal.
  1482. --*/
  1483. {
  1484. if ( Name1 == NULL ) {
  1485. return (Name2 == NULL);
  1486. } else if ( Name2 == NULL ) {
  1487. return FALSE;
  1488. }
  1489. return DnsNameCompare_W( (LPWSTR) Name1, (LPWSTR) Name2 );
  1490. }
  1491. BOOL
  1492. NlEqualDnsNameUtf8(
  1493. IN LPCSTR Name1,
  1494. IN LPCSTR Name2
  1495. )
  1496. /*++
  1497. Routine Description:
  1498. This routine compares two DNS names for equality.
  1499. Case is ignored. A single trailing . is ignored.
  1500. Null is compared equal to a zero length string.
  1501. Arguments:
  1502. Name1 - First DNS name to compare
  1503. Name2 - Second DNS name to compare
  1504. Return Value:
  1505. TRUE: DNS names are equal.
  1506. --*/
  1507. {
  1508. if ( Name1 == NULL ) {
  1509. return (Name2 == NULL);
  1510. } else if ( Name2 == NULL ) {
  1511. return FALSE;
  1512. }
  1513. return DnsNameCompare_UTF8( (LPSTR)Name1, (LPSTR)Name2 );
  1514. }
  1515. BOOL
  1516. NetpDcValidDnsDomain(
  1517. IN LPCWSTR DnsDomainName
  1518. )
  1519. /*++
  1520. Routine Description:
  1521. Returns whether the specified string is a valid DNS Domain name.
  1522. Arguments:
  1523. DnsDomainName - DNS domain name to validate.
  1524. Return Value:
  1525. TRUE - The specified name is syntactically a DNS Domain name.
  1526. FALSE - The specified name in not syntactically a DNS Domain name.
  1527. --*/
  1528. {
  1529. DNS_STATUS DnsStatus;
  1530. DnsStatus = DnsValidateDnsName_W( DnsDomainName );
  1531. if ( DnsStatus == ERROR_SUCCESS ||
  1532. DnsStatus == DNS_ERROR_NON_RFC_NAME ) {
  1533. return TRUE;
  1534. }
  1535. return FALSE;
  1536. }
  1537. ULONG
  1538. NetpDcElapsedTime(
  1539. IN ULONG StartTime
  1540. )
  1541. /*++
  1542. Routine Description:
  1543. Returns the time (in milliseconds) that has elapsed is StartTime.
  1544. Arguments:
  1545. StartTime - A time stamp from GetTickCount()
  1546. Return Value:
  1547. Returns the time (in milliseconds) that has elapsed is StartTime.
  1548. --*/
  1549. {
  1550. ULONG CurrentTime;
  1551. //
  1552. // If time has has wrapped,
  1553. // account for it.
  1554. //
  1555. CurrentTime = GetTickCount();
  1556. if ( CurrentTime >= StartTime ) {
  1557. return CurrentTime - StartTime;
  1558. } else {
  1559. return (0xFFFFFFFF-StartTime) + CurrentTime;
  1560. }
  1561. }
  1562. BOOL
  1563. NetpLogonGetCutf8String(
  1564. IN PVOID Message,
  1565. IN DWORD MessageSize,
  1566. IN OUT PCHAR *Where,
  1567. OUT LPSTR *Data
  1568. )
  1569. /*++
  1570. Routine Description:
  1571. Get a counted UTF-8 string (potentially compressed) from a message.
  1572. Return the uncompressed string as a . seperated zero-terminated string.
  1573. A trailing . is returned on the name since all packed strings are assumed
  1574. to be absolute names.
  1575. Arguments:
  1576. Message - Points to a buffer containing the message.
  1577. MessageSize - The number of bytes in the message buffer.
  1578. Where - Indirectly points to the current location in the buffer. The
  1579. data at the current location is validated (i.e., checked to ensure
  1580. its length is within the bounds of the message buffer and not too
  1581. long). If the data is valid, this current location is updated
  1582. to point to the byte following the data in the message buffer.
  1583. Data - Points to a location to return the DNS name.
  1584. A zero length string is returned as a NULL buffer.
  1585. The buffer returned should be freed via NetpMemoryFree.
  1586. Return Value:
  1587. TRUE - the data is valid.
  1588. FALSE - the data is invalid (e.g., DataSize is too big for the buffer.
  1589. --*/
  1590. {
  1591. CHAR DnsName[NL_MAX_DNS_LENGTH+1];
  1592. ULONG DnsNameLength = 0;
  1593. ULONG InitialOffset;
  1594. BYTE LabelSize;
  1595. LPBYTE LocalWhere;
  1596. BYTE PointerBytes[2];
  1597. WORD Pointer;
  1598. BOOLEAN WhereUpdated = FALSE;
  1599. BOOLEAN FirstLabel = TRUE;
  1600. LocalWhere = *Where;
  1601. InitialOffset = (ULONG)(*Where - ((LPBYTE)Message));
  1602. //
  1603. // Loop getting counted strings from the message.
  1604. //
  1605. for (;;) {
  1606. //
  1607. // Get the length of the current label from the buffer.
  1608. //
  1609. if ( !NetpLogonGetBytes( Message, MessageSize, &LocalWhere, 1, &LabelSize ) ) {
  1610. NlPrint(( NL_CRITICAL, "NetpLogonGetCutf8String: Can't get label size.\n" ));
  1611. return FALSE;
  1612. }
  1613. //
  1614. // If this is the end of the string,
  1615. // process it.
  1616. //
  1617. if ( LabelSize == 0 ) {
  1618. //
  1619. // If this is, then we've not updated the callers 'Where',
  1620. // do it now.
  1621. //
  1622. if ( !WhereUpdated ) {
  1623. WhereUpdated = TRUE;
  1624. *Where = LocalWhere;
  1625. }
  1626. //
  1627. // If the string is empty,
  1628. // return the empty string to the caller.
  1629. //
  1630. if ( DnsNameLength == 0 ) {
  1631. *Data = NULL;
  1632. return TRUE;
  1633. }
  1634. //
  1635. // Copy the DNS name to an allocated buffer.
  1636. //
  1637. DnsName[DnsNameLength] = '\0';
  1638. DnsNameLength++;
  1639. *Data = NetpMemoryAllocate( DnsNameLength );
  1640. if ( *Data == NULL ) {
  1641. NlPrint(( NL_CRITICAL, "NetpLogonGetCutf8String: Can't allocate buffer.\n" ));
  1642. return FALSE;
  1643. }
  1644. RtlCopyMemory( *Data, DnsName, DnsNameLength );
  1645. return TRUE;
  1646. //
  1647. // If this is a pointer,
  1648. // get rest of pointer.
  1649. //
  1650. } else if ( LabelSize & NL_DNS_COMPRESS_BYTE_MASK ) {
  1651. //
  1652. // Get the second byte of the pointer.
  1653. //
  1654. if ( !NetpLogonGetBytes( Message, MessageSize, &LocalWhere, 1, &PointerBytes[1] ) ) {
  1655. NlPrint(( NL_CRITICAL, "NetpLogonGetCutf8String: Can't get pointer byte.\n" ));
  1656. return FALSE;
  1657. }
  1658. //
  1659. // Convert the pointer to host order.
  1660. //
  1661. PointerBytes[0] = LabelSize;
  1662. Pointer = ntohs( *((LPWORD)PointerBytes) ) & ~NL_DNS_COMPRESS_WORD_MASK;
  1663. //
  1664. // Ensure the pointer points to before the beginning of this string.
  1665. // This ensures we terminate.
  1666. //
  1667. if ( Pointer >= InitialOffset ) {
  1668. NlPrint(( NL_CRITICAL,
  1669. "NetpLogonGetCutf8String: Pointer offset too large 0x%lx 0x%lx.\n",
  1670. Pointer,
  1671. InitialOffset ));
  1672. return FALSE;
  1673. }
  1674. //
  1675. // If we've not updated the callers 'Where',
  1676. // do it now.
  1677. //
  1678. if ( !WhereUpdated ) {
  1679. WhereUpdated = TRUE;
  1680. *Where = LocalWhere;
  1681. }
  1682. //
  1683. // Prepare the start processing the pointed to string.
  1684. //
  1685. InitialOffset = Pointer;
  1686. LocalWhere = ((LPBYTE)Message) + Pointer;
  1687. //
  1688. // If this is simply a counted label,
  1689. // process it.
  1690. //
  1691. } else {
  1692. //
  1693. // If this isn't the first label,
  1694. // add a '.' after the previous label.
  1695. //
  1696. if ( !FirstLabel ) {
  1697. DnsName[DnsNameLength] = '.';
  1698. DnsNameLength++;
  1699. } else {
  1700. FirstLabel = FALSE;
  1701. }
  1702. //
  1703. // Ensure the current label fits in the local buffer.
  1704. //
  1705. if ( DnsNameLength + LabelSize + 2 >= sizeof(DnsName) ) {
  1706. NlPrint(( NL_CRITICAL,
  1707. "NetpLogonGetCutf8String: Label to long %ld %ld.\n",
  1708. DnsNameLength,
  1709. LabelSize ));
  1710. return FALSE;
  1711. }
  1712. //
  1713. // Copy the label into the local buffer.
  1714. // (Leave an extra byte for a trailing '\0' and '.')
  1715. //
  1716. if ( !NetpLogonGetBytes(
  1717. Message,
  1718. MessageSize,
  1719. &LocalWhere,
  1720. LabelSize,
  1721. &DnsName[DnsNameLength] )) {
  1722. NlPrint(( NL_CRITICAL, "NetpLogonGetCutf8String: Can't get label.\n" ));
  1723. return FALSE;
  1724. }
  1725. DnsNameLength += LabelSize;
  1726. }
  1727. }
  1728. }
  1729. NET_API_STATUS
  1730. NetpDcBuildPing(
  1731. IN BOOL PdcOnly,
  1732. IN ULONG RequestCount,
  1733. IN LPCWSTR UnicodeComputerName,
  1734. IN LPCWSTR UnicodeUserName OPTIONAL,
  1735. IN LPCSTR ResponseMailslotName,
  1736. IN ULONG AllowableAccountControlBits,
  1737. IN PSID RequestedDomainSid OPTIONAL,
  1738. IN ULONG NtVersion,
  1739. OUT PVOID *Message,
  1740. OUT PULONG MessageSize
  1741. )
  1742. /*++
  1743. Routine Description:
  1744. Build the message to ping a DC to see if it exists.
  1745. Arguments:
  1746. PdcOnly - True if only the PDC should respond.
  1747. RequestCount - Retry count of this operation.
  1748. UnicodeComputerName - Netbios computer name of the machine to respond to.
  1749. UnicodeUserName - Account name of the user being pinged.
  1750. If NULL, DC will always respond affirmatively.
  1751. ResponseMailslotName - Name of the mailslot DC is to respond to.
  1752. AllowableAccountControlBits - Mask of allowable account types for UnicodeUserName.
  1753. RequestedDomainSid - Sid of the domain the message is destined to.
  1754. NtVersion - Version of the message.
  1755. 0: For backward compatibility.
  1756. NETLOGON_NT_VERSION_5: for NT 5.0 message.
  1757. NETLOGON_NT_VERSION_5EX: for extended NT 5.0 message
  1758. Message - Returns the message to be sent to the DC in question.
  1759. Buffer must be free using NetpMemoryFree().
  1760. MessageSize - Returns the size (in bytes) of the returned message
  1761. Return Value:
  1762. NO_ERROR - Operation completed successfully;
  1763. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  1764. --*/
  1765. {
  1766. NET_API_STATUS NetStatus;
  1767. LPSTR Where;
  1768. PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest = NULL;
  1769. LPSTR OemComputerName = NULL;
  1770. //
  1771. // If only the PDC should respond,
  1772. // build a primary query packet.
  1773. //
  1774. if ( PdcOnly ) {
  1775. PNETLOGON_LOGON_QUERY LogonQuery;
  1776. //
  1777. // Allocate memory for the primary query message.
  1778. //
  1779. SamLogonRequest = NetpMemoryAllocate( sizeof(NETLOGON_LOGON_QUERY) );
  1780. if( SamLogonRequest == NULL ) {
  1781. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1782. goto Cleanup;
  1783. }
  1784. LogonQuery = (PNETLOGON_LOGON_QUERY)SamLogonRequest;
  1785. //
  1786. // Translate to get an Oem computer name.
  1787. //
  1788. #ifndef WIN32_CHICAGO
  1789. OemComputerName = NetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
  1790. #else
  1791. OemComputerName = MyNetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
  1792. #endif
  1793. if ( OemComputerName == NULL ) {
  1794. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1795. goto Cleanup;
  1796. }
  1797. //
  1798. // Build the query message.
  1799. //
  1800. LogonQuery->Opcode = LOGON_PRIMARY_QUERY;
  1801. Where = LogonQuery->ComputerName;
  1802. NetpLogonPutOemString(
  1803. OemComputerName,
  1804. sizeof(LogonQuery->ComputerName),
  1805. &Where );
  1806. NetpLogonPutOemString(
  1807. (LPSTR) ResponseMailslotName,
  1808. sizeof(LogonQuery->MailslotName),
  1809. &Where );
  1810. NetpLogonPutUnicodeString(
  1811. (LPWSTR) UnicodeComputerName,
  1812. sizeof( LogonQuery->UnicodeComputerName ),
  1813. &Where );
  1814. // Join common code to add NT 5 specific data.
  1815. //
  1816. // If any DC can respond,
  1817. // build a logon query packet.
  1818. //
  1819. } else {
  1820. ULONG DomainSidSize;
  1821. //
  1822. // Allocate memory for the logon request message.
  1823. //
  1824. #ifndef WIN32_CHICAGO
  1825. if ( RequestedDomainSid != NULL ) {
  1826. DomainSidSize = RtlLengthSid( RequestedDomainSid );
  1827. } else {
  1828. DomainSidSize = 0;
  1829. }
  1830. #else // WIN32_CHICAGO
  1831. DomainSidSize = 0;
  1832. #endif // WIN32_CHICAGO
  1833. SamLogonRequest = NetpMemoryAllocate(
  1834. sizeof(NETLOGON_SAM_LOGON_REQUEST) +
  1835. DomainSidSize +
  1836. sizeof(DWORD) // for SID alignment on 4 byte boundary
  1837. );
  1838. if( SamLogonRequest == NULL ) {
  1839. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1840. goto Cleanup;
  1841. }
  1842. //
  1843. // Build the query message.
  1844. //
  1845. SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
  1846. SamLogonRequest->RequestCount = (WORD) RequestCount;
  1847. Where = (PCHAR) &SamLogonRequest->UnicodeComputerName;
  1848. NetpLogonPutUnicodeString(
  1849. (LPWSTR) UnicodeComputerName,
  1850. sizeof(SamLogonRequest->UnicodeComputerName),
  1851. &Where );
  1852. NetpLogonPutUnicodeString(
  1853. (LPWSTR) UnicodeUserName,
  1854. sizeof(SamLogonRequest->UnicodeUserName),
  1855. &Where );
  1856. NetpLogonPutOemString(
  1857. (LPSTR) ResponseMailslotName,
  1858. sizeof(SamLogonRequest->MailslotName),
  1859. &Where );
  1860. NetpLogonPutBytes(
  1861. &AllowableAccountControlBits,
  1862. sizeof(SamLogonRequest->AllowableAccountControlBits),
  1863. &Where );
  1864. //
  1865. // Place domain SID in the message.
  1866. //
  1867. NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
  1868. NetpLogonPutDomainSID( RequestedDomainSid, DomainSidSize, &Where );
  1869. }
  1870. NetpLogonPutNtToken( &Where, NtVersion );
  1871. //
  1872. // Return the message to the caller.
  1873. //
  1874. *Message = SamLogonRequest;
  1875. *MessageSize = (ULONG)(Where - (PCHAR)SamLogonRequest);
  1876. SamLogonRequest = NULL;
  1877. NetStatus = NO_ERROR;
  1878. //
  1879. // Free locally used resources.
  1880. //
  1881. Cleanup:
  1882. if ( OemComputerName != NULL ) {
  1883. NetpMemoryFree( OemComputerName );
  1884. }
  1885. if ( SamLogonRequest != NULL ) {
  1886. NetpMemoryFree( SamLogonRequest );
  1887. }
  1888. return NetStatus;
  1889. }
  1890. NET_API_STATUS
  1891. NetpDcPackFilterBinary(
  1892. IN LPCSTR Name,
  1893. IN LPBYTE Buffer,
  1894. IN ULONG BufferSize,
  1895. IN LPSTR *FilterBuffer,
  1896. IN PULONG FilterSize
  1897. )
  1898. /*++
  1899. Routine Description:
  1900. Pack a binary blob into an LDAP filter.
  1901. Arguments:
  1902. Name - Name of the string.
  1903. Buffer - Pointer to bytes to pack pack.
  1904. If NULL, this routine successfully returns after doing nothing.
  1905. BufferSize - Number of bytes in Buffer.
  1906. FilterBuffer - Specifies a pointer to the address of the buffer.
  1907. This buffer is reallocated as needed to extend the string.
  1908. If the buffer does not exist, it is allocated.
  1909. Buffer must be free using NetpMemoryFree().
  1910. FilterSize - Specifies/Returns the length of FilterBuffer.
  1911. Return Value:
  1912. NO_ERROR - Operation completed successfully;
  1913. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  1914. --*/
  1915. {
  1916. NET_API_STATUS NetStatus;
  1917. ULONG NewSize;
  1918. LPSTR NewBuffer;
  1919. ULONG NameSize;
  1920. LPBYTE Where;
  1921. LPSTR FilterElement = NULL;
  1922. ULONG FilterElementSize;
  1923. #define LDAP_BINARY_EQUAL "="
  1924. #define LDAP_BINARY_EQUAL_SIZE (sizeof(LDAP_BINARY_EQUAL)-1)
  1925. #define LDAP_BINARY_TEMP_SIZE 1024
  1926. //
  1927. // If there's nothing to pack,
  1928. // Pack nothing.
  1929. //
  1930. if ( Buffer == NULL || BufferSize == 0 ) {
  1931. return NO_ERROR;
  1932. }
  1933. //
  1934. // Allocate a buffer for storage local to this procedure.
  1935. // (Don't put in on the stack since we don't want to commit a huge stack.)
  1936. //
  1937. FilterElement = LocalAlloc( 0, LDAP_BINARY_TEMP_SIZE );
  1938. if ( FilterElement == NULL ) {
  1939. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1940. goto Cleanup;
  1941. }
  1942. //
  1943. // Build an escaped version of the buffer.
  1944. //
  1945. NetStatus = ldap_escape_filter_elementA (
  1946. Buffer,
  1947. BufferSize,
  1948. FilterElement,
  1949. LDAP_BINARY_TEMP_SIZE );
  1950. if ( NetStatus != NO_ERROR ) {
  1951. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1952. goto Cleanup;
  1953. }
  1954. //
  1955. // Compute the size of the new buffer.
  1956. //
  1957. if ( *FilterBuffer == NULL ) {
  1958. *FilterSize = 4; // (&)\0
  1959. }
  1960. NameSize = strlen( Name );
  1961. FilterElementSize = strlen( FilterElement );
  1962. NewSize = *FilterSize +
  1963. 1 + // (
  1964. NameSize +
  1965. LDAP_BINARY_EQUAL_SIZE +
  1966. FilterElementSize +
  1967. 1; // )
  1968. //
  1969. // Allocate a new buffer
  1970. //
  1971. NewBuffer = NetpMemoryAllocate( NewSize );
  1972. if ( NewBuffer == NULL ) {
  1973. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1974. goto Cleanup;
  1975. }
  1976. //
  1977. // Copy the existing buffer into the newly allocated space.
  1978. // (Initialize the buffer if this is the first allocation).
  1979. //
  1980. if ( *FilterBuffer == NULL ) {
  1981. strcpy( NewBuffer, "(&" );
  1982. } else {
  1983. RtlCopyMemory( NewBuffer, *FilterBuffer, *FilterSize );
  1984. NetpMemoryFree( *FilterBuffer );
  1985. *FilterBuffer = NULL;
  1986. }
  1987. //
  1988. // Append the new information
  1989. //
  1990. Where = NewBuffer + *FilterSize - 2;
  1991. strcpy( Where, "(");
  1992. Where ++;
  1993. RtlCopyMemory( Where, Name, NameSize );
  1994. Where += NameSize;
  1995. RtlCopyMemory( Where, LDAP_BINARY_EQUAL, LDAP_BINARY_EQUAL_SIZE );
  1996. Where += LDAP_BINARY_EQUAL_SIZE;
  1997. RtlCopyMemory( Where, FilterElement, FilterElementSize );
  1998. Where += FilterElementSize;
  1999. strcpy( Where, "))");
  2000. Where += 2;
  2001. //
  2002. // Tell the caller about the new filter.
  2003. //
  2004. *FilterBuffer = NewBuffer;
  2005. *FilterSize = NewSize;
  2006. NetStatus = NO_ERROR;
  2007. //
  2008. // Free locally used resources.
  2009. //
  2010. Cleanup:
  2011. if ( FilterElement != NULL ) {
  2012. LocalFree( FilterElement );
  2013. }
  2014. return NetStatus;
  2015. }
  2016. NET_API_STATUS
  2017. NetpDcPackFilterString(
  2018. IN LPCSTR Name,
  2019. IN LPCWSTR UnicodeString OPTIONAL,
  2020. IN LPSTR *FilterBuffer,
  2021. IN PULONG FilterSize
  2022. )
  2023. /*++
  2024. Routine Description:
  2025. Pack a Unicode String into the LDAP filter.
  2026. The actual packed string is the UTF-8 representation since that takes
  2027. less space on the wire.
  2028. Arguments:
  2029. Name - Name of the string.
  2030. UnicodeString - String to pack.
  2031. If NULL, this routine successfully returns after doing nothing.
  2032. FilterBuffer - Specifies a pointer to the address of the buffer.
  2033. This buffer is reallocated as needed to extend the string.
  2034. If the buffer does not exist, it is allocated.
  2035. Buffer must be free using NetpMemoryFree().
  2036. FilterSize - Specifies/Returns the length of FilterBuffer.
  2037. Return Value:
  2038. NO_ERROR - Operation completed successfully;
  2039. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  2040. --*/
  2041. {
  2042. NET_API_STATUS NetStatus;
  2043. LPSTR Utf8String = NULL;
  2044. ULONG Utf8StringSize;
  2045. //
  2046. // If there's nothing to pack,
  2047. // Pack nothing.
  2048. //
  2049. if ( UnicodeString == NULL || *UnicodeString == L'\0') {
  2050. return NO_ERROR;
  2051. }
  2052. //
  2053. // Convert to utf8.
  2054. //
  2055. Utf8String = NetpAllocUtf8StrFromWStr( UnicodeString );
  2056. if ( Utf8String == NULL ) {
  2057. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2058. goto Cleanup;
  2059. }
  2060. Utf8StringSize = strlen( Utf8String );
  2061. //
  2062. // Pack the UTF-8 string as binary.
  2063. // LDAP filters have a limited character set (UTF-8 doesn't).
  2064. // The LDAP API will put the UTF-8 string on the wire bit-for-bit
  2065. // indentical to the Utf8String (even though the filter buffer
  2066. // will contain jibberish).
  2067. //
  2068. NetStatus = NetpDcPackFilterBinary( Name,
  2069. Utf8String,
  2070. Utf8StringSize,
  2071. FilterBuffer,
  2072. FilterSize );
  2073. //
  2074. // Free locally used resources.
  2075. //
  2076. Cleanup:
  2077. if ( Utf8String != NULL ) {
  2078. NetpMemoryFree( Utf8String );
  2079. }
  2080. return NetStatus;
  2081. }
  2082. NET_API_STATUS
  2083. NetpDcBuildLdapFilter(
  2084. IN LPCWSTR UnicodeComputerName,
  2085. IN LPCWSTR UnicodeUserName OPTIONAL,
  2086. IN ULONG AllowableAccountControlBits,
  2087. IN PSID RequestedDomainSid OPTIONAL,
  2088. IN LPCWSTR RequestedDnsDomainName OPTIONAL,
  2089. IN GUID *RequestedDomainGuid OPTIONAL,
  2090. IN ULONG NtVersion,
  2091. OUT LPSTR *Message
  2092. )
  2093. /*++
  2094. Routine Description:
  2095. Build the LDAP filter to ping a DC to see if it exists.
  2096. Arguments:
  2097. UnicodeComputerName - Netbios computer name of the machine to respond to.
  2098. UnicodeUserName - Account name of the user being pinged.
  2099. If NULL, DC will always respond affirmatively.
  2100. AllowableAccountControlBits - Mask of allowable account types for UnicodeUserName.
  2101. RequestedDomainSid - Sid of the domain the message is destined to.
  2102. RequestedDnsDomainName - DNS Host Name. Host name of the domain the message
  2103. is destined to.
  2104. RequestedDomainGuid - Domain GUID of the domain this message is
  2105. destined to.
  2106. NtVersion - Version of the message.
  2107. 0: For backward compatibility.
  2108. NETLOGON_NT_VERSION_5: for NT 5.0 message.
  2109. NETLOGON_NT_VERSION_5EX: for extended NT 5.0 message
  2110. Message - Returns the message to be sent to the DC in question.
  2111. Buffer must be free using NetpMemoryFree().
  2112. Return Value:
  2113. NO_ERROR - Operation completed successfully;
  2114. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  2115. --*/
  2116. {
  2117. NET_API_STATUS NetStatus;
  2118. LPSTR FilterBuffer = NULL;
  2119. ULONG FilterSize = 0;
  2120. //
  2121. // Pack the text strings into the filter.
  2122. //
  2123. NetStatus = NetpDcPackFilterString(
  2124. NL_FILTER_DNS_DOMAIN_NAME,
  2125. RequestedDnsDomainName,
  2126. &FilterBuffer,
  2127. &FilterSize );
  2128. if ( NetStatus != NO_ERROR ) {
  2129. goto Cleanup;
  2130. }
  2131. NetStatus = NetpDcPackFilterString(
  2132. NL_FILTER_HOST_NAME,
  2133. UnicodeComputerName,
  2134. &FilterBuffer,
  2135. &FilterSize );
  2136. if ( NetStatus != NO_ERROR ) {
  2137. goto Cleanup;
  2138. }
  2139. NetStatus = NetpDcPackFilterString(
  2140. NL_FILTER_USER_NAME,
  2141. UnicodeUserName,
  2142. &FilterBuffer,
  2143. &FilterSize );
  2144. if ( NetStatus != NO_ERROR ) {
  2145. goto Cleanup;
  2146. }
  2147. //
  2148. // Pack the binary blobs into the filter
  2149. //
  2150. if ( AllowableAccountControlBits != 0 ) {
  2151. NetStatus = NetpDcPackFilterBinary(
  2152. NL_FILTER_ALLOWABLE_ACCOUNT_CONTROL,
  2153. (LPBYTE)&AllowableAccountControlBits,
  2154. sizeof(AllowableAccountControlBits),
  2155. &FilterBuffer,
  2156. &FilterSize );
  2157. if ( NetStatus != NO_ERROR ) {
  2158. goto Cleanup;
  2159. }
  2160. }
  2161. if ( RequestedDomainSid != NULL ) {
  2162. NetStatus = NetpDcPackFilterBinary(
  2163. NL_FILTER_DOMAIN_SID,
  2164. RequestedDomainSid,
  2165. #ifndef WIN32_CHICAGO
  2166. RtlLengthSid( RequestedDomainSid ),
  2167. #else // WIN32_CHICAGO
  2168. 0,
  2169. #endif // WIN32_CHICAGO
  2170. &FilterBuffer,
  2171. &FilterSize );
  2172. if ( NetStatus != NO_ERROR ) {
  2173. goto Cleanup;
  2174. }
  2175. }
  2176. if ( RequestedDomainGuid != NULL ) {
  2177. NetStatus = NetpDcPackFilterBinary(
  2178. NL_FILTER_DOMAIN_GUID,
  2179. (LPBYTE)RequestedDomainGuid,
  2180. sizeof(GUID),
  2181. &FilterBuffer,
  2182. &FilterSize );
  2183. if ( NetStatus != NO_ERROR ) {
  2184. goto Cleanup;
  2185. }
  2186. }
  2187. if ( NtVersion != NETLOGON_NT_VERSION_5 ) {
  2188. NetStatus = NetpDcPackFilterBinary(
  2189. NL_FILTER_NT_VERSION,
  2190. (LPBYTE)&NtVersion,
  2191. sizeof(NtVersion),
  2192. &FilterBuffer,
  2193. &FilterSize );
  2194. if ( NetStatus != NO_ERROR ) {
  2195. goto Cleanup;
  2196. }
  2197. }
  2198. //
  2199. // Return the filter to the caller.
  2200. //
  2201. NlAssert( FilterBuffer != NULL );
  2202. if ( FilterBuffer == NULL ) {
  2203. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2204. goto Cleanup;
  2205. }
  2206. *Message = FilterBuffer;
  2207. NetStatus = NO_ERROR;
  2208. //
  2209. // Free locally used resources.
  2210. //
  2211. Cleanup:
  2212. if ( NetStatus != NO_ERROR ) {
  2213. if ( FilterBuffer != NULL ) {
  2214. NetpMemoryFree( FilterBuffer );
  2215. }
  2216. }
  2217. return NetStatus;
  2218. }
  2219. PNL_DC_CACHE_ENTRY
  2220. NetpDcAllocateCacheEntry(
  2221. IN LPWSTR ServerName OPTIONAL,
  2222. IN LPSTR OemPrimaryDcName OPTIONAL,
  2223. IN LPWSTR UserName OPTIONAL,
  2224. IN LPWSTR DomainName OPTIONAL,
  2225. IN GUID *DomainGuid,
  2226. IN LPSTR DnsForestName OPTIONAL,
  2227. IN LPSTR DnsDomainName OPTIONAL,
  2228. IN LPSTR DnsHostName OPTIONAL,
  2229. IN LPSTR Utf8NetbiosDomainName OPTIONAL,
  2230. IN LPSTR Utf8NetbiosComputerName OPTIONAL,
  2231. IN LPSTR Utf8UserName OPTIONAL,
  2232. IN LPSTR Utf8DcSiteName OPTIONAL,
  2233. IN LPSTR Utf8ClientSiteName OPTIONAL,
  2234. IN ULONG Flags
  2235. )
  2236. /*++
  2237. Routine Description:
  2238. Allocate a cache entry and fill it in.
  2239. Arguments:
  2240. Various fields to fill into an allocated cache entry.
  2241. Return Value:
  2242. Pointer to a newly allocated cache entry
  2243. The cache entry should be freed by calling NetpDcDerefCacheEntry
  2244. NULL: The entry could not be allocated.
  2245. --*/
  2246. {
  2247. NET_API_STATUS NetStatus;
  2248. ULONG ServerNameSize = 0;
  2249. ULONG UserNameSize = 0;
  2250. ULONG DomainNameSize = 0;
  2251. ULONG DnsForestNameSize = 0;
  2252. ULONG DnsDomainNameSize = 0;
  2253. ULONG DnsHostNameSize = 0;
  2254. ULONG DcSiteNameSize = 0;
  2255. ULONG ClientSiteNameSize = 0;
  2256. ULONG CacheEntrySize;
  2257. PCHAR Where;
  2258. PNL_DC_CACHE_ENTRY NlDcCacheEntry;
  2259. //
  2260. // Determine the size of the cache entry to return.
  2261. //
  2262. // Sizeof the server name.
  2263. if ( Utf8NetbiosComputerName != NULL && Utf8NetbiosComputerName[0] != '\0' ) {
  2264. ServerNameSize = (NetpUtf8ToUnicodeLen( Utf8NetbiosComputerName ) + 1) * sizeof(WCHAR);
  2265. } else if ( ServerName != NULL && ServerName[0] != '\0') {
  2266. ServerNameSize = (wcslen(ServerName) + 1) * sizeof(WCHAR);
  2267. } else if ( OemPrimaryDcName != NULL ) {
  2268. ServerNameSize = (strlen(OemPrimaryDcName) + 1) * sizeof(WCHAR);
  2269. }
  2270. // Sizeof the user name.
  2271. if ( Utf8UserName != NULL && Utf8UserName[0] != '\0' ) {
  2272. UserNameSize = NetpUtf8ToUnicodeLen( Utf8UserName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2273. } else if ( UserName != NULL && UserName[0] != '\0') {
  2274. UserNameSize = (wcslen(UserName) + 1) * sizeof(WCHAR);
  2275. }
  2276. // Sizeof the netbios domain name.
  2277. if ( Utf8NetbiosDomainName != NULL && Utf8NetbiosDomainName[0] != '\0' ) {
  2278. DomainNameSize = NetpUtf8ToUnicodeLen( Utf8NetbiosDomainName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2279. } else if ( DomainName != NULL && DomainName[0] != '\0') {
  2280. DomainNameSize = (wcslen(DomainName) + 1) * sizeof(WCHAR);
  2281. }
  2282. // Sizeof the Dns Tree name.
  2283. if ( DnsForestName != NULL ) {
  2284. DnsForestNameSize = NetpUtf8ToUnicodeLen( DnsForestName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2285. }
  2286. // Sizeof the Dns Domain name.
  2287. if ( DnsDomainName != NULL ) {
  2288. DnsDomainNameSize = NetpUtf8ToUnicodeLen( DnsDomainName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2289. }
  2290. // Sizeof the Dns Host name.
  2291. if ( DnsHostName != NULL ) {
  2292. DnsHostNameSize = NetpUtf8ToUnicodeLen( DnsHostName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2293. }
  2294. // Sizeof the Dc Site name.
  2295. if ( Utf8DcSiteName != NULL ) {
  2296. DcSiteNameSize = NetpUtf8ToUnicodeLen( Utf8DcSiteName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2297. }
  2298. // Sizeof the Client Site name.
  2299. if ( Utf8ClientSiteName != NULL ) {
  2300. ClientSiteNameSize = NetpUtf8ToUnicodeLen( Utf8ClientSiteName ) * sizeof(WCHAR) + sizeof(WCHAR);
  2301. }
  2302. //
  2303. // Allocate the buffer to return.
  2304. //
  2305. CacheEntrySize = sizeof( NL_DC_CACHE_ENTRY ) +
  2306. ServerNameSize +
  2307. UserNameSize +
  2308. DomainNameSize +
  2309. DnsForestNameSize +
  2310. DnsDomainNameSize +
  2311. DnsHostNameSize +
  2312. DcSiteNameSize +
  2313. ClientSiteNameSize;
  2314. NlDcCacheEntry = NetpMemoryAllocate( CacheEntrySize );
  2315. if ( NlDcCacheEntry == NULL ) {
  2316. NlPrint(( NL_CRITICAL,
  2317. "NetpDcAllocateCacheEntry: %ws: not enough memory.\n" ));
  2318. return NULL;
  2319. }
  2320. RtlZeroMemory( NlDcCacheEntry, CacheEntrySize );
  2321. Where = (LPBYTE) (NlDcCacheEntry + 1 );
  2322. // Local reference.
  2323. NlDcCacheEntry->ReferenceCount = 1;
  2324. //
  2325. // Copy the collected information out to the caller.
  2326. //
  2327. NlDcCacheEntry->DomainGuid = *DomainGuid;
  2328. NlDcCacheEntry->ReturnFlags = Flags & DS_PING_FLAGS;
  2329. // Copy the server name (removing any \\)
  2330. if ( Utf8NetbiosComputerName != NULL && Utf8NetbiosComputerName[0] != '\0' ) {
  2331. NlDcCacheEntry->UnicodeNetbiosDcName = (LPWSTR) Where;
  2332. if ( Utf8NetbiosComputerName[0] == '\\' && Utf8NetbiosComputerName[1] == '\\' ) {
  2333. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8NetbiosComputerName+2 );
  2334. } else {
  2335. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8NetbiosComputerName );
  2336. }
  2337. } else if ( ServerName != NULL && ServerName[0] != '\0') {
  2338. NlDcCacheEntry->UnicodeNetbiosDcName = (LPWSTR) Where;
  2339. if ( ServerName[0] == L'\\' && ServerName[1] == L'\\' ) {
  2340. wcscpy( (LPWSTR)Where, ServerName+2 );
  2341. } else {
  2342. wcscpy( (LPWSTR)Where, ServerName );
  2343. }
  2344. } else if ( OemPrimaryDcName != NULL ) {
  2345. NlDcCacheEntry->UnicodeNetbiosDcName = (LPWSTR) Where;
  2346. if ( OemPrimaryDcName[0] == '\\' && OemPrimaryDcName[1] == '\\') {
  2347. NetpCopyStrToWStr( (LPWSTR)Where, OemPrimaryDcName+2 );
  2348. } else {
  2349. NetpCopyStrToWStr( (LPWSTR)Where, OemPrimaryDcName );
  2350. }
  2351. }
  2352. Where += ServerNameSize;
  2353. // Copy the user name,
  2354. if ( Utf8UserName != NULL && Utf8UserName[0] != '\0' ) {
  2355. NlDcCacheEntry->UnicodeUserName = (LPWSTR) Where;
  2356. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8UserName );
  2357. } else if ( UserName != NULL && UserName[0] != '\0') {
  2358. NlDcCacheEntry->UnicodeUserName = (LPWSTR) Where;
  2359. wcscpy( (LPWSTR)Where, UserName );
  2360. }
  2361. Where += UserNameSize;
  2362. // Copy the domain name.
  2363. if ( Utf8NetbiosDomainName != NULL && Utf8NetbiosDomainName[0] != '\0' ) {
  2364. NlDcCacheEntry->UnicodeNetbiosDomainName = (LPWSTR) Where;
  2365. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8NetbiosDomainName );
  2366. } else if ( DomainName != NULL && DomainName[0] != '\0') {
  2367. NlDcCacheEntry->UnicodeNetbiosDomainName = (LPWSTR) Where;
  2368. wcscpy( (LPWSTR)Where, DomainName );
  2369. }
  2370. Where += DomainNameSize;
  2371. // Copy the DnsForestName
  2372. if ( DnsForestName != NULL ) {
  2373. NlDcCacheEntry->UnicodeDnsForestName = (LPWSTR) Where;
  2374. NetpCopyUtf8StrToWStr( (LPWSTR)Where, DnsForestName );
  2375. }
  2376. Where += DnsForestNameSize;
  2377. // Copy the DnsDomainName
  2378. if ( DnsDomainName != NULL ) {
  2379. NlDcCacheEntry->UnicodeDnsDomainName = (LPWSTR) Where;
  2380. NetpCopyUtf8StrToWStr( (LPWSTR)Where, DnsDomainName );
  2381. }
  2382. Where += DnsDomainNameSize;
  2383. // Copy the DnsHostName
  2384. if ( DnsHostName != NULL ) {
  2385. NlDcCacheEntry->UnicodeDnsHostName = (LPWSTR) Where;
  2386. NetpCopyUtf8StrToWStr( (LPWSTR)Where, DnsHostName );
  2387. }
  2388. Where += DnsHostNameSize;
  2389. // Copy the DcSiteName
  2390. if ( Utf8DcSiteName != NULL ) {
  2391. NlDcCacheEntry->UnicodeDcSiteName = (LPWSTR) Where;
  2392. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8DcSiteName );
  2393. }
  2394. Where += DcSiteNameSize;
  2395. // Copy the ClientSiteName
  2396. if ( Utf8ClientSiteName != NULL ) {
  2397. NlDcCacheEntry->UnicodeClientSiteName = (LPWSTR) Where;
  2398. NetpCopyUtf8StrToWStr( (LPWSTR)Where, Utf8ClientSiteName );
  2399. }
  2400. Where += ClientSiteNameSize;
  2401. //
  2402. // Save the time when we created the entry
  2403. //
  2404. NlDcCacheEntry->CreationTime = GetTickCount();
  2405. return NlDcCacheEntry;
  2406. }
  2407. NET_API_STATUS
  2408. NetpDcParsePingResponse(
  2409. IN LPCWSTR DisplayDomainName,
  2410. IN PVOID Message,
  2411. IN ULONG MessageSize,
  2412. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry
  2413. )
  2414. /*++
  2415. Routine Description:
  2416. Parse the response message for a ping.
  2417. Arguments:
  2418. DisplayDomainName - Domain name to display on debugger if problems occur
  2419. Message - The message returned from a DC in question.
  2420. MessageSize - Specifies the size (in bytes) of the message
  2421. NlDcCacheEntry - On success, returns a pointer to the cache entry
  2422. describing the found DC. This entry must be dereferenced using
  2423. NetpDcDerefCacheEntry.
  2424. Return Value:
  2425. NO_ERROR - Operation completed successfully;
  2426. ERROR_INVALID_DATA - The message could not be recognized as a valid
  2427. response message.
  2428. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  2429. --*/
  2430. {
  2431. NET_API_STATUS NetStatus;
  2432. PNETLOGON_SAM_LOGON_RESPONSE_EX SamLogonResponseEx;
  2433. PNETLOGON_SAM_LOGON_RESPONSE SamLogonResponse;
  2434. PNETLOGON_PRIMARY PrimaryResponse;
  2435. DWORD SamLogonResponseSize;
  2436. LPWSTR ServerName = NULL;
  2437. LPSTR OemPrimaryDcName = NULL;
  2438. LPWSTR UserName = NULL;
  2439. LPWSTR DomainName = NULL;
  2440. GUID DomainGuid = {0};
  2441. GUID SiteGuid = {0};
  2442. USHORT LocalOpcode;
  2443. LPSTR DnsForestName = NULL;
  2444. LPSTR DnsDomainName = NULL;
  2445. LPSTR DnsHostName = NULL;
  2446. LPSTR Utf8NetbiosDomainName = NULL;
  2447. LPSTR Utf8NetbiosComputerName = NULL;
  2448. LPSTR Utf8UserName = NULL;
  2449. LPSTR Utf8DcSiteName = NULL;
  2450. LPSTR Utf8ClientSiteName = NULL;
  2451. ULONG LocalDcIpAddress;
  2452. SOCKET_ADDRESS DcSocketAddress = {0,0};
  2453. SOCKADDR_IN DcSockAddrIn;
  2454. ULONG Flags;
  2455. LPBYTE Where;
  2456. DWORD Version;
  2457. DWORD VersionFlags;
  2458. //
  2459. // Initialization.
  2460. //
  2461. SamLogonResponse = (PNETLOGON_SAM_LOGON_RESPONSE) Message;
  2462. SamLogonResponseSize = MessageSize;
  2463. *NlDcCacheEntry = NULL;
  2464. //
  2465. // Get the version of the responder.
  2466. //
  2467. Version = NetpLogonGetMessageVersion( SamLogonResponse,
  2468. &SamLogonResponseSize,
  2469. &VersionFlags );
  2470. //
  2471. // Process the message as a function of the opcode.
  2472. //
  2473. LocalOpcode = SamLogonResponse->Opcode;
  2474. switch ( LocalOpcode ) {
  2475. case LOGON_SAM_LOGON_RESPONSE:
  2476. case LOGON_SAM_USER_UNKNOWN:
  2477. case LOGON_SAM_PAUSE_RESPONSE:
  2478. //
  2479. // Ensure the version is expected.
  2480. //
  2481. if ( Version != LMNT_MESSAGE ) {
  2482. NlPrint(( NL_CRITICAL,
  2483. "NetpDcParsePingResponse: %ws: Version bad. %ld\n",
  2484. DisplayDomainName,
  2485. Version ));
  2486. NetStatus = ERROR_INVALID_DATA;
  2487. goto Cleanup;
  2488. }
  2489. //
  2490. // Pick up the Netbios name of the server that responded.
  2491. //
  2492. Where = (PCHAR) &SamLogonResponse->UnicodeLogonServer;
  2493. if ( !NetpLogonGetUnicodeString(
  2494. SamLogonResponse,
  2495. SamLogonResponseSize,
  2496. &Where,
  2497. sizeof(SamLogonResponse->UnicodeLogonServer),
  2498. &ServerName ) ) {
  2499. NlPrint(( NL_CRITICAL,
  2500. "NetpDcParsePingResponse: %ws server name bad.\n",
  2501. DisplayDomainName ));
  2502. NetStatus = ERROR_INVALID_DATA;
  2503. goto Cleanup;
  2504. }
  2505. //
  2506. // Ensure this is a UNC name.
  2507. //
  2508. if ( ServerName[0] != '\0' &&
  2509. (ServerName[0] != '\\' || ServerName[1] != '\\' )) {
  2510. NlPrint(( NL_CRITICAL,
  2511. "NetpDcParsePingResponse: %ws server name not UNC.\n",
  2512. DisplayDomainName ));
  2513. NetStatus = ERROR_INVALID_DATA;
  2514. goto Cleanup;
  2515. }
  2516. //
  2517. // Pick up the name of the account the response is for.
  2518. //
  2519. if ( !NetpLogonGetUnicodeString(
  2520. SamLogonResponse,
  2521. SamLogonResponseSize,
  2522. &Where,
  2523. sizeof(SamLogonResponse->UnicodeUserName ),
  2524. &UserName ) ) {
  2525. NlPrint(( NL_CRITICAL,
  2526. "NetpDcParsePingResponse: %ws: user name bad.\n",
  2527. DisplayDomainName ));
  2528. NetStatus = ERROR_INVALID_DATA;
  2529. goto Cleanup;
  2530. }
  2531. //
  2532. // Pick up the name of the domain the response is from.
  2533. //
  2534. if ( !NetpLogonGetUnicodeString(
  2535. SamLogonResponse,
  2536. SamLogonResponseSize,
  2537. &Where,
  2538. sizeof(SamLogonResponse->UnicodeDomainName ),
  2539. &DomainName ) ) {
  2540. NlPrint(( NL_CRITICAL,
  2541. "NetpDcParsePingResponse: %ws: domain name bad.\n",
  2542. DisplayDomainName ));
  2543. NetStatus = ERROR_INVALID_DATA;
  2544. goto Cleanup;
  2545. }
  2546. //
  2547. // Pick up the NT 5 specific responses.
  2548. //
  2549. if ( VersionFlags & NETLOGON_NT_VERSION_5) {
  2550. //
  2551. // Pick up the GUID of the domain the response is from.
  2552. //
  2553. if ( !NetpLogonGetGuid(
  2554. SamLogonResponse,
  2555. SamLogonResponseSize,
  2556. &Where,
  2557. &DomainGuid ) ) {
  2558. NlPrint(( NL_CRITICAL,
  2559. "NetpDcParsePingResponse: %ws: domain guid bad.\n",
  2560. DisplayDomainName ));
  2561. NetStatus = ERROR_INVALID_DATA;
  2562. goto Cleanup;
  2563. }
  2564. //
  2565. // Pick up the GUID of the site the responding DC is in.
  2566. //
  2567. if ( !NetpLogonGetGuid(
  2568. SamLogonResponse,
  2569. SamLogonResponseSize,
  2570. &Where,
  2571. &SiteGuid ) ) {
  2572. NlPrint(( NL_CRITICAL,
  2573. "NetpDcParsePingResponse: %ws site guid bad.\n",
  2574. DisplayDomainName ));
  2575. NetStatus = ERROR_INVALID_DATA;
  2576. goto Cleanup;
  2577. }
  2578. //
  2579. // Pick up the DNS domain name of the tree the responder is in.
  2580. //
  2581. if ( !NetpLogonGetCutf8String(
  2582. SamLogonResponse,
  2583. SamLogonResponseSize,
  2584. &Where,
  2585. &DnsForestName ) ) {
  2586. NlPrint(( NL_CRITICAL,
  2587. "NetpDcParsePingResponse: %ws DNS forest bad.\n",
  2588. DisplayDomainName ));
  2589. NetStatus = ERROR_INVALID_DATA;
  2590. goto Cleanup;
  2591. }
  2592. //
  2593. // Pick up the DNS domain name the responding DC is in.
  2594. //
  2595. if ( !NetpLogonGetCutf8String(
  2596. SamLogonResponse,
  2597. SamLogonResponseSize,
  2598. &Where,
  2599. &DnsDomainName ) ) {
  2600. NlPrint(( NL_CRITICAL,
  2601. "NetpDcParsePingResponse: %ws: DNS domain bad.\n",
  2602. DisplayDomainName ));
  2603. NetStatus = ERROR_INVALID_DATA;
  2604. goto Cleanup;
  2605. }
  2606. //
  2607. // Pick up the DNS host name of the responding DC.
  2608. //
  2609. if ( !NetpLogonGetCutf8String(
  2610. SamLogonResponse,
  2611. SamLogonResponseSize,
  2612. &Where,
  2613. &DnsHostName ) ) {
  2614. NlPrint(( NL_CRITICAL,
  2615. "NetpDcParsePingResponse: %ws: DNS host bad.\n",
  2616. DisplayDomainName ));
  2617. NetStatus = ERROR_INVALID_DATA;
  2618. goto Cleanup;
  2619. }
  2620. //
  2621. // Pick up the IP Address of the responding DC.
  2622. //
  2623. if ( !NetpLogonGetBytes(
  2624. SamLogonResponse,
  2625. SamLogonResponseSize,
  2626. &Where,
  2627. sizeof(SamLogonResponse->DcIpAddress ),
  2628. &LocalDcIpAddress) ) {
  2629. NlPrint(( NL_CRITICAL,
  2630. "NetpDcParsePingResponse: %ws: IP Address bad.\n",
  2631. DisplayDomainName ));
  2632. NetStatus = ERROR_INVALID_DATA;
  2633. goto Cleanup;
  2634. }
  2635. //
  2636. // Convert the IP address to a sockaddr
  2637. //
  2638. // One should find it mildly humorous that on the host we represent the
  2639. // IP address in net order and that on the net we represent it in host order.
  2640. // I'm chuckling as I write this.
  2641. //
  2642. if ( LocalDcIpAddress != 0 ) {
  2643. DcSockAddrIn.sin_family = AF_INET;
  2644. DcSockAddrIn.sin_port = 0;
  2645. DcSockAddrIn.sin_addr.S_un.S_addr = htonl(LocalDcIpAddress);
  2646. DcSocketAddress.lpSockaddr = (LPSOCKADDR) &DcSockAddrIn;
  2647. DcSocketAddress.iSockaddrLength = sizeof(SOCKADDR_IN);
  2648. }
  2649. //
  2650. // Pick up the flags desribing the responding DC.
  2651. //
  2652. if ( !NetpLogonGetBytes(
  2653. SamLogonResponse,
  2654. SamLogonResponseSize,
  2655. &Where,
  2656. sizeof(SamLogonResponse->Flags ),
  2657. &Flags) ) {
  2658. NlPrint(( NL_CRITICAL,
  2659. "NetpDcParsePingResponse: %ws: Flags bad.\n",
  2660. DisplayDomainName ));
  2661. NetStatus = ERROR_INVALID_DATA;
  2662. goto Cleanup;
  2663. }
  2664. //
  2665. // If not version 5,
  2666. // indicate version 5 specific fields are not present.
  2667. //
  2668. } else {
  2669. RtlZeroMemory( &DomainGuid, sizeof(DomainGuid) );
  2670. Flags = 0;
  2671. }
  2672. break;
  2673. case LOGON_SAM_LOGON_RESPONSE_EX:
  2674. case LOGON_SAM_USER_UNKNOWN_EX:
  2675. case LOGON_SAM_PAUSE_RESPONSE_EX:
  2676. //
  2677. // Map the opcode for easier use by the client.
  2678. //
  2679. switch ( LocalOpcode ) {
  2680. case LOGON_SAM_LOGON_RESPONSE_EX:
  2681. LocalOpcode = LOGON_SAM_LOGON_RESPONSE; break;
  2682. case LOGON_SAM_USER_UNKNOWN_EX:
  2683. LocalOpcode = LOGON_SAM_USER_UNKNOWN; break;
  2684. case LOGON_SAM_PAUSE_RESPONSE_EX:
  2685. LocalOpcode = LOGON_SAM_PAUSE_RESPONSE; break;
  2686. }
  2687. SamLogonResponseEx = (PNETLOGON_SAM_LOGON_RESPONSE_EX) SamLogonResponse;
  2688. //
  2689. // Ensure the version is expected.
  2690. //
  2691. if ( Version != LMNT_MESSAGE ) {
  2692. NlPrint(( NL_CRITICAL,
  2693. "NetpDcParsePingResponse: %ws: Version bad. %ld\n",
  2694. DisplayDomainName,
  2695. Version ));
  2696. NetStatus = ERROR_INVALID_DATA;
  2697. goto Cleanup;
  2698. }
  2699. //
  2700. // Pick up the flags desribing the responding DC.
  2701. //
  2702. Where = (PCHAR) &SamLogonResponseEx->Flags;
  2703. if ( !NetpLogonGetBytes(
  2704. SamLogonResponse,
  2705. SamLogonResponseSize,
  2706. &Where,
  2707. sizeof(SamLogonResponseEx->Flags ),
  2708. &Flags) ) {
  2709. NlPrint(( NL_CRITICAL,
  2710. "NetpDcParsePingResponse: %ws: Flags bad.\n",
  2711. DisplayDomainName ));
  2712. NetStatus = ERROR_INVALID_DATA;
  2713. goto Cleanup;
  2714. }
  2715. //
  2716. // Pick up the GUID of the domain the response is from.
  2717. //
  2718. if ( !NetpLogonGetGuid(
  2719. SamLogonResponse,
  2720. SamLogonResponseSize,
  2721. &Where,
  2722. &DomainGuid ) ) {
  2723. NlPrint(( NL_CRITICAL,
  2724. "NetpDcParsePingResponse: %ws: domain guid bad.\n",
  2725. DisplayDomainName ));
  2726. NetStatus = ERROR_INVALID_DATA;
  2727. goto Cleanup;
  2728. }
  2729. //
  2730. // Pick up the DNS domain name of the tree the responder is in.
  2731. //
  2732. if ( !NetpLogonGetCutf8String(
  2733. SamLogonResponse,
  2734. SamLogonResponseSize,
  2735. &Where,
  2736. &DnsForestName ) ) {
  2737. NlPrint(( NL_CRITICAL,
  2738. "NetpDcParsePingResponse: %ws DNS forest bad.\n",
  2739. DisplayDomainName ));
  2740. NetStatus = ERROR_INVALID_DATA;
  2741. goto Cleanup;
  2742. }
  2743. //
  2744. // Pick up the DNS domain name the responding DC is in.
  2745. //
  2746. if ( !NetpLogonGetCutf8String(
  2747. SamLogonResponse,
  2748. SamLogonResponseSize,
  2749. &Where,
  2750. &DnsDomainName ) ) {
  2751. NlPrint(( NL_CRITICAL,
  2752. "NetpDcParsePingResponse: %ws: DNS domain bad.\n",
  2753. DisplayDomainName ));
  2754. NetStatus = ERROR_INVALID_DATA;
  2755. goto Cleanup;
  2756. }
  2757. //
  2758. // Pick up the DNS host name of the responding DC.
  2759. //
  2760. if ( !NetpLogonGetCutf8String(
  2761. SamLogonResponse,
  2762. SamLogonResponseSize,
  2763. &Where,
  2764. &DnsHostName ) ) {
  2765. NlPrint(( NL_CRITICAL,
  2766. "NetpDcParsePingResponse: %ws: DNS host bad.\n",
  2767. DisplayDomainName ));
  2768. NetStatus = ERROR_INVALID_DATA;
  2769. goto Cleanup;
  2770. }
  2771. //
  2772. // Pick up the Netbios domain name
  2773. //
  2774. if ( !NetpLogonGetCutf8String(
  2775. SamLogonResponse,
  2776. SamLogonResponseSize,
  2777. &Where,
  2778. &Utf8NetbiosDomainName ) ) {
  2779. NlPrint(( NL_CRITICAL,
  2780. "NetpDcParsePingResponse: %ws: Netbios Domain name bad.\n",
  2781. DisplayDomainName ));
  2782. NetStatus = ERROR_INVALID_DATA;
  2783. goto Cleanup;
  2784. }
  2785. //
  2786. // Pick up the Netbios Computer name
  2787. //
  2788. if ( !NetpLogonGetCutf8String(
  2789. SamLogonResponse,
  2790. SamLogonResponseSize,
  2791. &Where,
  2792. &Utf8NetbiosComputerName ) ) {
  2793. NlPrint(( NL_CRITICAL,
  2794. "NetpDcParsePingResponse: %ws: Netbios Computer name bad.\n",
  2795. DisplayDomainName ));
  2796. NetStatus = ERROR_INVALID_DATA;
  2797. goto Cleanup;
  2798. }
  2799. //
  2800. // Pick up the user name
  2801. //
  2802. if ( !NetpLogonGetCutf8String(
  2803. SamLogonResponse,
  2804. SamLogonResponseSize,
  2805. &Where,
  2806. &Utf8UserName ) ) {
  2807. NlPrint(( NL_CRITICAL,
  2808. "NetpDcParsePingResponse: %ws: User name bad.\n",
  2809. DisplayDomainName ));
  2810. NetStatus = ERROR_INVALID_DATA;
  2811. goto Cleanup;
  2812. }
  2813. //
  2814. // Pick up the DC site name
  2815. //
  2816. if ( !NetpLogonGetCutf8String(
  2817. SamLogonResponse,
  2818. SamLogonResponseSize,
  2819. &Where,
  2820. &Utf8DcSiteName ) ) {
  2821. NlPrint(( NL_CRITICAL,
  2822. "NetpDcParsePingResponse: %ws: DC site name bad.\n",
  2823. DisplayDomainName ));
  2824. NetStatus = ERROR_INVALID_DATA;
  2825. goto Cleanup;
  2826. }
  2827. //
  2828. // Pick up the client site name
  2829. //
  2830. if ( !NetpLogonGetCutf8String(
  2831. SamLogonResponse,
  2832. SamLogonResponseSize,
  2833. &Where,
  2834. &Utf8ClientSiteName ) ) {
  2835. NlPrint(( NL_CRITICAL,
  2836. "NetpDcParsePingResponse: %ws: Client site name bad.\n",
  2837. DisplayDomainName ));
  2838. NetStatus = ERROR_INVALID_DATA;
  2839. goto Cleanup;
  2840. }
  2841. //
  2842. // If this message contains the IP address of the DC,
  2843. // grab it.
  2844. //
  2845. if ( VersionFlags & NETLOGON_NT_VERSION_5EX_WITH_IP ) {
  2846. CHAR LocalSockAddrSize;
  2847. //
  2848. // Grab the size of the SockAddr
  2849. //
  2850. if ( !NetpLogonGetBytes(
  2851. SamLogonResponse,
  2852. SamLogonResponseSize,
  2853. &Where,
  2854. sizeof(SamLogonResponseEx->DcSockAddrSize ),
  2855. &LocalSockAddrSize ) ) {
  2856. NlPrint(( NL_CRITICAL,
  2857. "NetpDcParsePingResponse: %ws: SockAddr size bad.\n",
  2858. DisplayDomainName ));
  2859. NetStatus = ERROR_INVALID_DATA;
  2860. goto Cleanup;
  2861. }
  2862. if ( LocalSockAddrSize > sizeof(DcSockAddrIn) ) {
  2863. NlPrint(( NL_CRITICAL,
  2864. "NetpDcParsePingResponse: %ws: SockAddr size too big %ld %ld.\n",
  2865. DisplayDomainName,
  2866. LocalSockAddrSize,
  2867. sizeof(DcSockAddrIn)));
  2868. NetStatus = ERROR_INVALID_DATA;
  2869. goto Cleanup;
  2870. }
  2871. //
  2872. // Grab the SockAddr itself.
  2873. //
  2874. if ( !NetpLogonGetBytes(
  2875. SamLogonResponse,
  2876. SamLogonResponseSize,
  2877. &Where,
  2878. LocalSockAddrSize,
  2879. &DcSockAddrIn ) ) {
  2880. NlPrint(( NL_CRITICAL,
  2881. "NetpDcParsePingResponse: %ws: SockAddr size bad.\n",
  2882. DisplayDomainName ));
  2883. NetStatus = ERROR_INVALID_DATA;
  2884. goto Cleanup;
  2885. }
  2886. //
  2887. // Build a SocketAddress to point to the SockAddr
  2888. //
  2889. DcSocketAddress.lpSockaddr = (LPSOCKADDR) &DcSockAddrIn;
  2890. DcSocketAddress.iSockaddrLength = LocalSockAddrSize;
  2891. }
  2892. break;
  2893. //
  2894. // Process a response to a primary query.
  2895. //
  2896. case LOGON_PRIMARY_RESPONSE:
  2897. PrimaryResponse = (PNETLOGON_PRIMARY)SamLogonResponse;
  2898. Where = PrimaryResponse->PrimaryDCName;
  2899. //
  2900. // Pick up the Netbios name of the server that responded.
  2901. //
  2902. if ( !NetpLogonGetOemString(
  2903. SamLogonResponse,
  2904. SamLogonResponseSize,
  2905. &Where,
  2906. sizeof(PrimaryResponse->PrimaryDCName),
  2907. &OemPrimaryDcName ) ) {
  2908. NlPrint(( NL_CRITICAL,
  2909. "NetpDcParsePingResponse: %ws:OEM server name bad.\n",
  2910. DisplayDomainName ));
  2911. NetStatus = ERROR_INVALID_DATA;
  2912. goto Cleanup;
  2913. }
  2914. //
  2915. // PDC for the specified domain is an NT PDC.
  2916. // Get the UNICODE machine name from the message.
  2917. //
  2918. if ( Version == LMNT_MESSAGE ) {
  2919. //
  2920. // Pick up the Netbios name of the server that responded.
  2921. //
  2922. if ( !NetpLogonGetUnicodeString(
  2923. SamLogonResponse,
  2924. SamLogonResponseSize,
  2925. &Where,
  2926. sizeof(PrimaryResponse->UnicodePrimaryDCName),
  2927. &ServerName ) ) {
  2928. NlPrint(( NL_CRITICAL,
  2929. "NetpDcParsePingResponse: %ws: server name bad.\n",
  2930. DisplayDomainName ));
  2931. NetStatus = ERROR_INVALID_DATA;
  2932. goto Cleanup;
  2933. }
  2934. //
  2935. // Pick up the Netbios domain name of the domain the response is from.
  2936. //
  2937. if ( !NetpLogonGetUnicodeString(
  2938. SamLogonResponse,
  2939. SamLogonResponseSize,
  2940. &Where,
  2941. sizeof(PrimaryResponse->UnicodeDomainName),
  2942. &DomainName ) ) {
  2943. NlPrint(( NL_CRITICAL,
  2944. "NetpDcParsePingResponse: %ws: domain name bad.\n",
  2945. DisplayDomainName ));
  2946. NetStatus = ERROR_INVALID_DATA;
  2947. goto Cleanup;
  2948. }
  2949. }
  2950. //
  2951. // Ensure caller knows this is a PDC.
  2952. //
  2953. RtlZeroMemory( &DomainGuid, sizeof(DomainGuid) );
  2954. Flags = DS_PDC_FLAG | DS_WRITABLE_FLAG;
  2955. break;
  2956. //
  2957. // Unknown response opcode.
  2958. //
  2959. default:
  2960. NlPrint(( NL_CRITICAL,
  2961. "NetpDcParsePingResponse: %ws: opcode bad. %ld\n",
  2962. DisplayDomainName,
  2963. LocalOpcode ));
  2964. NetStatus = ERROR_INVALID_DATA;
  2965. goto Cleanup;
  2966. }
  2967. //
  2968. // ASSERT: DC has been found.
  2969. //
  2970. //
  2971. // Allocate and initialize a cache entry.
  2972. //
  2973. *NlDcCacheEntry = NetpDcAllocateCacheEntry(
  2974. ServerName,
  2975. OemPrimaryDcName,
  2976. UserName,
  2977. DomainName,
  2978. &DomainGuid,
  2979. DnsForestName,
  2980. DnsDomainName,
  2981. DnsHostName,
  2982. Utf8NetbiosDomainName,
  2983. Utf8NetbiosComputerName,
  2984. Utf8UserName,
  2985. Utf8DcSiteName,
  2986. Utf8ClientSiteName,
  2987. Flags );
  2988. if ( *NlDcCacheEntry == NULL ) {
  2989. NlPrint(( NL_CRITICAL,
  2990. "NetpDcParsePingResponse: %ws: not enough memory.\n",
  2991. DisplayDomainName ));
  2992. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2993. goto Cleanup;
  2994. }
  2995. (*NlDcCacheEntry)->Opcode = LocalOpcode;
  2996. (*NlDcCacheEntry)->VersionFlags = VersionFlags;
  2997. //
  2998. // Fill the DC's SockAddr into the cache entry
  2999. //
  3000. if ( DcSocketAddress.iSockaddrLength != 0 ) {
  3001. NlAssert( DcSocketAddress.iSockaddrLength <= sizeof( (*NlDcCacheEntry)->SockAddrIn) );
  3002. RtlCopyMemory( &(*NlDcCacheEntry)->SockAddrIn,
  3003. DcSocketAddress.lpSockaddr,
  3004. DcSocketAddress.iSockaddrLength );
  3005. (*NlDcCacheEntry)->SockAddr.lpSockaddr = (LPSOCKADDR)
  3006. &(*NlDcCacheEntry)->SockAddrIn;
  3007. (*NlDcCacheEntry)->SockAddr.iSockaddrLength = DcSocketAddress.iSockaddrLength;
  3008. }
  3009. NetStatus = NO_ERROR;
  3010. Cleanup:
  3011. //
  3012. // On failure,
  3013. // delete any strings we may have allocated for return to the caller.
  3014. //
  3015. if ( NetStatus != NO_ERROR ) {
  3016. if ( *NlDcCacheEntry != NULL ) {
  3017. NetpMemoryFree( *NlDcCacheEntry );
  3018. *NlDcCacheEntry = NULL;
  3019. }
  3020. }
  3021. //
  3022. // Delete any buffers allocated locally.
  3023. //
  3024. if ( DnsForestName != NULL ) {
  3025. NetpMemoryFree( DnsForestName );
  3026. }
  3027. if ( DnsDomainName != NULL ) {
  3028. NetpMemoryFree( DnsDomainName );
  3029. }
  3030. if ( DnsHostName != NULL ) {
  3031. NetpMemoryFree( DnsHostName );
  3032. }
  3033. if ( Utf8NetbiosDomainName != NULL ) {
  3034. NetpMemoryFree( Utf8NetbiosDomainName );
  3035. }
  3036. if ( Utf8NetbiosComputerName != NULL ) {
  3037. NetpMemoryFree( Utf8NetbiosComputerName );
  3038. }
  3039. if ( Utf8UserName != NULL ) {
  3040. NetpMemoryFree( Utf8UserName );
  3041. }
  3042. if ( Utf8DcSiteName != NULL ) {
  3043. NetpMemoryFree( Utf8DcSiteName );
  3044. }
  3045. if ( Utf8ClientSiteName != NULL ) {
  3046. NetpMemoryFree( Utf8ClientSiteName );
  3047. }
  3048. return NetStatus;;
  3049. }
  3050. NET_API_STATUS
  3051. NetpDcFlagsToNameType(
  3052. IN ULONG Flags,
  3053. OUT PNL_DNS_NAME_TYPE NlDnsNameType
  3054. )
  3055. /*++
  3056. Routine Description:
  3057. Given the flags specified to DsGetDcName, return the type of the DNS
  3058. name to query to discover that type of DC.
  3059. Arguments:
  3060. Flags - Passes additional information to be used to process the request.
  3061. Flags can be a combination values bitwise or'ed together.
  3062. NlDnsNameType - Returns the type of DNS name to query.
  3063. Return Value:
  3064. NO_ERROR - Operation completed successfully;
  3065. ERROR_INVALID_FLAGS - The flags parameter has conflicting bits set.
  3066. --*/
  3067. {
  3068. ULONG LocalFlags;
  3069. //
  3070. // If more than one of this bits is set,
  3071. // that's invalid.
  3072. //
  3073. LocalFlags = Flags & (DS_KDC_REQUIRED|DS_PDC_REQUIRED|DS_GC_SERVER_REQUIRED);
  3074. if ( LocalFlags != 0 && !JUST_ONE_BIT( LocalFlags ) ) {
  3075. return ERROR_INVALID_FLAGS;
  3076. }
  3077. //
  3078. // Select the cache entry type based on the requested DC type.
  3079. //
  3080. if ( Flags & DS_PDC_REQUIRED ) {
  3081. *NlDnsNameType = NlDnsPdc;
  3082. } else if ( Flags & DS_ONLY_LDAP_NEEDED ) {
  3083. if ( Flags & DS_GC_SERVER_REQUIRED ) {
  3084. *NlDnsNameType = NlDnsGenericGc;
  3085. } else {
  3086. *NlDnsNameType = NlDnsLdap;
  3087. }
  3088. } else if ( Flags & DS_GC_SERVER_REQUIRED ) {
  3089. *NlDnsNameType = NlDnsGc;
  3090. } else if ( Flags & DS_KDC_REQUIRED ) {
  3091. *NlDnsNameType = NlDnsKdc;
  3092. } else {
  3093. *NlDnsNameType = NlDnsDc;
  3094. }
  3095. return NO_ERROR;
  3096. }
  3097. BOOL
  3098. NetpAppendUtf8Str(
  3099. IN OUT LPSTR To,
  3100. IN LPCSTR From,
  3101. IN ULONG ResultingStringLengthMax
  3102. )
  3103. /*++
  3104. Routine Description:
  3105. This routine appends a UTF8 string to a UTF8 string making sure
  3106. that it doesn't write beyond the buffer limit.
  3107. Arguments:
  3108. To - The string to append to.
  3109. From - The string to append.
  3110. ResultingStringLengthMax - Maximum allowed length of the resulting string
  3111. in bytes not counting the terminating null character.
  3112. Return Value:
  3113. TRUE: The string is successfully appended.
  3114. Otherwise, returns FALSE.
  3115. --*/
  3116. {
  3117. ULONG ToLen;
  3118. ULONG FromLen;
  3119. if ( To == NULL || From == NULL || ResultingStringLengthMax == 0 ) {
  3120. return FALSE;
  3121. }
  3122. ToLen = strlen(To);
  3123. FromLen = strlen(From);
  3124. if ( ToLen+FromLen > ResultingStringLengthMax ) {
  3125. return FALSE;
  3126. }
  3127. RtlCopyMemory( &To[ToLen], From, FromLen+1 );
  3128. return TRUE;
  3129. }
  3130. NET_API_STATUS
  3131. NetpDcBuildDnsName(
  3132. IN NL_DNS_NAME_TYPE NlDnsNameType,
  3133. IN GUID *DomainGuid OPTIONAL,
  3134. IN LPCWSTR SiteName OPTIONAL,
  3135. IN LPCSTR DnsDomainName,
  3136. OUT char DnsName[NL_MAX_DNS_LENGTH+1]
  3137. )
  3138. /*++
  3139. Routine Description:
  3140. This routine returns the textual DNS name for a particular domain and
  3141. name type.
  3142. Arguments:
  3143. NlDnsNameType - The specific type of name.
  3144. DomainGuid - Guid to append to DNS name.
  3145. For NlDnsDcByGuid, this is the GUID of the domain being located.
  3146. For NlDnsDsaCname, this is the GUID of the DSA being located.
  3147. SiteName - Name of the site to append to DNS name.
  3148. If NlDnsNameType is any of the *AtSite values,
  3149. this is the name of the site the DC is in.
  3150. DnsDomainName - Specifies the DNS domain for the name.
  3151. For NlDnsDcByGuid or any of the GC names,
  3152. this is the DNS domain name of the domain at the root of the tree of
  3153. domains.
  3154. For all others, this is the DNS domain for the DC.
  3155. DnsName - Textual representation of the DNS name.
  3156. The returned name is an absolute name (e.g., ends in a .)
  3157. Return Value:
  3158. NO_ERROR: The name was returned;
  3159. ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
  3160. cannot be concatenated.
  3161. --*/
  3162. {
  3163. char *FinalString;
  3164. ULONG DnsNameLength;
  3165. //
  3166. // All SRV record names names are prefixed by ldap.tcp (or kdc.tcp or gc.tcp),
  3167. // A records and CNAME records are not.
  3168. //
  3169. *DnsName = '\0';
  3170. if ( NlDnsSrvRecord( NlDnsNameType ) ) {
  3171. //
  3172. // Output the name of the service.
  3173. //
  3174. if ( NlDnsNameType == NlDnsGenericGc ||
  3175. NlDnsNameType == NlDnsGenericGcAtSite ) {
  3176. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_GC_SRV, NL_MAX_DNS_LENGTH) ) {
  3177. return ERROR_INVALID_DOMAINNAME;
  3178. }
  3179. } else if ( NlDnsKpwdRecord( NlDnsNameType )) {
  3180. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_KPWD_SRV, NL_MAX_DNS_LENGTH) ) {
  3181. return ERROR_INVALID_DOMAINNAME;
  3182. }
  3183. } else if ( NlDnsKdcRecord( NlDnsNameType ) ) {
  3184. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_KDC_SRV, NL_MAX_DNS_LENGTH) ) {
  3185. return ERROR_INVALID_DOMAINNAME;
  3186. }
  3187. } else {
  3188. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_LDAP_SRV, NL_MAX_DNS_LENGTH) ) {
  3189. return ERROR_INVALID_DOMAINNAME;
  3190. }
  3191. }
  3192. //
  3193. // Output the name of the transport.
  3194. //
  3195. if ( NlDcDnsNameTypeDesc[NlDnsNameType].IsTcp ) {
  3196. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_TCP, NL_MAX_DNS_LENGTH) ) {
  3197. return ERROR_INVALID_DOMAINNAME;
  3198. }
  3199. } else {
  3200. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_UDP, NL_MAX_DNS_LENGTH) ) {
  3201. return ERROR_INVALID_DOMAINNAME;
  3202. }
  3203. }
  3204. }
  3205. //
  3206. // If this is a site specific name,
  3207. // append the site name and the .sites. constant.
  3208. //
  3209. if ( NlDcDnsNameTypeDesc[NlDnsNameType].IsSiteSpecific ) {
  3210. if ( NULL == NetpCreateUtf8StrFromWStr( SiteName,
  3211. &DnsName[strlen(DnsName)],
  3212. NL_MAX_DNS_LENGTH+1-strlen(DnsName)) ) {
  3213. return ERROR_INVALID_DOMAINNAME;
  3214. }
  3215. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_AT_SITE, NL_MAX_DNS_LENGTH) ) {
  3216. return ERROR_INVALID_DOMAINNAME;
  3217. }
  3218. }
  3219. //
  3220. // Add the first label (or two) of the DNS name as a function of the name type.
  3221. //
  3222. switch (NlDnsNameType) {
  3223. case NlDnsLdap:
  3224. case NlDnsLdapAtSite:
  3225. case NlDnsRfc1510Kdc:
  3226. case NlDnsRfc1510KdcAtSite:
  3227. case NlDnsGenericGc:
  3228. case NlDnsGenericGcAtSite:
  3229. case NlDnsRfc1510UdpKdc:
  3230. case NlDnsRfc1510Kpwd:
  3231. case NlDnsRfc1510UdpKpwd:
  3232. break;
  3233. case NlDnsPdc:
  3234. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_PDC, NL_MAX_DNS_LENGTH) ) {
  3235. return ERROR_INVALID_DOMAINNAME;
  3236. }
  3237. break;
  3238. case NlDnsGc:
  3239. case NlDnsGcAtSite:
  3240. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_GC, NL_MAX_DNS_LENGTH) ) {
  3241. return ERROR_INVALID_DOMAINNAME;
  3242. }
  3243. break;
  3244. case NlDnsDc:
  3245. case NlDnsDcAtSite:
  3246. case NlDnsKdc:
  3247. case NlDnsKdcAtSite:
  3248. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_DC, NL_MAX_DNS_LENGTH) ) {
  3249. return ERROR_INVALID_DOMAINNAME;
  3250. }
  3251. break;
  3252. case NlDnsDcByGuid: {
  3253. RPC_STATUS RpcStatus;
  3254. char *StringGuid;
  3255. RpcStatus = UuidToStringA( DomainGuid, &StringGuid );
  3256. if ( RpcStatus != RPC_S_OK ) {
  3257. NlPrint(( NL_CRITICAL,
  3258. "NetpDcBuildDnsName: not enough memory.\n" ));
  3259. return ERROR_NOT_ENOUGH_MEMORY;
  3260. }
  3261. if ( !NetpAppendUtf8Str(DnsName, StringGuid, NL_MAX_DNS_LENGTH) ) {
  3262. return ERROR_INVALID_DOMAINNAME;
  3263. }
  3264. RpcStringFreeA( &StringGuid );
  3265. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_DC_BY_GUID, NL_MAX_DNS_LENGTH) ) {
  3266. return ERROR_INVALID_DOMAINNAME;
  3267. }
  3268. break;
  3269. }
  3270. case NlDnsLdapIpAddress:
  3271. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_DC_IP_ADDRESS, NL_MAX_DNS_LENGTH) ) {
  3272. return ERROR_INVALID_DOMAINNAME;
  3273. }
  3274. break;
  3275. case NlDnsGcIpAddress:
  3276. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_GC_IP_ADDRESS, NL_MAX_DNS_LENGTH) ) {
  3277. return ERROR_INVALID_DOMAINNAME;
  3278. }
  3279. break;
  3280. case NlDnsDsaCname:
  3281. {
  3282. RPC_STATUS RpcStatus;
  3283. char *StringGuid;
  3284. RpcStatus = UuidToStringA( DomainGuid, &StringGuid );
  3285. if ( RpcStatus != RPC_S_OK ) {
  3286. NlPrint(( NL_CRITICAL,
  3287. "NetpDcBuildDnsName: not enough memory.\n" ));
  3288. return ERROR_NOT_ENOUGH_MEMORY;
  3289. }
  3290. if ( !NetpAppendUtf8Str(DnsName, StringGuid, NL_MAX_DNS_LENGTH) ) {
  3291. return ERROR_INVALID_DOMAINNAME;
  3292. }
  3293. RpcStringFreeA( &StringGuid );
  3294. if ( !NetpAppendUtf8Str(DnsName, NL_DNS_DSA_IP_ADDRESS, NL_MAX_DNS_LENGTH) ) {
  3295. return ERROR_INVALID_DOMAINNAME;
  3296. }
  3297. break;
  3298. }
  3299. default:
  3300. return ERROR_INVALID_PARAMETER;
  3301. }
  3302. //
  3303. // Add it to the correct DNS domain.
  3304. // Ensuring it isn't too long.
  3305. //
  3306. if ( !NetpAppendUtf8Str(DnsName, DnsDomainName, NL_MAX_DNS_LENGTH) ) {
  3307. return ERROR_INVALID_DOMAINNAME;
  3308. }
  3309. DnsNameLength = strlen(DnsName);
  3310. //
  3311. // Ensure it is an absolute name.
  3312. //
  3313. if ( DnsName[DnsNameLength-1] != '.' ) {
  3314. if ( DnsNameLength+1 > NL_MAX_DNS_LENGTH ) {
  3315. return ERROR_INVALID_DOMAINNAME;
  3316. }
  3317. DnsName[DnsNameLength] = '.';
  3318. DnsName[DnsNameLength+1] = '\0';
  3319. }
  3320. return NO_ERROR;
  3321. }
  3322. VOID
  3323. NetpDcDerefCacheEntry(
  3324. IN PNL_DC_CACHE_ENTRY NlDcCacheEntry
  3325. )
  3326. /*++
  3327. Routine Description:
  3328. Decrement the reference count on a cache entry. If the count reaches zero,
  3329. delete the entry.
  3330. The count will only reach zero if the entry is already removed from the
  3331. global linked list.
  3332. Arguments:
  3333. NlDcCacheEntry - Cache entry to dereference.
  3334. Return Value:
  3335. None.
  3336. --*/
  3337. {
  3338. ULONG LocalReferenceCount;
  3339. EnterCriticalSection(&NlDcCritSect);
  3340. LocalReferenceCount = -- NlDcCacheEntry->ReferenceCount;
  3341. LeaveCriticalSection(&NlDcCritSect);
  3342. if ( LocalReferenceCount == 0 ) {
  3343. NetpMemoryFree(NlDcCacheEntry);
  3344. }
  3345. }
  3346. BOOL
  3347. NetpDcMatchResponse(
  3348. IN PNL_GETDC_CONTEXT Context,
  3349. IN PNL_DC_CACHE_ENTRY NlDcCacheEntry,
  3350. IN BOOL BeVerbose,
  3351. OUT PBOOL UsedNetbios
  3352. )
  3353. /*++
  3354. Routine Description:
  3355. This routine determines if the characteristics specified as input
  3356. parameters match the characteristics of the DC requested by the caller.
  3357. This routine is used to determine if a received ping response is suitable
  3358. to the original caller. This routine is also used to determine if a cache entry
  3359. is suitable to the original caller.
  3360. Arguments:
  3361. Context - Context describing the GetDc operation.
  3362. NlDcCacheEntry - Reponse to compare with.
  3363. BeVerbose - TRUE if problems are to be logged
  3364. UsedNetbios - Returns TRUE if the netbios domain name was used to do the
  3365. successful comparison.
  3366. Return Value:
  3367. TRUE - The parameters describe a suitable DC
  3368. --*/
  3369. {
  3370. BOOLEAN LocalUsedNetbios = FALSE;
  3371. //
  3372. // Initialization
  3373. //
  3374. *UsedNetbios = FALSE;
  3375. #ifdef notdef
  3376. // Only use GUID to be rename safe. Not to prevent discovery of a re-installed
  3377. // domain.
  3378. //
  3379. // Ensure the DomainGuid returned matches the one expected.
  3380. //
  3381. if ( Context->QueriedDomainGuid != NULL &&
  3382. !IsEqualGUID( &NlDcCacheEntry->DomainGuid, &NlDcZeroGuid) &&
  3383. !IsEqualGUID( &NlDcCacheEntry->DomainGuid, Context->QueriedDomainGuid) ) {
  3384. if ( BeVerbose ) {
  3385. NlPrint((NL_CRITICAL,
  3386. "NetpDcMatchResponse: %ws: %ws: Domain Guid isn't queried Guid\n",
  3387. NlDcCacheEntry->UnicodeNetbiosDcName,
  3388. Context->QueriedDisplayDomainName ));
  3389. }
  3390. return FALSE;
  3391. }
  3392. #endif // notdef
  3393. //
  3394. // Either the Netbios DC name or DNS DC name must have been returned.
  3395. //
  3396. if ( NlDcCacheEntry->UnicodeNetbiosDcName == NULL && NlDcCacheEntry->UnicodeDnsHostName == NULL ) {
  3397. if ( BeVerbose ) {
  3398. NlPrint((NL_CRITICAL,
  3399. "NetpDcMatchResponse: %ws: %ws: Neither Netbios nor DNS DC name available\n",
  3400. NlDcCacheEntry->UnicodeNetbiosDcName,
  3401. Context->QueriedDisplayDomainName ));
  3402. }
  3403. return FALSE;
  3404. }
  3405. //
  3406. // If we ping a DC, check that the responding DC name is the one requested.
  3407. //
  3408. // Process the special case when we ping a DC and the DC name can be both DNS and Netbios
  3409. //
  3410. if ( (Context->QueriedInternalFlags & (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST)) ==
  3411. (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST)) {
  3412. BOOL NameMatched = FALSE;
  3413. //
  3414. // Check if the DNS name matches
  3415. //
  3416. if ( NlDcCacheEntry->UnicodeDnsHostName != NULL &&
  3417. Context->QueriedDcName != NULL &&
  3418. NlEqualDnsName(NlDcCacheEntry->UnicodeDnsHostName,
  3419. Context->QueriedDcName) ) {
  3420. NameMatched = TRUE;
  3421. }
  3422. //
  3423. // If DNS name doesn't match, check if Netbios name does
  3424. //
  3425. if ( !NameMatched &&
  3426. NlDcCacheEntry->UnicodeNetbiosDcName != NULL &&
  3427. Context->QueriedDcName != NULL &&
  3428. (NlNameCompare(NlDcCacheEntry->UnicodeNetbiosDcName,
  3429. (LPWSTR)Context->QueriedDcName,
  3430. NAMETYPE_COMPUTER) == 0) ) {
  3431. NameMatched = TRUE;
  3432. }
  3433. //
  3434. // If neither name matches, fail
  3435. //
  3436. if ( !NameMatched ) {
  3437. if ( BeVerbose ) {
  3438. NlPrint(( NL_CRITICAL,
  3439. "NetpDcMatchResponse: Ping response with unmatched host name %ws %ws %ws\n",
  3440. Context->QueriedDcName,
  3441. NlDcCacheEntry->UnicodeDnsHostName,
  3442. NlDcCacheEntry->UnicodeNetbiosDcName ));
  3443. }
  3444. return FALSE;
  3445. }
  3446. //
  3447. // If the pinged DC name is exactly DNS,
  3448. // check that the returned DNS host name is same
  3449. //
  3450. } else if ( Context->QueriedInternalFlags & DS_PING_DNS_HOST ) {
  3451. if ( (NlDcCacheEntry->UnicodeDnsHostName == NULL) ||
  3452. (Context->QueriedDcName == NULL) ||
  3453. !NlEqualDnsName(NlDcCacheEntry->UnicodeDnsHostName, Context->QueriedDcName) ) {
  3454. if ( BeVerbose ) {
  3455. NlPrint(( NL_CRITICAL,
  3456. "NetpDcMatchResponse: Ping response with unmatched DNS host name %ws %ws\n",
  3457. Context->QueriedDcName,
  3458. NlDcCacheEntry->UnicodeDnsHostName ));
  3459. }
  3460. return FALSE;
  3461. }
  3462. //
  3463. // If the pinged DC name is exactly Netbios,
  3464. // check that the returned Netbios host name is same
  3465. //
  3466. } else if ( Context->QueriedInternalFlags & DS_PING_NETBIOS_HOST ) {
  3467. if ( (NlDcCacheEntry->UnicodeNetbiosDcName == NULL) ||
  3468. (Context->QueriedDcName == NULL) ||
  3469. NlNameCompare(NlDcCacheEntry->UnicodeNetbiosDcName,
  3470. (LPWSTR)Context->QueriedDcName,
  3471. NAMETYPE_COMPUTER) != 0 ) {
  3472. if ( BeVerbose ) {
  3473. NlPrint(( NL_CRITICAL,
  3474. "NetpDcMatchResponse: Ping response with unmatched Netbios host name %ws %ws\n",
  3475. Context->QueriedDcName,
  3476. NlDcCacheEntry->UnicodeNetbiosDcName ));
  3477. }
  3478. return FALSE;
  3479. }
  3480. }
  3481. //
  3482. // If asking for a GC,
  3483. // ensure the Tree name of the responding DC matches the one
  3484. // we're asking for.
  3485. //
  3486. if ( NlDnsGcName( Context->QueriedNlDnsNameType ) ) {
  3487. if (NlDcCacheEntry->UnicodeDnsForestName == NULL ||
  3488. Context->QueriedDnsDomainName == NULL ||
  3489. !NlEqualDnsName( NlDcCacheEntry->UnicodeDnsForestName, Context->QueriedDnsDomainName ) ) {
  3490. if ( BeVerbose ) {
  3491. NlPrint((NL_CRITICAL,
  3492. "NetpDcMatchResponse: %ws: asking for GC and tree name doesn't match request %ws %ws\n",
  3493. NlDcCacheEntry->UnicodeNetbiosDcName,
  3494. NlDcCacheEntry->UnicodeDnsForestName,
  3495. Context->QueriedDnsDomainName ));
  3496. }
  3497. return FALSE;
  3498. }
  3499. //
  3500. // Ensure the domain name returned matches the one expected.
  3501. //
  3502. } else {
  3503. BOOLEAN NetbiosSame;
  3504. BOOLEAN DnsSame;
  3505. //
  3506. // If neither of the domain names compared,
  3507. // the domain names don't match
  3508. //
  3509. NetbiosSame =
  3510. ( NlDcCacheEntry->UnicodeNetbiosDomainName != NULL &&
  3511. Context->QueriedNetbiosDomainName != NULL &&
  3512. NlNameCompare( (LPWSTR)NlDcCacheEntry->UnicodeNetbiosDomainName, (LPWSTR)Context->QueriedNetbiosDomainName, NAMETYPE_DOMAIN ) == 0 );
  3513. DnsSame =
  3514. NlDcCacheEntry->UnicodeDnsDomainName != NULL &&
  3515. Context->QueriedDnsDomainName != NULL &&
  3516. NlEqualDnsName( NlDcCacheEntry->UnicodeDnsDomainName, Context->QueriedDnsDomainName );
  3517. if ( !NetbiosSame && !DnsSame ) {
  3518. //
  3519. // Lanman PDC's don't return the domain name.
  3520. // (So don't complain about lack of domain name if this is a PDC query.)
  3521. //
  3522. if ( Context->DcQueryType != NlDcQueryPdc ||
  3523. NlDcCacheEntry->UnicodeNetbiosDomainName != NULL ||
  3524. NlDcCacheEntry->UnicodeDnsDomainName != NULL ) {
  3525. if ( BeVerbose ) {
  3526. NlPrint((NL_CRITICAL,
  3527. "NetpDcMatchResponse: %ws: Neither Netbios %ws nor DNS %ws domain matches queried name %ws %ws\n",
  3528. NlDcCacheEntry->UnicodeNetbiosDcName,
  3529. NlDcCacheEntry->UnicodeNetbiosDomainName,
  3530. NlDcCacheEntry->UnicodeDnsDomainName,
  3531. Context->QueriedNetbiosDomainName,
  3532. Context->QueriedDnsDomainName ));
  3533. }
  3534. //
  3535. // Finally check if the domain GUID matches which
  3536. // may be the case if the domain has been renamed
  3537. //
  3538. if ( Context->QueriedDomainGuid != NULL &&
  3539. !IsEqualGUID( &NlDcCacheEntry->DomainGuid, &NlDcZeroGuid) &&
  3540. IsEqualGUID( &NlDcCacheEntry->DomainGuid, Context->QueriedDomainGuid) ) {
  3541. if ( BeVerbose ) {
  3542. NlPrint(( NL_CRITICAL,
  3543. "NetpDcMatchResponse: %ws: But Domain Guid matches\n",
  3544. NlDcCacheEntry->UnicodeNetbiosDcName ));
  3545. }
  3546. } else {
  3547. return FALSE;
  3548. }
  3549. } else {
  3550. // Lanman PDCs always used netbios.
  3551. LocalUsedNetbios = TRUE;
  3552. }
  3553. }
  3554. //
  3555. // If only the domain name matched,
  3556. // tell the caller.
  3557. //
  3558. if ( NetbiosSame && !DnsSame ) {
  3559. LocalUsedNetbios = TRUE;
  3560. }
  3561. }
  3562. //
  3563. // Ensure the queried account name is the correct.
  3564. //
  3565. if ( Context->QueriedAccountName != NULL ) {
  3566. //
  3567. // If this is an NT 4 PDC responding to a PDC query,
  3568. // ignore the fact we queried for an account.
  3569. // We can't query both "PDC" and "account" at the same time
  3570. // from NT 4.
  3571. //
  3572. if ( NlDcCacheEntry->Opcode == LOGON_PRIMARY_RESPONSE &&
  3573. (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) == 0 &&
  3574. (NlDcCacheEntry->ReturnFlags & DS_PDC_FLAG) != 0 ) {
  3575. if ( BeVerbose ) {
  3576. NlPrint((NL_SESSION_SETUP,
  3577. "NetpDcMatchResponse: %ws: %ws: response for wrong account '%ws' s.b. '%ws' (but message from NT 4 PDC so use it).\n",
  3578. NlDcCacheEntry->UnicodeNetbiosDcName,
  3579. Context->QueriedDisplayDomainName,
  3580. NlDcCacheEntry->UnicodeUserName,
  3581. Context->QueriedAccountName ));
  3582. }
  3583. } else if ( NlDcCacheEntry->UnicodeUserName == NULL ||
  3584. _wcsicmp( (LPWSTR) Context->QueriedAccountName, NlDcCacheEntry->UnicodeUserName ) != 0 ) {
  3585. if ( BeVerbose ) {
  3586. NlPrint((NL_CRITICAL,
  3587. "NetpDcMatchResponse: %ws: %ws: response for wrong account '%ws' s.b. '%ws'\n",
  3588. NlDcCacheEntry->UnicodeNetbiosDcName,
  3589. Context->QueriedDisplayDomainName,
  3590. NlDcCacheEntry->UnicodeUserName,
  3591. Context->QueriedAccountName ));
  3592. }
  3593. return FALSE;
  3594. }
  3595. }
  3596. //
  3597. // Ensure the responding DC is still playing the correct role.
  3598. //
  3599. switch ( Context->DcQueryType ) {
  3600. case NlDcQueryLdap:
  3601. case NlDcQueryGenericDc:
  3602. // All DCs are suitable
  3603. break;
  3604. case NlDcQueryPdc:
  3605. if ( (NlDcCacheEntry->ReturnFlags & DS_PDC_FLAG) == 0 ) {
  3606. if ( BeVerbose ) {
  3607. NlPrint((NL_CRITICAL,
  3608. "NetpDcMatchResponse: %ws: %ws: Responder is not the PDC. 0x%lx\n",
  3609. NlDcCacheEntry->UnicodeNetbiosDcName,
  3610. Context->QueriedDisplayDomainName,
  3611. NlDcCacheEntry->ReturnFlags ));
  3612. }
  3613. return FALSE;
  3614. }
  3615. break;
  3616. case NlDcQueryGc:
  3617. if ( (NlDcCacheEntry->ReturnFlags & (DS_GC_FLAG|DS_DS_FLAG)) != (DS_GC_FLAG|DS_DS_FLAG) ) {
  3618. if ( BeVerbose ) {
  3619. NlPrint((NL_CRITICAL,
  3620. "NetpDcMatchResponse: %ws: %ws: Responder is not a GC server. 0x%lx\n",
  3621. NlDcCacheEntry->UnicodeNetbiosDcName,
  3622. Context->QueriedDisplayDomainName,
  3623. NlDcCacheEntry->ReturnFlags ));
  3624. }
  3625. return FALSE;
  3626. }
  3627. break;
  3628. case NlDcQueryGenericGc:
  3629. if ( (NlDcCacheEntry->ReturnFlags & DS_GC_FLAG) == 0 ) {
  3630. if ( BeVerbose ) {
  3631. NlPrint((NL_CRITICAL,
  3632. "NetpDcMatchResponse: %ws: %ws: Responder is not a GC server. 0x%lx\n",
  3633. NlDcCacheEntry->UnicodeNetbiosDcName,
  3634. Context->QueriedDisplayDomainName,
  3635. NlDcCacheEntry->ReturnFlags ));
  3636. }
  3637. return FALSE;
  3638. }
  3639. break;
  3640. case NlDcQueryKdc:
  3641. // Handle KDCs below.
  3642. break;
  3643. default:
  3644. if ( BeVerbose ) {
  3645. NlPrint((NL_CRITICAL,
  3646. "NetpDcMatchResponse: %ws: %ws: invalid query type 0x%lx 0x%lx\n",
  3647. NlDcCacheEntry->UnicodeNetbiosDcName,
  3648. Context->QueriedDisplayDomainName,
  3649. Context->DcQueryType,
  3650. NlDcCacheEntry->ReturnFlags ));
  3651. }
  3652. return FALSE;
  3653. }
  3654. //
  3655. // If we are not doing an NDNC discovery (i.e. we are discovering a real
  3656. // domain DC), disregard a response from an LDAP server servicing this
  3657. // name as NDNC
  3658. //
  3659. if ( NlDnsNonNdncName( Context->QueriedNlDnsNameType ) &&
  3660. (NlDcCacheEntry->ReturnFlags & DS_NDNC_FLAG) != 0 ) {
  3661. if ( BeVerbose ) {
  3662. NlPrint(( NL_CRITICAL,
  3663. "NetpDcMatchResponse: %ws: %ws: response not from real domain DC. 0x%lx\n",
  3664. NlDcCacheEntry->UnicodeNetbiosDcName,
  3665. Context->QueriedDisplayDomainName,
  3666. NlDcCacheEntry->ReturnFlags ));
  3667. }
  3668. return FALSE;
  3669. }
  3670. //
  3671. // If we need a DS server, ensure the responding DC is one.
  3672. //
  3673. if ( (Context->QueriedFlags & DS_DIRECTORY_SERVICE_REQUIRED) &&
  3674. (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) == 0 ) {
  3675. if ( BeVerbose ) {
  3676. NlPrint((NL_CRITICAL,
  3677. "NetpDcMatchResponse: %ws: %ws: response not from DS server. 0x%lx\n",
  3678. NlDcCacheEntry->UnicodeNetbiosDcName,
  3679. Context->QueriedDisplayDomainName,
  3680. NlDcCacheEntry->ReturnFlags ));
  3681. }
  3682. return FALSE;
  3683. }
  3684. //
  3685. // If we need machine running the timeserv, ensure the responding DC is one.
  3686. //
  3687. if ( (Context->QueriedFlags & (DS_TIMESERV_REQUIRED|DS_GOOD_TIMESERV_PREFERRED)) &&
  3688. (NlDcCacheEntry->ReturnFlags & DS_TIMESERV_FLAG) == 0 ) {
  3689. if ( BeVerbose ) {
  3690. NlPrint((NL_CRITICAL,
  3691. "NetpDcMatchResponse: %ws: %ws: response not from a Time Server. 0x%lx\n",
  3692. NlDcCacheEntry->UnicodeNetbiosDcName,
  3693. Context->QueriedDisplayDomainName,
  3694. NlDcCacheEntry->ReturnFlags ));
  3695. }
  3696. return FALSE;
  3697. }
  3698. //
  3699. // If we need machine running that is writable, ensure the responding DC is.
  3700. //
  3701. if ( (Context->QueriedFlags & DS_WRITABLE_REQUIRED) &&
  3702. (NlDcCacheEntry->ReturnFlags & DS_WRITABLE_FLAG) == 0 ) {
  3703. if ( BeVerbose ) {
  3704. NlPrint((NL_CRITICAL,
  3705. "NetpDcMatchResponse: %ws: %ws: Responder is not a writable server. 0x%lx\n",
  3706. NlDcCacheEntry->UnicodeNetbiosDcName,
  3707. Context->QueriedDisplayDomainName,
  3708. NlDcCacheEntry->ReturnFlags ));
  3709. }
  3710. return FALSE;
  3711. }
  3712. //
  3713. // If we need an LDAP server, ensure the responding server is
  3714. //
  3715. if ( (Context->QueriedFlags & DS_ONLY_LDAP_NEEDED) &&
  3716. (NlDcCacheEntry->ReturnFlags & DS_LDAP_FLAG) == 0 ) {
  3717. if ( BeVerbose ) {
  3718. NlPrint((NL_CRITICAL,
  3719. "NetpDcMatchResponse: %ws: %ws: Responder is not a LDAP server. 0x%lx\n",
  3720. NlDcCacheEntry->UnicodeNetbiosDcName,
  3721. Context->QueriedDisplayDomainName,
  3722. NlDcCacheEntry->ReturnFlags ));
  3723. }
  3724. return FALSE;
  3725. }
  3726. //
  3727. // If the caller wants only netbios names,
  3728. // ensure one is available.
  3729. //
  3730. if ( Context->QueriedFlags & DS_RETURN_FLAT_NAME ) {
  3731. if ( NlDcCacheEntry->UnicodeNetbiosDcName == NULL || NlDcCacheEntry->UnicodeNetbiosDomainName == NULL ) {
  3732. if ( BeVerbose ) {
  3733. NlPrint((NL_CRITICAL,
  3734. "NetpDcMatchResponse: %ws: %ws: Netbios server or domain name needed and missing.\n",
  3735. NlDcCacheEntry->UnicodeNetbiosDcName,
  3736. Context->QueriedDisplayDomainName ));
  3737. }
  3738. return FALSE;
  3739. }
  3740. }
  3741. //
  3742. // If the caller wants only dns names,
  3743. // ensure one is available.
  3744. //
  3745. if ( Context->QueriedFlags & DS_RETURN_DNS_NAME ) {
  3746. if ( NlDcCacheEntry->UnicodeDnsHostName == NULL || NlDcCacheEntry->UnicodeDnsDomainName == NULL ) {
  3747. if ( BeVerbose ) {
  3748. NlPrint((NL_CRITICAL,
  3749. "NetpDcMatchResponse: %ws: Dns server or domain name needed and missing.\n",
  3750. Context->QueriedDisplayDomainName ));
  3751. }
  3752. return FALSE;
  3753. }
  3754. }
  3755. //
  3756. // If the caller explicitly specified a sitename,
  3757. // ensure the DC is in the specified site.
  3758. //
  3759. if ( Context->DoingExplicitSite ) {
  3760. if ( NlDcCacheEntry->UnicodeDcSiteName == NULL ||
  3761. _wcsicmp( NlDcCacheEntry->UnicodeDcSiteName,
  3762. Context->QueriedSiteName ) != 0 ) {
  3763. if ( BeVerbose ) {
  3764. NlPrint((NL_CRITICAL,
  3765. "NetpDcMatchResponse: %ws: Responder is in site '%ws' but site '%ws' asked for.\n",
  3766. Context->QueriedDisplayDomainName,
  3767. NlDcCacheEntry->UnicodeDcSiteName,
  3768. Context->QueriedSiteName ));
  3769. }
  3770. return FALSE;
  3771. }
  3772. }
  3773. //
  3774. // If we should ignored responses from ourself,
  3775. // do so now.
  3776. //
  3777. if ( (Context->QueriedFlags & DS_AVOID_SELF) != 0 &&
  3778. NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  3779. //
  3780. // If response is from this computer,
  3781. // ignore it.
  3782. //
  3783. if ( NlNameCompare( NlDcCacheEntry->UnicodeNetbiosDcName,
  3784. (LPWSTR)Context->OurNetbiosComputerName,
  3785. NAMETYPE_COMPUTER ) == 0 ) {
  3786. if ( BeVerbose ) {
  3787. NlPrint((NL_SESSION_SETUP,
  3788. "NetpDcMatchResponse: %ws: response is from ourself and caller asked for AVOID_SELF.\n",
  3789. Context->QueriedDisplayDomainName ));
  3790. }
  3791. return FALSE;
  3792. }
  3793. }
  3794. //
  3795. // If we need machine running the KDC, ensure the responding DC is one.
  3796. //
  3797. if ( (Context->QueriedFlags & DS_KDC_REQUIRED) &&
  3798. (NlDcCacheEntry->ReturnFlags & DS_KDC_FLAG) == 0 ) {
  3799. if ( BeVerbose ) {
  3800. NlPrint((NL_CRITICAL,
  3801. "NetpDcMatchResponse: %ws: %ws: response not from KDC. 0x%lx\n",
  3802. NlDcCacheEntry->UnicodeNetbiosDcName,
  3803. Context->QueriedDisplayDomainName,
  3804. NlDcCacheEntry->ReturnFlags ));
  3805. }
  3806. return FALSE;
  3807. }
  3808. //
  3809. // If we need a DC running IP, ensure the responding DC has an IP address.
  3810. //
  3811. // Do this check after the KDC check. Kerberos always asks for IP_REQUIRED.
  3812. // We don't want this check to discard the entry for non-KDCs.
  3813. //
  3814. if ( (Context->QueriedFlags & DS_IP_REQUIRED) &&
  3815. NlDcCacheEntry->SockAddr.iSockaddrLength == 0 ) {
  3816. if ( BeVerbose ) {
  3817. NlPrint((NL_CRITICAL,
  3818. "NetpDcMatchResponse: %ws: %ws: response not from IP transport\n",
  3819. NlDcCacheEntry->UnicodeNetbiosDcName,
  3820. Context->QueriedDisplayDomainName ));
  3821. }
  3822. return FALSE;
  3823. }
  3824. //
  3825. // FINAL TEST!!!!
  3826. //
  3827. // Only do this test if the cache entry meets all of the other criteria.
  3828. //
  3829. // If we prefer a DS server and this DC is not one,
  3830. // just save this entry and continue looking.
  3831. // If we find no DS server, we'll use this entry as a last resort.
  3832. //
  3833. if ( (Context->QueriedFlags & DS_DIRECTORY_SERVICE_PREFERRED) &&
  3834. (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) == 0 ) {
  3835. //
  3836. // Ditch the previously saved cache entry if the new DC is closer.
  3837. //
  3838. if ( Context->ImperfectCacheEntry != NULL &&
  3839. (Context->ImperfectCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) == 0 &&
  3840. (NlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) != 0 ) {
  3841. NetpDcDerefCacheEntry( Context->ImperfectCacheEntry );
  3842. Context->ImperfectCacheEntry = NULL;
  3843. }
  3844. //
  3845. // Only save the first entry
  3846. //
  3847. if ( Context->ImperfectCacheEntry == NULL ) {
  3848. if ( BeVerbose ) {
  3849. NlPrint((NL_SESSION_SETUP,
  3850. "NetpDcMatchResponse: %ws: non-DS server responded when DS preferred\n",
  3851. Context->QueriedDisplayDomainName ));
  3852. }
  3853. //
  3854. // Reference the entry
  3855. //
  3856. NlDcCacheEntry->ReferenceCount ++;
  3857. Context->ImperfectCacheEntry = NlDcCacheEntry;
  3858. Context->ImperfectUsedNetbios = LocalUsedNetbios;
  3859. }
  3860. //
  3861. // Tell the caller that the match failed.
  3862. // The caller will use the above stored entry at his discretion.
  3863. return FALSE;
  3864. }
  3865. //
  3866. // FINAL TEST!!!!
  3867. //
  3868. // Only do this test if the cache entry meets all of the other criteria.
  3869. //
  3870. // If we prefer a "good" time server and this DC is not one,
  3871. // just save this entry and continue looking.
  3872. // If we find no "good" time server server, we'll use this entry as a last resort.
  3873. //
  3874. if ( (Context->QueriedFlags & DS_GOOD_TIMESERV_PREFERRED) &&
  3875. (NlDcCacheEntry->ReturnFlags & DS_GOOD_TIMESERV_FLAG) == 0 ) {
  3876. //
  3877. // Ditch the previously saved cache entry if the new DC is closer.
  3878. //
  3879. if ( Context->ImperfectCacheEntry != NULL &&
  3880. (Context->ImperfectCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) == 0 &&
  3881. (NlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) != 0 ) {
  3882. NetpDcDerefCacheEntry( Context->ImperfectCacheEntry );
  3883. Context->ImperfectCacheEntry = NULL;
  3884. }
  3885. //
  3886. // Only save the first entry
  3887. //
  3888. if ( Context->ImperfectCacheEntry == NULL ) {
  3889. if ( BeVerbose ) {
  3890. NlPrint((NL_SESSION_SETUP,
  3891. "NetpDcMatchResponse: %ws: non-good time server responded when one preferred\n",
  3892. Context->QueriedDisplayDomainName ));
  3893. }
  3894. //
  3895. // Reference the entry
  3896. //
  3897. NlDcCacheEntry->ReferenceCount ++;
  3898. Context->ImperfectCacheEntry = NlDcCacheEntry;
  3899. Context->ImperfectUsedNetbios = LocalUsedNetbios;
  3900. }
  3901. //
  3902. // Tell the caller that the match failed.
  3903. // The caller will use the above stored entry at his discretion.
  3904. return FALSE;
  3905. }
  3906. //
  3907. // All tests passed.
  3908. //
  3909. *UsedNetbios = LocalUsedNetbios;
  3910. return TRUE;
  3911. }
  3912. PNL_DC_CACHE_ENTRY
  3913. NetpDcFindCacheEntry(
  3914. IN PNL_GETDC_CONTEXT Context,
  3915. OUT PBOOL UsedNetbios,
  3916. OUT PBOOL ForcePing
  3917. )
  3918. /*++
  3919. Routine Description:
  3920. This routine finds a cache entry that matches the caller's query.
  3921. Arguments:
  3922. Context - Context describing the GetDc operation.
  3923. UsedNetbios - Returns TRUE if the netbios domain name was used to do the
  3924. successful comparison.
  3925. ForcePing - TRUE if the returned cache entry has to be pinged before it is used
  3926. Return Value:
  3927. On success, returns a pointer to the cache entry describing the found DC.
  3928. This entry must be dereferenced using NetpDcDerefCacheEntry.
  3929. NULL - no matching cache entry could be found.
  3930. --*/
  3931. {
  3932. PNL_DC_CACHE_ENTRY NlDcCacheEntry;
  3933. BOOL LocalUsedNetbios;
  3934. LONG QueryType; // Must be a signed number
  3935. //
  3936. // Check if there is a cache entry for this query type.
  3937. //
  3938. *ForcePing = FALSE;
  3939. EnterCriticalSection(&NlDcCritSect);
  3940. NlDcCacheEntry = Context->NlDcDomainEntry->Dc[Context->DcQueryType].NlDcCacheEntry;
  3941. if ( NlDcCacheEntry != NULL ) {
  3942. //
  3943. // Ensure the cache entry matches all the criteria.
  3944. //
  3945. if ( NetpDcMatchResponse(
  3946. Context,
  3947. NlDcCacheEntry,
  3948. FALSE,
  3949. &LocalUsedNetbios) ) {
  3950. NlPrint(( NL_DNS_MORE,
  3951. "Cache: %ws %ws: Cache entry %ld exists and was used.\n",
  3952. Context->NlDcDomainEntry->UnicodeNetbiosDomainName,
  3953. Context->NlDcDomainEntry->UnicodeDnsDomainName,
  3954. Context->DcQueryType ));
  3955. goto Cleanup;
  3956. } else {
  3957. BOOL Matched;
  3958. //
  3959. // If the only thing different is the account name,
  3960. // don't ditch the cache entry just for that reason.
  3961. //
  3962. if ( Context->QueriedAccountName != NULL ) {
  3963. LPCWSTR QueriedAccountName;
  3964. ULONG QueriedAllowableAccountControlBits;
  3965. QueriedAccountName = Context->QueriedAccountName;
  3966. Context->QueriedAccountName = NULL;
  3967. QueriedAllowableAccountControlBits = Context->QueriedAllowableAccountControlBits;
  3968. Context->QueriedAllowableAccountControlBits = 0;
  3969. Matched = NetpDcMatchResponse(
  3970. Context,
  3971. NlDcCacheEntry,
  3972. FALSE,
  3973. &LocalUsedNetbios);
  3974. Context->QueriedAccountName = QueriedAccountName;
  3975. Context->QueriedAllowableAccountControlBits = QueriedAllowableAccountControlBits;
  3976. if ( Matched ) {
  3977. NlPrint(( NL_DNS_MORE,
  3978. "Cache: %ws %ws: Cache entry %ld exists and was used (but account wrong).\n",
  3979. Context->NlDcDomainEntry->UnicodeNetbiosDomainName,
  3980. Context->NlDcDomainEntry->UnicodeDnsDomainName,
  3981. Context->DcQueryType ));
  3982. *ForcePing = TRUE;
  3983. goto Cleanup;
  3984. }
  3985. }
  3986. NlPrint(( NL_DNS_MORE,
  3987. "Cache: %ws %ws: Cache entry %ld exists and was NOT used.\n",
  3988. Context->NlDcDomainEntry->UnicodeNetbiosDomainName,
  3989. Context->NlDcDomainEntry->UnicodeDnsDomainName,
  3990. Context->DcQueryType ));
  3991. }
  3992. }
  3993. //
  3994. // Try to find a less specific cache entry that happens to match the criteria.
  3995. //
  3996. // For example, if I've previously cached an entry for a generic DC and it
  3997. // happens to be a PDC. If I later try to find a PDC, use the one I've already
  3998. // found.
  3999. //
  4000. for ( QueryType = Context->DcQueryType-1; QueryType>=0; QueryType-- ) {
  4001. //
  4002. // Do not return a GC entry if this is a non-GC (PDC) discovery.
  4003. // Accordingly, do not return a non-GC entry if this is a GC discovery.
  4004. // We want to ensure this match to return the correct closeness bit.
  4005. //
  4006. if ( QueryType == NlDcQueryGc || QueryType == NlDcQueryGenericGc ) {
  4007. if ( !NlDnsGcName( Context->QueriedNlDnsNameType ) ) {
  4008. continue;
  4009. }
  4010. } else {
  4011. if ( NlDnsGcName( Context->QueriedNlDnsNameType ) ) {
  4012. continue;
  4013. }
  4014. }
  4015. //
  4016. // If the cache entry matches all the criteria,
  4017. // use it.
  4018. //
  4019. NlDcCacheEntry = Context->NlDcDomainEntry->Dc[QueryType].NlDcCacheEntry;
  4020. if ( NlDcCacheEntry != NULL &&
  4021. NetpDcMatchResponse(
  4022. Context,
  4023. NlDcCacheEntry,
  4024. FALSE,
  4025. &LocalUsedNetbios) ) {
  4026. //
  4027. // I considered saving this cache entry as the preferred cache
  4028. // entry for this query type (by copying the pointer and
  4029. // incrementing the reference count). That'd ensure I'd
  4030. // consistently get this entry for this query type. But it'd
  4031. // also mean that I'd get this old entry once the original
  4032. // entry had been forced from the cache.
  4033. //
  4034. // Context->NlDcDomainEntry->Dc[Context->DcQueryType].NlDcCacheEntry = NlDcCacheEntry;
  4035. NlPrint(( NL_DNS_MORE,
  4036. "Cache: %ws %ws: Cache entry %ld used for %ld query.\n",
  4037. Context->NlDcDomainEntry->UnicodeNetbiosDomainName,
  4038. Context->NlDcDomainEntry->UnicodeDnsDomainName,
  4039. QueryType,
  4040. Context->DcQueryType ));
  4041. goto Cleanup;
  4042. }
  4043. }
  4044. //
  4045. // Entry isn't in the cache.
  4046. //
  4047. NlDcCacheEntry = NULL;
  4048. Cleanup:
  4049. if ( NlDcCacheEntry != NULL ) {
  4050. //
  4051. // Reference this entry.
  4052. //
  4053. NlDcCacheEntry->ReferenceCount++;
  4054. *UsedNetbios = LocalUsedNetbios;
  4055. }
  4056. LeaveCriticalSection(&NlDcCritSect);
  4057. return NlDcCacheEntry;
  4058. }
  4059. PNL_DC_DOMAIN_ENTRY
  4060. NetpDcFindDomainEntry(
  4061. IN GUID *DomainGuid OPTIONAL,
  4062. IN LPCWSTR NetbiosDomainName OPTIONAL,
  4063. IN LPCWSTR DnsDomainName OPTIONAL,
  4064. IN PNL_DC_DOMAIN_ENTRY NlDcDomainEntryToAvoid
  4065. )
  4066. /*++
  4067. Routine Description:
  4068. This routine finds a domain entry that matches the caller's query.
  4069. Arguments:
  4070. DomainGuid - Specifies the GUID of the domain to find.
  4071. NetbiosDomainName - Specifies the Netbios name of the domain to find.
  4072. DnsDomainName - Specifies the Dns name of the domain to find.
  4073. NlDcDomainEntryToAvoid - Specifies that this domain entry is not
  4074. to be returned even if it matches the description.
  4075. Return Value:
  4076. On success, returns a pointer to the domain cache entry describing a domain.
  4077. This entry must be dereference using NetpDcDerefDomainEntry.
  4078. NULL - no matching cache entry could be found.
  4079. --*/
  4080. {
  4081. PLIST_ENTRY DomainEntry;
  4082. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry = NULL;
  4083. ULONG EntryCount;
  4084. //
  4085. // Loop trying to find a cache entry that matches caller's query.
  4086. //
  4087. EnterCriticalSection(&NlDcCritSect);
  4088. EntryCount = 0;
  4089. for ( DomainEntry = NlDcDomainList.Flink ;
  4090. DomainEntry != &NlDcDomainList;
  4091. DomainEntry = DomainEntry->Flink ) {
  4092. EntryCount ++;
  4093. NlDcDomainEntry = CONTAINING_RECORD( DomainEntry, NL_DC_DOMAIN_ENTRY, Next);
  4094. //
  4095. // If this is the entry we're to avoid, skip it.
  4096. //
  4097. if ( NlDcDomainEntry == NlDcDomainEntryToAvoid ) {
  4098. NlDcDomainEntry = NULL;
  4099. continue;
  4100. }
  4101. //
  4102. // Ensure the DomainGuid returned matches the one expected.
  4103. //
  4104. if ( DomainGuid != NULL &&
  4105. !IsEqualGUID( &NlDcDomainEntry->DomainGuid, &NlDcZeroGuid) &&
  4106. !IsEqualGUID( &NlDcDomainEntry->DomainGuid, DomainGuid) ) {
  4107. NlDcDomainEntry = NULL;
  4108. continue;
  4109. }
  4110. //
  4111. // Ensure the Domain Name returned matches the one expected.
  4112. //
  4113. if ( ( NlDcDomainEntry->UnicodeNetbiosDomainName[0] == L'\0' ||
  4114. NetbiosDomainName == NULL ||
  4115. NlNameCompare( NlDcDomainEntry->UnicodeNetbiosDomainName, (LPWSTR)NetbiosDomainName, NAMETYPE_DOMAIN ) != 0 ) &&
  4116. (NlDcDomainEntry->UnicodeDnsDomainName == NULL ||
  4117. DnsDomainName == NULL ||
  4118. !NlEqualDnsName( NlDcDomainEntry->UnicodeDnsDomainName, DnsDomainName ) ) ) {
  4119. NlDcDomainEntry = NULL;
  4120. continue;
  4121. }
  4122. //
  4123. // If we've gotten this far,
  4124. // this entry matches the request.
  4125. //
  4126. // Reference this entry.
  4127. //
  4128. NlDcDomainEntry->ReferenceCount++;
  4129. break;
  4130. }
  4131. if ( NlDcDomainEntry == NULL ) {
  4132. NlAssert( EntryCount == NlDcDomainCount );
  4133. }
  4134. LeaveCriticalSection(&NlDcCritSect);
  4135. return NlDcDomainEntry;
  4136. }
  4137. VOID
  4138. NetpDcDerefDomainEntry(
  4139. IN PNL_DC_DOMAIN_ENTRY NlDcDomainEntry
  4140. )
  4141. /*++
  4142. Routine Description:
  4143. Decrement the reference count on a cache entry. If the count reaches zero,
  4144. delete the entry.
  4145. The count will only reach zero if the entry is already removed from the
  4146. global linked list.
  4147. Arguments:
  4148. NlDcDomainEntry - Cache entry to dereference.
  4149. Return Value:
  4150. None.
  4151. --*/
  4152. {
  4153. ULONG LocalReferenceCount;
  4154. EnterCriticalSection(&NlDcCritSect);
  4155. LocalReferenceCount = -- NlDcDomainEntry->ReferenceCount;
  4156. if ( LocalReferenceCount == 0 ) {
  4157. ULONG QueryType;
  4158. //
  4159. // Remove our reference to all of the cache entries for this domain.
  4160. //
  4161. for ( QueryType = 0; QueryType < NlDcQueryTypeCount; QueryType ++ ) {
  4162. if ( NlDcDomainEntry->Dc[QueryType].NlDcCacheEntry != NULL ) {
  4163. NetpDcDerefCacheEntry( NlDcDomainEntry->Dc[QueryType].NlDcCacheEntry );
  4164. }
  4165. }
  4166. //
  4167. // Free DnsName
  4168. //
  4169. if ( NlDcDomainEntry->UnicodeDnsDomainName != NULL ) {
  4170. NetpMemoryFree( NlDcDomainEntry->UnicodeDnsDomainName );
  4171. }
  4172. //
  4173. // Free the entry itself.
  4174. //
  4175. NetpMemoryFree(NlDcDomainEntry);
  4176. }
  4177. LeaveCriticalSection(&NlDcCritSect);
  4178. }
  4179. VOID
  4180. NetpDcDeleteDomainEntry(
  4181. IN PNL_DC_DOMAIN_ENTRY NlDcDomainEntry
  4182. )
  4183. /*++
  4184. Routine Description:
  4185. Remove a cache entry from the global list.
  4186. Enter with NlDcCritSect locked.
  4187. Arguments:
  4188. NlDcDomainEntry - Cache entry to remove.
  4189. Return Value:
  4190. None.
  4191. --*/
  4192. {
  4193. //
  4194. // Remove it.
  4195. //
  4196. RemoveEntryList( &NlDcDomainEntry->Next );
  4197. NlDcDomainCount --;
  4198. //
  4199. // Ensure any current references know it has been deleted.
  4200. //
  4201. NlDcDomainEntry->DeletedEntry = TRUE;
  4202. //
  4203. // Decrement the reference indicating it is on the list.
  4204. //
  4205. NetpDcDerefDomainEntry( NlDcDomainEntry );
  4206. }
  4207. BOOL
  4208. NetpDcUpdateDomainEntry(
  4209. IN PNL_DC_DOMAIN_ENTRY NlDcDomainEntry,
  4210. IN GUID *DomainGuid OPTIONAL,
  4211. IN LPCWSTR NetbiosDomainName OPTIONAL,
  4212. IN LPCWSTR DnsDomainName OPTIONAL
  4213. )
  4214. /*++
  4215. Routine Description:
  4216. This routine updates the domain entry to contain the passed in domain
  4217. name information.
  4218. Arguments:
  4219. NlDcDomainEntry - Domain entry to update.
  4220. DomainGuid - Specifies the GUID of the domain.
  4221. NetbiosDomainName - Specifies the Netbios name of the domain.
  4222. DnsDomainName - Specifies the Dns name of the domain.
  4223. Return Value:
  4224. TRUE - all names updated as requested.
  4225. FALSE - some names could not be updated.
  4226. --*/
  4227. {
  4228. BOOL NamesChanged = FALSE;
  4229. PNL_DC_DOMAIN_ENTRY DuplicateDomainEntry;
  4230. //
  4231. // If this entry has been deleted,
  4232. // don't bother updating it.
  4233. //
  4234. EnterCriticalSection(&NlDcCritSect);
  4235. if ( NlDcDomainEntry->DeletedEntry ) {
  4236. LeaveCriticalSection(&NlDcCritSect);
  4237. return TRUE;
  4238. }
  4239. //
  4240. // Fill in the Netbios domain name if it is not already filled in.
  4241. //
  4242. if ( NetbiosDomainName != NULL &&
  4243. ( NlDcDomainEntry->UnicodeNetbiosDomainName[0] == L'\0' ||
  4244. NlNameCompare( NlDcDomainEntry->UnicodeNetbiosDomainName, (LPWSTR)NetbiosDomainName, NAMETYPE_DOMAIN ) != 0 ) ) {
  4245. wcscpy( NlDcDomainEntry->UnicodeNetbiosDomainName, NetbiosDomainName );
  4246. NamesChanged = TRUE;
  4247. NlPrint(( NL_DNS_MORE,
  4248. "Cache: %ws %ws: Set netbios domain name\n",
  4249. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4250. NlDcDomainEntry->UnicodeDnsDomainName ));
  4251. }
  4252. //
  4253. // Fill in the DNS domain name if it is not already filled in.
  4254. //
  4255. if ( DnsDomainName != NULL &&
  4256. ( NlDcDomainEntry->UnicodeDnsDomainName == NULL ||
  4257. !NlEqualDnsName( NlDcDomainEntry->UnicodeDnsDomainName, DnsDomainName ) ) ) {
  4258. if ( NlDcDomainEntry->UnicodeDnsDomainName != NULL ) {
  4259. NetApiBufferFree( NlDcDomainEntry->UnicodeDnsDomainName );
  4260. }
  4261. NlDcDomainEntry->UnicodeDnsDomainName = NetpAllocWStrFromWStr( (LPWSTR) DnsDomainName );
  4262. if ( NlDcDomainEntry->UnicodeDnsDomainName == NULL ) {
  4263. LeaveCriticalSection(&NlDcCritSect);
  4264. return FALSE;
  4265. }
  4266. NamesChanged = TRUE;
  4267. NlPrint(( NL_DNS_MORE,
  4268. "Cache: %ws %ws: Set DNS domain name\n",
  4269. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4270. NlDcDomainEntry->UnicodeDnsDomainName ));
  4271. }
  4272. //
  4273. // Fill in the GUID if its not already filled in.
  4274. //
  4275. if ( DomainGuid != NULL &&
  4276. IsEqualGUID( &NlDcDomainEntry->DomainGuid, DomainGuid) ) {
  4277. NamesChanged = TRUE;
  4278. NlDcDomainEntry->DomainGuid = *DomainGuid;
  4279. }
  4280. //
  4281. // If the names have changed,
  4282. // perhaps this domain cache entry now duplicates another entry.
  4283. //
  4284. // Find any duplicate entries and merge them into this one.
  4285. //
  4286. if ( NamesChanged ) {
  4287. while ( (DuplicateDomainEntry = NetpDcFindDomainEntry(
  4288. &NlDcDomainEntry->DomainGuid,
  4289. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4290. NlDcDomainEntry->UnicodeDnsDomainName,
  4291. NlDcDomainEntry )) != NULL ) {
  4292. ULONG QueryType;
  4293. NlPrint(( NL_DNS_MORE,
  4294. "Cache: %ws %ws: is now a duplicate of %ws %ws\n",
  4295. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4296. NlDcDomainEntry->UnicodeDnsDomainName,
  4297. DuplicateDomainEntry->UnicodeNetbiosDomainName,
  4298. DuplicateDomainEntry->UnicodeDnsDomainName ));
  4299. //
  4300. // Move any cache entries from the duplicate to the new.
  4301. // ?? We could theoretically keep the 'better' of the two entries.
  4302. //
  4303. for ( QueryType = 0; QueryType < NlDcQueryTypeCount; QueryType ++ ) {
  4304. if ( NlDcDomainEntry->Dc[QueryType].NlDcCacheEntry == NULL &&
  4305. DuplicateDomainEntry->Dc[QueryType].NlDcCacheEntry != NULL ) {
  4306. NlDcDomainEntry->Dc[QueryType].NlDcCacheEntry =
  4307. DuplicateDomainEntry->Dc[QueryType].NlDcCacheEntry;
  4308. NlFlushNegativeCacheEntry( &NlDcDomainEntry->Dc[QueryType] );
  4309. DuplicateDomainEntry->Dc[QueryType].NlDcCacheEntry = NULL;
  4310. NlPrint(( NL_DNS_MORE,
  4311. "Cache: %ws %ws: grab entry %ld from %ws %ws\n",
  4312. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4313. NlDcDomainEntry->UnicodeDnsDomainName,
  4314. QueryType,
  4315. DuplicateDomainEntry->UnicodeNetbiosDomainName,
  4316. DuplicateDomainEntry->UnicodeDnsDomainName ));
  4317. }
  4318. }
  4319. //
  4320. // Delete the duplicate entry.
  4321. // (There may be an outstanding reference to this entry.)
  4322. //
  4323. NetpDcDeleteDomainEntry( DuplicateDomainEntry );
  4324. // Remove our reference.
  4325. NetpDcDerefDomainEntry( DuplicateDomainEntry );
  4326. }
  4327. }
  4328. LeaveCriticalSection(&NlDcCritSect);
  4329. return TRUE;
  4330. }
  4331. VOID
  4332. NetpDcInsertCacheEntry(
  4333. IN PNL_GETDC_CONTEXT Context,
  4334. IN PNL_DC_CACHE_ENTRY NlDcCacheEntry
  4335. )
  4336. /*++
  4337. Routine Description:
  4338. This routine inserts a cache entry onto the domain entry.
  4339. Arguments:
  4340. Context - Context describing the GetDc operation.
  4341. NlDcCacheEntry - Cache entry to use.
  4342. Return Value:
  4343. None.
  4344. --*/
  4345. {
  4346. PNL_DC_CACHE_ENTRY *CacheEntryPtr;
  4347. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry = Context->NlDcDomainEntry;
  4348. //
  4349. // If the caller explicitly asked for a particular site,
  4350. // and that site isn't the closest site.
  4351. // Avoid polluting the cache with this entry.
  4352. //
  4353. if ( Context->DoingExplicitSite &&
  4354. (NlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) == 0 ) {
  4355. NlPrint(( NL_DNS_MORE,
  4356. "Cache: %ws %ws: don't cache this entry since an explicit site '%ws' was asked for.\n",
  4357. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4358. NlDcDomainEntry->UnicodeDnsDomainName,
  4359. Context->QueriedSiteName ));
  4360. return;
  4361. }
  4362. //
  4363. // If there is no cache entry for this query type,
  4364. // or this cache entry is better than the old one,
  4365. // or the new cache entry was found via a 'force' rediscovery,
  4366. // or the new cache entry is for the same DC as the old entry,
  4367. // use the new cache entry.
  4368. //
  4369. EnterCriticalSection(&NlDcCritSect);
  4370. CacheEntryPtr = &NlDcDomainEntry->Dc[Context->DcQueryType].NlDcCacheEntry;
  4371. if ( *CacheEntryPtr == NULL ||
  4372. NlDcCacheEntry->DcQuality >= (*CacheEntryPtr)->DcQuality ||
  4373. (Context->QueriedFlags & DS_FORCE_REDISCOVERY) != 0 ||
  4374. (NlDcCacheEntry->UnicodeNetbiosDcName != NULL &&
  4375. (*CacheEntryPtr)->UnicodeNetbiosDcName != NULL &&
  4376. NlNameCompare( NlDcCacheEntry->UnicodeNetbiosDcName,
  4377. (*CacheEntryPtr)->UnicodeNetbiosDcName,
  4378. NAMETYPE_COMPUTER ) == 0 ) ||
  4379. (NlDcCacheEntry->UnicodeDnsHostName != NULL &&
  4380. (*CacheEntryPtr)->UnicodeDnsHostName != NULL &&
  4381. NlEqualDnsName( NlDcCacheEntry->UnicodeDnsHostName,
  4382. (*CacheEntryPtr)->UnicodeDnsHostName ) )
  4383. ) {
  4384. //
  4385. // Delink any existing cache entry.
  4386. //
  4387. if ( *CacheEntryPtr != NULL ) {
  4388. NlPrint(( NL_DNS_MORE,
  4389. "Cache: %ws %ws: Ditch existing cache entry %ld (Quality: %ld)\n",
  4390. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4391. NlDcDomainEntry->UnicodeDnsDomainName,
  4392. Context->DcQueryType,
  4393. (*CacheEntryPtr)->DcQuality ));
  4394. NetpDcDerefCacheEntry( *CacheEntryPtr );
  4395. *CacheEntryPtr = NULL;
  4396. }
  4397. //
  4398. // Link the cache entry onto the domain entry and increment the reference count
  4399. // to account for the new reference.
  4400. //
  4401. *CacheEntryPtr = NlDcCacheEntry;
  4402. NlDcCacheEntry->ReferenceCount ++;
  4403. //
  4404. // Indicate that the cache entry has been inserted
  4405. //
  4406. NlDcCacheEntry->CacheEntryFlags |= NL_DC_CACHE_ENTRY_INSERTED;
  4407. //
  4408. // Flush the negative cache.
  4409. //
  4410. NlFlushNegativeCacheEntry( &NlDcDomainEntry->Dc[Context->DcQueryType] );
  4411. //
  4412. // Update the domain entry to contain more information about the domain.
  4413. //
  4414. // If this is a GC discovery entry and the discovered forest name is different
  4415. // from the domain name of the GC, update the domain entry using the forest
  4416. // name only.
  4417. //
  4418. if ( NlDnsGcName( Context->QueriedNlDnsNameType ) &&
  4419. (NlDcCacheEntry->UnicodeDnsForestName == NULL ||
  4420. NlDcCacheEntry->UnicodeDnsDomainName == NULL ||
  4421. !NlEqualDnsName( NlDcCacheEntry->UnicodeDnsForestName,
  4422. NlDcCacheEntry->UnicodeDnsDomainName )) ) {
  4423. (VOID) NetpDcUpdateDomainEntry( NlDcDomainEntry,
  4424. NULL,
  4425. NULL,
  4426. NlDcCacheEntry->UnicodeDnsForestName );
  4427. } else {
  4428. (VOID) NetpDcUpdateDomainEntry( NlDcDomainEntry,
  4429. &NlDcCacheEntry->DomainGuid,
  4430. NlDcCacheEntry->UnicodeNetbiosDomainName,
  4431. NlDcCacheEntry->UnicodeDnsDomainName );
  4432. }
  4433. NlPrint(( NL_DNS_MORE,
  4434. "Cache: %ws %ws: Add cache entry %ld (Quality: %ld)\n",
  4435. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4436. NlDcDomainEntry->UnicodeDnsDomainName,
  4437. Context->DcQueryType,
  4438. NlDcCacheEntry->DcQuality ));
  4439. } else {
  4440. NlPrint(( NL_DNS_MORE,
  4441. "Cache: %ws %ws: Existing cache entry for type %ld is better %ld %ld\n",
  4442. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4443. NlDcDomainEntry->UnicodeDnsDomainName,
  4444. Context->DcQueryType,
  4445. NlDcCacheEntry->DcQuality,
  4446. (*CacheEntryPtr)->DcQuality ));
  4447. }
  4448. LeaveCriticalSection(&NlDcCritSect);
  4449. return;
  4450. }
  4451. PNL_DC_DOMAIN_ENTRY
  4452. NetpDcCreateDomainEntry(
  4453. IN GUID *DomainGuid OPTIONAL,
  4454. IN LPCWSTR NetbiosDomainName OPTIONAL,
  4455. IN LPCWSTR DnsDomainName OPTIONAL
  4456. )
  4457. /*++
  4458. Routine Description:
  4459. This routine finds an existing domain cache entry that matches the
  4460. caller's query or creates one.
  4461. Arguments:
  4462. DomainGuid - Specifies the GUID of the domain to find.
  4463. NetbiosDomainName - Specifies the Netbios name of the domain to find.
  4464. DnsDomainName - Specifies the Dns name of the domain to find.
  4465. Return Value:
  4466. On success, returns a pointer to the domain cache entry describing a domain.
  4467. This entry must be dereference using NetpDcDerefDomainEntry.
  4468. NULL - Entry could not be allocated.
  4469. --*/
  4470. {
  4471. PLIST_ENTRY DomainEntry;
  4472. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry;
  4473. //
  4474. // If there is an existing entry, use it.
  4475. //
  4476. EnterCriticalSection(&NlDcCritSect);
  4477. NlDcDomainEntry = NetpDcFindDomainEntry( DomainGuid, NetbiosDomainName, DnsDomainName, NULL );
  4478. if ( NlDcDomainEntry != NULL ) {
  4479. //
  4480. // Put the referenced entry at the front of the list.
  4481. //
  4482. RemoveEntryList( &NlDcDomainEntry->Next );
  4483. InsertHeadList( &NlDcDomainList, &NlDcDomainEntry->Next );
  4484. NlPrint(( NL_DNS_MORE,
  4485. "Cache: %ws %ws: Found existing domain cache entry\n",
  4486. NlDcDomainEntry->UnicodeNetbiosDomainName,
  4487. NlDcDomainEntry->UnicodeDnsDomainName ));
  4488. //
  4489. // Set the domain information in the domain entry.
  4490. //
  4491. // One might be tempted to put the domain name into the domain entry at this
  4492. // time. That'd be bogus since that caller doesn't know whether the passed
  4493. // in netbios and DNS name is really correct. For instance, in some instances,
  4494. // both passed in names are the netbios domain name.
  4495. //
  4496. if ( !NetpDcUpdateDomainEntry( NlDcDomainEntry,
  4497. DomainGuid,
  4498. NULL,
  4499. NULL ) ) {
  4500. // Remove our reference.
  4501. NetpDcDerefDomainEntry( NlDcDomainEntry );
  4502. NlDcDomainEntry = NULL;
  4503. }
  4504. //
  4505. // Otherwise allocate a new entry.
  4506. //
  4507. } else {
  4508. //
  4509. // Allocate a new entry.
  4510. //
  4511. NlDcDomainEntry = NetpMemoryAllocate( sizeof( NL_DC_DOMAIN_ENTRY ) );
  4512. if ( NlDcDomainEntry == NULL ) {
  4513. LeaveCriticalSection(&NlDcCritSect);
  4514. return NULL;
  4515. }
  4516. NlPrint(( NL_DNS_MORE,
  4517. "Cache: %ws %ws: Create new domain cache entry\n",
  4518. NetbiosDomainName,
  4519. DnsDomainName ));
  4520. //
  4521. // Initialize the entry.
  4522. //
  4523. RtlZeroMemory( NlDcDomainEntry, sizeof(NL_DC_DOMAIN_ENTRY) );
  4524. // One for our reference. One for being in global list.
  4525. NlDcDomainEntry->ReferenceCount = 2;
  4526. //
  4527. // Link a newly allocated entry into the global list.
  4528. InsertHeadList( &NlDcDomainList, &NlDcDomainEntry->Next );
  4529. NlDcDomainCount ++;
  4530. //
  4531. // If we already have enough entries,
  4532. // delete the LRU one.
  4533. //
  4534. if ( NlDcDomainCount > NL_DC_MAX_DOMAINS ) {
  4535. PNL_DC_DOMAIN_ENTRY TempNlDcDomainEntry =
  4536. CONTAINING_RECORD( NlDcDomainList.Blink, NL_DC_DOMAIN_ENTRY, Next);
  4537. if ( TempNlDcDomainEntry != NlDcDomainEntry ) {
  4538. NlAssert( TempNlDcDomainEntry->ReferenceCount == 1 );
  4539. NetpDcDeleteDomainEntry( TempNlDcDomainEntry );
  4540. NlPrint(( NL_CRITICAL,
  4541. "NetpDcGreateDomainEntry: LRU'ed out a domain entry.\n" ));
  4542. }
  4543. }
  4544. //
  4545. // Set the domain information in the domain entry.
  4546. //
  4547. //
  4548. // Since we allocated the entry, we can put the potentially bogus names
  4549. // on it. All entries need a name. Then if no DCs are found,
  4550. // this entry can act as a negative cache entry.
  4551. //
  4552. if ( !NetpDcUpdateDomainEntry( NlDcDomainEntry,
  4553. DomainGuid,
  4554. NetbiosDomainName,
  4555. DnsDomainName ) ) {
  4556. // Remove from the global linked list.
  4557. NetpDcDeleteDomainEntry( NlDcDomainEntry );
  4558. // Remove our reference.
  4559. NetpDcDerefDomainEntry( NlDcDomainEntry );
  4560. NlDcDomainEntry = NULL;
  4561. }
  4562. }
  4563. LeaveCriticalSection(&NlDcCritSect);
  4564. return NlDcDomainEntry;
  4565. }
  4566. ULONG
  4567. NetpDcGetPingWaitTime(
  4568. IN PNL_GETDC_CONTEXT Context
  4569. )
  4570. /*++
  4571. Routine Description:
  4572. This routine determines the wait time for a ping response
  4573. for a new DC that has not yet been pinged. The wait time
  4574. depends on the total number of DCs which have already been
  4575. pinged as follows:
  4576. For the first 5 DCs (including this new one) the wait time is the maximum timeout per ping
  4577. For the next 5 DCs (including this new one) the wait time is the median timeout per ping
  4578. For the rest of DCs (including this new one) the wait time is the minimum timeout per ping
  4579. The rational behind this distribution is that we want to reduce the network
  4580. traffic and reduce chances for network flooding (that is harmful for DCs)
  4581. in case all DCs are slow to respond due to high load. Thus, the first 10 DCs
  4582. have higher chances to be discovered before we impose greater network traffic
  4583. by pinging the rest of DCs. If the first 10 DCs happen to be slow we have to
  4584. reduce the wait timeout to a minimum as we want to cover a reasonable number
  4585. of DCs in the time left.
  4586. Arguments:
  4587. Context - Context describing the GetDc operation. The DcsPinged
  4588. field should be equal to the current total number of DCs on
  4589. the list to be pinged.
  4590. Return Value:
  4591. Wait time in milliseconds
  4592. --*/
  4593. {
  4594. //
  4595. // If there are at most 4 DCs already pinged ...
  4596. //
  4597. if ( Context->DcsPinged < 5 ) {
  4598. return NL_DC_MAX_PING_TIMEOUT; // 0.4 sec
  4599. //
  4600. // If there are 5 or more but less than 10 DCs pinged ...
  4601. //
  4602. } else if ( Context->DcsPinged < 10 ) {
  4603. return NL_DC_MED_PING_TIMEOUT; // 0.2 sec
  4604. //
  4605. // If there are already 10 or more DCs pinged ...
  4606. //
  4607. } else {
  4608. return NL_DC_MIN_PING_TIMEOUT; // 0.1 sec
  4609. }
  4610. }
  4611. NET_API_STATUS
  4612. NetpDcProcessAddressList(
  4613. IN PNL_GETDC_CONTEXT Context,
  4614. IN LPWSTR DnsHostName OPTIONAL,
  4615. IN PSOCKET_ADDRESS SockAddressList,
  4616. IN ULONG SockAddressCount,
  4617. IN BOOLEAN SiteSpecificAddress,
  4618. OUT PNL_DC_ADDRESS *FirstAddressInserted OPTIONAL
  4619. )
  4620. /*++
  4621. Routine Description:
  4622. This routine adds IP addresses to the list of addresses to ping
  4623. ensuring that all addresses are unique in the resulted list.
  4624. Arguments:
  4625. Context - Context describing the GetDc operation.
  4626. DnsHostName - Server name whose address list is being processed.
  4627. SockAddressList - List of socket addresses.
  4628. SockAddressCount - The number of socket addresses in SockAddressList.
  4629. SiteSpecificAddress - If TRUE, indicates that the addresses were
  4630. retrieved as a result of site specific DNS lookups.
  4631. FirstAddressInserted - Returns a pointer to the first entry inserted
  4632. into the returned list.
  4633. Return Value:
  4634. NO_ERROR - The operation was successful.
  4635. ERROR_NOT_ENOUGH_MEMORY - Not enough memory to complete the operation.
  4636. --*/
  4637. {
  4638. NET_API_STATUS NetStatus;
  4639. PNL_DC_ADDRESS DcAddress = NULL;
  4640. ULONG AddressIndex;
  4641. PLIST_ENTRY ListEntry;
  4642. WORD SavedPort;
  4643. //
  4644. // Initialization
  4645. //
  4646. if ( FirstAddressInserted != NULL ) {
  4647. *FirstAddressInserted = NULL;
  4648. }
  4649. //
  4650. // Loop through the socket address list keeping only the new ones
  4651. //
  4652. for ( AddressIndex = 0; AddressIndex < SockAddressCount; AddressIndex++ ) {
  4653. //
  4654. // Ignore addresses that are too big.
  4655. //
  4656. if ( SockAddressList[AddressIndex].iSockaddrLength >
  4657. sizeof(DcAddress->SockAddrIn) ) {
  4658. NlPrint(( NL_CRITICAL,
  4659. "NetpDcGetNameIp: %ws: bogus address length. %ld %ld\n",
  4660. Context->QueriedDisplayDomainName,
  4661. SockAddressList[AddressIndex].iSockaddrLength,
  4662. sizeof(DcAddress->SockAddrIn) ));
  4663. continue;
  4664. }
  4665. //
  4666. // Force the port number to be zero.
  4667. //
  4668. if ( SockAddressList[AddressIndex].lpSockaddr->sa_family == AF_INET ) {
  4669. ((SOCKADDR_IN *)(SockAddressList[AddressIndex].lpSockaddr))->sin_port = 0;
  4670. }
  4671. //
  4672. // If this address is already on the list,
  4673. // update the new address.
  4674. //
  4675. DcAddress = NULL ;
  4676. for ( ListEntry = Context->DcAddressList.Flink ;
  4677. ListEntry != &Context->DcAddressList ;
  4678. ListEntry = ListEntry->Flink) {
  4679. DcAddress = CONTAINING_RECORD( ListEntry, NL_DC_ADDRESS, Next );
  4680. if ( SockAddressList[AddressIndex].iSockaddrLength ==
  4681. DcAddress->SockAddress.iSockaddrLength &&
  4682. RtlEqualMemory( SockAddressList[AddressIndex].lpSockaddr,
  4683. DcAddress->SockAddress.lpSockaddr,
  4684. DcAddress->SockAddress.iSockaddrLength ) ) {
  4685. break;
  4686. }
  4687. DcAddress = NULL ;
  4688. }
  4689. //
  4690. // Update the site specific bit
  4691. //
  4692. if ( DcAddress != NULL ) {
  4693. if ( SiteSpecificAddress ) {
  4694. DcAddress->AddressFlags |= NL_DC_ADDRESS_SITE_SPECIFIC;
  4695. }
  4696. continue;
  4697. }
  4698. //
  4699. // Allocate structure describing the new address.
  4700. //
  4701. DcAddress = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_DC_ADDRESS) );
  4702. if ( DcAddress == NULL ) {
  4703. return ERROR_NOT_ENOUGH_MEMORY;
  4704. }
  4705. //
  4706. // Fill it in and link it at the end of the list.
  4707. //
  4708. DcAddress->SockAddress.iSockaddrLength =
  4709. SockAddressList[AddressIndex].iSockaddrLength;
  4710. DcAddress->SockAddress.lpSockaddr =
  4711. (LPSOCKADDR) &DcAddress->SockAddrIn;
  4712. RtlCopyMemory( DcAddress->SockAddress.lpSockaddr,
  4713. SockAddressList[AddressIndex].lpSockaddr,
  4714. SockAddressList[AddressIndex].iSockaddrLength );
  4715. if ( DnsHostName != NULL ) {
  4716. DcAddress->DnsHostName = NetpAllocWStrFromWStr( DnsHostName );
  4717. if ( DcAddress->DnsHostName == NULL ) {
  4718. LocalFree( DcAddress );
  4719. return ERROR_NOT_ENOUGH_MEMORY;
  4720. }
  4721. }
  4722. //
  4723. // Convert the address to text.
  4724. //
  4725. // The DC only supports UDP on port 389. So ignore the
  4726. // port number returned from DNS.
  4727. //
  4728. SavedPort = DcAddress->SockAddrIn.sin_port;
  4729. DcAddress->SockAddrIn.sin_port = 0;
  4730. NetStatus = NetpSockAddrToStr(
  4731. DcAddress->SockAddress.lpSockaddr,
  4732. DcAddress->SockAddress.iSockaddrLength,
  4733. DcAddress->SockAddrString );
  4734. if ( NetStatus != NO_ERROR ) {
  4735. NlPrint(( NL_CRITICAL,
  4736. "NetpDcGetNameSiteIp: %ws: Cannot NetpSockAddrToStr. %ld\n",
  4737. Context->QueriedDisplayDomainName,
  4738. NetStatus ));
  4739. if ( DcAddress->DnsHostName != NULL ) {
  4740. NetApiBufferFree( DcAddress->DnsHostName );
  4741. }
  4742. LocalFree( DcAddress );
  4743. return NetStatus;
  4744. }
  4745. DcAddress->SockAddrIn.sin_port = SavedPort;
  4746. DcAddress->LdapHandle = NULL;
  4747. DcAddress->AddressFlags = 0;
  4748. if ( SiteSpecificAddress ) {
  4749. DcAddress->AddressFlags |= NL_DC_ADDRESS_SITE_SPECIFIC;
  4750. }
  4751. DcAddress->AddressPingWait = NetpDcGetPingWaitTime( Context );
  4752. InsertTailList( &Context->DcAddressList, &DcAddress->Next );
  4753. Context->DcAddressCount++;
  4754. if ( FirstAddressInserted != NULL && *FirstAddressInserted == NULL ) {
  4755. *FirstAddressInserted = DcAddress;
  4756. }
  4757. }
  4758. return NO_ERROR;
  4759. }
  4760. #ifndef WIN32_CHICAGO
  4761. NET_API_STATUS
  4762. I_DsGetDcCache(
  4763. IN LPCWSTR NetbiosDomainName OPTIONAL,
  4764. IN LPCWSTR DnsDomainName OPTIONAL,
  4765. OUT PBOOLEAN InNt4Domain,
  4766. OUT LPDWORD InNt4DomainTime
  4767. )
  4768. /*++
  4769. Routine Description:
  4770. This routine finds a domain entry that matches the caller's query.
  4771. Arguments:
  4772. NetbiosDomainName - Specifies the Netbios name of the domain to find.
  4773. DnsDomainName - Specifies the Dns name of the domain to find.
  4774. At least one of the above parameters should be non-NULL.
  4775. InNt4Domain - Returns true if the domain is an NT 4.0 domain.
  4776. InNt4DomainTime - Returns the GetTickCount time of when the domain was
  4777. detected to be an NT 4.0 domain.
  4778. Return Value:
  4779. NO_ERROR: Information is returned about the domain.
  4780. ERROR_NO_SUCH_DOMAIN: cached information is not available for this domain.
  4781. --*/
  4782. {
  4783. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry;
  4784. NlDcDomainEntry = NetpDcFindDomainEntry(
  4785. NULL,
  4786. NetbiosDomainName,
  4787. DnsDomainName,
  4788. NULL );
  4789. if ( NlDcDomainEntry == NULL ) {
  4790. return ERROR_NO_SUCH_DOMAIN;
  4791. }
  4792. *InNt4Domain = NlDcDomainEntry->InNt4Domain;
  4793. *InNt4DomainTime = NlDcDomainEntry->InNt4DomainTime;
  4794. NetpDcDerefDomainEntry( NlDcDomainEntry );
  4795. return NO_ERROR;
  4796. }
  4797. #endif // WIN32_CHICAGO
  4798. NET_API_STATUS
  4799. NetpDcCheckSiteCovered(
  4800. IN PNL_GETDC_CONTEXT Context,
  4801. IN LPWSTR DnsDcName OPTIONAL,
  4802. OUT PBOOLEAN DcClose
  4803. )
  4804. /*++
  4805. Routine Description:
  4806. This routine determines whether the site passed in the context
  4807. structure is covered by the passed DC. It does so by looking up
  4808. SRV records registered for the name type specified in the passed
  4809. context and the specified site. If there is a record that belongs
  4810. to the specified DC, the site is covered by the DC. If no DC is
  4811. specified, the routine determines if the site is covered by any
  4812. DC in the domain specified in the passed context.
  4813. Arguments:
  4814. Context - Context describing the GetDc operation.
  4815. DnsDcName - DNS DC name.
  4816. DcClose - On success, indicates whether the DC is close or not.
  4817. Return Value:
  4818. NO_ERROR: The NlDcCacheEntry was returned;
  4819. ERROR_DNS_NOT_CONFIGURED: IP or DNS is not available on this computer.
  4820. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  4821. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  4822. this request.
  4823. Various Winsock errors.
  4824. --*/
  4825. {
  4826. NET_API_STATUS NetStatus = NO_ERROR;
  4827. BOOLEAN IsClose = FALSE;
  4828. HANDLE DsGetDcHandle = NULL;
  4829. PSOCKET_ADDRESS SockAddressList = NULL;
  4830. ULONG SockAddressCount = 0;
  4831. LPSTR Utf8DnsDomainName = NULL;
  4832. LPSTR Utf8DnsDcName = NULL;
  4833. LPSTR DnsHostName = NULL;
  4834. //
  4835. // Check that the site name is availbale
  4836. //
  4837. if ( Context->QueriedSiteName == NULL ) {
  4838. goto Cleanup;
  4839. }
  4840. //
  4841. // Convert the DNS name to Utf8
  4842. //
  4843. Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( Context->QueriedDnsDomainName );
  4844. if ( Utf8DnsDomainName == NULL ) {
  4845. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  4846. goto Cleanup;
  4847. }
  4848. if ( DnsDcName != NULL ) {
  4849. Utf8DnsDcName = NetpAllocUtf8StrFromWStr( DnsDcName );
  4850. if ( Utf8DnsDcName == NULL ) {
  4851. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  4852. goto Cleanup;
  4853. }
  4854. }
  4855. //
  4856. // Get a context for the DNS name queries.
  4857. //
  4858. NetStatus = NetpDcGetDcOpen( Utf8DnsDomainName,
  4859. DS_ONLY_DO_SITE_NAME, // Do site specific names only
  4860. Context->QueriedSiteName,
  4861. Context->QueriedDomainGuid,
  4862. // No need to pass the forest name since it's used only
  4863. // for the "by guid" name which is not site specific
  4864. NULL,
  4865. // Force fresh DNS lookups
  4866. (Context->QueriedFlags | DS_FORCE_REDISCOVERY) & DS_OPEN_VALID_FLAGS,
  4867. &DsGetDcHandle );
  4868. if ( NetStatus != NO_ERROR ) {
  4869. goto Cleanup;
  4870. }
  4871. //
  4872. // Loop getting addresses to query.
  4873. //
  4874. for ( ;; ) {
  4875. //
  4876. // Free any memory from a previous iteration.
  4877. //
  4878. if ( SockAddressList != NULL ) {
  4879. LocalFree( SockAddressList );
  4880. SockAddressList = NULL;
  4881. }
  4882. //
  4883. // Get the next set of IP addresses from DNS
  4884. //
  4885. NetStatus = NetpDcGetDcNext( DsGetDcHandle,
  4886. &SockAddressCount,
  4887. &SockAddressList,
  4888. &DnsHostName );
  4889. //
  4890. // Process the exeptional conditions
  4891. //
  4892. if ( NetStatus == NO_ERROR && SockAddressCount > 0 ) {
  4893. //
  4894. // If the DC is passed, check if this is its record.
  4895. //
  4896. if ( Utf8DnsDcName == NULL ||
  4897. NlEqualDnsNameUtf8(Utf8DnsDcName, DnsHostName) ) {
  4898. IsClose = TRUE;
  4899. break;
  4900. }
  4901. //
  4902. // If we're done, break out of the loop.
  4903. //
  4904. } else if ( NetStatus == ERROR_NO_MORE_ITEMS ) {
  4905. break;
  4906. //
  4907. // If DNS isn't available, blow this request away.
  4908. //
  4909. } else if ( NetStatus == ERROR_TIMEOUT ||
  4910. NetStatus == DNS_ERROR_RCODE_SERVER_FAILURE ) { // Server failed
  4911. break;
  4912. //
  4913. // If IP or DNS is not configured, tell the caller.
  4914. //
  4915. } else if ( NetStatus == DNS_ERROR_NO_TCPIP || // TCP/IP not configured
  4916. NetStatus == DNS_ERROR_NO_DNS_SERVERS ) { // DNS not configured
  4917. NlPrint(( NL_CRITICAL,
  4918. "NetpDcCheckSiteCovered: %ws: IP Not configured from DnsQuery.\n",
  4919. Context->QueriedDisplayDomainName ));
  4920. NetStatus = ERROR_DNS_NOT_CONFIGURED;
  4921. goto Cleanup;
  4922. //
  4923. // We don't handle any other error.
  4924. //
  4925. } else {
  4926. NlPrint(( NL_CRITICAL,
  4927. "NetpDcCheckSiteCovered: %ws: Unknown error from DnsQuery. %ld 0x%lx\n",
  4928. Context->QueriedDisplayDomainName,
  4929. NetStatus,
  4930. NetStatus ));
  4931. goto Cleanup;
  4932. }
  4933. }
  4934. Cleanup:
  4935. if ( SockAddressList != NULL) {
  4936. LocalFree( SockAddressList );
  4937. }
  4938. if ( DsGetDcHandle != NULL ) {
  4939. NetpDcGetDcClose( DsGetDcHandle );
  4940. }
  4941. if ( Utf8DnsDomainName != NULL ) {
  4942. NetpMemoryFree( Utf8DnsDomainName );
  4943. }
  4944. if ( Utf8DnsDcName != NULL ) {
  4945. NetpMemoryFree( Utf8DnsDcName );
  4946. }
  4947. if ( NetStatus == NO_ERROR ) {
  4948. *DcClose = IsClose;
  4949. }
  4950. return NetStatus;
  4951. }
  4952. NET_API_STATUS
  4953. NetpDcHandlePingResponse(
  4954. IN PNL_GETDC_CONTEXT Context,
  4955. IN PVOID ResponseBuffer,
  4956. IN ULONG ResponseSize,
  4957. IN ULONG PassedCacheEntryFlags,
  4958. IN PNL_DC_ADDRESS ResponseDcAddress OPTIONAL,
  4959. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  4960. OUT PBOOL UsedNetbios
  4961. )
  4962. /*++
  4963. Routine Description:
  4964. The response is parsed and a cache entry is created for the response.
  4965. The cache entry is returned to the caller.
  4966. Arguments:
  4967. Context - Context describing the GetDc operation.
  4968. ResponseBuffer - Specifies the message returned by the DC in question.
  4969. ResponseSize - Specifies the size (in bytes) of the message
  4970. ResponseDcAddress - If specified, gives the DC address of the DC that responded.
  4971. This address will be used rather than the one in the ResponseBuffer.
  4972. NULL indicates that Netbios was used to discover the DC.
  4973. PassedCacheEntryFlags - Passes flags indicating over which mechanism
  4974. the response was received: either mailslot or ldap.
  4975. NlDcCacheEntry - On success, returns a pointer to the cache entry
  4976. describing the found DC. This entry must be dereferenced using
  4977. NetpDcDerefCacheEntry.
  4978. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  4979. the returned cache entry.
  4980. Return Value:
  4981. NO_ERROR: The NlDcCacheEntry was returned;
  4982. ERROR_SEM_TIMEOUT: (Silly, but for consistency) Specifies that the message
  4983. doesn't match the criteria in Context
  4984. ERROR_INVALID_DATA - The message could not be recognized as a valid
  4985. response message.
  4986. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  4987. ERROR_SERVICE_NOT_ACTIVE - The netlogon service is paused on the server.
  4988. Returned only for server pings.
  4989. --*/
  4990. {
  4991. NET_API_STATUS NetStatus;
  4992. DWORD StartTime;
  4993. DWORD ElapsedTime;
  4994. DWORD LocalTimeout;
  4995. PNL_DC_CACHE_ENTRY LocalNlDcCacheEntry = NULL;
  4996. BOOL LocalUsedNetbios;
  4997. PDNS_RECORD DnsRecords = NULL;
  4998. LPBYTE Where;
  4999. //
  5000. // Initialization
  5001. //
  5002. *NlDcCacheEntry = NULL;
  5003. //
  5004. // Verbosity
  5005. //
  5006. #if NETLOGONDBG
  5007. NlPrint(( NL_MAILSLOT_TEXT,
  5008. "%ws: Received '%s' response.\n",
  5009. Context->QueriedDisplayDomainName,
  5010. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)ResponseBuffer)->Opcode)));
  5011. NlpDumpBuffer(NL_MAILSLOT_TEXT, ResponseBuffer, ResponseSize);
  5012. #endif // NETLOGONDBG
  5013. //
  5014. // Parse the response
  5015. //
  5016. NetStatus = NetpDcParsePingResponse(
  5017. Context->QueriedDisplayDomainName,
  5018. ResponseBuffer,
  5019. ResponseSize,
  5020. &LocalNlDcCacheEntry );
  5021. if ( NetStatus != NO_ERROR ) {
  5022. NlPrint((NL_CRITICAL,
  5023. "NetpDcHandlePingResponse: %ws: cannot NetpDcParsePingResponse: %ld\n",
  5024. Context->QueriedDisplayDomainName,
  5025. NetStatus ));
  5026. goto Cleanup;
  5027. }
  5028. //
  5029. // Set the passed cache entry flags
  5030. //
  5031. LocalNlDcCacheEntry->CacheEntryFlags |= PassedCacheEntryFlags;
  5032. //
  5033. // If our caller knows the DC's SockAddr,
  5034. // override the one that came back from the DC.
  5035. //
  5036. if ( ResponseDcAddress != NULL ) {
  5037. LocalNlDcCacheEntry->SockAddr.iSockaddrLength = ResponseDcAddress->SockAddress.iSockaddrLength;
  5038. LocalNlDcCacheEntry->SockAddr.lpSockaddr = (LPSOCKADDR)
  5039. &LocalNlDcCacheEntry->SockAddrIn;
  5040. RtlCopyMemory( LocalNlDcCacheEntry->SockAddr.lpSockaddr,
  5041. ResponseDcAddress->SockAddress.lpSockaddr,
  5042. ResponseDcAddress->SockAddress.iSockaddrLength );
  5043. }
  5044. //
  5045. // If a NT5 DC didn't return the close bit, perhaps
  5046. // our statically configured site (if any) is covered
  5047. // by the DC. Check it. But do this check only if
  5048. // the discovered domain is in our forest to avoid
  5049. // site name collisions between different forests.
  5050. //
  5051. #ifdef _NETLOGON_SERVER
  5052. if ( (LocalNlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 &&
  5053. (LocalNlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) == 0 &&
  5054. // site is configured on DC by definition
  5055. (!NlGlobalMemberWorkstation || NlGlobalParameters.SiteNameConfigured) &&
  5056. NlEqualDnsName(LocalNlDcCacheEntry->UnicodeDnsForestName, Context->QueriedDnsForestName) ) {
  5057. BOOLEAN ClosenessDetermined = FALSE;
  5058. //
  5059. // If we are querying for our configured site,
  5060. // check whether DC's site is the queried site or
  5061. // whether we got this DC as the result of site specific query
  5062. //
  5063. if ( Context->QueriedInternalFlags & DS_SITENAME_DEFAULTED ) {
  5064. //
  5065. // If the DC site name is the same as ours, the DC is obviously close
  5066. //
  5067. if ( LocalNlDcCacheEntry->UnicodeDcSiteName != NULL &&
  5068. _wcsicmp(Context->QueriedSiteName, LocalNlDcCacheEntry->UnicodeDcSiteName) == 0 ) {
  5069. NlPrint(( NL_MISC, "NetpDchandlePingResponse: %ws DC marked as close (same queried site)\n",
  5070. Context->QueriedDisplayDomainName ));
  5071. LocalNlDcCacheEntry->ReturnFlags |= DS_CLOSEST_FLAG;
  5072. ClosenessDetermined = TRUE;
  5073. //
  5074. // Otherwise, if this is actual DC discovery (not just a DC ping),
  5075. // we might already queried DNS for our site specific records:
  5076. // the address structure indicates that.
  5077. //
  5078. } else if ( (Context->QueriedInternalFlags & (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST)) == 0 &&
  5079. ResponseDcAddress != NULL ) {
  5080. if ( (ResponseDcAddress->AddressFlags & NL_DC_ADDRESS_SITE_SPECIFIC) != 0 ) {
  5081. NlPrint(( NL_MISC, "NetpDchandlePingResponse: %ws DC marked as close (via address)\n",
  5082. Context->QueriedDisplayDomainName ));
  5083. LocalNlDcCacheEntry->ReturnFlags |= DS_CLOSEST_FLAG;
  5084. }
  5085. ClosenessDetermined = TRUE;
  5086. }
  5087. //
  5088. // Otherwise, get our configured site from the global
  5089. // and check whether DC's site is our configured site
  5090. //
  5091. } else {
  5092. EnterCriticalSection( &NlGlobalSiteCritSect );
  5093. if ( NlGlobalUnicodeSiteName != NULL &&
  5094. LocalNlDcCacheEntry->UnicodeDcSiteName != NULL &&
  5095. _wcsicmp(NlGlobalUnicodeSiteName, LocalNlDcCacheEntry->UnicodeDcSiteName) == 0 ) {
  5096. NlPrint(( NL_MISC, "NetpDchandlePingResponse: %ws DC marked as close (same global site)\n",
  5097. Context->QueriedDisplayDomainName ));
  5098. LocalNlDcCacheEntry->ReturnFlags |= DS_CLOSEST_FLAG;
  5099. ClosenessDetermined = TRUE;
  5100. }
  5101. LeaveCriticalSection( &NlGlobalSiteCritSect );
  5102. }
  5103. //
  5104. // If we haven't detemined the closeness given the info we've got,
  5105. // we have to ask DNS
  5106. //
  5107. if ( !ClosenessDetermined ) {
  5108. BOOLEAN DcClose = FALSE;
  5109. NetStatus = NetpDcCheckSiteCovered( Context,
  5110. LocalNlDcCacheEntry->UnicodeDnsHostName,
  5111. &DcClose );
  5112. //
  5113. // Fail on hard error
  5114. //
  5115. if ( NetStatus == ERROR_NOT_ENOUGH_MEMORY ||
  5116. NetStatus == ERROR_INTERNAL_ERROR ) {
  5117. goto Cleanup;
  5118. }
  5119. //
  5120. // If the DC is a close to our static site, mark it as such
  5121. //
  5122. if ( NetStatus == NO_ERROR && DcClose ) {
  5123. NlPrint(( NL_MISC, "NetpDchandlePingResponse: %ws DC marked as close (via DNS)\n",
  5124. Context->QueriedDisplayDomainName ));
  5125. LocalNlDcCacheEntry->ReturnFlags |= DS_CLOSEST_FLAG;
  5126. }
  5127. }
  5128. }
  5129. //
  5130. // Decide whether we want to re-attempt to find a close DC in 15 minutes.
  5131. //
  5132. if ( (LocalNlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 &&
  5133. (LocalNlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) == 0 ) {
  5134. //
  5135. // Mark the cache to expire if any of the following is true:
  5136. //
  5137. // The DC returned our site name. Apparently our IP address
  5138. // maps to a site in a forest of the discovered DC but all
  5139. // DCs covering that site are currently down.
  5140. //
  5141. // There were site specific DNS records. It is possible that
  5142. // all DCs that registered those records are currently down
  5143. // but will come back up later. But do this case only if the
  5144. // domain is from our forest to avoid site name collisions
  5145. // between different forests.
  5146. //
  5147. // Our site name is statically configured. It is possible
  5148. // that our site got configured before a DC was installed
  5149. // into that site, so re-try to discover that DC later.
  5150. // But do this case only if the domain is from our forest
  5151. // because there is no reason to assume that there is some
  5152. // correlation in configuration between different forests.
  5153. //
  5154. //
  5155. // Check the first case separately (for performance reasons)
  5156. //
  5157. if ( LocalNlDcCacheEntry->UnicodeClientSiteName != NULL ) {
  5158. LocalNlDcCacheEntry->CacheEntryFlags |= NL_DC_CACHE_NONCLOSE_EXPIRE;
  5159. //
  5160. // If the first case didn't happen, try the other two
  5161. //
  5162. } else {
  5163. if ( ((Context->ContextFlags & NL_GETDC_SITE_SPECIFIC_DNS_AVAIL) != 0 ||
  5164. // site is configured on DC by definition
  5165. (!NlGlobalMemberWorkstation || NlGlobalParameters.SiteNameConfigured)) &&
  5166. NlEqualDnsName(LocalNlDcCacheEntry->UnicodeDnsForestName, Context->QueriedDnsForestName) ) {
  5167. LocalNlDcCacheEntry->CacheEntryFlags |= NL_DC_CACHE_NONCLOSE_EXPIRE;
  5168. }
  5169. }
  5170. }
  5171. #endif // _NETLOGON_SERVER
  5172. if ( LocalNlDcCacheEntry->SockAddrIn.sin_family == AF_INET ) {
  5173. // Force the port number to be zero.
  5174. LocalNlDcCacheEntry->SockAddrIn.sin_port = 0;
  5175. LocalNlDcCacheEntry->DcQuality += 2; // IP is a good quality
  5176. }
  5177. //
  5178. // Ensure the opcode is expected.
  5179. // (Ignore responses from paused DCs, too.)
  5180. //
  5181. switch ( LocalNlDcCacheEntry->Opcode ) {
  5182. case LOGON_SAM_USER_UNKNOWN:
  5183. //
  5184. // If we asked for a specific account,
  5185. // then this is a negative answer.
  5186. //
  5187. if ( Context->QueriedAccountName != NULL ) {
  5188. NlPrint((NL_CRITICAL,
  5189. "NetpDcHandlePingResponse: %ws: %ws: response says specified account not found.\n",
  5190. Context->QueriedDisplayDomainName,
  5191. Context->QueriedAccountName ));
  5192. Context->NoSuchUserResponse = TRUE;
  5193. NetStatus = ERROR_NO_SUCH_USER;
  5194. goto Cleanup;
  5195. }
  5196. /* DROP THROUGH */
  5197. case LOGON_SAM_LOGON_RESPONSE:
  5198. case LOGON_PRIMARY_RESPONSE:
  5199. break;
  5200. case LOGON_SAM_PAUSE_RESPONSE:
  5201. NlPrint((NL_CRITICAL,
  5202. "NetpDcHandlePingResponse: %ws: Netlogon is paused on the server. 0x%lx\n",
  5203. Context->QueriedDisplayDomainName,
  5204. LocalNlDcCacheEntry->Opcode ));
  5205. //
  5206. // If we are pinging a server and the netlogon service is paused
  5207. // on the server, tell the caller about this.
  5208. //
  5209. if ( Context->QueriedInternalFlags & (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST) ) {
  5210. NetStatus = ERROR_SERVICE_NOT_ACTIVE;
  5211. goto Cleanup;
  5212. }
  5213. NetStatus = ERROR_SEM_TIMEOUT;
  5214. goto Cleanup;
  5215. default:
  5216. NlPrint((NL_CRITICAL,
  5217. "NetpDcHandlePingResponse: %ws: response opcode not valid. 0x%lx\n",
  5218. Context->QueriedDisplayDomainName,
  5219. LocalNlDcCacheEntry->Opcode ));
  5220. NetStatus = ERROR_INVALID_DATA;
  5221. goto Cleanup;
  5222. }
  5223. //
  5224. // If we got any response from a DC that doesn't support the DS,
  5225. // note that fact for later.
  5226. //
  5227. // Check more than just DS_DS_FLAG since this might be an NT 5 DC where the
  5228. // DS simply isn't started yet or an AD/UNIX server
  5229. //
  5230. if ( (LocalNlDcCacheEntry->VersionFlags & (NETLOGON_NT_VERSION_5|
  5231. NETLOGON_NT_VERSION_5EX|
  5232. NETLOGON_NT_VERSION_5EX_WITH_IP)) == 0 ) {
  5233. Context->NonDsResponse = TRUE;
  5234. } else {
  5235. Context->DsResponse = TRUE;
  5236. }
  5237. //
  5238. // If we got any response from any DC,
  5239. // don't cache the fact that we couldn't find a DC.
  5240. // We may just be looking for the wrong type of DC.
  5241. //
  5242. Context->AvoidNegativeCache = TRUE;
  5243. //
  5244. // Ensure the response matches the original caller's requirements.
  5245. //
  5246. if ( !NetpDcMatchResponse( Context,
  5247. LocalNlDcCacheEntry,
  5248. TRUE,
  5249. &LocalUsedNetbios ) ) {
  5250. NetStatus = ERROR_SEM_TIMEOUT;
  5251. goto Cleanup;
  5252. }
  5253. //
  5254. // If we are doing a DC discovery (not just pings) and
  5255. // this is not a local call and
  5256. // we are going to return the DNS DC name,
  5257. // ensure that it can be resolved in DNS
  5258. //
  5259. // Note that we don't need to do this for pings as the
  5260. // caller will make the right choice w.r.t. which
  5261. // name (DNS or Netbios) to pick up given all the needed
  5262. // info in the internal structure we return to the caller.
  5263. //
  5264. // Note that we only ensure that the name can be resolved in DNS,
  5265. // we don't ensure that the IP adresses DNS returnes contain the one
  5266. // we used to ping the DC.
  5267. if ( (Context->QueriedInternalFlags & DS_DOING_DC_DISCOVERY) != 0 && // doing DC discovery
  5268. (PassedCacheEntryFlags & NL_DC_CACHE_LOCAL) == 0 && // not a local call
  5269. ((Context->QueriedFlags & DS_RETURN_DNS_NAME) != 0 || // caller requires DNS name
  5270. // caller doesn't require Netbios name and DNS domain name matched the query
  5271. ((Context->QueriedFlags & DS_RETURN_FLAT_NAME) == 0 && !LocalUsedNetbios)) ) {
  5272. //
  5273. // If the DC returned a DNS host name and
  5274. // we didn't yet resolve the DC name in DNS (i.e. we used Netbios) or
  5275. // we've got a DNS name that is different from the one the DC returned,
  5276. // resolve the DNS name the DC returned
  5277. //
  5278. if ( LocalNlDcCacheEntry->UnicodeDnsHostName != NULL && // we have a DNS name
  5279. (ResponseDcAddress == NULL || // name not yet resolved in DNS
  5280. ResponseDcAddress->DnsHostName == NULL || // name not yet resolved in DNS
  5281. !NlEqualDnsName(LocalNlDcCacheEntry->UnicodeDnsHostName, // already resolved but names are different
  5282. ResponseDcAddress->DnsHostName)) ) {
  5283. NetStatus = DnsQuery_W( LocalNlDcCacheEntry->UnicodeDnsHostName,
  5284. DNS_TYPE_A,
  5285. (Context->QueriedFlags & DS_FORCE_REDISCOVERY) ?
  5286. DNS_QUERY_BYPASS_CACHE : 0,
  5287. NULL, // No list of DNS servers
  5288. &DnsRecords,
  5289. NULL );
  5290. if ( NetStatus != NO_ERROR || DnsRecords == NULL ) {
  5291. NlPrint(( NL_CRITICAL,
  5292. "NetpDcHandlePingResponse: %ws: Failed DNS resolution for %ws (%ws): 0x%lx\n",
  5293. Context->QueriedDisplayDomainName,
  5294. LocalNlDcCacheEntry->UnicodeDnsHostName,
  5295. (ResponseDcAddress != NULL) ?
  5296. ResponseDcAddress->DnsHostName :
  5297. L"none",
  5298. NetStatus ));
  5299. NetStatus = ERROR_SEM_TIMEOUT;
  5300. goto Cleanup;
  5301. } else {
  5302. NlPrint(( NL_MISC,
  5303. "NetpDcHandlePingResponse: %ws: Successful DNS resolution for %ws (%ws)\n",
  5304. Context->QueriedDisplayDomainName,
  5305. LocalNlDcCacheEntry->UnicodeDnsHostName,
  5306. (ResponseDcAddress != NULL) ?
  5307. ResponseDcAddress->DnsHostName :
  5308. L"none" ));
  5309. }
  5310. }
  5311. }
  5312. //
  5313. // Compute the quality of this cache entry
  5314. //
  5315. // Some qualities are more important than others
  5316. //
  5317. if (LocalNlDcCacheEntry->ReturnFlags & DS_DS_FLAG) {
  5318. LocalNlDcCacheEntry->DcQuality += 1;
  5319. }
  5320. if (LocalNlDcCacheEntry->ReturnFlags & DS_GOOD_TIMESERV_FLAG) {
  5321. LocalNlDcCacheEntry->DcQuality += 1;
  5322. }
  5323. if (LocalNlDcCacheEntry->ReturnFlags & DS_KDC_FLAG) {
  5324. LocalNlDcCacheEntry->DcQuality += 5;
  5325. }
  5326. if (LocalNlDcCacheEntry->ReturnFlags & DS_TIMESERV_FLAG) {
  5327. LocalNlDcCacheEntry->DcQuality += 5;
  5328. }
  5329. if (LocalNlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) {
  5330. LocalNlDcCacheEntry->DcQuality += 10;
  5331. }
  5332. //
  5333. // We found it!
  5334. //
  5335. // Return the cache entry to the caller.
  5336. NetStatus = NO_ERROR;
  5337. *NlDcCacheEntry = LocalNlDcCacheEntry;
  5338. *UsedNetbios = LocalUsedNetbios;
  5339. LocalNlDcCacheEntry = NULL;
  5340. Cleanup:
  5341. if ( LocalNlDcCacheEntry != NULL ) {
  5342. NetpDcDerefCacheEntry( LocalNlDcCacheEntry );
  5343. }
  5344. if ( DnsRecords != NULL ) {
  5345. DnsRecordListFree( DnsRecords, DnsFreeRecordListDeep );
  5346. }
  5347. return NetStatus;
  5348. }
  5349. NET_API_STATUS
  5350. NetpDcGetPingResponse(
  5351. IN PNL_GETDC_CONTEXT Context,
  5352. IN ULONG Timeout,
  5353. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  5354. OUT PBOOL UsedNetbios
  5355. )
  5356. /*++
  5357. Routine Description:
  5358. This routine reads a ping response from the specified mailslot.
  5359. The response is parsed and a cache entry is created for the response.
  5360. The cache entry is returned to the caller.
  5361. Arguments:
  5362. Context - Context describing the GetDc operation.
  5363. Timeout - Maximum time (in milliseconds) to wait for the response.
  5364. NlDcCacheEntry - On success, returns a pointer to the cache entry
  5365. describing the found DC. This entry must be dereference using
  5366. NetpDcDerefCacheEntry.
  5367. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  5368. the returned cache entry.
  5369. Return Value:
  5370. NO_ERROR: The NlDcCacheEntry was returned;
  5371. ERROR_SEM_TIMEOUT: No response was available within Timeout milliseconds
  5372. ERROR_INVALID_DATA: We pinged a DC with a particular IP address and that DC
  5373. returned responce info that was in conflict with the requested info.
  5374. ERROR_SERVICE_NOT_ACTIVE - The netlogon service is paused on the pinged
  5375. server. Returned only for DC pings.
  5376. --*/
  5377. {
  5378. NET_API_STATUS NetStatus;
  5379. DWORD StartTime;
  5380. DWORD ElapsedTime;
  5381. DWORD BeginElapsedTime;
  5382. DWORD LocalTimeout;
  5383. LPBYTE Response;
  5384. DWORD ResponseSize;
  5385. PLIST_ENTRY ListEntry;
  5386. PNL_DC_ADDRESS DcAddress;
  5387. PNL_DC_ADDRESS UsedDcAddress;
  5388. PNL_DC_ADDRESS ResponseDcAddress;
  5389. int LdapError;
  5390. LDAP_TIMEVAL LdapTimeout;
  5391. PLDAPMessage LdapMessage = NULL;
  5392. PLDAPMessage CurrentEntry;
  5393. PLDAP_BERVAL *Berval = NULL;
  5394. ULONG LocalCacheEntryFlags = 0;
  5395. //
  5396. // Initialization
  5397. //
  5398. *NlDcCacheEntry = NULL;
  5399. StartTime = GetTickCount();
  5400. //
  5401. // Some timeouts are computed.
  5402. // Prevent timeouts from being ridiculously small.
  5403. // However, if this is DC pinging, allow 0 timeout
  5404. // for just checking if a response is available.
  5405. //
  5406. if ( Timeout < NL_DC_MIN_PING_TIMEOUT &&
  5407. (Context->QueriedInternalFlags & (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST)) == 0 ) {
  5408. Timeout = NL_DC_MIN_PING_TIMEOUT;
  5409. }
  5410. //
  5411. // Loop ignoring bogus responses.
  5412. //
  5413. for (;;) {
  5414. //
  5415. // Flag that we don't yet have a response.
  5416. //
  5417. Response = NULL;
  5418. ResponseDcAddress = NULL;
  5419. BeginElapsedTime = NetpDcElapsedTime( StartTime );
  5420. UsedDcAddress = NULL;
  5421. //
  5422. // Loop through the list of DCs we've started LDAP calls to.
  5423. //
  5424. for ( ListEntry = Context->DcAddressList.Flink ;
  5425. ListEntry != &Context->DcAddressList ;
  5426. ListEntry = ListEntry->Flink) {
  5427. //
  5428. // Cleanup from previous iteration.
  5429. if ( Berval != NULL ) {
  5430. ldap_value_free_len( Berval );
  5431. Berval = NULL;
  5432. }
  5433. if ( LdapMessage != NULL ) {
  5434. ldap_msgfree( LdapMessage );
  5435. LdapMessage = NULL;
  5436. }
  5437. //
  5438. // Skip this entry if no LDAP search has been started.
  5439. //
  5440. DcAddress = CONTAINING_RECORD( ListEntry, NL_DC_ADDRESS, Next );
  5441. if ( DcAddress->LdapHandle == NULL ) {
  5442. continue; // Continue with the next host
  5443. }
  5444. //
  5445. // Poll to see if a result is available for ANY of the searches
  5446. // I've done to this host.
  5447. //
  5448. LdapTimeout.tv_sec = 0;
  5449. LdapTimeout.tv_usec = 0;
  5450. LdapError = ldap_result(
  5451. DcAddress->LdapHandle,
  5452. LDAP_RES_ANY,
  5453. TRUE, // Return all of search
  5454. &LdapTimeout, // poll
  5455. &LdapMessage );
  5456. //
  5457. // If the request timed out, continue with the next host.
  5458. // We get this timeout if the response hasn't yet come back from the DC
  5459. //
  5460. if ( LdapError == 0 ) {
  5461. continue; // Continue with the next host
  5462. //
  5463. // Otherwise, check error conditions
  5464. //
  5465. } else if ( LdapError == -1 ) {
  5466. #if NETLOGONDBG
  5467. NlPrint(( NL_CRITICAL,
  5468. "NetpDcGetPingResponse: %ws: Cannot ldap_result ip address %s: %ld %s\n",
  5469. Context->QueriedDisplayDomainName,
  5470. DcAddress->SockAddrString,
  5471. DcAddress->LdapHandle->ld_errno,
  5472. ldap_err2stringA(DcAddress->LdapHandle->ld_errno) ));
  5473. #endif // NETLOGONDBG
  5474. //
  5475. // LDAP_TIMEOUT means the IP address exists but there is no LDAP server at that address.
  5476. // Don't ever try this machine again.
  5477. //
  5478. // All other status codes are unknown. Keep trying this machine since we don't know
  5479. // if this is a client side or server side failure.
  5480. //
  5481. if ( DcAddress->LdapHandle->ld_errno == LDAP_TIMEOUT ) {
  5482. if ( (DcAddress->AddressFlags & NL_DC_ADDRESS_NEVER_TRY_AGAIN) == 0 ) {
  5483. DcAddress->AddressFlags |= NL_DC_ADDRESS_NEVER_TRY_AGAIN;
  5484. Context->DcAddressCount--;
  5485. }
  5486. }
  5487. //
  5488. // ldap_result returned the answer. No need calling ldap_result again.
  5489. //
  5490. ldap_unbind( DcAddress->LdapHandle );
  5491. DcAddress->LdapHandle = NULL;
  5492. continue; // Continue with the next host
  5493. }
  5494. //
  5495. // Get the first entry returned. (There should only be one.)
  5496. //
  5497. CurrentEntry = ldap_first_entry( DcAddress->LdapHandle, LdapMessage );
  5498. if ( CurrentEntry == NULL ) {
  5499. //
  5500. // This means the server doesn't support the NETLOGON attribute.
  5501. // That's probably because the netlogon service is stopped.
  5502. //
  5503. NlPrint(( NL_MAILSLOT_TEXT,
  5504. "NetpDcGetPingResponse: %ws: Netlogon service stopped on DC at %s\n",
  5505. Context->QueriedDisplayDomainName,
  5506. DcAddress->SockAddrString ));
  5507. //
  5508. // Don't ever try this machine again.
  5509. //
  5510. if ( (DcAddress->AddressFlags & NL_DC_ADDRESS_NEVER_TRY_AGAIN) == 0 ) {
  5511. DcAddress->AddressFlags |= NL_DC_ADDRESS_NEVER_TRY_AGAIN;
  5512. Context->DcAddressCount--;
  5513. }
  5514. //
  5515. // ldap_result returned the answer. No need calling ldap_result again.
  5516. //
  5517. ldap_unbind( DcAddress->LdapHandle );
  5518. DcAddress->LdapHandle = NULL;
  5519. continue; // Continue with the next host
  5520. }
  5521. //
  5522. // Get the Netlogon attribute returned. (There should only be one.)
  5523. //
  5524. Berval = ldap_get_values_lenA( DcAddress->LdapHandle,
  5525. CurrentEntry,
  5526. NETLOGON_LDAP_ATTRIBUTE );
  5527. if ( Berval == NULL ) {
  5528. if ( DcAddress->LdapHandle->ld_errno != 0 ) {
  5529. #if NETLOGONDBG
  5530. NlPrint(( NL_CRITICAL,
  5531. "NetpDcGetPingResponse: %ws: Cannot ldap_get_values_len ip address %s: %ld %s\n",
  5532. Context->QueriedDisplayDomainName,
  5533. DcAddress->SockAddrString,
  5534. DcAddress->LdapHandle->ld_errno,
  5535. ldap_err2stringA(DcAddress->LdapHandle->ld_errno) ));
  5536. #endif // NETLOGONDBG
  5537. // ?? Some should be fatal
  5538. //
  5539. // If the DC returned that there isn't a NETLOGON attribute,
  5540. // then it really isn't a DC.
  5541. if ( DcAddress->LdapHandle->ld_errno == LDAP_NO_SUCH_ATTRIBUTE ) {
  5542. if ( (DcAddress->AddressFlags & NL_DC_ADDRESS_NEVER_TRY_AGAIN) == 0 ) {
  5543. DcAddress->AddressFlags |= NL_DC_ADDRESS_NEVER_TRY_AGAIN;
  5544. Context->DcAddressCount--;
  5545. }
  5546. }
  5547. }
  5548. //
  5549. // ldap_result returned the answer. No need calling ldap_result again.
  5550. //
  5551. ldap_unbind( DcAddress->LdapHandle );
  5552. DcAddress->LdapHandle = NULL;
  5553. continue; // Continue with the next host
  5554. }
  5555. if ( Berval[0] == NULL ) {
  5556. //
  5557. // ldap_result returned the answer. No need calling ldap_result again.
  5558. //
  5559. ldap_unbind( DcAddress->LdapHandle );
  5560. DcAddress->LdapHandle = NULL;
  5561. continue; // Continue with the next host
  5562. }
  5563. //
  5564. // Check to see if we have SockAddress
  5565. //
  5566. if ( DcAddress->SockAddress.iSockaddrLength != 0 ) {
  5567. ResponseDcAddress = DcAddress;
  5568. }
  5569. Response = Berval[0]->bv_val;
  5570. ResponseSize = Berval[0]->bv_len;
  5571. UsedDcAddress = DcAddress;
  5572. LocalCacheEntryFlags = NL_DC_CACHE_LDAP;
  5573. //
  5574. // ldap_result returned the answer. No need calling ldap_result again.
  5575. //
  5576. ldap_unbind( DcAddress->LdapHandle );
  5577. DcAddress->LdapHandle = NULL;
  5578. break;
  5579. }
  5580. //
  5581. // If we don't yet have a response,
  5582. // try mailslots.
  5583. if ( Response == NULL && Context->ResponseMailslotHandle != NULL ) {
  5584. //
  5585. // Set the mailslot read to return after the appropriate time.
  5586. // ?? This is now common code. I could set it when I create the mailslot.
  5587. //
  5588. if ( !SetMailslotInfo(
  5589. Context->ResponseMailslotHandle,
  5590. 0 ) ) { // zero timeout
  5591. NetStatus = GetLastError();
  5592. NlPrint((NL_CRITICAL,
  5593. "NetpDcGetPingResponse: %ws: cannot change temp mailslot timeout %ld\n",
  5594. Context->QueriedDisplayDomainName,
  5595. NetStatus ));
  5596. goto Cleanup;
  5597. }
  5598. //
  5599. // Read the response from the response mailslot
  5600. //
  5601. if ( !ReadFile( Context->ResponseMailslotHandle,
  5602. Context->ResponseBuffer,
  5603. Context->ResponseBufferSize,
  5604. &ResponseSize,
  5605. NULL )
  5606. #ifdef WIN32_CHICAGO
  5607. || (ResponseSize == 0)
  5608. #endif // WIN32_CHICAGO
  5609. ) {
  5610. NetStatus = GetLastError();
  5611. #ifdef WIN32_CHICAGO
  5612. if ((NetStatus == NO_ERROR) && (ResponseSize == 0))
  5613. {
  5614. NetStatus = ERROR_SEM_TIMEOUT;
  5615. }
  5616. #endif // WIN32_CHICAGO
  5617. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  5618. NlPrint((NL_CRITICAL,
  5619. "NetpDcGetPingResponse: %ws: cannot read temp mailslot timeout %ld\n",
  5620. Context->QueriedDisplayDomainName,
  5621. NetStatus ));
  5622. goto Cleanup;
  5623. }
  5624. /* Just drop through with no response */
  5625. } else {
  5626. Response = (LPBYTE) Context->ResponseBuffer;
  5627. LocalCacheEntryFlags = NL_DC_CACHE_MAILSLOT;
  5628. }
  5629. }
  5630. //
  5631. // See if this response meets our needs
  5632. //
  5633. if ( Response != NULL ) {
  5634. NetStatus = NetpDcHandlePingResponse(
  5635. Context,
  5636. Response,
  5637. ResponseSize,
  5638. LocalCacheEntryFlags,
  5639. ResponseDcAddress,
  5640. NlDcCacheEntry,
  5641. UsedNetbios );
  5642. //
  5643. // We are done if we have any response for a ping
  5644. //
  5645. if ( Context->QueriedInternalFlags &
  5646. (DS_PING_DNS_HOST | DS_PING_NETBIOS_HOST) ) {
  5647. //
  5648. // If the response conflicts with the request,
  5649. // tell that the caller
  5650. //
  5651. if ( NetStatus == ERROR_SEM_TIMEOUT ) {
  5652. NetStatus = ERROR_INVALID_DATA;
  5653. }
  5654. goto Cleanup;
  5655. }
  5656. //
  5657. // For a DC discovery, take an appropriate action depending
  5658. // on the response we got from the DC
  5659. //
  5660. switch ( NetStatus ) {
  5661. case ERROR_INVALID_DATA: // Response is garbled
  5662. break; // Continue processing more responses
  5663. case ERROR_SEM_TIMEOUT: // Doesn't match the request
  5664. case ERROR_NO_SUCH_USER: // User doesn't exist on this DC
  5665. if ( UsedDcAddress != NULL) {
  5666. NlPrint((NL_MAILSLOT_TEXT,
  5667. "NetpDcGetPingResponse: %ws: marked DC as NeverTryAgain %ld\n",
  5668. Context->QueriedDisplayDomainName,
  5669. NetStatus ));
  5670. if ( (UsedDcAddress->AddressFlags & NL_DC_ADDRESS_NEVER_TRY_AGAIN) == 0 ) {
  5671. UsedDcAddress->AddressFlags |= NL_DC_ADDRESS_NEVER_TRY_AGAIN;
  5672. Context->DcAddressCount--;
  5673. }
  5674. }
  5675. break; // Continue processing more responses
  5676. default:
  5677. goto Cleanup;
  5678. }
  5679. }
  5680. //
  5681. // If we still have no response,
  5682. // sleep a while waiting for one.
  5683. //
  5684. // (It's too bad I have to poll. But there's no way to create a
  5685. // wait on any of the above to come back. Perhaps, if there is exactly
  5686. // one object to wait on ...) ??
  5687. //
  5688. if ( Response == NULL ) {
  5689. ElapsedTime = NetpDcElapsedTime( StartTime );
  5690. #ifndef NETTEST_UTILITY
  5691. if ( ElapsedTime != BeginElapsedTime &&
  5692. ElapsedTime-BeginElapsedTime > 25 ) {
  5693. NlPrint((NL_CRITICAL,
  5694. "NetpDcGetPingResponse: it took %ld msecs to poll\n",
  5695. ElapsedTime-BeginElapsedTime ));
  5696. }
  5697. #endif // NETTEST_UTILITY
  5698. if ( ElapsedTime >= Timeout) {
  5699. NetStatus = ERROR_SEM_TIMEOUT;
  5700. goto Cleanup;
  5701. }
  5702. LocalTimeout = Timeout - ElapsedTime;
  5703. #ifdef notdef
  5704. NlPrint((NL_CRITICAL,
  5705. "NetpDcGetPingResponse: timeout is %ld %ld %ld %ld\n",
  5706. StartTime,
  5707. ElapsedTime,
  5708. Timeout,
  5709. LocalTimeout ));
  5710. #endif // notdef
  5711. //
  5712. // Since I'm polling, don't wait too long.
  5713. //
  5714. Sleep( min( LocalTimeout, NL_DC_MIN_PING_TIMEOUT ) );
  5715. }
  5716. }
  5717. /* NOT REACHED */
  5718. Cleanup:
  5719. if ( Berval != NULL ) {
  5720. ldap_value_free_len( Berval );
  5721. }
  5722. if ( LdapMessage != NULL ) {
  5723. ldap_msgfree( LdapMessage );
  5724. }
  5725. return NetStatus;
  5726. }
  5727. VOID
  5728. NetpDcFreeAddressList(
  5729. IN PNL_GETDC_CONTEXT Context
  5730. )
  5731. /*++
  5732. Routine Description:
  5733. This routine frees the address list associated with the current context.
  5734. Arguments:
  5735. Context - Context describing the GetDc operation.
  5736. Return Value:
  5737. None.
  5738. --*/
  5739. {
  5740. PNL_DC_ADDRESS DcAddress;
  5741. PLIST_ENTRY ListEntry;
  5742. //
  5743. // Loop deleting existing addresses.
  5744. //
  5745. while ( !IsListEmpty( &Context->DcAddressList ) ) {
  5746. ListEntry = RemoveHeadList( &Context->DcAddressList );
  5747. DcAddress = CONTAINING_RECORD( ListEntry, NL_DC_ADDRESS, Next );
  5748. //
  5749. // Free this DcAddress
  5750. //
  5751. if ( DcAddress->LdapHandle != NULL ) {
  5752. ldap_unbind( DcAddress->LdapHandle );
  5753. }
  5754. if ( DcAddress->DnsHostName != NULL ) {
  5755. NetApiBufferFree( DcAddress->DnsHostName );
  5756. }
  5757. LocalFree( DcAddress );
  5758. }
  5759. Context->DcAddressCount = 0;
  5760. }
  5761. NET_API_STATUS
  5762. NetpDcPingListIp(
  5763. IN PNL_GETDC_CONTEXT Context,
  5764. IN PNL_DC_ADDRESS FirstAddress OPTIONAL,
  5765. IN BOOL WaitForResponce,
  5766. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry OPTIONAL,
  5767. OUT PBOOL UsedNetbios OPTIONAL,
  5768. OUT PULONG DcPingCount
  5769. )
  5770. /*++
  5771. Routine Description:
  5772. This routine determines the name/address of a DC with the specified
  5773. characteristics using an IP-only algorithm.
  5774. Arguments:
  5775. Context - Context describing the GetDc operation.
  5776. FirstAddress - If specified, this must be one of the entries in
  5777. Context->DcAddressList. Only this entry and entries following it in
  5778. the list will be pinged.
  5779. WaitForResponce - TRUE if need to wait for ping responces (by calling
  5780. NetpDcGetPingResponse after each ping). TRUE is used when this is
  5781. synchronous DC discovery as with DsGetDcName. If FALSE, the pings
  5782. are sent asynchronously.
  5783. NlDcCacheEntry - On success, returns a pointer to the cache entry
  5784. describing the found DC. This entry must be dereferenced using
  5785. NetpDcDerefCacheEntry. Optional if WaitForResponce is FALSE.
  5786. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  5787. the returned cache entry. Optional if WaitForResponce is FALSE.
  5788. DcPingCount - Returns the number of DC's pinged.
  5789. If WaitForResponce is TRUE, use DcPingCount only if the return status is
  5790. ERROR_SEM_TIMEOUT.
  5791. Return Value:
  5792. NO_ERROR: The NlDcCacheEntry was returned if WaitForResponce was TRUE.
  5793. If WaitForResponce was FALSE, pings have been attempted to all addresses
  5794. specified in Context->DcAddressList, but there is no guarantee that all
  5795. the pings were successfully sent. The caller should check the value of
  5796. DcPingCount to determine the number of successfully pinged DCs.
  5797. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  5798. (Definitive status that we need not try again.)
  5799. ERROR_SEM_TIMEOUT: No DC responded to the request.
  5800. (Non-definitive status that we should try again.)
  5801. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  5802. --*/
  5803. {
  5804. NET_API_STATUS NetStatus;
  5805. NTSTATUS Status;
  5806. ULONG AddressIndex;
  5807. PLIST_ENTRY ListEntry;
  5808. PNL_DC_ADDRESS DcAddress;
  5809. char *AttributeList[2];
  5810. int LdapMessageId;
  5811. NlAssert( NlDcCacheEntry != NULL ||
  5812. (NlDcCacheEntry == NULL && !WaitForResponce) );
  5813. NlAssert( UsedNetbios != NULL ||
  5814. (UsedNetbios == NULL && !WaitForResponce) );
  5815. //
  5816. // Loop through the list pinging each entry.
  5817. //
  5818. *DcPingCount = 0;
  5819. if ( FirstAddress == NULL ) {
  5820. ListEntry = Context->DcAddressList.Flink;
  5821. } else {
  5822. ListEntry = &FirstAddress->Next;
  5823. }
  5824. for ( ;
  5825. ListEntry != &Context->DcAddressList ;
  5826. ListEntry = ListEntry->Flink) {
  5827. DcAddress = CONTAINING_RECORD( ListEntry, NL_DC_ADDRESS, Next );
  5828. //
  5829. // If we're certain this DC won't work,
  5830. // skip it.
  5831. //
  5832. if ( DcAddress->AddressFlags & NL_DC_ADDRESS_NEVER_TRY_AGAIN ) {
  5833. continue;
  5834. }
  5835. //
  5836. // Send the ping.
  5837. //
  5838. //
  5839. // Open a connection to the server unless we already have one
  5840. //
  5841. if ( DcAddress->LdapHandle == NULL ) {
  5842. //
  5843. // Get an LDAP handle to the server.
  5844. //
  5845. DcAddress->LdapHandle = cldap_openA( DcAddress->SockAddrString, 0 );
  5846. if ( DcAddress->LdapHandle == NULL ) {
  5847. NetStatus = GetLastError();
  5848. NlPrint(( NL_CRITICAL,
  5849. "NetpDcPingListIp: %ws: Cannot LdapOpen ip address %s: %ld\n",
  5850. Context->QueriedDisplayDomainName,
  5851. DcAddress->SockAddrString,
  5852. NetStatus ));
  5853. // Some statuses should be fatal ??
  5854. continue;
  5855. }
  5856. }
  5857. //
  5858. // Ping the server using UDP LDAP.
  5859. //
  5860. // Get the Netlogon parameters of the server.
  5861. //
  5862. NlPrint(( NL_MAILSLOT,
  5863. "NetpDcPingListIp: %ws: Sent UDP ping to %s\n",
  5864. Context->QueriedDisplayDomainName,
  5865. DcAddress->SockAddrString ));
  5866. AttributeList[0] = NETLOGON_LDAP_ATTRIBUTE;
  5867. AttributeList[1] = NULL;
  5868. LdapMessageId = ldap_searchA(
  5869. DcAddress->LdapHandle,
  5870. NULL, // DN
  5871. LDAP_SCOPE_BASE,
  5872. Context->LdapFilter,
  5873. AttributeList,
  5874. FALSE ); // Attributes and values
  5875. if ( LdapMessageId == -1 ) {
  5876. NlPrint(( NL_CRITICAL,
  5877. "NetpDcPingListIp: %ws: Cannot LdapOpen ip address %s: %ld %s\n",
  5878. Context->QueriedDisplayDomainName,
  5879. DcAddress->SockAddrString,
  5880. DcAddress->LdapHandle->ld_errno,
  5881. ldap_err2stringA(DcAddress->LdapHandle->ld_errno) ));
  5882. // Some statuses should be fatal ??
  5883. continue;
  5884. }
  5885. //
  5886. // Count the number of DCs we've pinged.
  5887. //
  5888. (*DcPingCount) ++;
  5889. if ( WaitForResponce ) {
  5890. //
  5891. // Get the response from the ping.
  5892. //
  5893. NlAssert( DcAddress->AddressPingWait != 0 );
  5894. NetStatus = NetpDcGetPingResponse(
  5895. Context,
  5896. DcAddress->AddressPingWait,
  5897. NlDcCacheEntry,
  5898. UsedNetbios );
  5899. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  5900. if ( NetStatus != NO_ERROR ) {
  5901. NlPrint(( NL_CRITICAL,
  5902. "NetpDcPingListIp: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  5903. Context->QueriedDisplayDomainName,
  5904. NetStatus ));
  5905. }
  5906. goto Cleanup;
  5907. }
  5908. }
  5909. }
  5910. if ( WaitForResponce ) {
  5911. NetStatus = ERROR_SEM_TIMEOUT;
  5912. } else {
  5913. NetStatus = NO_ERROR;
  5914. }
  5915. Cleanup:
  5916. return NetStatus;
  5917. }
  5918. NET_API_STATUS
  5919. NetpDcPingIp(
  5920. IN PNL_GETDC_CONTEXT Context,
  5921. OUT PULONG DcPingCount
  5922. )
  5923. /*++
  5924. Routine Description:
  5925. This routine sends a ping to a DC with the specified
  5926. characteristics using an IP-only algorithm.
  5927. Arguments:
  5928. Context - Context describing the GetDc operation.
  5929. DcPingCount - Returns the number of DC's pinged.
  5930. Return Value:
  5931. NO_ERROR: Pings have been attempted to all addresses
  5932. specified in Context->DcAddressList, but there is no guarantee that all
  5933. the pings were successfully sent. The caller should check the value of
  5934. DcPingCount to determine the number of successfully pinged DCs.
  5935. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  5936. (Definitive status that we need not try again.)
  5937. ERROR_SEM_TIMEOUT: No DC responded to the request.
  5938. (Non-definitive status that we should try again.)
  5939. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  5940. --*/
  5941. {
  5942. return NetpDcPingListIp( Context,
  5943. NULL,
  5944. FALSE,
  5945. NULL,
  5946. NULL,
  5947. DcPingCount );
  5948. }
  5949. NET_API_STATUS
  5950. NetpDcGetDcOpen(
  5951. IN LPCSTR DnsName,
  5952. IN ULONG OptionFlags,
  5953. IN LPCWSTR SiteName OPTIONAL,
  5954. IN GUID *DomainGuid OPTIONAL,
  5955. IN LPCSTR DnsForestName OPTIONAL,
  5956. IN ULONG Flags,
  5957. OUT PHANDLE RetGetDcContext
  5958. )
  5959. /*++
  5960. Routine Description:
  5961. Open a context for retrieval of the addresses of machines that have
  5962. registered LDAP.TCP.<xxx> SRV records.
  5963. Arguments:
  5964. DnsName - UTF-8 DNS name of the LDAP server to lookup
  5965. OptionFlags - Flags affecting the operation of the routine.
  5966. DS_ONLY_DO_SITE_NAME - Non-site names should be ignored.
  5967. SiteName - Name of site the client is in.
  5968. DomainGuid - Specifies the GUID of the domain specified by DnsName.
  5969. This value is used to handle the case of domain renames. If this
  5970. value is specified and DomainName has been renamed, DsGetDcName will
  5971. attempt to locate a DC in the domain having this specified DomainGuid.
  5972. DnsForestName - Specifies the name of the domain at the root of the tree
  5973. containing DnsName. This value is used in conjunction with DomainGuid
  5974. for finding DnsName if the domain has been renamed.
  5975. Flags - Passes additional information to be used to process the request.
  5976. Flags can be a combination values bitwise or'ed together.
  5977. Any of the following flags are allowed and have the same meaning as
  5978. for DsGetDcName:
  5979. DS_PDC_REQUIRED
  5980. DS_GC_SERVER_REQUIRED
  5981. DS_WRITABLE_REQUIRED
  5982. DS_FORCE_REDISCOVERY - Avoids DNS cache
  5983. If no flags are specified, no special DC role is required.
  5984. RetGetDcContext - Returns an opaque context.
  5985. This context must be freed using NetpDcGetDcClose.
  5986. Return Value:
  5987. Status of the operation.
  5988. NO_ERROR: GetDcContext was returned successfully.
  5989. --*/
  5990. {
  5991. NET_API_STATUS NetStatus = NO_ERROR;
  5992. PDSGETDC_CONTEXT GetDcContext = NULL;
  5993. ULONG Size;
  5994. //
  5995. // Verify the DC flags
  5996. //
  5997. if ( Flags & ~DS_OPEN_VALID_FLAGS ) {
  5998. NetStatus = ERROR_INVALID_FLAGS;
  5999. goto Cleanup;
  6000. }
  6001. //
  6002. // Verify the option flags
  6003. //
  6004. if ( OptionFlags & ~DS_OPEN_VALID_OPTION_FLAGS ) {
  6005. NetStatus = ERROR_INVALID_FLAGS;
  6006. goto Cleanup;
  6007. }
  6008. //
  6009. // Allocate a context
  6010. //
  6011. GetDcContext = LocalAlloc( LMEM_ZEROINIT, sizeof(DSGETDC_CONTEXT) );
  6012. if ( GetDcContext == NULL ) {
  6013. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6014. goto Cleanup;
  6015. }
  6016. //
  6017. // If the name has a well known prefix,
  6018. // strip off the prefix and convert it to a flag bit.
  6019. //
  6020. // An LDAP client gets a name of this from in a GC referral. By converting
  6021. // that name to this form, I will find a site specific GC.
  6022. //
  6023. if ( Flags == 0 ) {
  6024. if ( _strnicmp( DnsName, NL_DNS_GC, sizeof(NL_DNS_GC)-1) == 0 ) {
  6025. DnsName += sizeof(NL_DNS_GC)-1;
  6026. Flags |= DS_GC_SERVER_REQUIRED;
  6027. } else if ( _strnicmp( DnsName, NL_DNS_PDC, sizeof(NL_DNS_PDC)-1) == 0 ) {
  6028. DnsName += sizeof(NL_DNS_PDC)-1;
  6029. Flags |= DS_PDC_REQUIRED;
  6030. }
  6031. }
  6032. //
  6033. // Fill in the DNS name
  6034. //
  6035. Size = (strlen(DnsName) + 1) * sizeof(char);
  6036. GetDcContext->QueriedDnsName = LocalAlloc( 0, Size );
  6037. if ( GetDcContext->QueriedDnsName == NULL ) {
  6038. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6039. goto Cleanup;
  6040. }
  6041. RtlCopyMemory( GetDcContext->QueriedDnsName, DnsName, Size );
  6042. //
  6043. // Fill in the forest name if specified
  6044. //
  6045. if ( ARGUMENT_PRESENT(DnsForestName) ) {
  6046. Size = (strlen(DnsForestName) + 1) * sizeof(char);
  6047. GetDcContext->QueriedDnsForestName = LocalAlloc( 0, Size );
  6048. if ( GetDcContext->QueriedDnsForestName == NULL ) {
  6049. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6050. goto Cleanup;
  6051. }
  6052. RtlCopyMemory( GetDcContext->QueriedDnsForestName, DnsForestName, Size );
  6053. }
  6054. //
  6055. // Fill in the site name if specified
  6056. //
  6057. if ( ARGUMENT_PRESENT(SiteName) ) {
  6058. Size = (wcslen(SiteName) + 1) * sizeof(WCHAR);
  6059. GetDcContext->QueriedSiteName = LocalAlloc( 0, Size );
  6060. if ( GetDcContext->QueriedSiteName == NULL ) {
  6061. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6062. goto Cleanup;
  6063. }
  6064. RtlCopyMemory( GetDcContext->QueriedSiteName, SiteName, Size );
  6065. }
  6066. //
  6067. // Fill in flags
  6068. //
  6069. GetDcContext->QueriedInternalFlags = OptionFlags;
  6070. //
  6071. // Fill in domain GUID if specified
  6072. //
  6073. if ( ARGUMENT_PRESENT( DomainGuid ) ) {
  6074. GetDcContext->QueriedDomainGuid = *DomainGuid;
  6075. }
  6076. //
  6077. // Compute the initial DNS name type to query.
  6078. //
  6079. GetDcContext->FirstTime = TRUE;
  6080. GetDcContext->QueriedFlags = Flags;
  6081. NetStatus = NetpDcFlagsToNameType( Flags, &GetDcContext->NlDnsNameType );
  6082. if ( NetStatus != NO_ERROR ) {
  6083. goto Cleanup;
  6084. }
  6085. //
  6086. // If a site name was specified by the caller,
  6087. // and this name type supports a type specific query,
  6088. // start with the type specific query.
  6089. //
  6090. if ( GetDcContext->QueriedSiteName != NULL ) {
  6091. if ( NlDcDnsNameTypeDesc[GetDcContext->NlDnsNameType].SiteSpecificDnsNameType != NlDnsInvalid ) {
  6092. GetDcContext->NlDnsNameType = NlDcDnsNameTypeDesc[GetDcContext->NlDnsNameType].SiteSpecificDnsNameType;
  6093. }
  6094. }
  6095. //
  6096. // Return the context to the caller.
  6097. //
  6098. *RetGetDcContext = GetDcContext;
  6099. NetStatus = NO_ERROR;
  6100. //
  6101. // Cleanup
  6102. //
  6103. Cleanup:
  6104. if ( NetStatus != NO_ERROR ) {
  6105. if ( GetDcContext != NULL ) {
  6106. NetpDcGetDcClose( GetDcContext );
  6107. }
  6108. }
  6109. return NetStatus;
  6110. }
  6111. NET_API_STATUS
  6112. NetpDcGetDcNext(
  6113. IN HANDLE GetDcContextHandle,
  6114. OUT PULONG SockAddressCount OPTIONAL,
  6115. OUT LPSOCKET_ADDRESS *SockAddresses OPTIONAL,
  6116. OUT LPSTR *DnsHostName OPTIONAL
  6117. )
  6118. /*++
  6119. Routine Description:
  6120. Returns the next logical SRV record for the name opened by NetpDcGetDcOpen.
  6121. The returned record takes into account the weights and priorities specified
  6122. in the SRV records.
  6123. Arguments:
  6124. GetDcContextHandle - An opaque context describing the SRV records.
  6125. SockAddressCount - Returns the number of Addresses in SockAddresses.
  6126. If NULL, addresses will not be looked up.
  6127. SockAddresses - Returns an array SOCKET_ADDRESS structures for the server.
  6128. All returned addresses will be of family AF_INET or AF_INET6.
  6129. The returned sin_port field contains port from the SRV record.
  6130. A Port of 0 indicate no port is available from DNS.
  6131. This buffer should be freed using LocalFree().
  6132. DnsHostName - Returns a pointer to the DnsHostName in the SRV record.
  6133. A NULL is returned if no host name is known.
  6134. This buffer need not be freed. The buffer is valid until the call to
  6135. NetpDcGetDcClose or the next call to NetpDcGetDcNext.
  6136. Return Value:
  6137. NO_ERROR: Addresses were returned
  6138. ERROR_NO_MORE_ITEMS: No more addresses are available.
  6139. ERROR_FILEMARK_DETECTED: Caller has specified the DS_NOTIFY_AFTER_SITE_RECORDS flag
  6140. and NetpDcGetDcNext has processed all of the site specific SRV records. The caller
  6141. should take any action based on no site specific DCs being available, then
  6142. should call NetpDcGetDcNext to continue on to other DCs.
  6143. Any other errors returned are those detected while trying to find the A
  6144. records associated with the host of the SRV record. The caller can
  6145. note the error (perhaps so the caller can return this status to
  6146. his caller if no usefull server is found) then call NetpDcGetDcNext
  6147. again to get the next SRV record. The caller can inspect this error
  6148. and return immediately if the caller deems the error serious.
  6149. The following interesting errors might be returned:
  6150. DNS_ERROR_RCODE_NAME_ERROR: No A records are available for this SRV record.
  6151. ERROR_TIMEOUT: DNS server didn't respond in a reasonable time
  6152. --*/
  6153. {
  6154. NET_API_STATUS NetStatus;
  6155. PDSGETDC_CONTEXT GetDcContext = (PDSGETDC_CONTEXT) GetDcContextHandle;
  6156. PDNS_RECORD *DnsArray;
  6157. PDNS_RECORD SrvDnsRecord;
  6158. PDNS_RECORD DnsARecords = NULL;
  6159. CHAR DnsName[NL_MAX_DNS_LENGTH+1];
  6160. BOOLEAN NotifySiteChange = FALSE;
  6161. GUID *CurrentGuid;
  6162. LPCSTR CurrentDnsRecordName;
  6163. ULONG Index;
  6164. //
  6165. // Loop trying the various DNS record names.
  6166. //
  6167. for (;;) {
  6168. //
  6169. // If we aren't still processing a set of SRV records from the previous call,
  6170. // move on to the next name.
  6171. //
  6172. if ( GetDcContext->SrvContextHandle == NULL ) {
  6173. CurrentDnsRecordName = GetDcContext->QueriedDnsName;
  6174. //
  6175. // If this isn't the first call,
  6176. // compute the next DNS name to query.
  6177. //
  6178. if ( !GetDcContext->FirstTime ) {
  6179. //
  6180. // If we just completed the site specific records,
  6181. // and we've been asked to tell the caller when that's done,
  6182. // remember to tell the caller.
  6183. //
  6184. // Don't actually notify the caller until right before we're going to hit
  6185. // the wire.
  6186. //
  6187. if ( NlDcDnsNameTypeDesc[GetDcContext->NlDnsNameType].IsSiteSpecific &&
  6188. (GetDcContext->QueriedInternalFlags & DS_NOTIFY_AFTER_SITE_RECORDS) != 0 ) {
  6189. NotifySiteChange = TRUE;
  6190. }
  6191. //
  6192. // Compute the next name type to query.
  6193. //
  6194. GetDcContext->NlDnsNameType = NlDcDnsNameTypeDesc[GetDcContext->NlDnsNameType].NextDnsNameType;
  6195. if ( GetDcContext->NlDnsNameType == NlDnsInvalid ) {
  6196. //
  6197. // No more names to process.
  6198. //
  6199. NetStatus = ERROR_NO_MORE_ITEMS;
  6200. goto Cleanup;
  6201. }
  6202. //
  6203. // If the current name type is not a site specific name type,
  6204. // and we've been asked to do only site specific names.
  6205. // we're done.
  6206. //
  6207. if ( !NlDcDnsNameTypeDesc[GetDcContext->NlDnsNameType].IsSiteSpecific &&
  6208. (GetDcContext->QueriedInternalFlags & DS_ONLY_DO_SITE_NAME) != 0 ) {
  6209. NetStatus = ERROR_NO_MORE_ITEMS;
  6210. goto Cleanup;
  6211. }
  6212. //
  6213. // If this is the "by guid" name but we don't have a guid or forest name,
  6214. // go to the next name.
  6215. //
  6216. if ( NlDnsDcGuid( GetDcContext->NlDnsNameType ) ) {
  6217. //
  6218. // If no domain GUID was specified,
  6219. // go to the next name.
  6220. //
  6221. if ( IsEqualGUID( &GetDcContext->QueriedDomainGuid, &NlDcZeroGuid) ) {
  6222. continue;
  6223. }
  6224. //
  6225. // Otherwise try to find the domain by GUID
  6226. // This name is registered at the tree name.
  6227. //
  6228. if ( GetDcContext->QueriedDnsForestName == NULL ) {
  6229. continue;
  6230. }
  6231. CurrentDnsRecordName = GetDcContext->QueriedDnsForestName;
  6232. }
  6233. }
  6234. GetDcContext->FirstTime = FALSE;
  6235. //
  6236. // If we are to notify the caller when we're done with the site specific records,
  6237. // do so now.
  6238. //
  6239. if ( NotifySiteChange ) {
  6240. //
  6241. // We've already decided what SRV records to look up next.
  6242. // Flag that we've done so.
  6243. //
  6244. GetDcContext->FirstTime = TRUE;
  6245. NetStatus = ERROR_FILEMARK_DETECTED;
  6246. goto Cleanup;
  6247. }
  6248. //
  6249. // Build the DNS name to query.
  6250. //
  6251. NetStatus = NetpDcBuildDnsName(
  6252. GetDcContext->NlDnsNameType,
  6253. &GetDcContext->QueriedDomainGuid,
  6254. GetDcContext->QueriedSiteName,
  6255. CurrentDnsRecordName,
  6256. DnsName );
  6257. if ( NetStatus != NO_ERROR ) {
  6258. NlPrint(( NL_CRITICAL,
  6259. "NetpDcGetDcNext: %s: %ld: Cannot NetpDcBuildDnsName. %ld\n",
  6260. CurrentDnsRecordName,
  6261. GetDcContext->NlDnsNameType,
  6262. NetStatus ));
  6263. goto Cleanup;
  6264. }
  6265. //
  6266. // Get the SRV records from DNS.
  6267. //
  6268. NetStatus = NetpSrvOpen( DnsName,
  6269. (GetDcContext->QueriedFlags & DS_FORCE_REDISCOVERY) != 0 ?
  6270. DNS_QUERY_BYPASS_CACHE :
  6271. 0,
  6272. &GetDcContext->SrvContextHandle );
  6273. if ( NetStatus != NO_ERROR ) {
  6274. //
  6275. // If the specified record cannot be found in DNS,
  6276. // try the next name type.
  6277. //
  6278. if ( NlDcNoDnsRecord( NetStatus ) ) {
  6279. continue;
  6280. }
  6281. NlPrint(( NL_CRITICAL,
  6282. "NetpDcGetDcNext: %s: Cannot Query DNS. %ld 0x%lx\n",
  6283. DnsName,
  6284. NetStatus,
  6285. NetStatus ));
  6286. goto Cleanup;
  6287. }
  6288. }
  6289. //
  6290. // If we've got more SRV records to process for this DnsName,
  6291. // get the next SRV record.
  6292. //
  6293. NetStatus = NetpSrvNext( GetDcContext->SrvContextHandle,
  6294. SockAddressCount,
  6295. SockAddresses,
  6296. DnsHostName );
  6297. if ( NetStatus == NO_ERROR ) {
  6298. goto Cleanup;
  6299. //
  6300. // If we're done with this set of SRV records mark so for next time.
  6301. //
  6302. } else if ( NetStatus == ERROR_NO_MORE_ITEMS ) {
  6303. NetpSrvClose( GetDcContext->SrvContextHandle );
  6304. GetDcContext->SrvContextHandle = NULL;
  6305. continue;
  6306. //
  6307. // All other statuses are simply returned to our caller.
  6308. //
  6309. } else {
  6310. NlPrint(( NL_CRITICAL,
  6311. "NetpDcGetDcNext: %s: %ld: Cannot NetpSrvNext. %ld 0x%lx\n",
  6312. GetDcContext->QueriedDnsName,
  6313. GetDcContext->NlDnsNameType,
  6314. NetStatus,
  6315. NetStatus ));
  6316. goto Cleanup;
  6317. }
  6318. }
  6319. ASSERT( FALSE );
  6320. Cleanup:
  6321. if ( NlDcNoDnsRecord( NetStatus ) ) {
  6322. NetStatus = DNS_ERROR_RCODE_NAME_ERROR;
  6323. }
  6324. return NetStatus;
  6325. }
  6326. VOID
  6327. NetpDcGetDcClose(
  6328. IN HANDLE GetDcContextHandle
  6329. )
  6330. /*++
  6331. Routine Description:
  6332. Free the context allocated by NetpDcGetDcOpen
  6333. Arguments:
  6334. GetDcContextHandle - An opaque context describing the SRV records.
  6335. Return Value:
  6336. Status of the operation.
  6337. NO_ERROR: GetDcContext was returned successfully.
  6338. --*/
  6339. {
  6340. PDSGETDC_CONTEXT GetDcContext = (PDSGETDC_CONTEXT) GetDcContextHandle;
  6341. if ( GetDcContext != NULL ) {
  6342. //
  6343. // Free allocated names
  6344. //
  6345. if ( GetDcContext->QueriedDnsName != NULL ) {
  6346. LocalFree( GetDcContext->QueriedDnsName );
  6347. }
  6348. if ( GetDcContext->QueriedSiteName != NULL ) {
  6349. LocalFree( GetDcContext->QueriedSiteName );
  6350. }
  6351. if ( GetDcContext->QueriedDnsForestName != NULL ) {
  6352. LocalFree( GetDcContext->QueriedDnsForestName );
  6353. }
  6354. //
  6355. // Free the SRV context
  6356. //
  6357. if ( GetDcContext->SrvContextHandle != NULL ) {
  6358. NetpSrvClose( GetDcContext->SrvContextHandle );
  6359. }
  6360. //
  6361. // Free the context itself
  6362. //
  6363. LocalFree( GetDcContext );
  6364. }
  6365. }
  6366. NET_API_STATUS
  6367. NetpDcGetNameSiteIp(
  6368. IN PNL_GETDC_CONTEXT Context,
  6369. IN ULONG InternalFlags,
  6370. IN LPCWSTR SiteName OPTIONAL,
  6371. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  6372. OUT PBOOL UsedNetbios
  6373. )
  6374. /*++
  6375. Routine Description:
  6376. This routine determines the name/address of a DC with the specified
  6377. characteristics using an IP-only algorithm.
  6378. Arguments:
  6379. Context - Context describing the GetDc operation.
  6380. InternalFlags - Flags affecting the operation of the routine.
  6381. DS_ONLY_DO_SITE_NAME - Non-site names should be ignored.
  6382. SiteName - Specifies the name of the site the returned DC should be
  6383. "close" to. The parameter should typically be the site name of the
  6384. site the client is in. If not specified, the site name defaults to
  6385. the site of ComputerName.
  6386. NlDcCacheEntry - On success, returns a pointer to the cache entry
  6387. describing the found DC.
  6388. This entry must be dereferenced using NetpDcDerefCacheEntry.
  6389. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  6390. the returned cache entry.
  6391. Return Value:
  6392. NO_ERROR: The NlDcCacheEntry was returned;
  6393. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  6394. (Definitive status that we need not try again.)
  6395. ERROR_SEM_TIMEOUT: No DC responded to the request.
  6396. (Non-definitive status that we should try again.)
  6397. ERROR_DNS_NOT_CONFIGURED: IP or DNS is not available on this computer.
  6398. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  6399. ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
  6400. cannot be concatenated.
  6401. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  6402. this request.
  6403. Various Winsock errors.
  6404. --*/
  6405. {
  6406. NET_API_STATUS NetStatus;
  6407. HANDLE DsGetDcHandle = NULL;
  6408. PSOCKET_ADDRESS SockAddressList = NULL;
  6409. ULONG SockAddressCount;
  6410. PNL_DC_ADDRESS FirstDcToQuery;
  6411. PNL_DC_ADDRESS DcAddress;
  6412. ULONG DcPingCount = 0;
  6413. ULONG LocalMaxLdapServersPinged = 0xffffffff;
  6414. BOOLEAN SiteSpecificRecords = FALSE;
  6415. BOOLEAN DnsRecordFound = FALSE;
  6416. LPSTR Utf8DnsDomainName = NULL;
  6417. LPSTR Utf8DnsForestName = NULL;
  6418. LPSTR Utf8DnsHostName = NULL;
  6419. LPWSTR UnicodeDnsHostName = NULL;
  6420. //
  6421. // Ping the list of DCs found on the previous call.
  6422. //
  6423. NetStatus = NetpDcPingListIp(
  6424. Context,
  6425. NULL,
  6426. TRUE, // Wait for ping responce
  6427. NlDcCacheEntry,
  6428. UsedNetbios,
  6429. &DcPingCount );
  6430. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  6431. if ( NetStatus != NO_ERROR ) {
  6432. NlPrint(( NL_CRITICAL,
  6433. "NetpDcGetNameIp: %ws: Cannot NetpDcPingListIp. %ld\n",
  6434. Context->QueriedDisplayDomainName,
  6435. NetStatus ));
  6436. }
  6437. goto Cleanup;
  6438. }
  6439. //
  6440. // Convert the DNS name to Utf8
  6441. //
  6442. Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( Context->QueriedDnsDomainName );
  6443. if ( Utf8DnsDomainName == NULL ) {
  6444. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6445. goto Cleanup;
  6446. }
  6447. if ( Context->QueriedDnsForestName != NULL ) {
  6448. Utf8DnsForestName = NetpAllocUtf8StrFromWStr( Context->QueriedDnsForestName );
  6449. if ( Utf8DnsForestName == NULL ) {
  6450. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6451. goto Cleanup;
  6452. }
  6453. }
  6454. //
  6455. // Determine if we are doing site specific discovery
  6456. //
  6457. if ( SiteName != NULL &&
  6458. NlDcDnsNameTypeDesc[Context->QueriedNlDnsNameType].SiteSpecificDnsNameType != NlDnsInvalid ) {
  6459. SiteSpecificRecords = TRUE;
  6460. }
  6461. //
  6462. // Get a context for the DNS name queries.
  6463. //
  6464. NetStatus = NetpDcGetDcOpen( Utf8DnsDomainName,
  6465. DS_NOTIFY_AFTER_SITE_RECORDS | InternalFlags,
  6466. SiteName,
  6467. Context->QueriedDomainGuid,
  6468. Utf8DnsForestName,
  6469. Context->QueriedFlags & DS_OPEN_VALID_FLAGS,
  6470. &DsGetDcHandle );
  6471. if ( NetStatus != NO_ERROR ) {
  6472. goto Cleanup;
  6473. }
  6474. //
  6475. // Determine the maximum number of DCs we can ping
  6476. //
  6477. #ifdef _NETLOGON_SERVER
  6478. //
  6479. // In netlogon, the value is kept in global parameters
  6480. //
  6481. LocalMaxLdapServersPinged = NlGlobalParameters.MaxLdapServersPinged;
  6482. #else
  6483. //
  6484. // If we are not running in netlogon, we need to read
  6485. // the value directly from the registry
  6486. //
  6487. if ( !NlReadDwordNetlogonRegValue("MaxLdapServersPinged",
  6488. &LocalMaxLdapServersPinged) ) {
  6489. //
  6490. // If it's not set in registry, use the default
  6491. //
  6492. LocalMaxLdapServersPinged = DEFAULT_MAXLDAPSERVERSPINGED;
  6493. } else {
  6494. //
  6495. // Ensure that the value set in registry is in the valid range
  6496. //
  6497. if ( LocalMaxLdapServersPinged < MIN_MAXLDAPSERVERSPINGED ||
  6498. LocalMaxLdapServersPinged > MAX_MAXLDAPSERVERSPINGED ) {
  6499. LocalMaxLdapServersPinged = DEFAULT_MAXLDAPSERVERSPINGED;
  6500. }
  6501. }
  6502. #endif // _NETLOGON_SERVER
  6503. //
  6504. // Loop getting addresses to query.
  6505. //
  6506. for ( ;; ) {
  6507. //
  6508. // Free any memory from a previous iteration.
  6509. //
  6510. FirstDcToQuery = NULL;
  6511. if ( SockAddressList != NULL ) {
  6512. LocalFree( SockAddressList );
  6513. SockAddressList = NULL;
  6514. }
  6515. //
  6516. // Get the next set of IP addresses from DNS
  6517. //
  6518. NetStatus = NetpDcGetDcNext( DsGetDcHandle,
  6519. &SockAddressCount,
  6520. &SockAddressList,
  6521. &Utf8DnsHostName );
  6522. //
  6523. // Process the exeptional conditions
  6524. //
  6525. if ( NetStatus == NO_ERROR ) {
  6526. //
  6527. // Since a SRV record was found, the only reason to not find the DC is if
  6528. // the DC is down. That isn't a permanent condition.
  6529. //
  6530. Context->AvoidPermanentNegativeCache = TRUE;
  6531. //
  6532. // Indicate that DNS is up and running.
  6533. //
  6534. Context->ResponseFromDnsServer = TRUE;
  6535. //
  6536. // Indicate whether site specific records are available
  6537. //
  6538. if ( SiteSpecificRecords ) {
  6539. Context->ContextFlags |= NL_GETDC_SITE_SPECIFIC_DNS_AVAIL;
  6540. }
  6541. /* Drop out */
  6542. //
  6543. // If the A record cannot be found for the SRV record in DNS,
  6544. // try the other name type.
  6545. //
  6546. } else if ( NetStatus == DNS_ERROR_RCODE_NAME_ERROR) {
  6547. //
  6548. // Since a SRV record was found, the only reason to not find the DC is if
  6549. // the DC is down. That isn't a permanent condition.
  6550. //
  6551. Context->AvoidPermanentNegativeCache = TRUE;
  6552. //
  6553. // Indicate that DNS is up and running.
  6554. //
  6555. Context->ResponseFromDnsServer = TRUE;
  6556. NlPrint(( NL_CRITICAL,
  6557. "NetpDcGetNameIp: %ws: cannot find A record.\n",
  6558. Context->QueriedDisplayDomainName ));
  6559. continue;
  6560. //
  6561. // If we've processed all of the site specific SRV records and are about to move on,
  6562. // wait a little longer for the site specific DCs to respond.
  6563. //
  6564. } else if ( NetStatus == ERROR_FILEMARK_DETECTED ) {
  6565. //
  6566. // Only do this if there actually were site specific SRV records.
  6567. //
  6568. if ( DcPingCount ) {
  6569. NlPrint(( NL_CRITICAL,
  6570. "NetpDcGetNameIp: %ws: site specific SRV records done.\n",
  6571. Context->QueriedDisplayDomainName ));
  6572. //
  6573. // Get the response from the ping.
  6574. //
  6575. NetStatus = NetpDcGetPingResponse(
  6576. Context,
  6577. NL_DC_MED_PING_TIMEOUT, // wait for median time
  6578. NlDcCacheEntry,
  6579. UsedNetbios );
  6580. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  6581. if ( NetStatus != NO_ERROR ) {
  6582. NlPrint(( NL_CRITICAL,
  6583. "NetpDcGetNameIp: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  6584. Context->QueriedDisplayDomainName,
  6585. NetStatus ));
  6586. }
  6587. goto Cleanup;
  6588. }
  6589. }
  6590. //
  6591. // Indicate that all subsequent addresses are retrived as a result
  6592. // of non-site-specific DNS record lookup.
  6593. //
  6594. SiteSpecificRecords = FALSE;
  6595. continue;
  6596. //
  6597. // If we're done,
  6598. // break out of the loop.
  6599. //
  6600. } else if ( NetStatus == ERROR_NO_MORE_ITEMS ) {
  6601. //
  6602. // Indicate that DNS is up and running.
  6603. //
  6604. Context->ResponseFromDnsServer = TRUE;
  6605. break;
  6606. //
  6607. // If DNS isn't available,
  6608. // blow this request away.
  6609. //
  6610. } else if ( NetStatus == ERROR_TIMEOUT ||
  6611. NetStatus == DNS_ERROR_RCODE_SERVER_FAILURE ) { // Server failed
  6612. //
  6613. // DNS servers being down isn't a permanent condition.
  6614. //
  6615. Context->AvoidPermanentNegativeCache = TRUE;
  6616. break;
  6617. //
  6618. // If IP or DNS is not configured,
  6619. // tell the caller.
  6620. //
  6621. } else if ( NetStatus == DNS_ERROR_NO_TCPIP || // TCP/IP not configured
  6622. NetStatus == DNS_ERROR_NO_DNS_SERVERS ) { // DNS not configured
  6623. NlPrint(( NL_CRITICAL,
  6624. "NetpDcGetNameIp: %ws: IP Not configured from DnsQuery.\n",
  6625. Context->QueriedDisplayDomainName ));
  6626. NetStatus = ERROR_DNS_NOT_CONFIGURED;
  6627. goto Cleanup;
  6628. //
  6629. // We don't handle any other error.
  6630. //
  6631. } else {
  6632. NlPrint(( NL_CRITICAL,
  6633. "NetpDcGetNameIp: %ws: Unknown error from DnsQuery. %ld 0x%lx\n",
  6634. Context->QueriedDisplayDomainName,
  6635. NetStatus,
  6636. NetStatus ));
  6637. goto Cleanup;
  6638. }
  6639. DnsRecordFound = TRUE;
  6640. //
  6641. // Add new addresses to the list
  6642. //
  6643. if ( UnicodeDnsHostName != NULL ) {
  6644. NetApiBufferFree( UnicodeDnsHostName );
  6645. UnicodeDnsHostName = NULL;
  6646. }
  6647. UnicodeDnsHostName = NetpAllocWStrFromUtf8Str( Utf8DnsHostName );
  6648. if ( UnicodeDnsHostName == NULL ) {
  6649. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6650. goto Cleanup;
  6651. }
  6652. NetStatus = NetpDcProcessAddressList( Context,
  6653. UnicodeDnsHostName,
  6654. SockAddressList,
  6655. SockAddressCount,
  6656. SiteSpecificRecords,
  6657. &FirstDcToQuery );
  6658. if ( NetStatus != NO_ERROR ) {
  6659. goto Cleanup;
  6660. }
  6661. //
  6662. // Only process this list if new entries were added.
  6663. //
  6664. if ( FirstDcToQuery != NULL ) {
  6665. ULONG LocalDcPingCount = 0;
  6666. //
  6667. // Ping the new list of DCs.
  6668. //
  6669. NetStatus = NetpDcPingListIp(
  6670. Context,
  6671. FirstDcToQuery,
  6672. TRUE, // Wait for ping responce
  6673. NlDcCacheEntry,
  6674. UsedNetbios,
  6675. &LocalDcPingCount );
  6676. //
  6677. // If we sent a ping to at least one address for this DC,
  6678. // count this DC in the number of DCs pinged
  6679. //
  6680. if ( LocalDcPingCount > 0 ) {
  6681. Context->DcsPinged ++;
  6682. }
  6683. //
  6684. // Check error conditions
  6685. //
  6686. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  6687. if ( NetStatus != NO_ERROR ) {
  6688. NlPrint(( NL_CRITICAL,
  6689. "NetpDcGetNameIp: %ws: Cannot NetpDcPingListIp. %ld\n",
  6690. Context->QueriedDisplayDomainName,
  6691. NetStatus ));
  6692. }
  6693. goto Cleanup;
  6694. }
  6695. //
  6696. // Update the number of pings we sent
  6697. //
  6698. DcPingCount += LocalDcPingCount;
  6699. //
  6700. // Stop getting new addresses if we have reached
  6701. // the limit on the number of DCs we can ping
  6702. //
  6703. if ( Context->DcsPinged >= LocalMaxLdapServersPinged ) {
  6704. NlPrint(( NL_CRITICAL,
  6705. "NetpDcGetNameSiteIp: %ws: Reached the DC limit %lu %lu\n",
  6706. Context->QueriedDisplayDomainName,
  6707. Context->DcsPinged,
  6708. LocalMaxLdapServersPinged ));
  6709. break;
  6710. }
  6711. }
  6712. }
  6713. //
  6714. // If no DNS records could be found,
  6715. // this is a definitive failure.
  6716. //
  6717. if ( !DnsRecordFound ) {
  6718. NlPrint(( NL_CRITICAL,
  6719. "NetpDcGetNameIp: %ws: No data returned from DnsQuery.\n",
  6720. Context->QueriedDisplayDomainName ));
  6721. NetStatus = ERROR_NO_SUCH_DOMAIN;
  6722. goto Cleanup;
  6723. }
  6724. //
  6725. // If we could not send a ping to any of the DCs,
  6726. // or if there are no more DCs to ping,
  6727. // this is a definitive failure.
  6728. //
  6729. if ( DcPingCount == 0 || Context->DcAddressCount == 0 ) {
  6730. NlPrint(( NL_CRITICAL,
  6731. "NetpDcGetNameIp: %ws: Couldn't ping any DCs.\n",
  6732. Context->QueriedDisplayDomainName ));
  6733. NetStatus = ERROR_NO_SUCH_DOMAIN;
  6734. goto Cleanup;
  6735. }
  6736. NetStatus = ERROR_SEM_TIMEOUT;
  6737. Cleanup:
  6738. if ( SockAddressList != NULL) {
  6739. LocalFree( SockAddressList );
  6740. }
  6741. if ( DsGetDcHandle != NULL ) {
  6742. NetpDcGetDcClose( DsGetDcHandle );
  6743. }
  6744. if ( Utf8DnsDomainName != NULL ) {
  6745. NetpMemoryFree( Utf8DnsDomainName );
  6746. }
  6747. if ( Utf8DnsForestName != NULL ) {
  6748. NetpMemoryFree( Utf8DnsForestName );
  6749. }
  6750. if ( UnicodeDnsHostName != NULL ) {
  6751. NetApiBufferFree( UnicodeDnsHostName );
  6752. }
  6753. //
  6754. // Note that Utf8DnsHostName should not be freed
  6755. // as it wasn't allocated
  6756. //
  6757. return NetStatus;
  6758. }
  6759. NET_API_STATUS
  6760. NetpDcGetNameIp(
  6761. IN PNL_GETDC_CONTEXT Context,
  6762. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  6763. OUT PBOOL UsedNetbios
  6764. )
  6765. /*++
  6766. Routine Description:
  6767. This routine determines the name/address of a DC with the specified
  6768. characteristics using an IP-only algorithm.
  6769. This routine handles the case where the site of the DC found isn't the
  6770. 'closest' site to the client. In that case, the DC found will indicate
  6771. which site is the closest site. This routine will try to find a DC in that
  6772. closest site.
  6773. Arguments:
  6774. Context - Context describing the GetDc operation.
  6775. NlDcCacheEntry - On success, returns a pointer to the cache entry
  6776. describing the found DC.
  6777. This entry must be dereferenced using NetpDcDerefCacheEntry.
  6778. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  6779. the returned cache entry.
  6780. Return Value:
  6781. NO_ERROR: The NlDcCacheEntry was returned;
  6782. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  6783. (Definitive status that we need not try again.)
  6784. ERROR_SEM_TIMEOUT: No DC responded to the request.
  6785. (Non-definitive status that we should try again.)
  6786. ERROR_DNS_NOT_CONFIGURED: IP or DNS is not available on this computer.
  6787. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  6788. ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
  6789. cannot be concatenated.
  6790. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  6791. this request.
  6792. Various Winsock errors.
  6793. --*/
  6794. {
  6795. NET_API_STATUS NetStatus;
  6796. PNL_DC_CACHE_ENTRY ClosestNlDcCacheEntry;
  6797. BOOL ClosestUsedNetbios;
  6798. //
  6799. // Try the operation as it was passed to us.
  6800. //
  6801. NetStatus = NetpDcGetNameSiteIp( Context,
  6802. Context->DoingExplicitSite ?
  6803. DS_ONLY_DO_SITE_NAME :
  6804. 0, // No flags this time
  6805. Context->QueriedSiteName,
  6806. NlDcCacheEntry,
  6807. UsedNetbios );
  6808. if ( NetStatus != NO_ERROR ) {
  6809. return NetStatus;
  6810. }
  6811. //
  6812. // If the queried DC type doesn't have site specific DCs,
  6813. // return the found DC to the caller.
  6814. //
  6815. if ( NlDcDnsNameTypeDesc[Context->QueriedNlDnsNameType].SiteSpecificDnsNameType == NlDnsInvalid ) {
  6816. return NO_ERROR;
  6817. }
  6818. //
  6819. // If the caller explicitly specified a sitename,
  6820. // return the found DC to the caller.
  6821. //
  6822. if ( Context->DoingExplicitSite ) {
  6823. return NO_ERROR;
  6824. }
  6825. //
  6826. // If the responding DC is in the closest site,
  6827. // or the DC doesn't know the closest site,
  6828. // or we've already tried the closest site (this case shouldn't happen),
  6829. // return the found DC to the caller.
  6830. //
  6831. if ( ((*NlDcCacheEntry)->ReturnFlags & DS_CLOSEST_FLAG ) != 0 ||
  6832. (*NlDcCacheEntry)->UnicodeClientSiteName == NULL ||
  6833. (Context->QueriedSiteName != NULL &&
  6834. _wcsicmp( (*NlDcCacheEntry)->UnicodeClientSiteName,
  6835. Context->QueriedSiteName ) == 0 ) ) {
  6836. return NO_ERROR;
  6837. }
  6838. //
  6839. // Free up any existing addresses that have been pinged.
  6840. //
  6841. // We're starting the operation over again.
  6842. NetpDcFreeAddressList( Context );
  6843. //
  6844. // Try the operation again just trying to find a DC in the right site.
  6845. //
  6846. NlPrint(( NL_MISC,
  6847. "NetpDcGetNameIp: %ws Trying to find a DC in a closer site: %ws\n",
  6848. Context->QueriedDisplayDomainName,
  6849. (*NlDcCacheEntry)->UnicodeClientSiteName ));
  6850. NetStatus = NetpDcGetNameSiteIp( Context,
  6851. DS_ONLY_DO_SITE_NAME,
  6852. (*NlDcCacheEntry)->UnicodeClientSiteName,
  6853. &ClosestNlDcCacheEntry,
  6854. &ClosestUsedNetbios );
  6855. if ( NetStatus != NO_ERROR ) {
  6856. return NO_ERROR;
  6857. }
  6858. //
  6859. // If we found a closer DC,
  6860. // ditch the first entry and use the new one.
  6861. //
  6862. NetpDcDerefCacheEntry( *NlDcCacheEntry );
  6863. *NlDcCacheEntry = ClosestNlDcCacheEntry;
  6864. *UsedNetbios = ClosestUsedNetbios;
  6865. return NO_ERROR;
  6866. }
  6867. NET_API_STATUS
  6868. NetpDcGetNameNetbios(
  6869. PNL_GETDC_CONTEXT Context,
  6870. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  6871. OUT PBOOL UsedNetbios
  6872. )
  6873. /*++
  6874. Routine Description:
  6875. This routine determines the name/address of a DC with the specified
  6876. characteristics using a Netbios algorithm.
  6877. Arguments:
  6878. Context - Context describing the GetDc operation.
  6879. NlDcCacheEntry - On success, returns a pointer to the cache entry
  6880. describing the found DC.
  6881. This entry must be dereferenced using NetpDcDerefCacheEntry.
  6882. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  6883. the returned cache entry.
  6884. Return Value:
  6885. NO_ERROR: The NlDcCacheEntry was returned;
  6886. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  6887. (Definitive status that we need not try again.)
  6888. ERROR_SEM_TIMEOUT: No DC responded to the request.
  6889. (Non-definitive status that we should try again.)
  6890. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  6891. ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
  6892. cannot be concatenated.
  6893. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  6894. this request.
  6895. --*/
  6896. {
  6897. NET_API_STATUS NetStatus;
  6898. NTSTATUS Status;
  6899. BOOL Flush1cName = FALSE;
  6900. BOOL Flush1bName = FALSE;
  6901. //
  6902. // Avoid querying for GCs.
  6903. //
  6904. // GCs don't have their own Netbios name. I could simply send to the 1C name, but
  6905. // 1) It would be wasteful to send to all of the DCs when only some are GCs.
  6906. // 2) WINS only registers a max of 25 DC addresses per name.
  6907. //
  6908. if ( NlDnsGcName(Context->QueriedNlDnsNameType) ) {
  6909. NlPrint(( NL_CRITICAL,
  6910. "NetpDcGetNameNetbios: %ws: Cannot query for GC using netbios.\n",
  6911. Context->QueriedDisplayDomainName ));
  6912. NetStatus = ERROR_NO_SUCH_DOMAIN;
  6913. goto Cleanup;
  6914. }
  6915. //
  6916. // Flush Netbios cache if this is forced rediscovery
  6917. //
  6918. if ( Context->QueriedFlags & DS_FORCE_REDISCOVERY ) {
  6919. Flush1bName = TRUE;
  6920. Flush1cName = TRUE;
  6921. }
  6922. //
  6923. // If there is an alternate ping message,
  6924. // send it first.
  6925. //
  6926. if ( Context->AlternatePingMessageSize != 0 ) {
  6927. //
  6928. // If only the PDC should responsd,
  6929. // send the alternate ping to DomainName[1B].
  6930. //
  6931. // If any DC can respond,
  6932. // broadcast it to DomainName[1C] groupname.
  6933. //
  6934. //
  6935. // If this is a request for a PDC with an account,
  6936. // the "primary" message is a normal "primary query" sent for
  6937. // backward compatibility with NT 4 and earlier. This message
  6938. // is a "logon user" that NT 5 will understand.
  6939. //
  6940. // (More specifically, NT 4 understands this query but we'll discard
  6941. // the response since the response from NT 4 doesn't flag the response
  6942. // as being from the PDC.)
  6943. //
  6944. //
  6945. // If this is a request for a writable DC,
  6946. // this request is the datagram send of the "logon user" message to DomainName[1C].
  6947. //
  6948. #if NETLOGONDBG
  6949. NlPrint((NL_MAILSLOT,
  6950. "Sent '%s' message to %ws[%s] on all transports.\n",
  6951. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)(Context->AlternatePingMessage))->Opcode),
  6952. (LPWSTR) Context->QueriedNetbiosDomainName,
  6953. NlDgrNameType( Context->DcQueryType == NlDcQueryPdc ?
  6954. PrimaryDomainBrowser : // 0x1B name
  6955. DomainName))); // 0x1C name
  6956. #endif // NETLOGONDBG
  6957. Status = NlBrowserSendDatagram(
  6958. Context->SendDatagramContext,
  6959. (Context->QueriedFlags & DS_IP_REQUIRED ) ? ALL_IP_TRANSPORTS : 0,
  6960. (LPWSTR) Context->QueriedNetbiosDomainName,
  6961. Context->DcQueryType == NlDcQueryPdc ?
  6962. PrimaryDomainBrowser : // 0x1B name
  6963. DomainName, // 0x1C name
  6964. NULL, // All transports
  6965. NETLOGON_LM_MAILSLOT_A,
  6966. Context->AlternatePingMessage,
  6967. Context->AlternatePingMessageSize,
  6968. Context->DcQueryType == NlDcQueryPdc ?
  6969. &Flush1bName :
  6970. &Flush1cName );
  6971. if ( !NT_SUCCESS(Status)) {
  6972. NetStatus = NetpNtStatusToApiStatus( Status );
  6973. NlPrint(( NL_CRITICAL,
  6974. "NetpDcGetNameNetbios: %ws: Cannot NlBrowserSendDatagram. (ALT) %ld\n",
  6975. Context->QueriedDisplayDomainName,
  6976. NetStatus ));
  6977. if ( NlDcUseGenericStatus(NetStatus) ) {
  6978. NetStatus = ERROR_NO_SUCH_DOMAIN;
  6979. }
  6980. goto Cleanup;
  6981. }
  6982. //
  6983. // Get the response from the ping.
  6984. //
  6985. NetStatus = NetpDcGetPingResponse(
  6986. Context,
  6987. NL_DC_MIN_PING_TIMEOUT,
  6988. NlDcCacheEntry,
  6989. UsedNetbios );
  6990. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  6991. if ( NetStatus != NO_ERROR ) {
  6992. NlPrint(( NL_CRITICAL,
  6993. "NetpDcGetNameNetbios: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  6994. Context->QueriedDisplayDomainName,
  6995. NetStatus ));
  6996. }
  6997. goto Cleanup;
  6998. }
  6999. }
  7000. //
  7001. // If this is a PDC query,
  7002. // Broadcast to DomainName[1B] unique name
  7003. // registered only by the PDC. (Currently, only NT 3.5 (and newer) PDCs register
  7004. // this name and accept incoming mailslot messages on the name.)
  7005. //
  7006. if ( Context->DcQueryType == NlDcQueryPdc ||
  7007. (Context->QueriedFlags & DS_WRITABLE_REQUIRED) != 0 ) {
  7008. #if NETLOGONDBG
  7009. NlPrint((NL_MAILSLOT,
  7010. "Sent '%s' message to %ws[%s] on all transports.\n",
  7011. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)(Context->PingMessage))->Opcode),
  7012. (LPWSTR) Context->QueriedNetbiosDomainName,
  7013. NlDgrNameType(PrimaryDomainBrowser))); // 0x1B name
  7014. #endif // NETLOGONDBG
  7015. Status = NlBrowserSendDatagram(
  7016. Context->SendDatagramContext,
  7017. (Context->QueriedFlags & DS_IP_REQUIRED ) ? ALL_IP_TRANSPORTS : 0,
  7018. (LPWSTR) Context->QueriedNetbiosDomainName,
  7019. PrimaryDomainBrowser, // 0x1B name
  7020. NULL, // All transports
  7021. NETLOGON_LM_MAILSLOT_A,
  7022. Context->PingMessage,
  7023. Context->PingMessageSize,
  7024. &Flush1bName );
  7025. if ( !NT_SUCCESS(Status)) {
  7026. NetStatus = NetpNtStatusToApiStatus( Status );
  7027. NlPrint(( NL_CRITICAL,
  7028. "NetpDcGetNameNetbios: %ws: Cannot NlBrowserSendDatagram. (1B) %ld\n",
  7029. Context->QueriedDisplayDomainName,
  7030. NetStatus ));
  7031. if ( NlDcUseGenericStatus(NetStatus) ) {
  7032. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7033. }
  7034. goto Cleanup;
  7035. }
  7036. //
  7037. // Get the response from the ping.
  7038. //
  7039. NetStatus = NetpDcGetPingResponse(
  7040. Context,
  7041. NL_DC_MIN_PING_TIMEOUT,
  7042. NlDcCacheEntry,
  7043. UsedNetbios );
  7044. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  7045. if ( NetStatus != NO_ERROR ) {
  7046. NlPrint(( NL_CRITICAL,
  7047. "NetpDcGetNameNetbios: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  7048. Context->QueriedDisplayDomainName,
  7049. NetStatus ));
  7050. }
  7051. goto Cleanup;
  7052. }
  7053. }
  7054. //
  7055. // If this is the second or third iteration,
  7056. // or if this isn't a PDC query,
  7057. // broadcast to DomainName[1C] groupname
  7058. // registered only by DCs. (Currently, only NT DCs register
  7059. // this name.)
  7060. //
  7061. // If this is a request for a writable DC,
  7062. // this request is the datagram send of the "primary query" message to DomainName[1C].
  7063. //
  7064. if ( Context->TryCount != 0 ||
  7065. (Context->DcQueryType != NlDcQueryPdc &&
  7066. (Context->QueriedFlags & DS_WRITABLE_REQUIRED) == 0 )) {
  7067. #if NETLOGONDBG
  7068. NlPrint((NL_MAILSLOT,
  7069. "Sent '%s' message to %ws[%s] on all transports.\n",
  7070. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)(Context->PingMessage))->Opcode),
  7071. (LPWSTR) Context->QueriedNetbiosDomainName,
  7072. NlDgrNameType(DomainName))); // 0x1C name
  7073. #endif // NETLOGONDBG
  7074. Status = NlBrowserSendDatagram(
  7075. Context->SendDatagramContext,
  7076. (Context->QueriedFlags & DS_IP_REQUIRED ) ? ALL_IP_TRANSPORTS : 0,
  7077. (LPWSTR) Context->QueriedNetbiosDomainName,
  7078. DomainName, // 0x1C name
  7079. NULL, // All transports
  7080. NETLOGON_LM_MAILSLOT_A,
  7081. Context->PingMessage,
  7082. Context->PingMessageSize,
  7083. &Flush1cName );
  7084. if ( !NT_SUCCESS(Status)) {
  7085. NetStatus = NetpNtStatusToApiStatus( Status );
  7086. NlPrint(( NL_CRITICAL,
  7087. "NetpDcGetNameNetbios: %ws: Cannot NlBrowserSendDatagram. (1C) %ld\n",
  7088. Context->QueriedDisplayDomainName,
  7089. NetStatus ));
  7090. if ( NlDcUseGenericStatus(NetStatus) ) {
  7091. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7092. }
  7093. goto Cleanup;
  7094. }
  7095. //
  7096. // Get the response from the ping.
  7097. //
  7098. NetStatus = NetpDcGetPingResponse(
  7099. Context,
  7100. NL_DC_MIN_PING_TIMEOUT,
  7101. NlDcCacheEntry,
  7102. UsedNetbios );
  7103. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  7104. if ( NetStatus != NO_ERROR ) {
  7105. NlPrint(( NL_CRITICAL,
  7106. "NetpDcGetNameNetbios: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  7107. Context->QueriedDisplayDomainName,
  7108. NetStatus ));
  7109. }
  7110. goto Cleanup;
  7111. }
  7112. }
  7113. NetStatus = ERROR_SEM_TIMEOUT;
  7114. Cleanup:
  7115. return NetStatus;
  7116. }
  7117. DWORD
  7118. NetpDcInitializeContext(
  7119. IN PVOID SendDatagramContext OPTIONAL,
  7120. IN LPCWSTR ComputerName OPTIONAL,
  7121. IN LPCWSTR AccountName OPTIONAL,
  7122. IN ULONG AllowableAccountControlBits,
  7123. IN LPCWSTR NetbiosDomainName OPTIONAL,
  7124. IN LPCWSTR DnsDomainName OPTIONAL,
  7125. IN LPCWSTR DnsForestName OPTIONAL,
  7126. IN PSID RequestedDomainSid OPTIONAL,
  7127. IN GUID *DomainGuid OPTIONAL,
  7128. IN LPCWSTR SiteName OPTIONAL,
  7129. IN LPCWSTR DcNameToPing OPTIONAL,
  7130. IN PSOCKET_ADDRESS DcSocketAddressList OPTIONAL,
  7131. IN ULONG DcSocketAddressCount,
  7132. IN ULONG Flags,
  7133. IN ULONG InternalFlags,
  7134. IN ULONG InitializationType,
  7135. IN OUT PNL_GETDC_CONTEXT Context
  7136. )
  7137. /*++
  7138. Routine Description:
  7139. This routine initializes the Context data struct describing the GetDc operation.
  7140. Arguments:
  7141. SendDatagramContext - Specifies context to pass a NlBrowserSendDatagram
  7142. ComputerName - Specifies the NETBIOS name of this computer.
  7143. If NULL, the name will be dynamically determined.
  7144. AccountName - Account name to pass on the ping request.
  7145. If NULL, no account name will be sent.
  7146. AllowableAccountControlBits - Mask of allowable account types for AccountName.
  7147. Valid bits are those specified by USER_MACHINE_ACCOUNT_MASK.
  7148. Invalid bits are ignored. If more than one bit is specified, the
  7149. account can be of any of the specified types.
  7150. NetbiosDomainName - The Netbios name of the domain to query.
  7151. (e.g., microsoft). Either NetbiosDomainName or DnsDomainName or both
  7152. must be specified.
  7153. DnsDomainName - The DNS-style name of the domain to query.
  7154. (e.g., microsoft.com)
  7155. DnsForestName - The DNS-style name of the tree the queried domain is in.
  7156. RequestedDomainSid - Sid of the domain the message is destined to.
  7157. If NULL, no domain sid will be sent in the ping request.
  7158. DomainGuid - Specifies the Domain GUID of the domain being queried.
  7159. This value is used to handle the case of domain renames. If this
  7160. value is specified and DomainName has been renamed, DsGetDcName will
  7161. attempt to locate a DC in the domain having this specified DomainGuid.
  7162. SiteName - Specifies the site name of the site the returned DC should be
  7163. "close" to. The parameter should typically be the site name of the
  7164. site the client is in. If not specified, the site name defaults to
  7165. the site of ComputerName.
  7166. DcNameToPing - The name of the DC to ping. If set, Context is the
  7167. ping context, not a discovery one.
  7168. DcSocketAddressList - A list of socket addresses to ping. Ignored if only
  7169. Context flags need to be initialized.
  7170. DcSocketAddressCount - The number of socket addresses in DcSocketAddressList.
  7171. Ignored if only Context flags need to be initialized.
  7172. Flags - Passes additional information to be used to process the request.
  7173. Flags can be a combination values bitwise or'ed together.
  7174. InternalFlags - Internal Flags used to pass additional information
  7175. DoFlagInitialization - TRUE if only Contex flags need to be initialize.
  7176. If FALSE, this must be the ping part initialization only.
  7177. Context - the Context data struct describing the GetDc operation.
  7178. Return Value:
  7179. NO_ERROR: The initialization was successful
  7180. ERROR_INVALID_FLAGS - The flags parameter has conflicting or superfluous
  7181. bits set.
  7182. ERROR_INVALID_PARAMETER - One of the parameters is invalid.
  7183. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  7184. this request.
  7185. Various Winsock errors.
  7186. --*/
  7187. {
  7188. NET_API_STATUS NetStatus = NO_ERROR;
  7189. LPWSTR LocalComputerName = NULL;
  7190. CHAR ResponseMailslotName[MAX_PATH+1];
  7191. ULONG ExtraVersionBits = 0;
  7192. PNL_DC_ADDRESS DcAddress = NULL;
  7193. //
  7194. // Do flag initialization
  7195. //
  7196. if ( InitializationType & NL_GETDC_CONTEXT_INITIALIZE_FLAGS ) {
  7197. //
  7198. // Treat zero length domain name as NULL.
  7199. //
  7200. if ( DnsDomainName != NULL && *DnsDomainName == L'\0' ) {
  7201. DnsDomainName = NULL;
  7202. }
  7203. if ( DnsForestName != NULL && *DnsForestName == L'\0' ) {
  7204. DnsForestName = NULL;
  7205. }
  7206. if ( NetbiosDomainName != NULL && *NetbiosDomainName == L'\0' ) {
  7207. NetbiosDomainName = NULL;
  7208. }
  7209. if ( SiteName != NULL && *SiteName == L'\0' ) {
  7210. SiteName = NULL;
  7211. }
  7212. if ( DcNameToPing != NULL && *DcNameToPing == L'\0' ) {
  7213. DcNameToPing = NULL;
  7214. }
  7215. //
  7216. // Initialization
  7217. //
  7218. RtlZeroMemory( Context, sizeof(*Context) );
  7219. Context->FreeOurNetbiosComputerName = FALSE;
  7220. Context->QueriedAccountName = AccountName;
  7221. Context->QueriedAllowableAccountControlBits = AllowableAccountControlBits;
  7222. Context->QueriedNetbiosDomainName = NetbiosDomainName;
  7223. Context->QueriedDnsDomainName = DnsDomainName;
  7224. Context->QueriedDnsForestName = DnsForestName;
  7225. Context->QueriedDcName = DcNameToPing;
  7226. if ( DnsDomainName != NULL ) {
  7227. Context->QueriedDisplayDomainName = DnsDomainName;
  7228. } else {
  7229. Context->QueriedDisplayDomainName = NetbiosDomainName;
  7230. }
  7231. Context->QueriedDomainGuid = DomainGuid;
  7232. Context->QueriedFlags = Flags;
  7233. Context->QueriedInternalFlags = InternalFlags;
  7234. Context->SendDatagramContext = SendDatagramContext;
  7235. Context->ImperfectCacheEntry = NULL;
  7236. InitializeListHead( &Context->DcAddressList );
  7237. Context->DcAddressCount = 0;
  7238. Context->QueriedSiteName = SiteName;
  7239. Context->DoingExplicitSite = Context->QueriedSiteName != NULL &&
  7240. (Context->QueriedInternalFlags & DS_SITENAME_DEFAULTED) == 0;
  7241. Context->ResponseBuffer = NULL;
  7242. Context->ResponseMailslotHandle = NULL;
  7243. //
  7244. // Don't pass confusing bits
  7245. //
  7246. if ( Context->QueriedAccountName == NULL ) {
  7247. Context->QueriedAllowableAccountControlBits = 0;
  7248. }
  7249. //
  7250. // Validate the passed in flags
  7251. //
  7252. if ( (Context->QueriedFlags & ~DSGETDC_VALID_FLAGS) != 0 ) {
  7253. NlPrint(( NL_CRITICAL,
  7254. "NetpDcInitializeContext: %ws: invalid flags %lx\n",
  7255. Context->QueriedDisplayDomainName,
  7256. Flags ));
  7257. NetStatus = ERROR_INVALID_FLAGS;
  7258. goto Cleanup;
  7259. }
  7260. if ( Context->QueriedFlags & DS_GC_SERVER_REQUIRED ) {
  7261. //
  7262. // The DC ignores pings with superfluous info.
  7263. // So catch the caller here.
  7264. //
  7265. if ( Context->QueriedAccountName != NULL ||
  7266. Context->QueriedAllowableAccountControlBits != 0 ||
  7267. RequestedDomainSid != NULL ) {
  7268. NlPrint(( NL_CRITICAL,
  7269. "NetpDcInitializeContext: %ws: GC queried and invalid parameters specified.\n",
  7270. Context->QueriedDisplayDomainName ));
  7271. NetStatus = ERROR_INVALID_PARAMETER;
  7272. goto Cleanup;
  7273. }
  7274. }
  7275. //
  7276. // The Only LDAP bit is mutually exclusive with almost everything.
  7277. //
  7278. // Don't error out. Rather, treat the only LDAP bit as an advisory
  7279. // that these other bits should affect the decision of which DC to find.
  7280. //
  7281. if ( Context->QueriedFlags & DS_ONLY_LDAP_NEEDED ) {
  7282. Context->QueriedFlags &= ~(
  7283. DS_DIRECTORY_SERVICE_REQUIRED |
  7284. DS_DIRECTORY_SERVICE_PREFERRED |
  7285. DS_TIMESERV_REQUIRED |
  7286. DS_GOOD_TIMESERV_PREFERRED |
  7287. DS_PDC_REQUIRED |
  7288. DS_KDC_REQUIRED );
  7289. }
  7290. //
  7291. // Convert the flags to the type of DNS name to lookup.
  7292. //
  7293. NetStatus = NetpDcFlagsToNameType( Context->QueriedFlags, &Context->QueriedNlDnsNameType );
  7294. if ( NetStatus != NO_ERROR ) {
  7295. NlPrint(( NL_CRITICAL,
  7296. "NetpDcInitializeContext: %ws: cannot convert flags to nametype %ld\n",
  7297. Context->QueriedDisplayDomainName,
  7298. NetStatus ));
  7299. goto Cleanup;
  7300. }
  7301. Context->DcQueryType = NlDcDnsNameTypeDesc[Context->QueriedNlDnsNameType].DcQueryType;
  7302. //
  7303. // The Good Time Service preferred bit is mutually exclusive with almost everything.
  7304. //
  7305. if ( Context->QueriedFlags & DS_GOOD_TIMESERV_PREFERRED ) {
  7306. if ( Context->QueriedFlags & (
  7307. DS_DIRECTORY_SERVICE_REQUIRED |
  7308. DS_DIRECTORY_SERVICE_PREFERRED |
  7309. DS_GC_SERVER_REQUIRED |
  7310. DS_PDC_REQUIRED |
  7311. DS_KDC_REQUIRED |
  7312. DS_WRITABLE_REQUIRED )) {
  7313. NlPrint(( NL_CRITICAL,
  7314. "NetpDcInitializeContext: %ws: flags not compatible with 'Good Time' %lx\n",
  7315. Context->QueriedDisplayDomainName,
  7316. Flags ));
  7317. NetStatus = ERROR_INVALID_FLAGS;
  7318. goto Cleanup;
  7319. }
  7320. }
  7321. //
  7322. // If the caller needs the PDC,
  7323. // ditch the DS preferred flag (there is only one PDC),
  7324. // ditch the writable flag (the PDC is always writable)
  7325. //
  7326. if ( (Context->QueriedFlags & DS_PDC_REQUIRED ) != 0 ) {
  7327. Context->QueriedFlags &= ~(DS_DIRECTORY_SERVICE_PREFERRED|DS_WRITABLE_REQUIRED);
  7328. }
  7329. //
  7330. // If the caller says that an NT 5.0 DC is both preferred and required,
  7331. // ditch the preferred bit.
  7332. //
  7333. if ( (Context->QueriedFlags & DS_NT50_REQUIRED ) != 0 &&
  7334. (Context->QueriedFlags & DS_DIRECTORY_SERVICE_PREFERRED) != 0 ) {
  7335. Context->QueriedFlags &= ~DS_DIRECTORY_SERVICE_PREFERRED;
  7336. }
  7337. //
  7338. // Ensure we have a computername.
  7339. //
  7340. if ( ComputerName == NULL ) {
  7341. #ifndef WIN32_CHICAGO
  7342. //
  7343. // On a cluster, use the physical netbios name since this name is
  7344. // used to receive returned mailslot packets.
  7345. //
  7346. NetStatus = NetpGetComputerNameEx ( &LocalComputerName, TRUE );
  7347. #else
  7348. NetStatus = NetpGetComputerName ( &LocalComputerName);
  7349. #endif // WIN32_CHICAGO
  7350. if ( NetStatus != NO_ERROR ) {
  7351. goto Cleanup;
  7352. }
  7353. ComputerName = LocalComputerName;
  7354. Context->FreeOurNetbiosComputerName = TRUE;
  7355. }
  7356. Context->OurNetbiosComputerName = ComputerName;
  7357. //
  7358. // Get the domain entry describing this domain.
  7359. //
  7360. Context->NlDcDomainEntry = NetpDcCreateDomainEntry(
  7361. Context->QueriedDomainGuid,
  7362. Context->QueriedNetbiosDomainName,
  7363. Context->QueriedDnsDomainName );
  7364. if ( Context->NlDcDomainEntry == NULL ) {
  7365. NlPrint(( NL_CRITICAL,
  7366. "NetpDcInitializeContext: not enough memory for DomainEntry.\n" ));
  7367. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  7368. goto Cleanup;
  7369. }
  7370. }
  7371. //
  7372. // Do the ping initialization part
  7373. //
  7374. if ( InitializationType & NL_GETDC_CONTEXT_INITIALIZE_PING ) {
  7375. if ( Context->QueriedFlags & DS_GC_SERVER_REQUIRED ) {
  7376. ExtraVersionBits |= NETLOGON_NT_VERSION_GC;
  7377. }
  7378. if ( Context->QueriedFlags & DS_IP_REQUIRED ) {
  7379. ExtraVersionBits |= NETLOGON_NT_VERSION_IP;
  7380. }
  7381. if ( Context->QueriedFlags & DS_PDC_REQUIRED ) {
  7382. ExtraVersionBits |= NETLOGON_NT_VERSION_PDC;
  7383. }
  7384. //
  7385. // See if we are to neutralize NT4 emulation
  7386. //
  7387. #ifdef _NETLOGON_SERVER
  7388. //
  7389. // In netlogon, the boolean is kept in global parameters
  7390. //
  7391. if ( NlGlobalParameters.NeutralizeNt4Emulator ) {
  7392. ExtraVersionBits |= NETLOGON_NT_VERSION_AVOID_NT4EMUL;
  7393. }
  7394. #else
  7395. //
  7396. // If we are not running in netlogon, we need to read
  7397. // the boolean directly from the registry
  7398. //
  7399. {
  7400. DWORD LocalNeutralizeNt4Emulator = 0;
  7401. NT_PRODUCT_TYPE NtProductType;
  7402. if ( !RtlGetNtProductType( &NtProductType ) ) {
  7403. NtProductType = NtProductWinNt;
  7404. }
  7405. //
  7406. // On DC, we always neutrilize NT4 emulation
  7407. //
  7408. if ( NtProductType == NtProductLanManNt ) {
  7409. LocalNeutralizeNt4Emulator = 1;
  7410. //
  7411. // On wksta, read the registry
  7412. //
  7413. } else {
  7414. NlReadDwordNetlogonRegValue( "NeutralizeNt4Emulator",
  7415. &LocalNeutralizeNt4Emulator );
  7416. }
  7417. if ( LocalNeutralizeNt4Emulator ) {
  7418. ExtraVersionBits |= NETLOGON_NT_VERSION_AVOID_NT4EMUL;
  7419. }
  7420. }
  7421. #endif // _NETLOGON_SERVER
  7422. //
  7423. // If we're querying by netbios name,
  7424. // initialize for doing the query.
  7425. //
  7426. if ( Context->QueriedNetbiosDomainName != NULL ) {
  7427. //
  7428. // Allocate the response buffer
  7429. //
  7430. // (This buffer could be allocated on the stack ofNetpDcGetPingResponse()
  7431. // except the buffer is large and we want to avoid stack overflows.)
  7432. // (DWORD align it.)
  7433. //
  7434. Context->ResponseBuffer = LocalAlloc( 0,
  7435. ( MAX_RANDOM_MAILSLOT_RESPONSE/sizeof(DWORD) ) * sizeof(DWORD)
  7436. );
  7437. if ( Context->ResponseBuffer == NULL ) {
  7438. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  7439. goto Cleanup;
  7440. }
  7441. Context->ResponseBufferSize =
  7442. ( MAX_RANDOM_MAILSLOT_RESPONSE/sizeof(DWORD) ) * sizeof(DWORD);
  7443. //
  7444. // Open a mailslot to get ping responses on.
  7445. //
  7446. //
  7447. // We need to "Randomize" the mailslot name so that this api can have
  7448. // more than one invocation at a time. If we don't, the fact that
  7449. // mailslots must have unique names will prevent the second invocation
  7450. // of this api from functioning until the first has ended and deleted
  7451. // the mailslot. NetpLogonCreateRandomMailslot does this for us
  7452. // and creates the mailslot in the process.
  7453. //
  7454. NetStatus = NetpLogonCreateRandomMailslot( ResponseMailslotName,
  7455. &Context->ResponseMailslotHandle );
  7456. if (NetStatus != NO_ERROR ) {
  7457. NlPrint(( NL_CRITICAL,
  7458. "NetpDcInitializeContext: %ws: cannot create temp mailslot %ld\n",
  7459. Context->QueriedDisplayDomainName,
  7460. NetStatus));
  7461. goto Cleanup;
  7462. }
  7463. //
  7464. // Build the ping message.
  7465. //
  7466. // If the account name is specified, don't generate a "primary query"
  7467. // message since it doesn't have an account name in it.
  7468. //
  7469. NetStatus = NetpDcBuildPing(
  7470. (Context->DcQueryType == NlDcQueryPdc ||
  7471. (Context->QueriedFlags & DS_WRITABLE_REQUIRED) != 0),
  7472. 0, // RequestCount,
  7473. Context->OurNetbiosComputerName, // Netbios name of this computer
  7474. Context->QueriedAccountName,
  7475. ResponseMailslotName,
  7476. Context->QueriedAllowableAccountControlBits,
  7477. RequestedDomainSid,
  7478. // We really need the IP address, so don't ask for simple 5EX version
  7479. NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX_WITH_IP|ExtraVersionBits,
  7480. &Context->PingMessage,
  7481. &Context->PingMessageSize );
  7482. if (NetStatus != NO_ERROR ) {
  7483. NlPrint(( NL_CRITICAL,
  7484. "NetpDcInitializeContext: %ws: cannot build ping message %ld\n",
  7485. Context->QueriedDisplayDomainName,
  7486. NetStatus));
  7487. goto Cleanup;
  7488. }
  7489. //
  7490. // Build the alternate ping message if we do a DC discovery.
  7491. //
  7492. // For writable DCs and PDCs, the ping message built above is the "primary query" message
  7493. // (used to find a PDC in pre-NT 5.0 domains) and the message build below is the
  7494. // "logon user" message.
  7495. //
  7496. // If the account name is specified by the original caller,
  7497. // the "logon user" message allows us to prefer DCs that have the account.
  7498. //
  7499. // If a writable DC is requested,
  7500. // sending this message to NT 5 DCs allows us to return ANY NT 5 DC.
  7501. //
  7502. if ( ((Context->QueriedInternalFlags & DS_DOING_DC_DISCOVERY) != 0) &&
  7503. (((Context->QueriedFlags & DS_WRITABLE_REQUIRED) != 0) ||
  7504. (Context->QueriedAccountName != NULL && Context->DcQueryType == NlDcQueryPdc)) ) {
  7505. NetStatus = NetpDcBuildPing(
  7506. FALSE,
  7507. 0, // RequestCount,
  7508. Context->OurNetbiosComputerName, // Netbios name of this computer
  7509. Context->QueriedAccountName,
  7510. ResponseMailslotName,
  7511. Context->QueriedAllowableAccountControlBits,
  7512. RequestedDomainSid,
  7513. // We really need the IP address, so don't ask for simple 5EX version
  7514. NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX_WITH_IP|ExtraVersionBits,
  7515. &Context->AlternatePingMessage,
  7516. &Context->AlternatePingMessageSize );
  7517. if (NetStatus != NO_ERROR ) {
  7518. NlPrint(( NL_CRITICAL,
  7519. "NetpDcInitializeContext: %ws: cannot build alternate ping message %ld\n",
  7520. Context->QueriedDisplayDomainName,
  7521. NetStatus));
  7522. goto Cleanup;
  7523. }
  7524. }
  7525. }
  7526. //
  7527. // Build the LDAP filter.
  7528. //
  7529. NetStatus = NetpDcBuildLdapFilter(
  7530. Context->OurNetbiosComputerName, // Netbios name of this computer
  7531. Context->QueriedAccountName,
  7532. Context->QueriedAllowableAccountControlBits,
  7533. RequestedDomainSid,
  7534. Context->QueriedDnsDomainName,
  7535. Context->QueriedDomainGuid,
  7536. // Don't ask for 5EX_WITH_IP version since the server doesn't know the right IP address over LDAP
  7537. NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX|ExtraVersionBits,
  7538. &Context->LdapFilter );
  7539. if (NetStatus != NO_ERROR ) {
  7540. NlPrint(( NL_CRITICAL,
  7541. "NetpDcInitializeContext: %ws: cannot build ldap filter %ld\n",
  7542. Context->QueriedDisplayDomainName,
  7543. NetStatus));
  7544. goto Cleanup;
  7545. }
  7546. //
  7547. // Add the socket address to the address list.
  7548. //
  7549. if ( DcSocketAddressCount > 0 ) {
  7550. NetStatus = NetpDcProcessAddressList( Context,
  7551. (LPWSTR) DcNameToPing,
  7552. DcSocketAddressList,
  7553. DcSocketAddressCount,
  7554. FALSE, // Don't know if site specific
  7555. NULL );
  7556. if ( NetStatus != NO_ERROR ) {
  7557. goto Cleanup;
  7558. }
  7559. }
  7560. }
  7561. Cleanup:
  7562. return NetStatus;
  7563. }
  7564. VOID
  7565. NetpDcUninitializeContext(
  7566. IN OUT PNL_GETDC_CONTEXT Context
  7567. )
  7568. /*++
  7569. Routine Description:
  7570. This routine cleans up the Context data struct describing the GetDc operation.
  7571. Arguments:
  7572. Context - the Context data struct describing the GetDc operation.
  7573. Return Value:
  7574. None.
  7575. --*/
  7576. {
  7577. if ( Context->FreeOurNetbiosComputerName && Context->OurNetbiosComputerName != NULL ) {
  7578. NetApiBufferFree((LPWSTR) Context->OurNetbiosComputerName);
  7579. }
  7580. if ( Context->ResponseBuffer != NULL ) {
  7581. LocalFree( Context->ResponseBuffer );
  7582. }
  7583. if ( Context->ResponseMailslotHandle != NULL ) {
  7584. CloseHandle(Context->ResponseMailslotHandle);
  7585. }
  7586. if ( Context->PingMessage != NULL ) {
  7587. NetpMemoryFree( Context->PingMessage );
  7588. }
  7589. if ( Context->AlternatePingMessage != NULL ) {
  7590. NetpMemoryFree( Context->AlternatePingMessage );
  7591. }
  7592. if ( Context->LdapFilter != NULL ) {
  7593. NetpMemoryFree( Context->LdapFilter );
  7594. }
  7595. if ( Context->NlDcDomainEntry != NULL ) {
  7596. NetpDcDerefDomainEntry( Context->NlDcDomainEntry );
  7597. }
  7598. if ( Context->ImperfectCacheEntry != NULL ) {
  7599. NetpDcDerefCacheEntry( Context->ImperfectCacheEntry );
  7600. }
  7601. NetpDcFreeAddressList( Context );
  7602. }
  7603. NET_API_STATUS
  7604. NlPingDcNameWithContext (
  7605. IN PNL_GETDC_CONTEXT Context,
  7606. IN ULONG NumberOfPings,
  7607. IN BOOLEAN WaitForResponse,
  7608. IN ULONG Timeout,
  7609. OUT PBOOL UsedNetbios OPTIONAL,
  7610. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry OPTIONAL
  7611. )
  7612. /*++
  7613. Routine Description:
  7614. Ping the specified DC using the appropriate ping mechanism
  7615. and optionally wait for the ping responses. Several pings
  7616. will be attempted up to the specified limit.
  7617. Arguments:
  7618. Context - Desribes the DC to ping.
  7619. NumberOfPings - Total number of pings to send.
  7620. WaitForResponse -
  7621. If TRUE, this API will send up to NumberOfPings pings and wait for a
  7622. response from the DC. The API will return the success code depending
  7623. on whether or not the DC responds successfully.
  7624. If FALSE, pings will be sent and no responses will be collected.
  7625. The API will return the success code depending on whether or not
  7626. all of the requeted pings were successfully sent.
  7627. Timeout - Total ammount of time in milliseconds to wait for ping responses.
  7628. Ignored if WaitForResponse is FALSE.
  7629. UsedNetbios - Returns TRUE if the netbios domain name was used to match
  7630. the returned cache entry. Ignored if WaitForResponse is FALSE.
  7631. NlDcCacheEntry - Returns the data structure describing response received
  7632. from the DC. Should be freed by calling NetpMemoryFree. Ignored if
  7633. WaitForResponse is FALSE.
  7634. Return Value:
  7635. NO_ERROR - Success.
  7636. ERROR_NO_LOGON_SERVERS - No DC could be found
  7637. ERROR_NO_SUCH_USER - The DC doesn't have the user account specified in the
  7638. ping Context.
  7639. ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
  7640. domain controller of the specified domain.
  7641. ERROR_SERVICE_NOT_ACTIVE - The netlogon service is paused on the server.
  7642. --*/
  7643. {
  7644. NTSTATUS Status;
  7645. NET_API_STATUS NetStatus;
  7646. ULONG RetryCount;
  7647. PNL_DC_CACHE_ENTRY NlLocalDcCacheEntry = NULL;
  7648. BOOL LocalUsedNetbios;
  7649. ULONG IpPingCount;
  7650. ULONG TotalPingsSent = 0;
  7651. //
  7652. // If we have no mechanism to send the pings, error out.
  7653. //
  7654. if ( (Context->QueriedInternalFlags &
  7655. (DS_PING_USING_LDAP | DS_PING_USING_MAILSLOT)) == 0 ) {
  7656. return ERROR_NO_LOGON_SERVERS;
  7657. }
  7658. //
  7659. // Ping repeatedely the DC
  7660. //
  7661. for ( RetryCount=0; RetryCount<NumberOfPings; RetryCount++ ) {
  7662. //
  7663. // Send the ldap ping
  7664. //
  7665. if ( Context->QueriedInternalFlags & DS_PING_USING_LDAP ) {
  7666. NetStatus = NetpDcPingIp( Context, &IpPingCount );
  7667. //
  7668. // If we cannot send any ldap ping, do not error out. Rather, indicate
  7669. // to avoid the ldap mechanism and try the mailslot one only.
  7670. //
  7671. if ( NetStatus != NO_ERROR || IpPingCount == 0 ) {
  7672. NlPrint((NL_CRITICAL,
  7673. "NlPingDcNameWithContext: cannot send %ld ldap pings: PingsSent = %ld, Error = 0x%lx\n",
  7674. Context->DcAddressCount,
  7675. IpPingCount,
  7676. NetStatus ));
  7677. Context->QueriedInternalFlags &= ~DS_PING_USING_LDAP;
  7678. } else {
  7679. TotalPingsSent += IpPingCount;
  7680. NlPrint((NL_MISC,
  7681. "NlPingDcNameWithContext: Sent %ld/%ld ldap pings to %ws\n",
  7682. IpPingCount,
  7683. Context->DcAddressCount,
  7684. Context->QueriedDcName ));
  7685. }
  7686. }
  7687. //
  7688. // Send the mailslot ping
  7689. //
  7690. if ( Context->QueriedInternalFlags & DS_PING_USING_MAILSLOT ) {
  7691. #if NETLOGONDBG
  7692. NlPrint((NL_MAILSLOT,
  7693. "NlPingDcNameWithContext: Sent '%s' message to %ws[%s] on %ws.\n",
  7694. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)(Context->PingMessage))->Opcode),
  7695. Context->QueriedDcName,
  7696. NlDgrNameType(ComputerName),
  7697. NULL ));
  7698. #endif // NETLOGONDBG
  7699. // Skip over \\ in unc server name
  7700. Status = NlBrowserSendDatagram(
  7701. Context->SendDatagramContext,
  7702. (Context->QueriedFlags & DS_IP_REQUIRED ) ? ALL_IP_TRANSPORTS : 0,
  7703. (LPWSTR) Context->QueriedDcName,
  7704. ComputerName,
  7705. NULL, // All transports
  7706. NETLOGON_LM_MAILSLOT_A,
  7707. Context->PingMessage,
  7708. Context->PingMessageSize,
  7709. NULL ); // Don't flush Netbios cache
  7710. //
  7711. // If we cannot write the maislot, do not error out. Rather, indicate
  7712. // to avoid the mailslot mechanism and try the ldap one only.
  7713. //
  7714. if ( !NT_SUCCESS(Status) ) {
  7715. NlPrint((NL_CRITICAL,
  7716. "NlPingDcNameWithContext: cannot write netlogon mailslot: 0x%lx\n",
  7717. Status));
  7718. Context->QueriedInternalFlags &= ~DS_PING_USING_MAILSLOT;
  7719. } else {
  7720. TotalPingsSent ++;
  7721. }
  7722. }
  7723. //
  7724. // If we didn't send any ping, error out. Otherwise, try to get a
  7725. // response. It is possible that we will not do any more pings
  7726. // if either of the ping mechanisms is to be avoided, but we want
  7727. // to give all of the time left to those pings which have been sent.
  7728. //
  7729. if ( TotalPingsSent == 0 ) {
  7730. NetStatus = ERROR_NO_LOGON_SERVERS;
  7731. goto Cleanup;
  7732. }
  7733. //
  7734. // Get the response from the ping.
  7735. //
  7736. if ( WaitForResponse ) {
  7737. if ( NlLocalDcCacheEntry != NULL ) {
  7738. NetpMemoryFree( NlLocalDcCacheEntry );
  7739. NlLocalDcCacheEntry = NULL;
  7740. }
  7741. NetStatus = NetpDcGetPingResponse(
  7742. Context,
  7743. Timeout/NumberOfPings,
  7744. &NlLocalDcCacheEntry,
  7745. &LocalUsedNetbios );
  7746. //
  7747. // If no error, we've successfully found the DC.
  7748. //
  7749. if ( NetStatus == NO_ERROR ) {
  7750. if ( NlLocalDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_LDAP ) {
  7751. NlPrint((NL_MISC,
  7752. "NlPingDcNameWithContext: %ws responded over IP.\n",
  7753. Context->QueriedDcName ));
  7754. }
  7755. if ( NlLocalDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_MAILSLOT ) {
  7756. NlPrint((NL_MISC,
  7757. "NlPingDcNameWithContext: %ws responded on a mailslot.\n",
  7758. Context->QueriedDcName ));
  7759. }
  7760. goto Cleanup;
  7761. //
  7762. // If we've timed out, retry
  7763. //
  7764. } else if ( NetStatus == ERROR_SEM_TIMEOUT ) {
  7765. NlPrint((NL_MISC,
  7766. "NlPingDcNameWithContext: Ping response timeout for %ws.\n",
  7767. Context->QueriedDcName ));
  7768. continue;
  7769. //
  7770. // If the DC we've successfully pinged and got response from
  7771. // returns responce info that is in conflict with the requested
  7772. // info, error out.
  7773. //
  7774. } else if ( NetStatus == ERROR_INVALID_DATA ) {
  7775. NlPrint((NL_CRITICAL,
  7776. "NlPingDcNameWithContext: Invalid response returned from %ws.\n",
  7777. Context->QueriedDcName ));
  7778. NetStatus = ERROR_DOMAIN_TRUST_INCONSISTENT;
  7779. goto Cleanup;
  7780. //
  7781. // Tell the caller that the netlogon service is paused
  7782. // on the server.
  7783. //
  7784. } else if ( NetStatus == ERROR_SERVICE_NOT_ACTIVE ) {
  7785. NlPrint((NL_CRITICAL,
  7786. "NlPingDcNameWithContext: Netlogon is paused on %ws.\n",
  7787. Context->QueriedDcName ));
  7788. goto Cleanup;
  7789. //
  7790. // Check if there is no such account
  7791. //
  7792. } else if ( NetStatus == ERROR_NO_SUCH_USER ) {
  7793. NlPrint((NL_CRITICAL,
  7794. "NlPingDcNameWithContext: No such user %ws on %ws.\n",
  7795. Context->QueriedAccountName,
  7796. Context->QueriedDcName ));
  7797. goto Cleanup;
  7798. } else {
  7799. NlPrint((NL_CRITICAL,
  7800. "NlPingDcNameWithContext: Wrong return code from NetpDcGetPingResponse: 0x%lx\n",
  7801. NetStatus));
  7802. NetStatus = ERROR_NO_LOGON_SERVERS;
  7803. goto Cleanup;
  7804. }
  7805. }
  7806. }
  7807. if ( WaitForResponse ) {
  7808. NlPrint(( NL_CRITICAL,
  7809. "NlPingDcNameWithContext: Can't ping the DC %ws.\n", Context->QueriedDcName ));
  7810. NetStatus = ERROR_NO_LOGON_SERVERS;
  7811. } else {
  7812. //
  7813. // If we are requested to only send the pings and
  7814. // we couldn't send all of the requested pings,
  7815. //
  7816. if ( TotalPingsSent < NumberOfPings ) {
  7817. NetStatus = ERROR_NO_LOGON_SERVERS;
  7818. } else {
  7819. NetStatus = NO_ERROR;
  7820. }
  7821. }
  7822. Cleanup:
  7823. //
  7824. // Return the DC info to the caller.
  7825. //
  7826. if ( NlLocalDcCacheEntry != NULL ) {
  7827. if ( NetStatus == NO_ERROR && WaitForResponse && NlDcCacheEntry != NULL ) {
  7828. *NlDcCacheEntry = NlLocalDcCacheEntry;
  7829. } else {
  7830. NetpMemoryFree( NlLocalDcCacheEntry );
  7831. }
  7832. }
  7833. if ( NetStatus == NO_ERROR && WaitForResponse && UsedNetbios != NULL ) {
  7834. *UsedNetbios = LocalUsedNetbios;
  7835. }
  7836. return NetStatus;
  7837. }
  7838. DWORD
  7839. NetpGetGcUsingNetbios(
  7840. IN PNL_GETDC_CONTEXT Context,
  7841. IN DWORD OrigTimeout,
  7842. IN DWORD OrigRetryCount,
  7843. OUT PNL_DC_CACHE_ENTRY *DomainControllerCacheEntry
  7844. )
  7845. /*++
  7846. Routine Description:
  7847. This routine tries to find a GC using a Netbios domain name.
  7848. Arguments:
  7849. Context - Context describing the initial attempt to find a DC.
  7850. DomainControllerCacheEntry -
  7851. Return a pointer to a private PNL_DC_CACHE_ENTRY
  7852. structure describing the domain controller selected. The returned
  7853. structure must be dereferenced using NetpDcDerefCacheEntry.
  7854. Return Value:
  7855. The status code that is to be returned by the caller.
  7856. --*/
  7857. {
  7858. NET_API_STATUS NetStatus;
  7859. ULONG ElapsedTime;
  7860. ULONG TimeToWait;
  7861. ULONG LocalFlags;
  7862. ULONG LocalInternalFlags;
  7863. PDOMAIN_CONTROLLER_INFOW DcDomainControllerInfo = NULL;
  7864. PNL_DC_CACHE_ENTRY GcDomainControllerCacheEntry = NULL;
  7865. NlPrint(( NL_MAILSLOT,
  7866. "%ws: Try to find a GC using netbios domain name.\n",
  7867. Context->QueriedNetbiosDomainName ));
  7868. //
  7869. // Reduce the timeout to be the time we haven't already spent.
  7870. // (But allow a minimum of 2 seconds)
  7871. //
  7872. ElapsedTime = NetpDcElapsedTime( Context->StartTime );
  7873. if ( ElapsedTime < OrigTimeout ) {
  7874. TimeToWait = max((OrigTimeout - ElapsedTime), NL_DC_MIN_ITERATION_TIMEOUT);
  7875. } else {
  7876. TimeToWait = NL_DC_MIN_ITERATION_TIMEOUT;
  7877. }
  7878. #ifdef notdef
  7879. NlPrint((NL_CRITICAL,
  7880. "NetpGetGcUsingNetbios: timeout is %ld %ld %ld %ld\n",
  7881. Context->StartTime,
  7882. ElapsedTime,
  7883. OrigTimeout,
  7884. TimeToWait ));
  7885. #endif // notdef
  7886. //
  7887. // Compute the flags to use to find a DC
  7888. //
  7889. // Only keep the 'force' bit from the ones passed by the caller.
  7890. // Any other bit could only serve to confuse finding a GC.
  7891. //
  7892. LocalFlags = (Context->QueriedFlags & DS_FORCE_REDISCOVERY);
  7893. // Prefer a DS to ensure we get back a forest name if we can.
  7894. LocalFlags |= DS_DIRECTORY_SERVICE_PREFERRED;
  7895. //
  7896. // Compute the internal flags used to find a DC
  7897. //
  7898. // Keep only the internal flags that still apply to this call.
  7899. //
  7900. LocalInternalFlags = (Context->QueriedInternalFlags & DS_IS_PRIMARY_DOMAIN);
  7901. // Tell NetpDcGetName not to cache failures.
  7902. LocalInternalFlags |= DS_DONT_CACHE_FAILURE;
  7903. // Since we're only using the data in the ping response and we're not
  7904. // actually using the returned DC, don't require a close DC.
  7905. LocalInternalFlags |= DS_CLOSE_DC_NOT_NEEDED;
  7906. // Ensure the named domain is really the root domain
  7907. //
  7908. // It wouldn't be fatal to allow this. However, we cannot support it
  7909. // for DNS names. So we don't want folks to stumble upon this working for
  7910. // Netbios domain names.
  7911. //
  7912. // However, if the caller didn't pass the domain name, don't require the
  7913. // root domain. The caller just wants to find a GC and doesn't know the
  7914. // forest name. This will be the case for Win9x clients who passed NULL
  7915. // and we don't know the forest name on Win9x (so we couldn't get the
  7916. // forest name in DsIGetDcName).
  7917. if ( (Context->QueriedInternalFlags & DS_CALLER_PASSED_NULL_DOMAIN) == 0 ) {
  7918. LocalInternalFlags |= DS_REQUIRE_ROOT_DOMAIN;
  7919. }
  7920. //
  7921. // Simply try to find a DC in the named domain.
  7922. //
  7923. // Don't try to find a DC in the named site. Their might not be one.
  7924. // We only know that there'll be a GC in the named site. Luckily,
  7925. // Netbios isn't very site aware in the first place.
  7926. //
  7927. // Even if the DC found happens to be a close GC, don't use it.
  7928. // That'd unfairly load the GCs that happen to be in the root domain.
  7929. // We should spread the load to all the GCs in the site.
  7930. //
  7931. NetStatus = NetpDcGetName(
  7932. Context->SendDatagramContext,
  7933. Context->OurNetbiosComputerName,
  7934. NULL, // No AccountName
  7935. 0, // No AccountControlBits
  7936. Context->QueriedNetbiosDomainName,
  7937. NULL, // We've already shown that the DNS name doesn't work
  7938. NULL, // We don't know the forest name
  7939. NULL, // RequestedDomainSid,
  7940. Context->QueriedDomainGuid,
  7941. NULL, // There might not be a DC in the named site.
  7942. LocalFlags,
  7943. LocalInternalFlags,
  7944. TimeToWait,
  7945. OrigRetryCount == 0 ?
  7946. 1 :
  7947. OrigRetryCount,
  7948. &DcDomainControllerInfo,
  7949. NULL );
  7950. if ( NetStatus != NO_ERROR ) {
  7951. NlPrint(( NL_CRITICAL,
  7952. "%ws: Cannot even find a DC much less a GC.\n",
  7953. Context->QueriedNetbiosDomainName ));
  7954. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7955. goto Cleanup;
  7956. }
  7957. //
  7958. // Make sure we know know the name of the forest.
  7959. //
  7960. if ( DcDomainControllerInfo->DnsForestName == NULL ) {
  7961. NlPrint(( NL_CRITICAL,
  7962. "%ws: DC %ws doesn't know the forest name so can't find a GC.\n",
  7963. Context->QueriedNetbiosDomainName,
  7964. DcDomainControllerInfo->DomainControllerName ));
  7965. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7966. goto Cleanup;
  7967. }
  7968. NlPrint(( NL_MAILSLOT,
  7969. "%ws: DC %ws says the forest name is %ws.\n",
  7970. Context->QueriedNetbiosDomainName,
  7971. DcDomainControllerInfo->DomainControllerName,
  7972. DcDomainControllerInfo->DnsForestName ));
  7973. //
  7974. // Compute the flags used for finding a GC given a forest name
  7975. //
  7976. //
  7977. // If the caller wasn't explicit about the format of the returned name,
  7978. // be consistant with the original request.
  7979. //
  7980. LocalFlags = Context->QueriedFlags;
  7981. if ( (LocalFlags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) == 0 ) {
  7982. //
  7983. // If the caller specified only a Netbios domain name,
  7984. // or if we didn't know whether the name was a DNS or netbios name,
  7985. // then we should return a netbios name to the caller.
  7986. //
  7987. // (In the later case, we can infer that the name is a netbios name
  7988. // since UsedNetbios is true.)
  7989. //
  7990. if ( Context->QueriedDnsDomainName == NULL ||
  7991. (Context->QueriedInternalFlags & DS_NAME_FORMAT_AMBIGUOUS) != 0 ) {
  7992. LocalFlags |= DS_RETURN_FLAT_NAME;
  7993. }
  7994. }
  7995. LocalFlags |= DS_AVOID_SELF; // Already tried self
  7996. //
  7997. // Tell netlogon not to cache this failed attempt.
  7998. // The main routine will do it.
  7999. //
  8000. LocalInternalFlags = Context->QueriedInternalFlags;
  8001. LocalInternalFlags |= DS_DONT_CACHE_FAILURE;
  8002. //
  8003. // Try to find a GC in the returned forest name
  8004. //
  8005. NetStatus = NetpDcGetName(
  8006. Context->SendDatagramContext,
  8007. Context->OurNetbiosComputerName,
  8008. NULL, // No AccountName
  8009. 0, // No AccountControlBits
  8010. NULL, // Do not specify the Netbios name for a GC search
  8011. DcDomainControllerInfo->DnsForestName,
  8012. DcDomainControllerInfo->DnsForestName,
  8013. NULL, // RequestedDomainSid,
  8014. Context->QueriedDomainGuid,
  8015. Context->QueriedSiteName,
  8016. LocalFlags,
  8017. LocalInternalFlags,
  8018. TimeToWait,
  8019. OrigRetryCount == 0 ?
  8020. 1 :
  8021. OrigRetryCount,
  8022. NULL,
  8023. &GcDomainControllerCacheEntry );
  8024. if ( NetStatus != NO_ERROR ) {
  8025. NlPrint(( NL_CRITICAL,
  8026. "%ws: Cannot find a GC in forest %ws.\n",
  8027. Context->QueriedNetbiosDomainName,
  8028. DcDomainControllerInfo->DnsForestName ));
  8029. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8030. goto Cleanup;
  8031. }
  8032. Cleanup:
  8033. //
  8034. // Return the found GC to the caller.
  8035. //
  8036. if ( NetStatus == NO_ERROR ) {
  8037. *DomainControllerCacheEntry = GcDomainControllerCacheEntry;
  8038. } else if ( GcDomainControllerCacheEntry != NULL ) {
  8039. NetpDcDerefCacheEntry( GcDomainControllerCacheEntry );
  8040. }
  8041. if ( DcDomainControllerInfo != NULL ) {
  8042. NetApiBufferFree( DcDomainControllerInfo );
  8043. }
  8044. return NetStatus;
  8045. }
  8046. DWORD
  8047. NetpGetBetterDc(
  8048. IN PNL_GETDC_CONTEXT Context,
  8049. IN DWORD OrigTimeout,
  8050. IN DWORD OrigRetryCount,
  8051. IN OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry
  8052. )
  8053. /*++
  8054. Routine Description:
  8055. This routine decides whether a better DC can be found. This routine
  8056. is only found if we used netbios to find the current DC. It allows
  8057. us to overcome some of the weaknesses of netbios.
  8058. If the found DC isn't in the closest site, we attempt to find one
  8059. in the closest site using DNS.
  8060. Arguments:
  8061. Context - Context describing the initial attempt to find a DC.
  8062. NlDcCacheEntry - Passes in a pointer to a private
  8063. PNL_DC_CACHE_ENTRY structure describing the original found DC.
  8064. This structure may be dereferenced by this routine.
  8065. If DomainControllerInfo is NULL, then NlDcCacheEntry returns a
  8066. pointer to a private PNL_DC_CACHE_ENTRY
  8067. structure describing the domain controller selected. The returned
  8068. structure must be dereferenced using NetpDcDerefCacheEntry. This
  8069. may be the original structure or a newly allocated one.
  8070. Return Value:
  8071. The status code that is to be returned by the caller.
  8072. --*/
  8073. {
  8074. NET_API_STATUS NetStatus;
  8075. ULONG ElapsedTime;
  8076. ULONG TimeToWait;
  8077. ULONG LocalFlags = Context->QueriedFlags;
  8078. ULONG LocalInternalFlags = Context->QueriedInternalFlags;
  8079. LPWSTR LocalSiteName;
  8080. PNL_DC_CACHE_ENTRY LocalDomainControllerCacheEntry;
  8081. //
  8082. // If the DC we've got has a DNS domain name,
  8083. // and that DC told us what site we're in,
  8084. // and that DC isn't in the closest site.
  8085. // try to get a DC in that closest site.
  8086. //
  8087. if ( (*NlDcCacheEntry)->UnicodeDnsDomainName != NULL &&
  8088. (*NlDcCacheEntry)->UnicodeClientSiteName != NULL &&
  8089. ((*NlDcCacheEntry)->ReturnFlags & DS_CLOSEST_FLAG ) == 0 &&
  8090. (Context->QueriedInternalFlags & DS_CLOSE_DC_NOT_NEEDED) == 0 ) {
  8091. NlPrint(( NL_DNS,
  8092. "%ws %ws: Try to find a close DC using DNS\n",
  8093. (*NlDcCacheEntry)->UnicodeDnsDomainName,
  8094. (*NlDcCacheEntry)->UnicodeClientSiteName ));
  8095. //
  8096. // Otherwise, the original passed in DC is just fine.
  8097. //
  8098. } else {
  8099. return NO_ERROR;
  8100. }
  8101. //
  8102. // Reduce the timeout to be the time we haven't already spent.
  8103. // (But allow a minimum of 2 seconds)
  8104. //
  8105. ElapsedTime = NetpDcElapsedTime( Context->StartTime );
  8106. if ( ElapsedTime < OrigTimeout ) {
  8107. TimeToWait = max((OrigTimeout - ElapsedTime), NL_DC_MIN_ITERATION_TIMEOUT);
  8108. } else {
  8109. TimeToWait = NL_DC_MIN_ITERATION_TIMEOUT;
  8110. }
  8111. #ifdef notdef
  8112. NlPrint((NL_CRITICAL,
  8113. "NetpGetBetterDc: timeout is %ld %ld %ld %ld\n",
  8114. Context->StartTime,
  8115. ElapsedTime,
  8116. OrigTimeout,
  8117. TimeToWait ));
  8118. #endif // notdef
  8119. LocalFlags |= DS_AVOID_SELF; // Already tried self
  8120. //
  8121. // Adjust the InternalFlags to match the new request.
  8122. //
  8123. LocalSiteName = (LPWSTR) Context->QueriedSiteName;
  8124. if ( LocalInternalFlags & DS_SITENAME_DEFAULTED ) {
  8125. LocalInternalFlags &= ~DS_SITENAME_DEFAULTED;
  8126. LocalSiteName = NULL;
  8127. }
  8128. //
  8129. // This routine is called only if Netbios was used to
  8130. // discover the previously found DC. So, unless the
  8131. // caller required the DNS info be returned, we will
  8132. // return the Netbios format to the caller so ensure
  8133. // the Netbios format match for the better DC.
  8134. //
  8135. if ( (Context->QueriedFlags & DS_RETURN_DNS_NAME) == 0 ) {
  8136. LocalFlags |= DS_RETURN_FLAT_NAME;
  8137. }
  8138. //
  8139. // If the caller wasn't explicit about the format of the returned name,
  8140. // be consistant with the original request.
  8141. //
  8142. //
  8143. // Tell netlogon not to cache this failed retry attempt.
  8144. //
  8145. LocalInternalFlags |= DS_DONT_CACHE_FAILURE;
  8146. //
  8147. // Go get the DC using DNS and an explicit site name
  8148. // Request only the appropriate structure to be returned
  8149. //
  8150. NetStatus = NetpDcGetName(
  8151. Context->SendDatagramContext,
  8152. Context->OurNetbiosComputerName,
  8153. Context->QueriedAccountName,
  8154. Context->QueriedAllowableAccountControlBits,
  8155. NULL, // No Netbios domain name (Done that. Been there.)
  8156. (*NlDcCacheEntry)->UnicodeDnsDomainName,
  8157. Context->QueriedDnsForestName != NULL ?
  8158. Context->QueriedDnsForestName :
  8159. (*NlDcCacheEntry)->UnicodeDnsForestName,
  8160. NULL, // RequestedDomainSid,
  8161. Context->QueriedDomainGuid != NULL ?
  8162. Context->QueriedDomainGuid :
  8163. (IsEqualGUID( &(*NlDcCacheEntry)->DomainGuid, &NlDcZeroGuid) ?
  8164. NULL :
  8165. &(*NlDcCacheEntry)->DomainGuid ),
  8166. LocalSiteName != NULL ?
  8167. LocalSiteName :
  8168. (*NlDcCacheEntry)->UnicodeClientSiteName,
  8169. LocalFlags,
  8170. LocalInternalFlags,
  8171. TimeToWait,
  8172. OrigRetryCount == 0 ?
  8173. 1 :
  8174. OrigRetryCount,
  8175. NULL,
  8176. &LocalDomainControllerCacheEntry );
  8177. if ( NetStatus == NO_ERROR ) {
  8178. NetpDcDerefCacheEntry( *NlDcCacheEntry );
  8179. *NlDcCacheEntry = LocalDomainControllerCacheEntry;
  8180. }
  8181. return NO_ERROR;
  8182. }
  8183. DWORD
  8184. NetpDcGetName(
  8185. IN PVOID SendDatagramContext OPTIONAL,
  8186. IN LPCWSTR ComputerName OPTIONAL,
  8187. IN LPCWSTR AccountName OPTIONAL,
  8188. IN ULONG AllowableAccountControlBits,
  8189. IN LPCWSTR NetbiosDomainName OPTIONAL,
  8190. IN LPCWSTR DnsDomainName OPTIONAL,
  8191. IN LPCWSTR DnsForestName OPTIONAL,
  8192. IN PSID RequestedDomainSid OPTIONAL,
  8193. IN GUID *DomainGuid OPTIONAL,
  8194. IN LPCWSTR SiteName OPTIONAL,
  8195. IN ULONG Flags,
  8196. IN ULONG InternalFlags,
  8197. IN DWORD Timeout,
  8198. IN DWORD RetryCount,
  8199. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo OPTIONAL,
  8200. OUT PNL_DC_CACHE_ENTRY *DomainControllerCacheEntry OPTIONAL
  8201. )
  8202. /*++
  8203. Routine Description:
  8204. NetpDcGetName is a worker function for DsGetDcName. It has the following
  8205. characteristics. It is synchronous. It executes in the caller (it does
  8206. not RPC to Netlogon). It implements a cache of responses. The cache must
  8207. previously have been initialized via NetpDcInitializeCache. The cache should
  8208. be free upon process cleanup (or DLL unload) using NetpDcUninitializeCache.
  8209. The DsGetDcName API returns the name of a DC in a specified domain.
  8210. The domain may be trusted (directly or indirectly) by the caller or
  8211. may be untrusted. DC selection criteria are supplied to the API to
  8212. indicate preference for a DC with particular characteristics.
  8213. The DsGetDcName API is available in an ANSI and UNICODE versions.
  8214. The DsGetDcName API does not require any particular access to the
  8215. specified domain. DsGetDcName does not ensure the returned domain
  8216. controller is currently available by default. Rather, the caller
  8217. should attempt to use the returned domain controller. If the domain
  8218. controller is indeed not available, the caller should repeat the
  8219. DsGetDcName call specifying the DS_FORCE_REDISCOVERY flag.
  8220. The DsGetDcName API is remoted to the Netlogon service on the machine
  8221. specified by ComputerName.
  8222. Arguments:
  8223. SendDatagramContext - Specifies context to pass a NlBrowserSendDatagram
  8224. ComputerName - Specifies the NETBIOS name of this computer.
  8225. If NULL, the name will be dynamically determined.
  8226. AccountName - Account name to pass on the ping request.
  8227. If NULL, no account name will be sent.
  8228. AllowableAccountControlBits - Mask of allowable account types for AccountName.
  8229. Valid bits are those specified by USER_MACHINE_ACCOUNT_MASK.
  8230. Invalid bits are ignored. If more than one bit is specified, the
  8231. account can be of any of the specified types.
  8232. NetbiosDomainName - The Netbios name of the domain to query.
  8233. (e.g., microsoft). Either NetbiosDomainName or DnsDomainName or both
  8234. must be specified.
  8235. DnsDomainName - The DNS-style name of the domain to query.
  8236. (e.g., microsoft.com)
  8237. DnsForestName - The DNS-style name of the tree the queried domain is in.
  8238. RequestedDomainSid - Sid of the domain the message is destined to.
  8239. If NULL, no domain sid will be sent in the ping request.
  8240. DomainGuid - Specifies the Domain GUID of the domain being queried.
  8241. This value is used to handle the case of domain renames. If this
  8242. value is specified and DomainName has been renamed, DsGetDcName will
  8243. attempt to locate a DC in the domain having this specified DomainGuid.
  8244. SiteName - Specifies the site name of the site the returned DC should be
  8245. "close" to. The parameter should typically be the site name of the
  8246. site the client is in. If not specified, the site name defaults to
  8247. the site of ComputerName.
  8248. Flags - Passes additional information to be used to process the request.
  8249. Flags can be a combination values bitwise or'ed together.
  8250. InternalFlags - Internal Flags used to pass additional information
  8251. Timeout - Maximum time (in milliseconds) caller is willing to wait on
  8252. this operation.
  8253. RetryCount - Number of times the "ping" will be sent within the Timeout period
  8254. DomainControllerInfo - Returns a pointer to a DOMAIN_CONTROLLER_INFO
  8255. structure describing the domain controller selected. The returned
  8256. structure must be deallocated using NetApiBufferFree.
  8257. DomainControllerCacheEntry - Returns a pointer to an internal structure describing
  8258. the domain controller selected. The structure is private and is not returned
  8259. to an external caller. Either DomainControllerInfo or DomainControllerCacheEntry
  8260. should be set on input. The returned structure must be dereferenced using
  8261. NetpDcDerefCacheEntry.
  8262. Return Value:
  8263. NO_ERROR: The NlDcCacheEntry was returned;
  8264. ERROR_NO_SUCH_DOMAIN: No DC is available for the specified domain or
  8265. the domain does not exist.
  8266. ERROR_NO_SUCH_USER: A DC responded that the specified user account
  8267. doesn't exist
  8268. ERROR_INVALID_FLAGS - The flags parameter has conflicting or superfluous
  8269. bits set.
  8270. ERROR_INTERNAL_ERROR: Unhandled situation detected.
  8271. ERROR_INVALID_DOMAINNAME: Domain's name is too long. Additional labels
  8272. cannot be concatenated.
  8273. ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process
  8274. this request.
  8275. Various Winsock errors.
  8276. --*/
  8277. {
  8278. NET_API_STATUS NetStatus;
  8279. NL_GETDC_CONTEXT Context;
  8280. PNL_DC_CACHE_ENTRY NlDcCacheEntry = NULL;
  8281. BOOL UseIp = TRUE;
  8282. BOOL UseNetbios = TRUE;
  8283. BOOLEAN AtleastOneTimeout = FALSE;
  8284. PVOID PingResponseMessage = NULL;
  8285. DWORD PingResponseMessageSize;
  8286. PLIST_ENTRY ListEntry;
  8287. BOOLEAN OnlyTryOnce = FALSE;
  8288. BOOL UsedNetbios;
  8289. ULONG ExtraVersionBits = 0;
  8290. ULONG ElapsedTime;
  8291. ULONG IterationWaitTime;
  8292. ULONG TimeToWait;
  8293. LPWSTR LocalQueriedlNetbiosDomainName = NULL;
  8294. #ifdef _NETLOGON_SERVER
  8295. //
  8296. // Prevent any outer exception handler from obscuring bugs in this code.
  8297. //
  8298. try {
  8299. #endif // _NETLOGON_SERVER
  8300. //
  8301. // Treat zero length domain name as NULL.
  8302. //
  8303. if ( DnsDomainName != NULL && *DnsDomainName == L'\0' ) {
  8304. DnsDomainName = NULL;
  8305. }
  8306. if ( DnsForestName != NULL && *DnsForestName == L'\0' ) {
  8307. DnsForestName = NULL;
  8308. }
  8309. if ( NetbiosDomainName != NULL && *NetbiosDomainName == L'\0' ) {
  8310. NetbiosDomainName = NULL;
  8311. }
  8312. //
  8313. // Initialization
  8314. //
  8315. NetStatus = NetpDcInitializeContext(
  8316. SendDatagramContext,
  8317. ComputerName,
  8318. AccountName,
  8319. AllowableAccountControlBits,
  8320. NetbiosDomainName,
  8321. DnsDomainName,
  8322. DnsForestName,
  8323. RequestedDomainSid,
  8324. DomainGuid,
  8325. SiteName,
  8326. NULL, // Not a ping request
  8327. NULL, // No socket addresses
  8328. 0, // 0 socket addresses
  8329. Flags,
  8330. InternalFlags | DS_DOING_DC_DISCOVERY, // This is a DC discovery
  8331. NL_GETDC_CONTEXT_INITIALIZE_FLAGS, // Flag initialization only
  8332. &Context );
  8333. if ( NetStatus != NO_ERROR ) {
  8334. goto Cleanup;
  8335. }
  8336. //
  8337. // Ask Netlogon if this machine satisfies these requirements
  8338. // It's better to use the local machine than going out on the net and trying to
  8339. // discover one.
  8340. //
  8341. if ( Context.QueriedFlags & DS_GC_SERVER_REQUIRED ) {
  8342. ExtraVersionBits |= NETLOGON_NT_VERSION_GC;
  8343. }
  8344. if ( Context.QueriedFlags & DS_IP_REQUIRED ) {
  8345. ExtraVersionBits |= NETLOGON_NT_VERSION_IP;
  8346. }
  8347. if ( Context.QueriedFlags & DS_PDC_REQUIRED ) {
  8348. ExtraVersionBits |= NETLOGON_NT_VERSION_PDC;
  8349. }
  8350. #ifdef _NETLOGON_SERVER
  8351. if ( NlGlobalParameters.NeutralizeNt4Emulator ) {
  8352. ExtraVersionBits |= NETLOGON_NT_VERSION_AVOID_NT4EMUL;
  8353. }
  8354. if ( (Context.QueriedFlags & DS_AVOID_SELF) == 0 ) {
  8355. LPSTR Utf8DnsDomainName = NULL;
  8356. if ( DnsDomainName != NULL ) {
  8357. Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
  8358. if ( Utf8DnsDomainName == NULL ) {
  8359. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8360. goto Cleanup;
  8361. }
  8362. }
  8363. NetStatus = NlGetLocalPingResponse(
  8364. L"<Local>",
  8365. FALSE, // not an LDAP ping
  8366. NetbiosDomainName,
  8367. Utf8DnsDomainName,
  8368. DomainGuid,
  8369. RequestedDomainSid,
  8370. Context.DcQueryType == NlDcQueryPdc,
  8371. Context.OurNetbiosComputerName, // Netbios name of this computer
  8372. Context.QueriedAccountName,
  8373. Context.QueriedAllowableAccountControlBits,
  8374. LMNT_MESSAGE,
  8375. NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP|NETLOGON_NT_VERSION_LOCAL|ExtraVersionBits,
  8376. NULL, // No incoming socket address
  8377. &PingResponseMessage,
  8378. &PingResponseMessageSize );
  8379. if ( Utf8DnsDomainName != NULL ) {
  8380. NetApiBufferFree( Utf8DnsDomainName );
  8381. }
  8382. if ( NetStatus != ERROR_NO_SUCH_DOMAIN ) {
  8383. if (NetStatus != NO_ERROR ) {
  8384. NlPrint(( NL_CRITICAL,
  8385. "NetpDcGetName: %ws: cannot get local ping response %ld\n",
  8386. Context.QueriedDisplayDomainName,
  8387. NetStatus));
  8388. goto Cleanup;
  8389. }
  8390. //
  8391. // See if this response meets our needs
  8392. //
  8393. NetStatus = NetpDcHandlePingResponse(
  8394. &Context,
  8395. PingResponseMessage,
  8396. PingResponseMessageSize,
  8397. NL_DC_CACHE_LOCAL, // local response
  8398. NULL,
  8399. &NlDcCacheEntry,
  8400. &UsedNetbios );
  8401. switch ( NetStatus ) {
  8402. case ERROR_SEM_TIMEOUT: // Doesn't match the request
  8403. case ERROR_INVALID_DATA: // Response is garbled
  8404. case ERROR_NO_SUCH_USER: // User doesn't exist on this DC
  8405. break;
  8406. default:
  8407. goto Cleanup;
  8408. }
  8409. }
  8410. }
  8411. //
  8412. // If this is primary DC discovery, first cache the DC info written by
  8413. // the join process, if any. We will pick up this cached info later.
  8414. // We do this even if the current DC discovery is more specific than
  8415. // the generic DC discovery used by the join process. Indeed, the join
  8416. // DC may turn out to be more specific and may satisfy the current request.
  8417. //
  8418. // This is a potentially lengthy operation since the DC will be pinged.
  8419. //
  8420. EnterCriticalSection(&NlDcCritSect);
  8421. if ( !NlGlobalJoinLogicDone &&
  8422. (Context.QueriedInternalFlags & DS_IS_PRIMARY_DOMAIN) != 0 ) {
  8423. NlGlobalJoinLogicDone = TRUE;
  8424. if ( NlCacheJoinDomainControllerInfo() == NO_ERROR ) {
  8425. //
  8426. // It is bogus to force rediscovery on the first attempt for
  8427. // a particular type. For example, forcing discovery would
  8428. // avoid the cache thereby mising the join DC we just cached.
  8429. //
  8430. Context.QueriedFlags &= ~DS_FORCE_REDISCOVERY;
  8431. }
  8432. }
  8433. LeaveCriticalSection(&NlDcCritSect);
  8434. #endif // _NETLOGON_SERVER
  8435. //
  8436. // If discovery isn't being forced,
  8437. // do any optimizations that will speed getting the results to the caller.
  8438. //
  8439. if ( (Context.QueriedFlags & DS_FORCE_REDISCOVERY) == 0 ) {
  8440. ULONG CacheEntryElapsedTime;
  8441. DWORD NegativeCacheElapsedTime = 0xFFFFFFFF;
  8442. ULONG CacheEntryRefreshPeriod = 0xFFFFFFFF; // Infinity
  8443. BOOL SimilarQueryFailed = FALSE;
  8444. BOOL ForcePing;
  8445. //
  8446. // If there is a cache entry for this operation,
  8447. // Use it.
  8448. //
  8449. NlDcCacheEntry = NetpDcFindCacheEntry( &Context, &UsedNetbios, &ForcePing );
  8450. if ( NlDcCacheEntry != NULL ) {
  8451. CacheEntryElapsedTime = NetpDcElapsedTime(NlDcCacheEntry->CreationTime);
  8452. }
  8453. //
  8454. // If the cached DC is not close,
  8455. // check if it's time to re-discover a close one.
  8456. //
  8457. if ( NlDcCacheEntry != NULL &&
  8458. (NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_NONCLOSE_EXPIRE) != 0 &&
  8459. CacheEntryElapsedTime > NL_DC_CLOSE_SITE_TIMEOUT ) {
  8460. NlPrint(( NL_MISC,
  8461. "NetpDcGetName: %ws cache not for closest site and it is too old. %ld\n",
  8462. Context.QueriedDisplayDomainName,
  8463. NetpDcElapsedTime( NlDcCacheEntry->CreationTime ) ));
  8464. NetpDcDerefCacheEntry( NlDcCacheEntry );
  8465. NlDcCacheEntry = NULL;
  8466. }
  8467. //
  8468. // Determine the appropriate cache entry refresh interval.
  8469. // Notice that a cache entry will never expire if we are
  8470. // not running in netlogon's process.
  8471. //
  8472. #ifdef _NETLOGON_SERVER
  8473. //
  8474. // Get the value as configured in seconds
  8475. //
  8476. if ( Context.QueriedFlags & DS_BACKGROUND_ONLY ) {
  8477. CacheEntryRefreshPeriod = NlGlobalParameters.BackgroundSuccessfulRefreshPeriod;
  8478. } else {
  8479. CacheEntryRefreshPeriod = NlGlobalParameters.NonBackgroundSuccessfulRefreshPeriod;
  8480. }
  8481. // If the value converted into milliseconds fits into a ULONG, use it
  8482. if ( CacheEntryRefreshPeriod <= MAXULONG/1000 ) {
  8483. CacheEntryRefreshPeriod *= 1000; // convert to milliseconds
  8484. // Otherwise, use the max ULONG
  8485. } else {
  8486. CacheEntryRefreshPeriod = MAXULONG; // infinity
  8487. }
  8488. #endif // _NETLOGON_SERVER
  8489. //
  8490. // If this cache entry is too old,
  8491. // ping the DC to see if it still plays the same role.
  8492. //
  8493. if ( NlDcCacheEntry != NULL &&
  8494. (ForcePing ||
  8495. CacheEntryElapsedTime > CacheEntryRefreshPeriod) ) {
  8496. if ( ForcePing ) {
  8497. NlPrint(( NL_MISC,
  8498. "NetpDcGetName: %ws cache doesn't have right account name.\n",
  8499. Context.QueriedDisplayDomainName ));
  8500. } else {
  8501. NlPrint(( NL_MISC,
  8502. "NetpDcGetName: %ws cache is too old. %ld\n",
  8503. Context.QueriedDisplayDomainName,
  8504. NetpDcElapsedTime( NlDcCacheEntry->CreationTime ) ));
  8505. }
  8506. //
  8507. // Indicate which mechanism should be used to ping the DC
  8508. //
  8509. if ( NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_LDAP ) {
  8510. //
  8511. // Add the cached DC address to the list of quried addresses
  8512. //
  8513. if ( NlDcCacheEntry->SockAddr.iSockaddrLength != 0 ) {
  8514. NetStatus = NetpDcProcessAddressList( &Context,
  8515. NlDcCacheEntry->UnicodeDnsHostName,
  8516. &NlDcCacheEntry->SockAddr,
  8517. 1,
  8518. FALSE, // Don't know if site specific
  8519. NULL );
  8520. if ( NetStatus != NO_ERROR ) {
  8521. goto Cleanup;
  8522. }
  8523. //
  8524. // Prefer DNS name for ldap pings
  8525. //
  8526. if ( NlDcCacheEntry->UnicodeDnsHostName != NULL ) {
  8527. Context.QueriedDcName = NlDcCacheEntry->UnicodeDnsHostName;
  8528. Context.QueriedInternalFlags |= DS_PING_DNS_HOST;
  8529. } else if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  8530. Context.QueriedDcName = NlDcCacheEntry->UnicodeNetbiosDcName;
  8531. Context.QueriedInternalFlags |= DS_PING_NETBIOS_HOST;
  8532. }
  8533. Context.QueriedInternalFlags |= DS_PING_USING_LDAP;
  8534. } else {
  8535. NlPrint(( NL_CRITICAL,
  8536. "NetpDcGetName: %ws cache says use ldap but has no address\n",
  8537. Context.QueriedDisplayDomainName ));
  8538. }
  8539. } else if ( NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_MAILSLOT ) {
  8540. //
  8541. // We must have Netbios name for mailslot pings
  8542. //
  8543. if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL &&
  8544. NlDcCacheEntry->UnicodeNetbiosDomainName != NULL ) {
  8545. Context.QueriedDcName = NlDcCacheEntry->UnicodeNetbiosDcName;
  8546. //
  8547. // If we don't have the Netbios domain name in Context,
  8548. // use the one from the cache entry
  8549. //
  8550. if ( Context.QueriedNetbiosDomainName == NULL ) {
  8551. LocalQueriedlNetbiosDomainName =
  8552. NetpAllocWStrFromWStr( NlDcCacheEntry->UnicodeNetbiosDomainName );
  8553. if ( LocalQueriedlNetbiosDomainName == NULL ) {
  8554. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8555. goto Cleanup;
  8556. }
  8557. Context.QueriedNetbiosDomainName = LocalQueriedlNetbiosDomainName;
  8558. }
  8559. Context.QueriedInternalFlags |= DS_PING_NETBIOS_HOST;
  8560. Context.QueriedInternalFlags |= DS_PING_USING_MAILSLOT;
  8561. } else {
  8562. NlPrint(( NL_CRITICAL,
  8563. "NetpDcGetName: %ws cache says use maislot but has no Netbios name\n",
  8564. Context.QueriedDisplayDomainName ));
  8565. }
  8566. }
  8567. //
  8568. // Ping the DC using the specified mechanism
  8569. //
  8570. if ( Context.QueriedInternalFlags & (DS_PING_DNS_HOST|DS_PING_NETBIOS_HOST) ) {
  8571. ULONG PingStartTime;
  8572. ULONG PingElapsedTime;
  8573. PNL_DC_CACHE_ENTRY PingedNlDcCacheEntry = NULL;
  8574. //
  8575. // Do the ping part initialization of Context.
  8576. // Some of the arguments passed below are ignored by the API.
  8577. // Instead, the corresponding fields of Context initialized
  8578. // in the flag part of the context initialization are used.
  8579. //
  8580. NetStatus = NetpDcInitializeContext(
  8581. SendDatagramContext,
  8582. ComputerName,
  8583. AccountName,
  8584. Context.QueriedAllowableAccountControlBits,
  8585. NetbiosDomainName, // Ignored for ping initialization
  8586. DnsDomainName, // Ignored for ping initialization
  8587. DnsForestName, // Ignored for ping initialization
  8588. RequestedDomainSid,
  8589. DomainGuid, // Ignored for ping initialization
  8590. SiteName, // Ignored for ping initialization
  8591. NULL, // Quieried DC name has been just set
  8592. NULL, // Socket address has been just set
  8593. 0, // No socket addresses passed here
  8594. Flags, // Ignored for ping initialization
  8595. InternalFlags, // Ignored for ping initialization
  8596. NL_GETDC_CONTEXT_INITIALIZE_PING, // Ping initialization
  8597. &Context );
  8598. if ( NetStatus != NO_ERROR ) {
  8599. NetpDcDerefCacheEntry( NlDcCacheEntry );
  8600. NlDcCacheEntry = NULL;
  8601. goto Cleanup;
  8602. }
  8603. //
  8604. // Ping the cached DC name
  8605. //
  8606. // We send one ping and wait for maximum time
  8607. // that we give to a ping response. If the DC
  8608. // is slow to respond here, it will be given the
  8609. // second chance since we will leave its address
  8610. // at the front of the list. (Here we assume that
  8611. // the ping is LDAP. If it's mailslot, the 0.4 second
  8612. // timeout should be large enough for the datagram
  8613. // response time; however, the DC will not be prefered
  8614. // later if it's slow to respond here)
  8615. //
  8616. PingStartTime = GetTickCount();
  8617. NetStatus = NlPingDcNameWithContext(
  8618. &Context,
  8619. 1, // Send 1 ping
  8620. TRUE, // Wait for response
  8621. NL_DC_MAX_PING_TIMEOUT, // Give maximum timeout per ping
  8622. &UsedNetbios,
  8623. &PingedNlDcCacheEntry );
  8624. //
  8625. // Clear all ping related bits to not confuse the DC
  8626. // discovery if it happens. But leave the Netbios domain
  8627. // name and the DC address on the list to validate the
  8628. // DC's response if it happens to arrive after the 0.5
  8629. // second timeout.
  8630. //
  8631. Context.QueriedDcName = NULL;
  8632. Context.QueriedInternalFlags &= ~( DS_PING_DNS_HOST |
  8633. DS_PING_NETBIOS_HOST |
  8634. DS_PING_USING_LDAP |
  8635. DS_PING_USING_MAILSLOT );
  8636. //
  8637. // On success, update the cache entry. The new cache entry
  8638. // returned from the pinged DC may contain new information
  8639. // such as a new client site name. So we want to use it
  8640. // instead of the currently cached one.
  8641. //
  8642. NetpDcDerefCacheEntry( NlDcCacheEntry );
  8643. NlDcCacheEntry = NULL;
  8644. if ( NetStatus == NO_ERROR ) {
  8645. NlDcCacheEntry = PingedNlDcCacheEntry;
  8646. }
  8647. //
  8648. // Update the timeout.
  8649. //
  8650. PingElapsedTime = NetpDcElapsedTime( PingStartTime );
  8651. if ( Timeout > PingElapsedTime ) {
  8652. Timeout -= PingElapsedTime;
  8653. }
  8654. //
  8655. // If we have no ping mechanism for this cache entry, ditch it
  8656. //
  8657. } else {
  8658. NetpDcDerefCacheEntry( NlDcCacheEntry );
  8659. NlDcCacheEntry = NULL;
  8660. }
  8661. }
  8662. if ( NlDcCacheEntry != NULL ) {
  8663. NlPrint(( NL_MISC,
  8664. "NetpDcGetName: %ws using cached information\n",
  8665. Context.QueriedDisplayDomainName ));
  8666. NetStatus = NO_ERROR;
  8667. goto Cleanup;
  8668. }
  8669. #ifdef _NETLOGON_SERVER
  8670. //
  8671. // Since there is no cache entry,
  8672. // check if we've attempted to find a DC recently.
  8673. //
  8674. EnterCriticalSection(&NlDcCritSect);
  8675. if ( Context.NlDcDomainEntry->Dc[Context.DcQueryType].NegativeCacheTime != 0 ) {
  8676. NegativeCacheElapsedTime = NetpDcElapsedTime( Context.NlDcDomainEntry->Dc[Context.DcQueryType].NegativeCacheTime );
  8677. //
  8678. // If this couldn't be discovered in the last 45 seconds,
  8679. //
  8680. if ( NegativeCacheElapsedTime < (NlGlobalParameters.NegativeCachePeriod*1000) ) {
  8681. NlPrint(( NL_MISC,
  8682. "NetpDcGetName: %ws similar query failed recently %ld\n",
  8683. Context.QueriedDisplayDomainName,
  8684. NegativeCacheElapsedTime ));
  8685. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8686. Context.AvoidNegativeCache = TRUE;
  8687. LeaveCriticalSection(&NlDcCritSect);
  8688. goto Cleanup;
  8689. }
  8690. //
  8691. // The negative cache timeout hasn't elapsed yet.
  8692. // But indicate that similar query failed in the past
  8693. // to make a decision as to whether we want to retry
  8694. // the DC discovery for background callers below.
  8695. //
  8696. SimilarQueryFailed = TRUE;
  8697. }
  8698. //
  8699. // If the caller wants an NT5 (or newer) DC but we know
  8700. // this domain is NT4, adjust the negative cache elapsed
  8701. // time to be the time passed since the latest failure for
  8702. // similar query.
  8703. //
  8704. if ( (Context.QueriedFlags & DS_NT50_WANTED) != 0 &&
  8705. Context.NlDcDomainEntry->InNt4Domain ) {
  8706. DWORD InNt4DomainElapsedTime;
  8707. InNt4DomainElapsedTime = NetpDcElapsedTime( Context.NlDcDomainEntry->InNt4DomainTime );
  8708. if ( InNt4DomainElapsedTime < NegativeCacheElapsedTime ) {
  8709. NegativeCacheElapsedTime = InNt4DomainElapsedTime;
  8710. }
  8711. //
  8712. // Indicate that similar query failed in the past
  8713. //
  8714. SimilarQueryFailed = TRUE;
  8715. }
  8716. //
  8717. // If the caller only wants a DC for a background task
  8718. // and we know a similar query failed in the past,
  8719. // see if it is time to try again.
  8720. //
  8721. if ( (Context.QueriedFlags & DS_BACKGROUND_ONLY) != 0 && SimilarQueryFailed ) {
  8722. if ( NegativeCacheElapsedTime < (Context.NlDcDomainEntry->Dc[Context.DcQueryType].ExpBackoffPeriod*1000) ) {
  8723. NlPrint(( NL_MISC,
  8724. "NetpDcGetName: %ws similar background query failed recently %ld\n",
  8725. Context.QueriedDisplayDomainName,
  8726. NegativeCacheElapsedTime ));
  8727. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8728. Context.AvoidNegativeCache = TRUE;
  8729. LeaveCriticalSection(&NlDcCritSect);
  8730. goto Cleanup;
  8731. }
  8732. //
  8733. // If we've already spent all the time we're willing to spend on
  8734. // background tasks, blow this one away.
  8735. //
  8736. if ( NlGlobalParameters.BackgroundRetryQuitTime != 0 &&
  8737. NlTimeHasElapsedEx(
  8738. &Context.NlDcDomainEntry->Dc[Context.DcQueryType].BackgroundRetryInitTime,
  8739. &NlGlobalParameters.BackgroundRetryQuitTime_100ns,
  8740. NULL ) ) {
  8741. NlPrint(( NL_MISC,
  8742. "NetpDcGetName: %ws avoiding all future background queries\n",
  8743. Context.QueriedDisplayDomainName ));
  8744. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8745. Context.AvoidNegativeCache = TRUE;
  8746. LeaveCriticalSection(&NlDcCritSect);
  8747. goto Cleanup;
  8748. }
  8749. //
  8750. // If the negative cache entry has been marked permanent,
  8751. // blow this one away.
  8752. //
  8753. if ( Context.NlDcDomainEntry->Dc[Context.DcQueryType].PermanentNegativeCache ) {
  8754. NlPrint(( NL_MISC,
  8755. "NetpDcGetName: %ws is permanently negative cached.\n",
  8756. Context.QueriedDisplayDomainName ));
  8757. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8758. Context.AvoidNegativeCache = TRUE;
  8759. LeaveCriticalSection(&NlDcCritSect);
  8760. goto Cleanup;
  8761. }
  8762. //
  8763. // We're going to try again.
  8764. // Adjust the exponential backoff period.
  8765. //
  8766. Context.NlDcDomainEntry->Dc[Context.DcQueryType].ExpBackoffPeriod *= 2;
  8767. if ( Context.NlDcDomainEntry->Dc[Context.DcQueryType].ExpBackoffPeriod >
  8768. NlGlobalParameters.BackgroundRetryMaximumPeriod ) {
  8769. Context.NlDcDomainEntry->Dc[Context.DcQueryType].ExpBackoffPeriod =
  8770. NlGlobalParameters.BackgroundRetryMaximumPeriod;
  8771. }
  8772. }
  8773. LeaveCriticalSection(&NlDcCritSect);
  8774. #endif // _NETLOGON_SERVER
  8775. //
  8776. // If a good time server is preferred,
  8777. // and we already have a suitable cache entry,
  8778. // only try once to find a good time server.
  8779. //
  8780. if ( (Context.QueriedFlags & DS_GOOD_TIMESERV_PREFERRED) != 0 &&
  8781. Context.ImperfectCacheEntry != NULL ) {
  8782. //
  8783. // Don't cache the fact that we couldn't find a DC.
  8784. //
  8785. Context.AvoidNegativeCache = TRUE;
  8786. NlPrint(( NL_MISC,
  8787. "NetpDcGetName: %ws: Only try once to find good timeserver.\n",
  8788. Context.QueriedDisplayDomainName ));
  8789. OnlyTryOnce = TRUE;
  8790. }
  8791. //
  8792. // If an NT 5.0 DC is wanted,
  8793. // handle the case where we know we're in an NT 4.0 domain.
  8794. //
  8795. if ((Context.QueriedFlags & DS_NT50_WANTED) != 0 ) {
  8796. EnterCriticalSection(&NlDcCritSect);
  8797. if ( Context.NlDcDomainEntry->InNt4Domain ) {
  8798. //
  8799. // If we recently found that this was an NT 4.0 domain,
  8800. // fail the call immediately.
  8801. //
  8802. if ( NetpDcElapsedTime(Context.NlDcDomainEntry->InNt4DomainTime) <= NL_NT4_AVOIDANCE_TIME ) {
  8803. //
  8804. // If the caller only prefers an NT 5.0 machine,
  8805. // let him find an NT 4.0 DC normally.
  8806. //
  8807. if ( Context.QueriedFlags & DS_DIRECTORY_SERVICE_PREFERRED ) {
  8808. //
  8809. // If we had an NT 4 DC cached,
  8810. // Use it now.
  8811. //
  8812. if ( Context.ImperfectCacheEntry != NULL ) {
  8813. LeaveCriticalSection(&NlDcCritSect);
  8814. NlDcCacheEntry = Context.ImperfectCacheEntry;
  8815. UsedNetbios = Context.ImperfectUsedNetbios;
  8816. Context.ImperfectCacheEntry = NULL;
  8817. NlPrint(( NL_MISC,
  8818. "NetpDcGetName: %ws: Avoid finding NT 5.0 DC in NT 4.0 domain (Use previously cached entry.)\n",
  8819. Context.QueriedDisplayDomainName ));
  8820. NetStatus = NO_ERROR;
  8821. goto Cleanup;
  8822. }
  8823. Context.QueriedFlags &= ~DS_DIRECTORY_SERVICE_PREFERRED;
  8824. NlPrint(( NL_MISC,
  8825. "NetpDcGetName: %ws: Avoid finding NT 5.0 DC in NT 4.0 domain (Ditch preferred)\n",
  8826. Context.QueriedDisplayDomainName ));
  8827. //
  8828. // If the caller needs an NT 5.0 DC,
  8829. // fail the call immediately.
  8830. //
  8831. } else {
  8832. //
  8833. // Don't cache the fact that we couldn't find a DC.
  8834. // The InNt4Domain cache is more sophisticated than the
  8835. // simple negative cache.
  8836. //
  8837. Context.AvoidNegativeCache = TRUE;
  8838. LeaveCriticalSection(&NlDcCritSect);
  8839. NlPrint(( NL_MISC,
  8840. "NetpDcGetName: %ws: Avoid finding NT 5.0 DC in NT 4.0 domain\n",
  8841. Context.QueriedDisplayDomainName ));
  8842. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8843. goto Cleanup;
  8844. }
  8845. //
  8846. // If it's been a while since we found out,
  8847. // we'll retry the operation (but only once).
  8848. //
  8849. // This minimizes the cost, but still allows us to find an NT 5 DC
  8850. // if it was just temporarily down.
  8851. //
  8852. } else {
  8853. //
  8854. // Don't cache the fact that we couldn't find a DC.
  8855. // The InNt4Domain cache is more sophisticated than the
  8856. // simple negative cache.
  8857. //
  8858. Context.AvoidNegativeCache = TRUE;
  8859. NlPrint(( NL_MISC,
  8860. "NetpDcGetName: %ws: Only try once to find NT 5.0 DC in NT 4.0 domain.\n",
  8861. Context.QueriedDisplayDomainName ));
  8862. OnlyTryOnce = TRUE;
  8863. }
  8864. }
  8865. LeaveCriticalSection(&NlDcCritSect);
  8866. }
  8867. }
  8868. //
  8869. // If we did not initialize the ping part of Context earlier
  8870. // to do host pings, do it here.
  8871. //
  8872. if ( Context.LdapFilter == NULL && Context.PingMessage == NULL ) {
  8873. NetStatus = NetpDcInitializeContext(
  8874. SendDatagramContext,
  8875. ComputerName,
  8876. AccountName,
  8877. Context.QueriedAllowableAccountControlBits,
  8878. NetbiosDomainName,
  8879. DnsDomainName,
  8880. DnsForestName,
  8881. RequestedDomainSid,
  8882. DomainGuid,
  8883. SiteName,
  8884. NULL, // Not a ping request
  8885. NULL, // No socket addresses
  8886. 0, // 0 socket addresses
  8887. Flags,
  8888. InternalFlags,
  8889. NL_GETDC_CONTEXT_INITIALIZE_PING, // Ping part initialization
  8890. &Context );
  8891. if ( NetStatus != NO_ERROR ) {
  8892. goto Cleanup;
  8893. }
  8894. }
  8895. //
  8896. // Loop until we've made several attempts to find the DC
  8897. //
  8898. Context.StartTime = GetTickCount();
  8899. for ( Context.TryCount = 0;
  8900. Context.TryCount < RetryCount;
  8901. Context.TryCount ++ ) {
  8902. //
  8903. // If a DNS domain name is known,
  8904. // use DNS to find a DC.
  8905. //
  8906. if ( Context.QueriedDnsDomainName == NULL ) {
  8907. UseIp = FALSE;
  8908. } else if ( UseIp ) {
  8909. //
  8910. // Try using DNS/IP to find the DC.
  8911. //
  8912. NetStatus = NetpDcGetNameIp(
  8913. &Context,
  8914. &NlDcCacheEntry,
  8915. &UsedNetbios );
  8916. //
  8917. // If we found it,
  8918. // return it.
  8919. //
  8920. if ( NetStatus == NO_ERROR ) {
  8921. goto Cleanup;
  8922. //
  8923. // If DNS isn't configured,
  8924. // mark that we don't want to try DNS again.
  8925. // (Drop through to Netbios.)
  8926. //
  8927. } else if ( NetStatus == ERROR_DNS_NOT_CONFIGURED ) {
  8928. UseIp = FALSE;
  8929. //
  8930. // If DNS has the name registered,
  8931. // but the DCs haven't yet responded,
  8932. // indicate we need to keep on waiting.
  8933. // (Drop through to Netbios.)
  8934. //
  8935. } else if ( NetStatus == ERROR_SEM_TIMEOUT ) {
  8936. AtleastOneTimeout = TRUE;
  8937. //
  8938. // If DNS doesn't have the name registered,
  8939. // indicate we don't need to try DNS again.
  8940. // (Drop through to Netbios.)
  8941. //
  8942. } else if ( NetStatus == ERROR_NO_SUCH_DOMAIN ) {
  8943. UseIp = FALSE;
  8944. //
  8945. // All other problems are DNS detected errors to return to
  8946. // the caller.
  8947. //
  8948. } else {
  8949. NlPrint(( NL_CRITICAL,
  8950. "NetpDcGetName: %ws: cannot find DC via IP/DNS %ld\n",
  8951. Context.QueriedDisplayDomainName,
  8952. NetStatus));
  8953. goto Cleanup;
  8954. }
  8955. }
  8956. //
  8957. // If a Netbios domain name is known,
  8958. // use Netbios to find a DC.
  8959. //
  8960. if ( Context.QueriedNetbiosDomainName == NULL ) {
  8961. UseNetbios = FALSE;
  8962. } else if ( UseNetbios ) {
  8963. NetStatus = NetpDcGetNameNetbios(
  8964. &Context,
  8965. &NlDcCacheEntry,
  8966. &UsedNetbios );
  8967. //
  8968. // If we found it,
  8969. // return it.
  8970. //
  8971. if ( NetStatus == NO_ERROR ) {
  8972. goto Cleanup;
  8973. //
  8974. // If Netbios sent the datagram successfully,
  8975. // but the DCs haven't yet responded,
  8976. // indicate we need to keep on waiting.
  8977. // (Drop through to next iteration.)
  8978. //
  8979. } else if ( NetStatus == ERROR_SEM_TIMEOUT ) {
  8980. AtleastOneTimeout = TRUE;
  8981. //
  8982. // If Netbios couldn't send the datagram,
  8983. // indicate we don't need to try Netbios again.
  8984. // (Drop through to next iteration.)
  8985. //
  8986. } else if ( NetStatus == ERROR_NO_SUCH_DOMAIN ) {
  8987. UseNetbios = FALSE;
  8988. //
  8989. // All other problems are DNS detected errors to return to
  8990. // the caller.
  8991. //
  8992. } else {
  8993. NlPrint(( NL_CRITICAL,
  8994. "NetpDcGetName: %ws: cannot find DC via Netbios %ld\n",
  8995. Context.QueriedDisplayDomainName,
  8996. NetStatus));
  8997. goto Cleanup;
  8998. }
  8999. }
  9000. //
  9001. // If there are no more mechanisms to try,
  9002. // we're done.
  9003. //
  9004. if ( !UseIp && !UseNetbios ) {
  9005. NlPrint(( NL_CRITICAL,
  9006. "NetpDcGetName: %ws: IP and Netbios are both done.\n",
  9007. Context.QueriedDisplayDomainName ));
  9008. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9009. goto Cleanup;
  9010. }
  9011. //
  9012. // If no datagrams were sent successfully,
  9013. // we're done.
  9014. //
  9015. if ( !AtleastOneTimeout ) {
  9016. NlPrint(( NL_CRITICAL,
  9017. "NetpDcGetName: %ws: no datagrams were sent\n",
  9018. Context.QueriedDisplayDomainName ));
  9019. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9020. goto Cleanup;
  9021. }
  9022. //
  9023. // If we should only try once,
  9024. // we've done that first try.
  9025. //
  9026. if ( OnlyTryOnce ) {
  9027. //
  9028. // Wait a short amount of time to ensure the response has a chance
  9029. // to reach us.
  9030. //
  9031. NetStatus = NetpDcGetPingResponse(
  9032. &Context,
  9033. NL_NT4_ONE_TRY_TIME,
  9034. &NlDcCacheEntry,
  9035. &UsedNetbios );
  9036. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  9037. if ( NetStatus != NO_ERROR ) {
  9038. NlPrint(( NL_CRITICAL,
  9039. "NetpDcGetName: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  9040. Context.QueriedDisplayDomainName,
  9041. NetStatus ));
  9042. }
  9043. goto Cleanup;
  9044. }
  9045. //
  9046. // So we couldn't get an NT5 DC (or a good time server).
  9047. //
  9048. // If the caller requires an NT 5.0 DC,
  9049. // we're done so error out early.
  9050. //
  9051. if ( (Context.QueriedFlags & (DS_DIRECTORY_SERVICE_PREFERRED|DS_GOOD_TIMESERV_PREFERRED)) == 0 ) {
  9052. NlPrint(( NL_MISC,
  9053. "NetpDcGetName: %ws: Only try once done.\n",
  9054. Context.QueriedDisplayDomainName ));
  9055. break;
  9056. }
  9057. //
  9058. // If an NT 4 DC has already been found,
  9059. // (or a non-good time server has already been found),
  9060. // use it since the caller didn't require NT5 DC
  9061. // (or a good time server).
  9062. //
  9063. if ( Context.ImperfectCacheEntry != NULL ) {
  9064. if ( Context.QueriedFlags & DS_DIRECTORY_SERVICE_PREFERRED ) {
  9065. NlPrint(( NL_MISC,
  9066. "NetpDcGetName: %ws: Tried once to find NT 5.0 DC (Using found NT 4.0 DC).\n",
  9067. Context.QueriedDisplayDomainName ));
  9068. } else if (Context.QueriedFlags & DS_GOOD_TIMESERV_PREFERRED) {
  9069. NlPrint(( NL_MISC,
  9070. "NetpDcGetName: %ws: Tried once to find good timeserver (Using previously found DC).\n",
  9071. Context.QueriedDisplayDomainName ));
  9072. }
  9073. //
  9074. // Drop through to handle this in the cleanup section
  9075. //
  9076. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9077. goto Cleanup;
  9078. }
  9079. //
  9080. // Here we don't have an imperfect cache entry and the caller
  9081. // doesn't require an NT5 DC. Let him continue to find an
  9082. // NT 4.0 DC normally.
  9083. //
  9084. Context.QueriedFlags &= ~DS_DIRECTORY_SERVICE_PREFERRED;
  9085. NlPrint(( NL_MISC,
  9086. "NetpDcGetName: %ws: Only try once reset.\n",
  9087. Context.QueriedDisplayDomainName ));
  9088. OnlyTryOnce = FALSE;
  9089. }
  9090. //
  9091. // Wait up to 1/RetryCount'th of the total available time for responses to come back.
  9092. // The caller will either resort to a less preferable candidate or
  9093. // will repeat the pings. In either case, we'd rather this candidate won.
  9094. //
  9095. // Always wait a short amount of time here. Consider the case that DNS
  9096. // took 20 seconds to find that there was no DNS server. We still want
  9097. // to give Netbios a decent amount of time to find a DC.
  9098. //
  9099. ElapsedTime = NetpDcElapsedTime( Context.StartTime );
  9100. #ifdef notdef
  9101. NlPrint((NL_CRITICAL,
  9102. "NetpDcGetName: timeout is %ld %ld %ld %ld\n",
  9103. Context.StartTime,
  9104. ElapsedTime,
  9105. Timeout,
  9106. Context.TryCount ));
  9107. #endif // notdef
  9108. IterationWaitTime = (Timeout*(Context.TryCount+1))/RetryCount;
  9109. if ( IterationWaitTime > ElapsedTime &&
  9110. (IterationWaitTime - ElapsedTime) > NL_DC_MIN_ITERATION_TIMEOUT ) {
  9111. TimeToWait = IterationWaitTime - ElapsedTime;
  9112. } else {
  9113. TimeToWait = NL_DC_MIN_ITERATION_TIMEOUT;
  9114. }
  9115. NetStatus = NetpDcGetPingResponse(
  9116. &Context,
  9117. TimeToWait,
  9118. &NlDcCacheEntry,
  9119. &UsedNetbios );
  9120. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  9121. if ( NetStatus != NO_ERROR ) {
  9122. NlPrint(( NL_CRITICAL,
  9123. "NetpDcGetName: %ws: Cannot NetpDcGetPingResponse. %ld\n",
  9124. Context.QueriedDisplayDomainName,
  9125. NetStatus ));
  9126. }
  9127. goto Cleanup;
  9128. }
  9129. //
  9130. // If at least one NT 4.0 DC is available in the domain,
  9131. // and no NT 5.0 DCs (of any type) are available,
  9132. // and we asked for an NT 5.0 DC,
  9133. // early out now since our caller is impatient.
  9134. //
  9135. // Don't be tempted to leave out the last test. If we're not
  9136. // explicitly asking for an NT 5.0 DC, we might not ping any NT 5.0 DCs
  9137. // even though they exist in the domain.
  9138. //
  9139. //
  9140. if ( Context.NonDsResponse &&
  9141. !Context.DsResponse &&
  9142. (Context.QueriedFlags & DS_NT50_REQUIRED) != 0 ) {
  9143. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9144. goto Cleanup;
  9145. }
  9146. //
  9147. // If we've waited long enough for a perfect DC,
  9148. // drop out an use the imperfect one.
  9149. //
  9150. if ( Context.ImperfectCacheEntry != NULL ) {
  9151. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9152. goto Cleanup;
  9153. }
  9154. }
  9155. //
  9156. // Tried two times and still can't find one.
  9157. //
  9158. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9159. Cleanup:
  9160. ////////////////////////////////
  9161. // //
  9162. // First, hanle failure cases //
  9163. // //
  9164. ////////////////////////////////
  9165. //
  9166. // If the problem is simply that the DCs don't have a user account for the named user,
  9167. // change the status code.
  9168. //
  9169. if ( NetStatus == ERROR_NO_SUCH_DOMAIN && Context.NoSuchUserResponse ) {
  9170. NetStatus = ERROR_NO_SUCH_USER;
  9171. }
  9172. //
  9173. // If no DC has been found so far,
  9174. // handle it.
  9175. //
  9176. if ( NetStatus == ERROR_NO_SUCH_DOMAIN ) {
  9177. //
  9178. // If there is a cache entry that might not be perfect,
  9179. // see if it is satisfactory.
  9180. //
  9181. if ( Context.ImperfectCacheEntry != NULL ) {
  9182. //
  9183. // Handle NT 4 DC found when DS preferred.
  9184. //
  9185. if ( Context.QueriedFlags & DS_DIRECTORY_SERVICE_PREFERRED ) {
  9186. //
  9187. // If we actually attempted to discover a NT5 DC and
  9188. // found only NT4 DC, reset the InNt4DomainTime stamp
  9189. //
  9190. if ( Context.NonDsResponse && !Context.DsResponse ) {
  9191. EnterCriticalSection(&NlDcCritSect);
  9192. Context.NlDcDomainEntry->InNt4Domain = TRUE;
  9193. Context.NlDcDomainEntry->InNt4DomainTime = GetTickCount();
  9194. LeaveCriticalSection(&NlDcCritSect);
  9195. NlPrint(( NL_MISC,
  9196. "NetpDcGetName: %ws: Domain is a _new_ NT 4.0 domain.\n",
  9197. Context.QueriedDisplayDomainName ));
  9198. } else {
  9199. NlPrint(( NL_MISC,
  9200. "NetpDcGetName: %ws: Domain is still NT 4.0 domain.\n",
  9201. Context.QueriedDisplayDomainName ));
  9202. }
  9203. NlDcCacheEntry = Context.ImperfectCacheEntry;
  9204. UsedNetbios = Context.ImperfectUsedNetbios;
  9205. Context.ImperfectCacheEntry = NULL;
  9206. NetStatus = NO_ERROR;
  9207. //
  9208. // Handle regular timeserv found when good timeserv preferred.
  9209. //
  9210. } else if (Context.QueriedFlags & DS_GOOD_TIMESERV_PREFERRED) {
  9211. NlPrint(( NL_MISC,
  9212. "NetpDcGetName: %ws: Domain has no good timeserv.\n",
  9213. Context.QueriedDisplayDomainName ));
  9214. NlDcCacheEntry = Context.ImperfectCacheEntry;
  9215. UsedNetbios = Context.ImperfectUsedNetbios;
  9216. Context.ImperfectCacheEntry = NULL;
  9217. NetStatus = NO_ERROR;
  9218. }
  9219. }
  9220. }
  9221. //
  9222. // If this is a failed attempt to find a GC using a netbios name,
  9223. // try to find a DC then using the forest name returned from that DC.
  9224. //
  9225. // If the DNS name is different than the netbios name,
  9226. // that DNS has already been given a chance.
  9227. //
  9228. if ( NetStatus == ERROR_NO_SUCH_DOMAIN &&
  9229. NlDnsGcName(Context.QueriedNlDnsNameType) &&
  9230. Context.QueriedNetbiosDomainName != NULL &&
  9231. ( Context.QueriedDnsDomainName == NULL ||
  9232. _wcsicmp( Context.QueriedDnsDomainName,
  9233. Context.QueriedNetbiosDomainName ) == 0 ) ) {
  9234. NetStatus = NetpGetGcUsingNetbios(
  9235. &Context,
  9236. Timeout,
  9237. RetryCount,
  9238. &NlDcCacheEntry );
  9239. //
  9240. // If this was successful, we certainly used DNS
  9241. //
  9242. if ( NetStatus == NO_ERROR ) {
  9243. UsedNetbios = FALSE;
  9244. }
  9245. }
  9246. //
  9247. // If still no such dc could be found,
  9248. // update the cache appropriately.
  9249. //
  9250. if ( NetStatus == ERROR_NO_SUCH_DOMAIN ) {
  9251. //
  9252. // If at least one NT 4.0 DC is available in the domain,
  9253. // and no NT 5.0 DCs (of any type) are available,
  9254. // and we asked for an NT 5.0 DC,
  9255. // flag that this is an NT 4.0 domain.
  9256. //
  9257. // Don't be tempted to leave out the last test. If we're not
  9258. // explicitly asking for an NT 5.0 DC, we might not ping any NT 5.0 DCs
  9259. // even though they exist in the domain.
  9260. //
  9261. //
  9262. if ( Context.NonDsResponse &&
  9263. !Context.DsResponse &&
  9264. (Context.QueriedFlags & DS_NT50_REQUIRED) != 0 ) {
  9265. EnterCriticalSection(&NlDcCritSect);
  9266. Context.NlDcDomainEntry->InNt4Domain = TRUE;
  9267. Context.NlDcDomainEntry->InNt4DomainTime = GetTickCount();
  9268. LeaveCriticalSection(&NlDcCritSect);
  9269. NlPrint(( NL_MISC,
  9270. "NetpDcGetName: %ws: Domain is an NT 4.0 domain.\n",
  9271. Context.QueriedDisplayDomainName ));
  9272. }
  9273. //
  9274. // If this call isn't a retry of a successful query.
  9275. // update the cache to reflect this failure.
  9276. //
  9277. if ( (Context.QueriedInternalFlags & DS_DONT_CACHE_FAILURE) == 0 ) {
  9278. //
  9279. // If this was a forced attempt to find a DC,
  9280. // delete any existing cache entry.
  9281. //
  9282. // There's no use keeping this entry around.
  9283. //
  9284. if ( Context.QueriedFlags & DS_FORCE_REDISCOVERY ) {
  9285. EnterCriticalSection( &NlDcCritSect );
  9286. if ( Context.NlDcDomainEntry->Dc[Context.DcQueryType].NlDcCacheEntry != NULL ) {
  9287. NlPrint(( NL_DNS_MORE,
  9288. "Cache: %ws %ws: Ditch cache entry %ld since force couldn't find DC\n",
  9289. Context.NlDcDomainEntry->UnicodeNetbiosDomainName,
  9290. Context.NlDcDomainEntry->UnicodeDnsDomainName,
  9291. Context.DcQueryType ));
  9292. NetpDcDerefCacheEntry( Context.NlDcDomainEntry->Dc[Context.DcQueryType].NlDcCacheEntry );
  9293. Context.NlDcDomainEntry->Dc[Context.DcQueryType].NlDcCacheEntry = NULL;
  9294. }
  9295. LeaveCriticalSection( &NlDcCritSect );
  9296. }
  9297. #ifdef _NETLOGON_SERVER
  9298. //
  9299. // Cache the fact that we couldn't find a DC.
  9300. //
  9301. if ( !Context.AvoidNegativeCache ) {
  9302. EnterCriticalSection( &NlDcCritSect );
  9303. Context.NlDcDomainEntry->Dc[Context.DcQueryType].NegativeCacheTime =
  9304. GetTickCount();
  9305. LeaveCriticalSection( &NlDcCritSect );
  9306. }
  9307. #endif // _NETLOGON_SERVER
  9308. }
  9309. }
  9310. //
  9311. // Initialize the first background failure time if:
  9312. //
  9313. // This is failed attempt and we don't have a reason
  9314. // not to cache it
  9315. //
  9316. // OR
  9317. //
  9318. // This is failed attempt and the caller wanted a NT5
  9319. // DC but this is a NT4 domain
  9320. //
  9321. #ifdef _NETLOGON_SERVER
  9322. if ( (NetStatus == ERROR_NO_SUCH_DOMAIN &&
  9323. (Context.QueriedInternalFlags & DS_DONT_CACHE_FAILURE) == 0 &&
  9324. !Context.AvoidNegativeCache)
  9325. || // OR
  9326. (NetStatus == ERROR_NO_SUCH_DOMAIN &&
  9327. (Context.QueriedFlags & DS_NT50_WANTED) != 0 &&
  9328. Context.NlDcDomainEntry != NULL &&
  9329. Context.NlDcDomainEntry->InNt4Domain) ) {
  9330. EnterCriticalSection( &NlDcCritSect );
  9331. //
  9332. // If this is the first failure,
  9333. // cache the time of the first failure.
  9334. //
  9335. if ( Context.NlDcDomainEntry->Dc[Context.DcQueryType].BackgroundRetryInitTime.QuadPart == 0 ) {
  9336. NlQuerySystemTime ( &Context.NlDcDomainEntry->Dc[Context.DcQueryType].BackgroundRetryInitTime );
  9337. Context.NlDcDomainEntry->Dc[Context.DcQueryType].ExpBackoffPeriod =
  9338. NlGlobalParameters.BackgroundRetryInitialPeriod;
  9339. }
  9340. //
  9341. // If this is a trusted domain (e.g., we're sure that the DNS name specified is a DNS name),
  9342. // and we got a response from a DNS server (implying net connectivity),
  9343. // and we didn't find a reason to avoid the permanent cache (e.g., found a SRV record),
  9344. // then we think we'll never be able to find a DC in this domain.
  9345. //
  9346. // (Notice the implication that the DNS server got the SRV entries before
  9347. // this machine got the trusted domain list entry.)
  9348. if ( (Context.QueriedInternalFlags & DS_IS_TRUSTED_DOMAIN) != 0 &&
  9349. Context.ResponseFromDnsServer &&
  9350. !Context.AvoidPermanentNegativeCache ) {
  9351. Context.NlDcDomainEntry->Dc[Context.DcQueryType].PermanentNegativeCache = TRUE;
  9352. NlPrint(( NL_DNS,
  9353. "Cache: %ws %ws: Cache entry %ld marked permanently negative.\n",
  9354. Context.NlDcDomainEntry->UnicodeNetbiosDomainName,
  9355. Context.NlDcDomainEntry->UnicodeDnsDomainName,
  9356. Context.DcQueryType ));
  9357. }
  9358. LeaveCriticalSection( &NlDcCritSect );
  9359. }
  9360. #endif // _NETLOGON_SERVER
  9361. ////////////////////////////////
  9362. // //
  9363. // Now, hanle success cases //
  9364. // //
  9365. ////////////////////////////////
  9366. //
  9367. // Update the cache. See if we really want to use this entry.
  9368. //
  9369. if ( NetStatus == NO_ERROR ) {
  9370. //
  9371. // If this entry hasn't been inserted, we haven't
  9372. // yet used it to set the site name as appropriate.
  9373. //
  9374. if ( (NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_ENTRY_INSERTED) == 0 ) {
  9375. #ifdef _NETLOGON_SERVER
  9376. //
  9377. // If the domain being queried is the domain this machine is
  9378. // a member of,
  9379. // save the name of the site for the next call.
  9380. //
  9381. // Avoid setting the site name to NULL if the DC is NT4 DC since
  9382. // NT4 is not site aware. If the site name is NULL but the DC
  9383. // is NT5 DC, set the site to NULL to indicate that this machine
  9384. // is not in a site.
  9385. //
  9386. if ( ( NlDcCacheEntry->UnicodeClientSiteName != NULL ||
  9387. NlDcCacheEntry->ReturnFlags & DS_DS_FLAG ) &&
  9388. (Context.QueriedInternalFlags & DS_IS_PRIMARY_DOMAIN) != 0 ) {
  9389. NlSetDynamicSiteName( NlDcCacheEntry->UnicodeClientSiteName );
  9390. }
  9391. #endif // _NETLOGON_SERVER
  9392. //
  9393. // Insert the cache entry into the cache.
  9394. //
  9395. NetpDcInsertCacheEntry( &Context, NlDcCacheEntry );
  9396. }
  9397. //
  9398. // If we successfully found an NT 5.0 DC,
  9399. // flag that this is not an NT 4.0 domain.
  9400. //
  9401. EnterCriticalSection(&NlDcCritSect);
  9402. if ( (Context.QueriedFlags & DS_NT50_REQUIRED) != 0 &&
  9403. Context.NlDcDomainEntry->InNt4Domain ) {
  9404. Context.NlDcDomainEntry->InNt4Domain = FALSE;
  9405. Context.NlDcDomainEntry->InNt4DomainTime = 0;
  9406. NlPrint(( NL_MISC,
  9407. "NetpDcGetName: %ws: Domain is an NT 5.0 domain.\n",
  9408. Context.QueriedDisplayDomainName ));
  9409. }
  9410. LeaveCriticalSection(&NlDcCritSect);
  9411. //
  9412. // If the caller requires that the DC be in the root domain,
  9413. // and this one isn't,
  9414. // fail.
  9415. //
  9416. if ( (Context.QueriedInternalFlags & DS_REQUIRE_ROOT_DOMAIN) != 0 &&
  9417. NlDcCacheEntry->UnicodeDnsDomainName != NULL &&
  9418. NlDcCacheEntry->UnicodeDnsForestName != NULL &&
  9419. !NlEqualDnsName( NlDcCacheEntry->UnicodeDnsDomainName,
  9420. NlDcCacheEntry->UnicodeDnsForestName ) ) {
  9421. NlPrint(( NL_MISC,
  9422. "NetpDcGetName: %ws: Domain isn't the root domain %ws %ws.\n",
  9423. Context.QueriedDisplayDomainName,
  9424. NlDcCacheEntry->UnicodeDnsDomainName,
  9425. NlDcCacheEntry->UnicodeDnsForestName ));
  9426. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9427. }
  9428. }
  9429. //
  9430. // If we used netbios to find a DC,
  9431. // see if failling back to DNS would get a better DC.
  9432. //
  9433. if ( NetStatus == NO_ERROR && UsedNetbios ) {
  9434. NetStatus = NetpGetBetterDc( &Context,
  9435. Timeout,
  9436. RetryCount,
  9437. &NlDcCacheEntry );
  9438. }
  9439. //
  9440. // Prepare the returned data.
  9441. //
  9442. // Convert cache entry into controller info if requested
  9443. //
  9444. if ( NetStatus == NO_ERROR && DomainControllerInfo != NULL ) {
  9445. WCHAR IpAddressString[NL_SOCK_ADDRESS_LENGTH+1];
  9446. WCHAR IpAddressStringSize;
  9447. ULONG DomainControllerInfoSize;
  9448. ULONG DnsHostNameSize;
  9449. ULONG NetbiosDcNameSize=0;
  9450. ULONG DnsDomainNameSize;
  9451. ULONG NetbiosDomainNameSize = 0;
  9452. ULONG DcSiteNameSize = 0;
  9453. ULONG ClientSiteNameSize = 0;
  9454. ULONG DnsForestNameSize = 0;
  9455. ULONG ReturnFlags = NlDcCacheEntry->ReturnFlags;
  9456. BOOL LocalUsedNetbios = UsedNetbios;
  9457. LPBYTE Where;
  9458. //
  9459. // If the user requested DNS names, then we need to send
  9460. // back dns names
  9461. //
  9462. if (( Flags & DS_RETURN_DNS_NAME) == DS_RETURN_DNS_NAME) {
  9463. LocalUsedNetbios = FALSE;
  9464. }
  9465. //
  9466. // Compute the size of the controller info entry.
  9467. //
  9468. DomainControllerInfoSize = sizeof(DOMAIN_CONTROLLER_INFOW);
  9469. if ( NlDcCacheEntry->UnicodeDnsHostName != NULL &&
  9470. (Context.QueriedFlags & DS_RETURN_FLAT_NAME) == 0 &&
  9471. !LocalUsedNetbios ) {
  9472. DnsHostNameSize = (wcslen(NlDcCacheEntry->UnicodeDnsHostName) + 1) * sizeof(WCHAR);
  9473. // DomainControllerName
  9474. DomainControllerInfoSize += DnsHostNameSize + 2 * sizeof(WCHAR);
  9475. } else if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  9476. NetbiosDcNameSize = (wcslen(NlDcCacheEntry->UnicodeNetbiosDcName) + 1) * sizeof(WCHAR);
  9477. // DomainControllerName
  9478. DomainControllerInfoSize += NetbiosDcNameSize + 2 * sizeof(WCHAR);
  9479. } else {
  9480. // This can't ever happen. (But better to fail than to AV.)
  9481. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9482. goto Cleanup;
  9483. }
  9484. if ( NlDcCacheEntry->SockAddr.iSockaddrLength != 0 ) {
  9485. NetStatus = NetpSockAddrToWStr(
  9486. NlDcCacheEntry->SockAddr.lpSockaddr,
  9487. NlDcCacheEntry->SockAddr.iSockaddrLength,
  9488. IpAddressString );
  9489. if ( NetStatus != NO_ERROR ) {
  9490. NlPrint(( NL_CRITICAL,
  9491. "NetpDcGetName: %ws: Cannot NetpSockAddrToWStr. 0x%lx\n",
  9492. Context.QueriedDisplayDomainName,
  9493. NetStatus ));
  9494. goto Cleanup;
  9495. }
  9496. IpAddressStringSize = (wcslen(IpAddressString) + 1) * sizeof(WCHAR);
  9497. // DomainControllerAddress
  9498. DomainControllerInfoSize += IpAddressStringSize + 2 * sizeof(WCHAR);
  9499. } else if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  9500. if ( NetbiosDcNameSize == 0 ) {
  9501. NetbiosDcNameSize = (wcslen(NlDcCacheEntry->UnicodeNetbiosDcName) + 1) * sizeof(WCHAR);
  9502. }
  9503. // DomainControllerAddress
  9504. DomainControllerInfoSize += NetbiosDcNameSize + 2 * sizeof(WCHAR);
  9505. } else {
  9506. // This can't ever happen. (But better to fail than to AV.)
  9507. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9508. goto Cleanup;
  9509. }
  9510. if ( NlDcCacheEntry->UnicodeDnsDomainName != NULL &&
  9511. (Context.QueriedFlags & DS_RETURN_FLAT_NAME) == 0 &&
  9512. !LocalUsedNetbios ) {
  9513. DnsDomainNameSize = (wcslen(NlDcCacheEntry->UnicodeDnsDomainName) + 1) * sizeof(WCHAR);
  9514. // DomainName
  9515. DomainControllerInfoSize += DnsDomainNameSize;
  9516. } else if ( NlDcCacheEntry->UnicodeNetbiosDomainName != NULL ) {
  9517. NetbiosDomainNameSize = (wcslen(NlDcCacheEntry->UnicodeNetbiosDomainName) + 1) * sizeof(WCHAR);
  9518. // DomainName
  9519. DomainControllerInfoSize += NetbiosDomainNameSize;
  9520. } else if ( LocalUsedNetbios &&
  9521. Context.QueriedNetbiosDomainName != NULL ) {
  9522. // Lanman PDC or SAMBA Domain Master brower.
  9523. NetbiosDomainNameSize = (wcslen(Context.QueriedNetbiosDomainName) + 1) * sizeof(WCHAR);
  9524. // DomainName
  9525. DomainControllerInfoSize += NetbiosDomainNameSize;
  9526. } else {
  9527. // This can't ever happen. (But better to fail than to AV.)
  9528. NetStatus = ERROR_NO_SUCH_DOMAIN;
  9529. goto Cleanup;
  9530. }
  9531. if ( NlDcCacheEntry->UnicodeDnsForestName != NULL ) {
  9532. DnsForestNameSize = (wcslen(NlDcCacheEntry->UnicodeDnsForestName) + 1) * sizeof(WCHAR);
  9533. // TreeName
  9534. DomainControllerInfoSize += DnsForestNameSize;
  9535. }
  9536. if ( NlDcCacheEntry->UnicodeDcSiteName != NULL ) {
  9537. DcSiteNameSize = (wcslen(NlDcCacheEntry->UnicodeDcSiteName) + 1) * sizeof(WCHAR);
  9538. // DcSiteName
  9539. DomainControllerInfoSize += DcSiteNameSize;
  9540. }
  9541. if ( NlDcCacheEntry->UnicodeClientSiteName != NULL ) {
  9542. ClientSiteNameSize = (wcslen(NlDcCacheEntry->UnicodeClientSiteName) + 1) * sizeof(WCHAR);
  9543. // ClientSiteName
  9544. DomainControllerInfoSize += ClientSiteNameSize;
  9545. }
  9546. //
  9547. // Allocate the controller info entry.
  9548. //
  9549. NetStatus = NetApiBufferAllocate(
  9550. DomainControllerInfoSize,
  9551. DomainControllerInfo );
  9552. if ( NetStatus == NO_ERROR ) {
  9553. Where = (LPBYTE)((*DomainControllerInfo) + 1);
  9554. //
  9555. // Copy information into the allocated buffer.
  9556. //
  9557. (*DomainControllerInfo)->DomainControllerName = (LPWSTR)Where;
  9558. *((LPWSTR)Where)++ = L'\\';
  9559. *((LPWSTR)Where)++ = L'\\';
  9560. if (NlDcCacheEntry->UnicodeDnsHostName != NULL &&
  9561. (Context.QueriedFlags & DS_RETURN_FLAT_NAME) == 0 &&
  9562. !LocalUsedNetbios ) {
  9563. RtlCopyMemory( Where,
  9564. NlDcCacheEntry->UnicodeDnsHostName,
  9565. DnsHostNameSize );
  9566. Where += DnsHostNameSize;
  9567. ReturnFlags |= DS_DNS_CONTROLLER_FLAG;
  9568. } else {
  9569. RtlCopyMemory( Where,
  9570. NlDcCacheEntry->UnicodeNetbiosDcName,
  9571. NetbiosDcNameSize );
  9572. Where += NetbiosDcNameSize;
  9573. }
  9574. (*DomainControllerInfo)->DomainControllerAddress = (LPWSTR)Where;
  9575. *((LPWSTR)Where)++ = L'\\';
  9576. *((LPWSTR)Where)++ = L'\\';
  9577. if ( NlDcCacheEntry->SockAddr.iSockaddrLength != 0 ) {
  9578. RtlCopyMemory( Where,
  9579. IpAddressString,
  9580. IpAddressStringSize );
  9581. Where += IpAddressStringSize;
  9582. (*DomainControllerInfo)->DomainControllerAddressType = DS_INET_ADDRESS;
  9583. } else {
  9584. RtlCopyMemory( Where,
  9585. NlDcCacheEntry->UnicodeNetbiosDcName,
  9586. NetbiosDcNameSize );
  9587. Where += NetbiosDcNameSize;
  9588. (*DomainControllerInfo)->DomainControllerAddressType = DS_NETBIOS_ADDRESS;
  9589. }
  9590. (*DomainControllerInfo)->DomainGuid = NlDcCacheEntry->DomainGuid;
  9591. (*DomainControllerInfo)->DomainName = (LPWSTR)Where;
  9592. if (NlDcCacheEntry->UnicodeDnsDomainName != NULL &&
  9593. (Context.QueriedFlags & DS_RETURN_FLAT_NAME) == 0 &&
  9594. !LocalUsedNetbios ) {
  9595. RtlCopyMemory( Where,
  9596. NlDcCacheEntry->UnicodeDnsDomainName,
  9597. DnsDomainNameSize );
  9598. Where += DnsDomainNameSize;
  9599. ReturnFlags |= DS_DNS_DOMAIN_FLAG;
  9600. } else if ( NlDcCacheEntry->UnicodeNetbiosDomainName != NULL ) {
  9601. RtlCopyMemory( Where,
  9602. NlDcCacheEntry->UnicodeNetbiosDomainName,
  9603. NetbiosDomainNameSize );
  9604. Where += NetbiosDomainNameSize;
  9605. } else if ( LocalUsedNetbios &&
  9606. Context.QueriedNetbiosDomainName != NULL ) {
  9607. RtlCopyMemory( Where,
  9608. Context.QueriedNetbiosDomainName,
  9609. NetbiosDomainNameSize );
  9610. Where += NetbiosDomainNameSize;
  9611. } else {
  9612. NetStatus = ERROR_INTERNAL_ERROR;
  9613. goto Cleanup;
  9614. }
  9615. if (NlDcCacheEntry->UnicodeDnsForestName != NULL ) {
  9616. (*DomainControllerInfo)->DnsForestName = (LPWSTR)Where;
  9617. RtlCopyMemory( Where,
  9618. NlDcCacheEntry->UnicodeDnsForestName,
  9619. DnsForestNameSize );
  9620. Where += DnsForestNameSize;
  9621. ReturnFlags |= DS_DNS_FOREST_FLAG;
  9622. } else {
  9623. (*DomainControllerInfo)->DnsForestName = NULL;
  9624. }
  9625. if (NlDcCacheEntry->UnicodeDcSiteName != NULL ) {
  9626. (*DomainControllerInfo)->DcSiteName = (LPWSTR)Where;
  9627. RtlCopyMemory( Where,
  9628. NlDcCacheEntry->UnicodeDcSiteName,
  9629. DcSiteNameSize );
  9630. Where += DcSiteNameSize;
  9631. } else {
  9632. (*DomainControllerInfo)->DcSiteName = NULL;
  9633. }
  9634. if (NlDcCacheEntry->UnicodeClientSiteName != NULL ) {
  9635. (*DomainControllerInfo)->ClientSiteName = (LPWSTR)Where;
  9636. RtlCopyMemory( Where,
  9637. NlDcCacheEntry->UnicodeClientSiteName,
  9638. ClientSiteNameSize );
  9639. Where += ClientSiteNameSize;
  9640. } else {
  9641. (*DomainControllerInfo)->ClientSiteName = NULL;
  9642. }
  9643. (*DomainControllerInfo)->Flags = ReturnFlags;
  9644. }
  9645. }
  9646. //
  9647. // Return the cache entry if requested
  9648. //
  9649. if ( NetStatus == NO_ERROR && DomainControllerCacheEntry != NULL ) {
  9650. *DomainControllerCacheEntry = NlDcCacheEntry;
  9651. NlDcCacheEntry = NULL;
  9652. }
  9653. //
  9654. // Free local resources
  9655. //
  9656. NetpDcUninitializeContext( &Context );
  9657. if ( PingResponseMessage != NULL ) {
  9658. NetpMemoryFree( PingResponseMessage );
  9659. }
  9660. if ( NlDcCacheEntry != NULL ) {
  9661. NetpDcDerefCacheEntry( NlDcCacheEntry );
  9662. }
  9663. if ( LocalQueriedlNetbiosDomainName != NULL ) {
  9664. NetApiBufferFree( LocalQueriedlNetbiosDomainName );
  9665. }
  9666. #ifdef _NETLOGON_SERVER
  9667. } except( NL_EXCEPTION) {
  9668. NetStatus = NetpNtStatusToApiStatus(GetExceptionCode());
  9669. }
  9670. #endif // _NETLOGON_SERVER
  9671. return NetStatus;
  9672. }
  9673. DWORD
  9674. DsIGetDcName(
  9675. IN LPCWSTR ComputerName OPTIONAL,
  9676. IN LPCWSTR AccountName OPTIONAL,
  9677. IN ULONG AllowableAccountControlBits,
  9678. IN LPCWSTR DomainName OPTIONAL,
  9679. IN LPCWSTR DnsForestName OPTIONAL,
  9680. IN GUID *DomainGuid OPTIONAL,
  9681. IN LPCWSTR SiteName OPTIONAL,
  9682. IN ULONG Flags,
  9683. IN ULONG InternalFlags,
  9684. IN PVOID SendDatagramContext OPTIONAL,
  9685. IN DWORD Timeout,
  9686. IN LPWSTR NetbiosPrimaryDomainName OPTIONAL,
  9687. IN LPWSTR DnsPrimaryDomainName OPTIONAL,
  9688. IN GUID *PrimaryDomainGuid OPTIONAL,
  9689. IN LPWSTR DnsTrustedDomainName OPTIONAL,
  9690. IN LPWSTR NetbiosTrustedDomainName OPTIONAL,
  9691. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  9692. )
  9693. /*++
  9694. Routine Description:
  9695. Same as DsGetDcNameW.
  9696. This is the internal routine called by DsGetDcNameW (in the context of
  9697. the original caller) or DsrGetDcName (in the context of the Netlogon
  9698. service) to actaully implement DsGetDcNameW.
  9699. Arguments:
  9700. Same as DsGetDcNameW except for the following additional parameters:
  9701. ComputerName - Specifies the NETBIOS name of this computer.
  9702. If NULL, the name will be dynamically determined.
  9703. AccountName - Account name to pass on the ping request.
  9704. If NULL, no account name will be sent.
  9705. AllowableAccountControlBits - Mask of allowable account types for AccountName.
  9706. Valid bits are those specified by UF_MACHINE_ACCOUNT_MASK.
  9707. Invalid bits are ignored. If more than one bit is specified, the
  9708. account can be of any of the specified types.
  9709. DnsForestName - The DNS-style name of the tree the queried domain is in.
  9710. SendDatagramContext - Specifies context to pass a NlBrowserSendDatagram
  9711. Timeout - Maximum time (in milliseconds) caller is willing to wait on
  9712. this operation.
  9713. InternalFlags - Internal Flags used to pass additional information
  9714. NetbiosPrimaryDomainName - Netbios name of the domain this machine belongs
  9715. to.
  9716. DnsPrimaryDomainName - DNS name of the domain this machine belongs to.
  9717. PrimaryDomainGuid - GUID of the primary domain.
  9718. DnsTrustedName - DNS name of the queried domain as was found in the
  9719. trust list.
  9720. If not specified, the specified domain isn't a trusted domain.
  9721. NetbiosTrustedDomainName - Netbios name of the queried domain as was found in the
  9722. trust list.
  9723. If not specified, the specified domain isn't a trusted domain.
  9724. Return Value:
  9725. Same as DsGetDcNameW.
  9726. --*/
  9727. {
  9728. NET_API_STATUS NetStatus;
  9729. LPCWSTR NetbiosDomainName = NULL;
  9730. LPCWSTR DnsDomainName = NULL;
  9731. ULONG SamAllowableAccountControlBits;
  9732. ULONG FormatCount = 0;
  9733. BOOLEAN CallerSpecifiedDomain = FALSE;
  9734. #ifdef WIN32_CHICAGO
  9735. LPSTR pDomainName = NULL;
  9736. #endif // WIN32_CHICAGO
  9737. //
  9738. // Ensure caller didn't specify both name type flags.
  9739. //
  9740. if ( ((Flags & (DS_IS_FLAT_NAME|DS_IS_DNS_NAME)) ==
  9741. (DS_IS_FLAT_NAME|DS_IS_DNS_NAME) ) ||
  9742. ((Flags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) ==
  9743. (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) ) {
  9744. NetStatus = ERROR_INVALID_FLAGS;
  9745. goto Cleanup;
  9746. }
  9747. //
  9748. // If the caller specified, DS_RETURN_DNS_NAME, we should really
  9749. // set DS_IP_REQUIRED.
  9750. if ((Flags & DS_RETURN_DNS_NAME) == DS_RETURN_DNS_NAME) {
  9751. Flags |= DS_IP_REQUIRED;
  9752. }
  9753. //
  9754. // If the caller didn't specify a domain name,
  9755. // use our domain name.
  9756. //
  9757. if ( DomainName == NULL || *DomainName == L'\0' ) {
  9758. #ifndef WIN32_CHICAGO
  9759. //
  9760. // If the caller wants a GC,
  9761. // the domain to use is the tree name.
  9762. //
  9763. // If we don't yet know our tree name,
  9764. // it is better to try with our primary name than to not try at all
  9765. //
  9766. //
  9767. if ( (Flags & DS_GC_SERVER_REQUIRED) != 0 &&
  9768. DnsForestName != NULL ) {
  9769. NetbiosDomainName = NULL;
  9770. DnsDomainName = DnsForestName;
  9771. //
  9772. // Otherwise, the domain to use is the primary domain name.
  9773. // Do this even if the primary domain name is a workgroup
  9774. // name because there might be a DC in this workgroup/domain
  9775. // which the caller is trying to discover.
  9776. //
  9777. } else {
  9778. NetbiosDomainName = NetbiosPrimaryDomainName;
  9779. DnsDomainName = DnsPrimaryDomainName;
  9780. InternalFlags |= DS_IS_PRIMARY_DOMAIN | DS_IS_TRUSTED_DOMAIN;
  9781. }
  9782. #else // WIN32_CHICAGO
  9783. #define NETWORK_PROVIDER_KEY ("System\\CurrentControlSet\\Services\\Msnp32\\NetworkProvider")
  9784. #define AUTH_AGENT_VALUE ("AuthenticatingAgent")
  9785. do {
  9786. HKEY hRegKey = NULL;
  9787. DWORD dwError = 0, dwSize = 0, dwType;
  9788. if (ERROR_SUCCESS != (dwError = RegOpenKeyEx(
  9789. HKEY_LOCAL_MACHINE,
  9790. NETWORK_PROVIDER_KEY,
  9791. 0,
  9792. KEY_ALL_ACCESS,
  9793. &hRegKey ) ) )
  9794. {
  9795. break;
  9796. }
  9797. // get domain name size
  9798. if ( ERROR_SUCCESS != ( dwError = RegQueryValueEx (
  9799. hRegKey,
  9800. AUTH_AGENT_VALUE,
  9801. NULL,
  9802. &dwType,
  9803. NULL,
  9804. &dwSize )))
  9805. {
  9806. break;
  9807. }
  9808. if (dwSize == 0 )
  9809. {
  9810. break;
  9811. }
  9812. pDomainName = (LPSTR) NetpMemoryAllocate((dwSize+1 ) * sizeof(WCHAR));
  9813. if (pDomainName == NULL)
  9814. {
  9815. break;
  9816. }
  9817. // get domainname
  9818. if ( ERROR_SUCCESS != ( dwError = RegQueryValueEx (
  9819. hRegKey,
  9820. AUTH_AGENT_VALUE,
  9821. NULL,
  9822. &dwType,
  9823. (PUCHAR) pDomainName,
  9824. &dwSize )))
  9825. {
  9826. break;
  9827. }
  9828. NetbiosDomainName = (LPCWSTR)NetpAllocWStrFromOemStr(pDomainName);
  9829. } while (FALSE);
  9830. if (NetbiosDomainName == NULL)
  9831. {
  9832. NetStatus = ERROR_INVALID_DOMAINNAME;
  9833. goto Cleanup;
  9834. }
  9835. //
  9836. // Indicate that the caller passed NULL as the domain name
  9837. //
  9838. InternalFlags |= DS_CALLER_PASSED_NULL_DOMAIN;
  9839. #endif // WIN32_CHICAGO
  9840. //
  9841. // If the caller did specify a domain name,
  9842. // validate it.
  9843. //
  9844. } else {
  9845. CallerSpecifiedDomain = TRUE;
  9846. //
  9847. // If the specified domain name is a syntactically valid DNS name,
  9848. // use it as a DNS name.
  9849. //
  9850. // Don't even try to check if caller claims it is a NETBIOS name.
  9851. //
  9852. if ( (Flags & DS_IS_FLAT_NAME) == 0 &&
  9853. NetpDcValidDnsDomain( DomainName )) {
  9854. DnsDomainName = DomainName;
  9855. FormatCount ++;
  9856. //
  9857. // If the primary domain name specified is not a
  9858. // workgroup name and
  9859. // If the caller specified the DNS primary domain name,
  9860. // we don't need to guess the Netbios domain name.
  9861. //
  9862. if ( (InternalFlags & DS_PRIMARY_NAME_IS_WORKGROUP) == 0 &&
  9863. DnsPrimaryDomainName != NULL &&
  9864. NlEqualDnsName( DnsPrimaryDomainName, DomainName ) ) {
  9865. //
  9866. // If the DNS name is specified on input,
  9867. // don't fall back to the Netbios name for discovery.
  9868. // There is no benefit to trying the Netbios name.
  9869. // Also, when using the netbios name, we don't know when all
  9870. // of the DCs have responded negatively. So, we can't early
  9871. // out.
  9872. //
  9873. if ( NetbiosPrimaryDomainName != NULL &&
  9874. NlEqualDnsName( DnsPrimaryDomainName, NetbiosPrimaryDomainName ) ) {
  9875. // Unless, of course the netbios and DNS domain names are spelled the same.
  9876. NetbiosDomainName = NetbiosPrimaryDomainName;
  9877. }
  9878. InternalFlags |= DS_IS_PRIMARY_DOMAIN | DS_IS_TRUSTED_DOMAIN;
  9879. Flags |= DS_IS_DNS_NAME;
  9880. //
  9881. // But return the DNS name unless the caller has explicitly
  9882. // asked for the Netbios name.
  9883. //
  9884. if ( (Flags & DS_RETURN_FLAT_NAME) == 0 ) {
  9885. Flags |= DS_RETURN_DNS_NAME;
  9886. }
  9887. //
  9888. // If the caller specified the DNS trusted domain name,
  9889. // we don't need to guess the Netbios domain name.
  9890. //
  9891. } else if ( DnsTrustedDomainName != NULL &&
  9892. NlEqualDnsName( DnsTrustedDomainName, DomainName ) ) {
  9893. //
  9894. // If the DNS name is specified on input,
  9895. // don't fall back to the Netbios name for discovery.
  9896. // There is no benefit to trying the Netbios name.
  9897. // Also, when using the netbios name, we don't know when all
  9898. // of the DCs have responded negatively. So, we can't early
  9899. // out.
  9900. //
  9901. if ( NetbiosTrustedDomainName != NULL &&
  9902. NlEqualDnsName( DnsTrustedDomainName, NetbiosTrustedDomainName ) ) {
  9903. // Unless, of course the netbios and DNS domain names are spelled the same.
  9904. NetbiosDomainName = NetbiosTrustedDomainName;
  9905. }
  9906. InternalFlags |= DS_IS_TRUSTED_DOMAIN;
  9907. Flags |= DS_IS_DNS_NAME;
  9908. //
  9909. // But return the DNS name unless the caller has explicitly
  9910. // asked for the Netbios name.
  9911. //
  9912. if ( (Flags & DS_RETURN_FLAT_NAME) == 0 ) {
  9913. Flags |= DS_RETURN_DNS_NAME;
  9914. }
  9915. }
  9916. }
  9917. //
  9918. // If the specified domain name is a syntactically valid Netbios name,
  9919. // use it as a Netbios name.
  9920. // (Don't even try to check if caller claims it is a DNS name)
  9921. //
  9922. if ( (Flags & DS_IS_DNS_NAME) == 0 &&
  9923. NetpIsDomainNameValid( (LPWSTR) DomainName )) {
  9924. NetbiosDomainName = DomainName;
  9925. FormatCount ++;
  9926. //
  9927. // If the primary domain name specified is not a
  9928. // workgroup name and
  9929. // If the caller specified the Netbios primary domain name,
  9930. // we don't need to guess the DNS domain name.
  9931. //
  9932. if ( (InternalFlags & DS_PRIMARY_NAME_IS_WORKGROUP) == 0 &&
  9933. NetbiosPrimaryDomainName != NULL &&
  9934. NlNameCompare( (LPWSTR) DomainName,
  9935. NetbiosPrimaryDomainName,
  9936. NAMETYPE_DOMAIN ) == 0 ) {
  9937. //
  9938. // Use both the DNS name and the Netbios name to do the discovery
  9939. // (Use the DNS name since it is rename safe.)
  9940. //
  9941. DnsDomainName = DnsPrimaryDomainName;
  9942. InternalFlags |= DS_IS_PRIMARY_DOMAIN | DS_IS_TRUSTED_DOMAIN;
  9943. Flags |= DS_IS_FLAT_NAME;
  9944. //
  9945. // But return the netbios name unless the caller has explicitly
  9946. // asked for the DNS name.
  9947. //
  9948. if ( (Flags & DS_RETURN_DNS_NAME) == 0 ) {
  9949. Flags |= DS_RETURN_FLAT_NAME;
  9950. }
  9951. //
  9952. // If the caller specified a DNS trust domain name that corresponds
  9953. // to the queried Netbios domain name, use it.
  9954. //
  9955. } else if ( NetbiosTrustedDomainName != NULL &&
  9956. NlNameCompare( (LPWSTR) DomainName,
  9957. NetbiosTrustedDomainName,
  9958. NAMETYPE_DOMAIN ) == 0 ) {
  9959. //
  9960. // Use both the DNS name and the Netbios name to do the discovery
  9961. // (Use the DNS name since it is rename safe.)
  9962. //
  9963. DnsDomainName = DnsTrustedDomainName;
  9964. InternalFlags |= DS_IS_TRUSTED_DOMAIN;
  9965. Flags |= DS_IS_FLAT_NAME;
  9966. //
  9967. // But return the netbios name unless the caller has explicitly
  9968. // asked for the DNS name.
  9969. //
  9970. if ( (Flags & DS_RETURN_DNS_NAME) == 0 ) {
  9971. Flags |= DS_RETURN_FLAT_NAME;
  9972. }
  9973. }
  9974. }
  9975. }
  9976. //
  9977. // Disallow single labeled DNS domain names if the caller
  9978. // specified one unless the domain is proven to exist
  9979. // (i.e. trusted) or we are forced to allow such names.
  9980. //
  9981. // Note that we don't really trust the caller here:
  9982. // We won't allow single labeled DNS domain name even if
  9983. // the caller claims it's a DNS name (via DS_IS_DNS_NAME)
  9984. //
  9985. if ( DnsDomainName != NULL &&
  9986. CallerSpecifiedDomain &&
  9987. (InternalFlags & DS_IS_TRUSTED_DOMAIN) == 0 ) {
  9988. BOOLEAN SingleLabel = FALSE;
  9989. LPWSTR Period = NULL;
  9990. LPWSTR SecondPeriod = NULL;
  9991. Period = wcsstr( DnsDomainName, L"." );
  9992. //
  9993. // If there is no period in the name,
  9994. // the name is single labeled
  9995. //
  9996. if ( Period == NULL ) {
  9997. SingleLabel = TRUE;
  9998. //
  9999. // If there is a period, the name is
  10000. // single labeled if this is the only
  10001. // period and it's either the first
  10002. // or the last character in the name
  10003. //
  10004. } else {
  10005. SecondPeriod = wcsstr( Period+1, L"." );
  10006. if ( SecondPeriod == NULL ) {
  10007. if ( Period == DnsDomainName ||
  10008. *(Period+1) == UNICODE_NULL ) {
  10009. SingleLabel = TRUE;
  10010. }
  10011. }
  10012. }
  10013. //
  10014. // If this is single label DNS name,
  10015. // disallow it unless we are forced
  10016. // otherwise via the registry config
  10017. //
  10018. if ( SingleLabel ) {
  10019. DWORD LocalAllowSingleLabelDnsDomain = 0;
  10020. //
  10021. // In netlogon, the boolean is kept in global parameters
  10022. //
  10023. #ifdef _NETLOGON_SERVER
  10024. if ( !NlGlobalParameters.AllowSingleLabelDnsDomain ) {
  10025. NlPrint(( NL_MISC, "DsIGetDcName: Ignore single label DNS domain name %ws\n", DnsDomainName ));
  10026. DnsDomainName = NULL;
  10027. }
  10028. //
  10029. // If we are not running in netlogon, we need to read
  10030. // the boolean directly from the registry
  10031. //
  10032. #else
  10033. NlReadDwordNetlogonRegValue( "AllowSingleLabelDnsDomain",
  10034. &LocalAllowSingleLabelDnsDomain );
  10035. if ( !LocalAllowSingleLabelDnsDomain ) {
  10036. NlPrint(( NL_MISC, "DsIGetDcName: Ignore single label DNS domain name %ws\n", DnsDomainName ));
  10037. DnsDomainName = NULL;
  10038. }
  10039. #endif // _NETLOGON_SERVER
  10040. }
  10041. }
  10042. //
  10043. // If the name is neither a netbios or DNS name,
  10044. // give up.
  10045. //
  10046. if ( NetbiosDomainName == NULL && DnsDomainName == NULL ) {
  10047. NetStatus = ERROR_INVALID_DOMAINNAME;
  10048. goto Cleanup;
  10049. }
  10050. //
  10051. // If this is the primary domain,
  10052. // and the caller didn't specify a GUID,
  10053. // use the GUID of the primary domain.
  10054. //
  10055. if ( (InternalFlags & DS_IS_PRIMARY_DOMAIN) != 0 &&
  10056. DomainGuid == NULL ) {
  10057. DomainGuid = PrimaryDomainGuid;
  10058. }
  10059. //
  10060. // If the format is ambiguous,
  10061. // pass that info on down.
  10062. //
  10063. if ( FormatCount > 1 ) {
  10064. InternalFlags |= DS_NAME_FORMAT_AMBIGUOUS;
  10065. }
  10066. //
  10067. // Map the AllowableAccountControlBits to the SAM representation.
  10068. //
  10069. SamAllowableAccountControlBits = 0;
  10070. if ( AllowableAccountControlBits & UF_TEMP_DUPLICATE_ACCOUNT ) {
  10071. SamAllowableAccountControlBits |= USER_TEMP_DUPLICATE_ACCOUNT;
  10072. }
  10073. if ( AllowableAccountControlBits & UF_NORMAL_ACCOUNT ) {
  10074. SamAllowableAccountControlBits |= USER_NORMAL_ACCOUNT;
  10075. }
  10076. if (AllowableAccountControlBits & UF_INTERDOMAIN_TRUST_ACCOUNT){
  10077. SamAllowableAccountControlBits |= USER_INTERDOMAIN_TRUST_ACCOUNT;
  10078. }
  10079. if (AllowableAccountControlBits & UF_WORKSTATION_TRUST_ACCOUNT){
  10080. SamAllowableAccountControlBits |= USER_WORKSTATION_TRUST_ACCOUNT;
  10081. }
  10082. if ( AllowableAccountControlBits & UF_SERVER_TRUST_ACCOUNT ) {
  10083. SamAllowableAccountControlBits |= USER_SERVER_TRUST_ACCOUNT;
  10084. }
  10085. //
  10086. // Try finding a DC with this information.
  10087. //
  10088. NetStatus = NetpDcGetName(
  10089. SendDatagramContext,
  10090. ComputerName,
  10091. AccountName,
  10092. SamAllowableAccountControlBits,
  10093. NetbiosDomainName,
  10094. DnsDomainName,
  10095. DnsForestName,
  10096. NULL, // No Domain Sid
  10097. DomainGuid,
  10098. SiteName,
  10099. Flags,
  10100. InternalFlags,
  10101. Timeout,
  10102. MAX_DC_RETRIES,
  10103. DomainControllerInfo,
  10104. NULL );
  10105. Cleanup:
  10106. #ifdef WIN32_CHICAGO
  10107. if (pDomainName)
  10108. {
  10109. NetpMemoryFree(pDomainName);
  10110. pDomainName = NULL;
  10111. if ( NetbiosDomainName != NULL ) {
  10112. NetpMemoryFree((LPWSTR)NetbiosDomainName);
  10113. }
  10114. }
  10115. #endif // WIN32_CHICAGO
  10116. return NetStatus;
  10117. }
  10118. #ifndef WIN32_CHICAGO
  10119. NET_API_STATUS
  10120. NlParseSubnetString(
  10121. IN LPCWSTR SubnetName,
  10122. OUT PULONG SubnetAddress,
  10123. OUT PULONG SubnetMask,
  10124. OUT LPBYTE SubnetBitCount
  10125. )
  10126. /*++
  10127. Routine Description:
  10128. Convert the subnet name to address and bit count.
  10129. Arguments:
  10130. SubnetName - Subnet string
  10131. SubnetAddress - Returns the subnet number in Network byte order.
  10132. SubnetMask - Returns the subnet mask in network byte order
  10133. SubnetBitCount - Returns the number of leftmost significant bits in the
  10134. SubnetAddress
  10135. Return Value:
  10136. NO_ERROR: success
  10137. ERROR_INVALID_NAME: Syntax of SubnetName is bad.
  10138. WSANOTINITIALISED: WSA needs to be initialized before making this call
  10139. --*/
  10140. {
  10141. LPWSTR SlashPointer;
  10142. WCHAR *End;
  10143. ULONG LocalBitCount;
  10144. WCHAR LocalSubnetName[NL_SOCK_ADDRESS_LENGTH*2];
  10145. WCHAR CanonicalSubnetName[NL_SOCK_ADDRESS_LENGTH+1];
  10146. ULONG CanonicalSubnetNameLen;
  10147. WCHAR CanonicalBitCount[3];
  10148. static ULONG BitMask[] = {
  10149. 0x00000000,
  10150. 0x00000080,
  10151. 0x000000C0,
  10152. 0x000000E0,
  10153. 0x000000F0,
  10154. 0x000000F8,
  10155. 0x000000FC,
  10156. 0x000000FE,
  10157. 0x000000FF,
  10158. 0x000080FF,
  10159. 0x0000C0FF,
  10160. 0x0000E0FF,
  10161. 0x0000F0FF,
  10162. 0x0000F8FF,
  10163. 0x0000FCFF,
  10164. 0x0000FEFF,
  10165. 0x0000FFFF,
  10166. 0x0080FFFF,
  10167. 0x00C0FFFF,
  10168. 0x00E0FFFF,
  10169. 0x00F0FFFF,
  10170. 0x00F8FFFF,
  10171. 0x00FCFFFF,
  10172. 0x00FEFFFF,
  10173. 0x00FFFFFF,
  10174. 0x80FFFFFF,
  10175. 0xC0FFFFFF,
  10176. 0xE0FFFFFF,
  10177. 0xF0FFFFFF,
  10178. 0xF8FFFFFF,
  10179. 0xFCFFFFFF,
  10180. 0xFEFFFFFF,
  10181. 0xFFFFFFFF };
  10182. INT WsaStatus;
  10183. SOCKADDR_IN SockAddrIn;
  10184. INT SockAddrSize;
  10185. //
  10186. // Copy the string to where we can munge it.
  10187. //
  10188. if ( wcslen(SubnetName) + 1 > sizeof(LocalSubnetName)/sizeof(WCHAR) ) {
  10189. NlPrint(( NL_CRITICAL,
  10190. "NlParseSubnetString: %ws: String too long\n", SubnetName ));
  10191. return ERROR_INVALID_NAME;
  10192. }
  10193. wcscpy( LocalSubnetName, SubnetName );
  10194. //
  10195. // Find the subnet bit count.
  10196. //
  10197. SlashPointer = wcschr( LocalSubnetName, L'/' );
  10198. if ( SlashPointer == NULL ) {
  10199. NlPrint(( NL_CRITICAL,
  10200. "NlParseSubnetString: %ws: Bit Count missing %ld\n", SubnetName ));
  10201. return ERROR_INVALID_NAME;
  10202. }
  10203. //
  10204. // Zero terminate the address portion of the subnet name.
  10205. //
  10206. *SlashPointer = L'\0';
  10207. //
  10208. // Get the BitCount portion.
  10209. //
  10210. LocalBitCount = wcstoul( SlashPointer+1, &End, 10 );
  10211. if ( LocalBitCount == 0 || LocalBitCount > 32 ) {
  10212. NlPrint(( NL_CRITICAL,
  10213. "NlParseSubnetString: %ws: Bit Count bad %ld\n", SubnetName, LocalBitCount ));
  10214. return ERROR_INVALID_NAME;
  10215. }
  10216. if ( *End != L'\0' ) {
  10217. NlPrint(( NL_CRITICAL,
  10218. "NlParseSubnetString: %ws: Bit Count not at string end\n", SubnetName ));
  10219. return ERROR_INVALID_NAME;
  10220. }
  10221. ultow( LocalBitCount, CanonicalBitCount, 10 );
  10222. if ( wcscmp( SlashPointer+1, CanonicalBitCount ) != 0 ) {
  10223. NlPrint(( NL_CRITICAL,
  10224. "NlParseSubnetString: %ws: not canonical %ws\n", SlashPointer+1, CanonicalBitCount ));
  10225. return ERROR_INVALID_NAME;
  10226. }
  10227. *SubnetBitCount = (BYTE)LocalBitCount;
  10228. //
  10229. // Convert the address portion to binary.
  10230. //
  10231. SockAddrSize = sizeof(SockAddrIn);
  10232. WsaStatus = WSAStringToAddressW( (LPWSTR)LocalSubnetName,
  10233. AF_INET,
  10234. NULL,
  10235. (PSOCKADDR)&SockAddrIn,
  10236. &SockAddrSize );
  10237. if ( WsaStatus != 0 ) {
  10238. WsaStatus = WSAGetLastError();
  10239. NlPrint(( NL_CRITICAL,
  10240. "NlParseSubnetString: %ws: Wsa Error %ld\n", SubnetName, WsaStatus ));
  10241. if ( WsaStatus == WSAEFAULT ||
  10242. WsaStatus == WSAEINVAL ) {
  10243. return ERROR_INVALID_NAME;
  10244. }
  10245. return WsaStatus;
  10246. }
  10247. if ( SockAddrIn.sin_family != AF_INET ) {
  10248. NlPrint(( NL_CRITICAL,
  10249. "NlParseSubnetString: %ws: Not AF_INET\n", SubnetName ));
  10250. return ERROR_INVALID_NAME;
  10251. }
  10252. *SubnetAddress = SockAddrIn.sin_addr.S_un.S_addr;
  10253. *SubnetMask = BitMask[*SubnetBitCount];
  10254. //
  10255. // Require that the passed in string be in canonical form.
  10256. // (e.g., no leading zeros). Since this text string is used as the
  10257. // name of an object in the DS, we don't want two different text strings
  10258. // to represent the same subnet.
  10259. //
  10260. CanonicalSubnetNameLen = sizeof(CanonicalSubnetName)/sizeof(WCHAR);
  10261. WsaStatus = WSAAddressToStringW( (PSOCKADDR)&SockAddrIn,
  10262. SockAddrSize,
  10263. NULL,
  10264. CanonicalSubnetName,
  10265. &CanonicalSubnetNameLen );
  10266. if ( WsaStatus != 0 ) {
  10267. WsaStatus = WSAGetLastError();
  10268. NlPrint(( NL_CRITICAL,
  10269. "NlParseSubnetString: %ws: Wsa Error %ld\n", SubnetName, WsaStatus ));
  10270. if ( WsaStatus == WSAEFAULT ||
  10271. WsaStatus == WSAEINVAL ) {
  10272. return ERROR_INVALID_NAME;
  10273. }
  10274. return WsaStatus;
  10275. }
  10276. if ( wcscmp( LocalSubnetName, CanonicalSubnetName ) != 0 ) {
  10277. NlPrint(( NL_CRITICAL,
  10278. "NlParseSubnetString: %ws: not canonical %ws\n", SubnetName, CanonicalSubnetName ));
  10279. return ERROR_INVALID_NAME;
  10280. }
  10281. //
  10282. // Ensure there are no bits set that aren't included in the subnet mask.
  10283. //
  10284. if ( (*SubnetAddress) & ~(*SubnetMask)) {
  10285. NlPrint(( NL_CRITICAL,
  10286. "NlParseSubnetString: %ws: bits not in subnet mask %8.8lX %8.8lX\n", SubnetName, *SubnetAddress, *SubnetMask ));
  10287. return ERROR_INVALID_NAME;
  10288. }
  10289. //
  10290. // Ensure the subnet mask isn't 0 since
  10291. // RFC950 says "the value of all zeros and all ones should not be assigned
  10292. // to physical subnets" since all zeros implies "this network" and all ones
  10293. // implies "all hosts"
  10294. if ( *SubnetAddress == 0 ||
  10295. *SubnetAddress == *SubnetMask ) {
  10296. NlPrint(( NL_CRITICAL,
  10297. "NlParseSubnetString: %ws: all zero or all one subnet address %8.8lX %8.8lX\n", SubnetName, *SubnetAddress, *SubnetMask ));
  10298. return ERROR_INVALID_NAME;
  10299. }
  10300. return NO_ERROR;
  10301. }
  10302. #endif // WIN32_CHICAGO
  10303. #ifdef _NETLOGON_SERVER
  10304. VOID
  10305. NetpDcFlushNegativeCache(
  10306. VOID
  10307. )
  10308. /*++
  10309. Routine Description:
  10310. Flush any failures to discover a DC.
  10311. Arguments:
  10312. None.
  10313. Return Value:
  10314. None.
  10315. --*/
  10316. {
  10317. PLIST_ENTRY DomainEntry;
  10318. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry;
  10319. ULONG QueryType;
  10320. //
  10321. // Loop through each cache entry
  10322. //
  10323. EnterCriticalSection(&NlDcCritSect);
  10324. for ( DomainEntry = NlDcDomainList.Flink ;
  10325. DomainEntry != &NlDcDomainList;
  10326. DomainEntry = DomainEntry->Flink ) {
  10327. NlDcDomainEntry = CONTAINING_RECORD( DomainEntry, NL_DC_DOMAIN_ENTRY, Next);
  10328. //
  10329. // Clear the failure time for each query type.
  10330. //
  10331. for ( QueryType = 0; QueryType < NlDcQueryTypeCount; QueryType ++ ) {
  10332. NlFlushNegativeCacheEntry( &NlDcDomainEntry->Dc[QueryType] );
  10333. }
  10334. }
  10335. LeaveCriticalSection(&NlDcCritSect);
  10336. return;
  10337. }
  10338. #endif // _NETLOGON_SERVER
  10339. NET_API_STATUS
  10340. NetpDcInitializeCache(
  10341. VOID
  10342. )
  10343. /*++
  10344. Routine Description:
  10345. Initialize the cache of discovered DCs.
  10346. Arguments:
  10347. None.
  10348. Return Value:
  10349. NO_ERROR - Operation completed successfully;
  10350. ERROR_NOT_ENOUGH_MEMORY - The cache could not be allocated.
  10351. --*/
  10352. {
  10353. NET_API_STATUS NetStatus = NO_ERROR;
  10354. try {
  10355. InitializeCriticalSection( &NlDcCritSect );
  10356. } except( EXCEPTION_EXECUTE_HANDLER ) {
  10357. NlPrint(( NL_CRITICAL,
  10358. "NetpDcInitializeCache: Cannot initialize NlDcCritSect\n" ));
  10359. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  10360. }
  10361. InitializeListHead( &NlDcDomainList );
  10362. NlDcDomainCount = 0;
  10363. RtlZeroMemory( &NlDcZeroGuid, sizeof(NlDcZeroGuid) );
  10364. NlDcDnsFailureTime = 0;
  10365. return NetStatus;
  10366. }
  10367. VOID
  10368. NetpDcUninitializeCache(
  10369. VOID
  10370. )
  10371. /*++
  10372. Routine Description:
  10373. Uninitialize the cache of discovered DCs.
  10374. Arguments:
  10375. None.
  10376. Return Value:
  10377. None.
  10378. --*/
  10379. {
  10380. //
  10381. // Delete existing domain entries.
  10382. //
  10383. EnterCriticalSection( &NlDcCritSect );
  10384. while (!IsListEmpty(&NlDcDomainList)) {
  10385. PNL_DC_DOMAIN_ENTRY NlDcDomainEntry =
  10386. CONTAINING_RECORD( NlDcDomainList.Flink, NL_DC_DOMAIN_ENTRY, Next);
  10387. NlAssert( NlDcDomainEntry->ReferenceCount == 1 );
  10388. NetpDcDeleteDomainEntry( NlDcDomainEntry );
  10389. }
  10390. LeaveCriticalSection( &NlDcCritSect );
  10391. DeleteCriticalSection( &NlDcCritSect );
  10392. }