static DWORD LWNetSrvGetDCNameDiscoverInternal( IN PCSTR pszDnsDomainName, IN OPTIONAL PCSTR pszSiteName, IN OPTIONAL PCSTR pszPrimaryDomain, IN DWORD dwDsFlags, IN DWORD dwBlackListCount, IN OPTIONAL PSTR* ppszAddressBlackList, IN PLWNET_DC_LIST_QUERY_METHOD pfnDCListQuery, OUT PLWNET_DC_INFO* ppDcInfo, OUT OPTIONAL PDNS_SERVER_INFO* ppServerArray, OUT OPTIONAL PDWORD pdwServerCount, OUT PBOOLEAN pbFailedFindWritable ) // // Algorithm: // // - DNS query for desired site & required DC type (pdc, kdc, gc). // - note that if no site is specified, use "un-sited" lookup. // - If no site specified: // - CLDAP to one DC to get actual site // - use new site info that to do DNS query for updated DC list // - CLDAP to DCs in parallel to find the first responder // (meeting any additional criteria -- writable, etc) // { DWORD dwError = 0; PLWNET_DC_INFO pDcInfo = NULL; PDNS_SERVER_INFO pServerArray = NULL; PDNS_SERVER_INFO pServersInPrimaryDomain = NULL; DWORD dwServersInPrimaryDomainCount = 0; DWORD dwServerCount = 0; PLWNET_DC_INFO pSiteDcInfo = NULL; PDNS_SERVER_INFO pSiteServerArray = NULL; DWORD dwSiteServerCount = 0; BOOLEAN bFailedFindWritable = FALSE; // Get server list dwError = pfnDCListQuery(pszDnsDomainName, pszSiteName, dwDsFlags, &pServerArray, &dwServerCount); BAIL_ON_LWNET_ERROR(dwError); LWNetFilterFromBlackList( dwBlackListCount, ppszAddressBlackList, &dwServerCount, pServerArray); if (!dwServerCount) { dwError = ERROR_DOMAIN_BLACKLISTED; BAIL_ON_LWNET_ERROR(dwError); } if (pszPrimaryDomain) { dwError = LWNetFindServersInDomain( pServerArray, dwServerCount, pszPrimaryDomain, &pServersInPrimaryDomain, &dwServersInPrimaryDomainCount); BAIL_ON_LWNET_ERROR(dwError); LWNetFilterFromBlackList( dwBlackListCount, ppszAddressBlackList, &dwServersInPrimaryDomainCount, pServersInPrimaryDomain); if (dwServersInPrimaryDomainCount > 0) { dwError = LWNetSrvPingCLdapArray(pszDnsDomainName, dwDsFlags, pServersInPrimaryDomain, dwServersInPrimaryDomainCount, 0, &pDcInfo, &bFailedFindWritable); } if (dwServersInPrimaryDomainCount == 0 || dwError == NERR_DCNotFound) { dwError = LWNetSrvPingCLdapArray(pszDnsDomainName, dwDsFlags, pServerArray, dwServerCount, 0, &pDcInfo, &bFailedFindWritable); BAIL_ON_LWNET_ERROR(dwError); } } else { // Do CLDAP dwError = LWNetSrvPingCLdapArray(pszDnsDomainName, dwDsFlags, pServerArray, dwServerCount, 0, &pDcInfo, &bFailedFindWritable); BAIL_ON_LWNET_ERROR(dwError); } // If there is no client site, then we are done (though we do not // expect this to ever happen). if (IsNullOrEmptyString(pDcInfo->pszClientSiteName)) { LWNET_LOG_ALWAYS("Missing client site name from " "DC response from %s (%s)", LWNET_SAFE_LOG_STRING(pDcInfo->pszDomainControllerName), LWNET_SAFE_LOG_STRING(pDcInfo->pszDomainControllerAddress)); goto cleanup; } // If a site was passed in, there is nothing more to do. if (!IsNullOrEmptyString(pszSiteName)) { goto cleanup; } // There was no site passed in, so we need to look at the // CLDAP response to get the client site. // If we got the correct site already, we are done. if (LWNetSrvIsInSameSite(pDcInfo)) { if (LWNetIsUpdateKrb5AffinityEnabled(dwDsFlags, pszSiteName, pDcInfo) && !pszSiteName && pDcInfo->pszClientSiteName) { dwError = pfnDCListQuery( pszDnsDomainName, pDcInfo->pszClientSiteName, dwDsFlags, &pSiteServerArray, &dwSiteServerCount); if (dwError == 0) { // Use the site-specific DC. LWNET_SAFE_FREE_MEMORY(pServerArray); dwServerCount = 0; LWNetFilterFromBlackList( dwBlackListCount, ppszAddressBlackList, &dwSiteServerCount, pSiteServerArray); if (!dwSiteServerCount) { dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } pServerArray = pSiteServerArray; dwServerCount = dwSiteServerCount; pSiteServerArray = NULL; dwSiteServerCount = 0; } } dwError = 0; goto cleanup; } // Now we need to use the client site to find a site-specific DC. dwError = LWNetSrvGetDCNameDiscover(pszDnsDomainName, pDcInfo->pszClientSiteName, pszPrimaryDomain, dwDsFlags, dwBlackListCount, ppszAddressBlackList, &pSiteDcInfo, &pSiteServerArray, &dwSiteServerCount, &bFailedFindWritable); if (NERR_DCNotFound == dwError) { if (bFailedFindWritable) { LWNET_LOG_WARNING("No writable DC in client site '%s' for domain '%s'", pDcInfo->pszClientSiteName, pszDnsDomainName); } // Count not find site-specific DC, so use the original DC. dwError = 0; goto cleanup; } BAIL_ON_LWNET_ERROR(dwError); // Use the site-specific DC. LWNET_SAFE_FREE_DC_INFO(pDcInfo); LWNET_SAFE_FREE_MEMORY(pServerArray); dwServerCount = 0; pDcInfo = pSiteDcInfo; pServerArray = pSiteServerArray; dwServerCount = dwSiteServerCount; pSiteDcInfo = NULL; pSiteServerArray = NULL; dwSiteServerCount = 0; error: cleanup: LWNET_SAFE_FREE_DC_INFO(pSiteDcInfo); LWNET_SAFE_FREE_MEMORY(pSiteServerArray); LWNET_SAFE_FREE_MEMORY(pServersInPrimaryDomain); dwSiteServerCount = 0; if (dwError) { LWNET_SAFE_FREE_DC_INFO(pDcInfo); LWNET_SAFE_FREE_MEMORY(pServerArray); dwServerCount = 0; } *ppDcInfo = pDcInfo; if (ppServerArray) { *ppServerArray = pServerArray; *pdwServerCount = dwServerCount; } else { LWNET_SAFE_FREE_MEMORY(pServerArray); } *pbFailedFindWritable = bFailedFindWritable; return dwError; }
DWORD LWNetSrvPingCLdapArray( IN PCSTR pszDnsDomainName, IN DWORD dwDsFlags, IN PDNS_SERVER_INFO pServerArray, IN DWORD dwServerCount, IN OPTIONAL DWORD dwTimeoutSeconds, OUT PLWNET_DC_INFO* ppDcInfo, OUT PBOOLEAN pbFailedFindWritable ) { DWORD dwError = 0; DWORD dwMaxConnCount = 0; DWORD dwIncrementalConnCount = 0; DWORD dwActualTimeoutSeconds = 0; DWORD dwSingleConnTimeoutSeconds = 0; DWORD dwSingleConnTimeoutMilliseconds = 0; DWORD dwTimeoutMilliseconds = 0; LWNET_UNIX_MS_TIME_T StopTime = 0; LWNET_UNIX_MS_TIME_T CurrentTime = 0; PLWNET_DC_INFO pDcInfo = NULL; BOOLEAN bFailedFindWritable = FALSE; PLWNET_CLDAP_CONNECTION_CONTEXT pConnections = NULL; DWORD dwActualConnCount = 0; DWORD dwUsedServers = 0; DWORD dwIndexConn = 0; struct pollfd *Readfds = NULL; // The basic scheme is a big loop over: // // 1. starting some CLDAP searches // 2. polling for responses // 3. processing responses // // When starting CLDAP searches, a portion of the // available connections will be started followed // by a short poll until the maximum number of // connections has been used. Once the maximum // number of connections are in use, the polling // timeout will be the single search timeout for // the oldest connection. This will start the // maximum number of connections reasonably fast // yet prevent excess network traffic if a server // responds quickly. // // Active connections will be tracked in a list // that is ordered by start time. If several // servers respond at the same time, the one // nearest the end of the list will be the one // that responded the fastest. dwMaxConnCount = CT_MIN(LWNetConfigGetCLdapMaximumConnections(), dwServerCount); dwIncrementalConnCount = CT_MAX(dwMaxConnCount / 5, LWNET_CLDAP_MINIMUM_INCREMENTAL_CONNECTIONS); if (dwTimeoutSeconds > 0) { dwActualTimeoutSeconds = dwTimeoutSeconds; } else { dwActualTimeoutSeconds = LWNetConfigGetCLdapSearchTimeoutSeconds(); } dwSingleConnTimeoutSeconds = CT_MIN(LWNetConfigGetCLdapSingleConnectionTimeoutSeconds(), dwActualTimeoutSeconds); dwSingleConnTimeoutMilliseconds = dwSingleConnTimeoutSeconds * 1000; dwError = LWNetAllocateMemory( dwMaxConnCount * sizeof(*pConnections), OUT_PPVOID(&pConnections)); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetAllocateMemory( dwMaxConnCount * sizeof(*Readfds), OUT_PPVOID(&Readfds)); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetGetSystemTimeInMs(&CurrentTime); BAIL_ON_LWNET_ERROR(dwError); StopTime = CurrentTime + (dwActualTimeoutSeconds * 1000); for (;;) { LWNetSrvPingCLdapNewConnections( pszDnsDomainName, dwSingleConnTimeoutMilliseconds, dwMaxConnCount, dwServerCount, dwIncrementalConnCount, pServerArray, pConnections, Readfds, &dwActualConnCount, &dwUsedServers); if (dwActualConnCount == 0) { dwError = NERR_DCNotFound; BAIL_ON_LWNET_ERROR(dwError); } dwError = LWNetGetSystemTimeInMs(&CurrentTime); BAIL_ON_LWNET_ERROR(dwError); if (CurrentTime > StopTime) { dwError = NERR_DCNotFound; BAIL_ON_LWNET_ERROR(dwError); } if (dwActualConnCount < dwMaxConnCount && dwUsedServers < dwServerCount) { // If there are more connections available, // use a short timeout dwTimeoutMilliseconds = LWNET_CLDAP_SHORT_POLL_TIMEOUT_MILLISECONDS; } else { if (pConnections[0].StartTime + dwSingleConnTimeoutMilliseconds > CurrentTime) { // Use the remaining time for the oldest connection dwTimeoutMilliseconds = pConnections[0].StartTime + dwSingleConnTimeoutMilliseconds - CurrentTime; dwTimeoutMilliseconds = CT_MIN(dwSingleConnTimeoutMilliseconds, StopTime - CurrentTime); } else { // The oldest connection has exceeded its limit so // use the shortest possible timeout to check which // connections have responded now. dwTimeoutMilliseconds = 1; } } dwError = LWNetSrvPingCLdapPoll( dwTimeoutMilliseconds, dwActualConnCount, Readfds); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetGetSystemTimeInMs(&CurrentTime); BAIL_ON_LWNET_ERROR(dwError); LWNetSrvPingCLdapProcessConnections( dwDsFlags, CurrentTime, dwSingleConnTimeoutMilliseconds, dwActualConnCount, Readfds, pConnections, &pDcInfo, &bFailedFindWritable); if (pDcInfo) { break; } dwActualConnCount = LWNetSrvPingCLdapPackArrays( dwActualConnCount, pConnections, Readfds); } error: if (pConnections) { for (dwIndexConn = 0 ; dwIndexConn < dwActualConnCount ; dwIndexConn++) { LWNetSrvPingCLdapEnd(&pConnections[dwIndexConn]); } LW_SAFE_FREE_MEMORY(pConnections); } LW_SAFE_FREE_MEMORY(Readfds); if (dwError) { LWNET_SAFE_FREE_DC_INFO(pDcInfo); } *ppDcInfo = pDcInfo; *pbFailedFindWritable = pDcInfo ? FALSE : bFailedFindWritable; return dwError; }
DWORD LWNetBuildDCInfo( IN PBYTE pBuffer, IN DWORD dwBufferSize, OUT PLWNET_DC_INFO* ppDCInfo ) { DWORD dwError = 0; PLWNET_DC_INFO pDCInfo = NULL; PACKED_ARRAY netLogon = { 0 }; dwError = LWNetAllocateMemory(sizeof(LWNET_DC_INFO), (PVOID*)&pDCInfo); BAIL_ON_LWNET_ERROR(dwError); netLogon.pStart = pBuffer; netLogon.pCur = pBuffer; netLogon.totalSize = dwBufferSize; dwError = LWNetReadLEDword(&pDCInfo->dwDomainControllerAddressType, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadLEDword(&pDCInfo->dwFlags, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadGUID((PBYTE)&pDCInfo->pucDomainGUID[0], &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszDnsForestName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszFullyQualifiedDomainName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszDomainControllerName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszNetBIOSDomainName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszNetBIOSHostName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszUserName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszDCSiteName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadString(&pDCInfo->pszClientSiteName, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadLEDword(&pDCInfo->dwVersion, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadLEWord(&pDCInfo->wLMToken, &netLogon); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetReadLEWord(&pDCInfo->wNTToken, &netLogon); BAIL_ON_LWNET_ERROR(dwError); error: if (dwError) { LWNET_SAFE_FREE_DC_INFO(pDCInfo); } *ppDCInfo = pDCInfo; return dwError; }
static DWORD LWNetSrvPingCLdapProcess( IN PLWNET_CLDAP_CONNECTION_CONTEXT pContext, IN DWORD dwDsFlags, IN LWNET_UNIX_MS_TIME_T StopTime, OUT PLWNET_DC_INFO* ppDcInfo, OUT PBOOLEAN pbFailedFindWritable ) { DWORD dwError = 0; DWORD dwResultType = 0; LDAPMessage* pMessage = NULL; PBYTE pNetlogonAttributeValue = NULL; DWORD dwNetlogonAttributeSize = 0; PLWNET_DC_INFO pDcInfo = NULL; BOOLEAN bFailedFindWritable = FALSE; struct timeval timeout = {0}; LDAP *ld = NULL; ld = LwLdapGetSession(pContext->hDirectory); dwResultType = ldap_result( ld, pContext->msgid, 0, &timeout, &pMessage); if (dwResultType == 0) { // timed out goto error; } else if (dwResultType == -1) { // -1 = problem dwError = LDAP_NO_SUCH_OBJECT; LWNET_LOG_VERBOSE("Caught LDAP_NO_SUCH_OBJECT Error on ldap search"); } else { // returns result type if (dwResultType != LDAP_RES_SEARCH_ENTRY) { dwError = LDAP_NO_SUCH_OBJECT; LWNET_LOG_DEBUG("Caught incorrect result type on ldap search: %d", dwError); } } dwError = LwMapLdapErrorToLwError(dwError); BAIL_ON_LWNET_ERROR(dwError); dwError = LwLdapGetBytes( pContext->hDirectory, pMessage, NETLOGON_LDAP_ATTRIBUTE_NAME, &pNetlogonAttributeValue, &dwNetlogonAttributeSize); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetBuildDCInfo(pNetlogonAttributeValue, dwNetlogonAttributeSize, &pDcInfo); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetAllocateString(pContext->pServerInfo->pszAddress, &pDcInfo->pszDomainControllerAddress); BAIL_ON_LWNET_ERROR(dwError); pDcInfo->dwPingTime = (DWORD)(StopTime - pContext->StartTime); if (StopTime < pContext->StartTime) { LWNET_LOG_ERROR("Stop time is earlier than start time"); } if (!LWNetSrvIsMatchingDcInfo(pDcInfo, dwDsFlags)) { dwError = LW_ERROR_NO_SUCH_OBJECT; if (LWNetSrvIsMatchingDcInfo(pDcInfo, dwDsFlags & ~DS_WRITABLE_REQUIRED)) { // We found something, but it failed only because it did // not satisfy writability. bFailedFindWritable = TRUE; } } error: if (pMessage) { ldap_msgfree(pMessage); } LWNET_SAFE_FREE_MEMORY(pNetlogonAttributeValue); if (dwError) { LWNET_SAFE_FREE_DC_INFO(pDcInfo); LWNetSrvPingCLdapEnd(pContext); } *ppDcInfo = pDcInfo; *pbFailedFindWritable = bFailedFindWritable; return dwError; }
int main( int argc, char* argv[] ) { DWORD dwError = 0; PSTR pszTargetFQDN = NULL; PSTR pszSiteName = NULL; PSTR pszPrimaryDomain = NULL; PLWNET_DC_INFO pDCInfo = NULL; DWORD dwFlags = 0; CHAR szErrorBuf[1024]; INT i = 0; dwError = ParseArgs( argc, argv, &pszTargetFQDN, &pszSiteName, &pszPrimaryDomain, &dwFlags ); BAIL_ON_LWNET_ERROR(dwError); lwnet_init_logging_to_file(LWNET_LOG_LEVEL_VERBOSE, TRUE, ""); dwError = LWNetGetDCNameExt( NULL, pszTargetFQDN, pszSiteName, pszPrimaryDomain, dwFlags, 0, NULL, &pDCInfo ); BAIL_ON_LWNET_ERROR(dwError); printf("Printing LWNET_DC_INFO fields:\n"); printf("===============================\n"); if(pDCInfo == NULL) { printf("<NULL>"); } else { printf("dwDomainControllerAddressType = %u\n", pDCInfo->dwDomainControllerAddressType); printf("dwFlags = %u\n", pDCInfo->dwFlags); printf("dwVersion = %u\n", pDCInfo->dwVersion); printf("wLMToken = %u\n", pDCInfo->wLMToken); printf("wNTToken = %u\n", pDCInfo->wNTToken); safePrintString("pszDomainControllerName", pDCInfo->pszDomainControllerName); safePrintString("pszDomainControllerAddress", pDCInfo->pszDomainControllerAddress); printf("pucDomainGUID(hex) = "); for(i = 0; i < LWNET_GUID_SIZE; i++) { printf("%.2X ", pDCInfo->pucDomainGUID[i]); } printf("\n"); safePrintString("pszNetBIOSDomainName", pDCInfo->pszNetBIOSDomainName); safePrintString("pszFullyQualifiedDomainName", pDCInfo->pszFullyQualifiedDomainName); safePrintString("pszDnsForestName", pDCInfo->pszDnsForestName); safePrintString("pszDCSiteName", pDCInfo->pszDCSiteName); safePrintString("pszClientSiteName", pDCInfo->pszClientSiteName); safePrintString("pszNetBIOSHostName", pDCInfo->pszNetBIOSHostName); safePrintString("pszUserName", pDCInfo->pszUserName); } error: if (dwError) { DWORD dwLen = LwGetErrorString(dwError, szErrorBuf, 1024); if (dwLen) { fprintf( stderr, "Failed communication with the LWNET Agent. Error code %u (%s).\n%s\n", dwError, LW_PRINTF_STRING(LwWin32ExtErrorToName(dwError)), szErrorBuf); } else { fprintf( stderr, "Failed communication with the LWNET Agent. Error code %u (%s).\n", dwError, LW_PRINTF_STRING(LwWin32ExtErrorToName(dwError))); } } LWNET_SAFE_FREE_DC_INFO(pDCInfo); LWNET_SAFE_FREE_STRING(pszTargetFQDN); LWNET_SAFE_FREE_STRING(pszSiteName); return dwError; }