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 LWNetDnsQueryWithBuffer( IN PCSTR pszQuestion, IN BOOLEAN bReInit, IN BOOLEAN bUseTcp, OUT PVOID pBuffer, IN DWORD dwBufferSize, OUT PDWORD pdwResponseSize ) { DWORD dwError = 0; PDNS_RESPONSE_HEADER pHeader = (PDNS_RESPONSE_HEADER)pBuffer; int responseSize = 0; BOOLEAN bInLock = FALSE; #if HAVE_DECL_RES_NINIT union { struct __res_state res; #ifdef __LWI_AIX__ // struct __res_state was enlarged from 720 in AIX 5.2 to 824 in AIX // 5.3. This means calling res_ninit on AIX 5.3 on a structure compiled // on AIX 5.2 will result in a buffer overflow. Furthermore, even on // AIX 5.3, res_ninit seems to expect 1596 bytes in the structure (1491 // on AIX 5.2). As a workaround, this padding will ensure enough space // is allocated on the stack. char buffer[2048]; #endif } resLocal = { {0} }; res_state res = &resLocal.res; #else struct __res_state *res = &_res; #endif LWNET_LOCK_RESOLVER_API(bInLock); #if HAVE_DECL_RES_NINIT if (res_ninit(res) != 0) #else if (res_init() != 0) #endif { dwError = ERROR_NOT_FOUND; BAIL_ON_LWNET_ERROR(dwError); } if (dwBufferSize < CT_MIN(sizeof(DNS_RESPONSE_HEADER), MAX_DNS_UDP_BUFFER)) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LWNET_ERROR(dwError); } // TODO: Add lock on calling resolver due to global options, which may or // may not be safe depending on the system. if (bUseTcp) { res->options |= RES_USEVC; } else { res->options &= ~(RES_USEVC); } /* Assertion: pResolverContext != NULL && pResolverContext->bLocked == TRUE */ #if HAVE_DECL_RES_NINIT responseSize = res_nquery(res, pszQuestion, ns_c_in, ns_t_srv, (PBYTE) pBuffer, dwBufferSize); #else responseSize = res_query(pszQuestion, ns_c_in, ns_t_srv, (PBYTE) pBuffer, dwBufferSize); #endif if (responseSize < 0) { LWNET_LOG_VERBOSE("DNS lookup for '%s' failed with errno %d, h_errno = %d", pszQuestion, errno, h_errno); dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } if (responseSize < CT_FIELD_OFFSET(DNS_RESPONSE_HEADER, data)) { dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } if (responseSize > dwBufferSize) { dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } LWNetDnsFixHeaderForEndianness(pHeader); if (!LWNetDnsIsValidResponse(pHeader)) { dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } error: #if HAVE_DECL_RES_NINIT res_nclose(res); #else /* Indicate that we are done with the resolver, except on HPUX which does not implement the res_close function. */ #ifndef __LWI_HP_UX__ res_close(); #endif #endif LWNET_UNLOCK_RESOLVER_API(bInLock); if (dwError) { responseSize = 0; } *pdwResponseSize = responseSize; return dwError; }