Leaked source code of windows server 2003
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.

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