/* Decompose a NetBIOS Second Layer name into an array of name components */ DWORD LWNetNbName2ToParts( PBYTE NbNameL2, PSTR **retNbNameParts, PDWORD retNbNamePartsLen) { DWORD dwError = 0; DWORD numParts = 0; DWORD len = 0; DWORD i = 0; UINT8 *ptr = NULL; PSTR *NBNameParts = NULL; /* Count number of parts in NetBIOS name */ ptr = NbNameL2; while (*ptr) { len = (int) *ptr++; ptr += len; numParts++; } len++; dwError = LWNetAllocateMemory((numParts+1) * sizeof(*NBNameParts), (PVOID*)&NBNameParts); BAIL_ON_LWNET_ERROR(dwError); /* Reset and allocate strings for the various parts */ ptr = NbNameL2; i = 0; while (*ptr) { len = (int) *ptr++; dwError = LWNetAllocateMemory((len + 1) * sizeof(char), (PVOID*)&NBNameParts[i]); BAIL_ON_LWNET_ERROR(dwError); strncat(NBNameParts[i++], (char *) ptr, len); ptr += len; } ptr++; *retNbNameParts = NBNameParts; *retNbNamePartsLen = ptr - NbNameL2; cleanup: return dwError; error: if (NBNameParts) { for (i=0; NBNameParts[i]; i++) { LWNET_SAFE_FREE_MEMORY(NBNameParts[i]); } LWNET_SAFE_FREE_MEMORY(NBNameParts); } goto cleanup; }
DWORD LWNetSrvOpenServer( uid_t peerUID, gid_t peerGID, PHANDLE phServer ) { DWORD dwError = 0; PLWNET_SRV_API_STATE pServerState = NULL; dwError = LWNetAllocateMemory( sizeof(LWNET_SRV_API_STATE), (PVOID*)&pServerState); BAIL_ON_LWNET_ERROR(dwError); pServerState->peerUID = peerUID; pServerState->peerGID = peerGID; *phServer = (HANDLE)pServerState; cleanup: return dwError; error: *phServer = (HANDLE)NULL; if (pServerState) { LWNetSrvCloseServer((HANDLE)pServerState); } goto cleanup; }
DWORD LWNetDnsBuildSRVRecord( IN PDNS_RESPONSE_HEADER pHeader, IN PDNS_RECORD pAnswerRecord, IN PDLINKEDLIST pAdditionalsList, OUT PDNS_SRV_INFO_RECORD* ppSRVInfoRecord ) { DWORD dwError = 0; PDNS_SRV_INFO_RECORD pSRVInfoRecord = NULL; PBYTE pCurrent = NULL; DWORD dwNameLen = 0; if (pAnswerRecord->wDataLen < (4 * sizeof(WORD))) { dwError = DNS_ERROR_BAD_PACKET; BAIL_ON_LWNET_ERROR(dwError); } dwError = LWNetAllocateMemory(sizeof(DNS_SRV_INFO_RECORD), (PVOID*)&pSRVInfoRecord); BAIL_ON_LWNET_ERROR(dwError); pCurrent = pAnswerRecord->pData; pSRVInfoRecord->wPriority = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); pSRVInfoRecord->wWeight = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); pSRVInfoRecord->wPort = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); dwError = LWNetDnsParseName(pHeader, pCurrent, &dwNameLen, &pSRVInfoRecord->pszTarget); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetDnsGetAddressForServer(pAdditionalsList, pSRVInfoRecord->pszTarget, &pSRVInfoRecord->pszAddress); BAIL_ON_LWNET_ERROR(dwError); error: if (dwError) { if (pSRVInfoRecord) { LWNetDnsFreeSRVInfoRecord(pSRVInfoRecord); pSRVInfoRecord = NULL; } } *ppSRVInfoRecord = pSRVInfoRecord; return dwError; }
static DWORD LWNetFindServersInDomain( IN PDNS_SERVER_INFO pServerArray, IN DWORD dwServerCount, IN PCSTR pszDomainName, OUT PDNS_SERVER_INFO* ppServersInDomain, OUT PDWORD pdwServersInDomainCount ) { DWORD dwError = 0; DWORD i = 0; DWORD dwServersInDomainCount = 0; DWORD dwServerIndex = 0; PDNS_SERVER_INFO pServersInDomain = NULL; for (i = 0; i < dwServerCount; i++) { if (LWNetServerIsInDomain(&pServerArray[i], pszDomainName)) { dwServersInDomainCount++; } } if (dwServersInDomainCount) { dwError = LWNetAllocateMemory(dwServersInDomainCount * sizeof(*pServerArray), OUT_PPVOID(&pServersInDomain)); BAIL_ON_LWNET_ERROR(dwError); for (i = 0; i < dwServerCount; i++) { if (LWNetServerIsInDomain(&pServerArray[i], pszDomainName)) { pServersInDomain[dwServerIndex++] = pServerArray[i]; } } } *ppServersInDomain = pServersInDomain; *pdwServersInDomainCount = dwServersInDomainCount; cleanup: return dwError; error: *ppServersInDomain = NULL; *pdwServersInDomainCount = 0; LWNET_SAFE_FREE_MEMORY(pServersInDomain); goto cleanup; }
DWORD LWNetGetErrorMessageForLoggingEvent( DWORD dwErrCode, PSTR* ppszErrorMsg ) { DWORD dwErrorBufferSize = 0; DWORD dwError = 0; DWORD dwLen = 0; PSTR pszErrorMsg = NULL; PSTR pszErrorBuffer = NULL; dwErrorBufferSize = LwGetErrorString(dwErrCode, NULL, 0); if (!dwErrorBufferSize) goto cleanup; dwError = LWNetAllocateMemory( dwErrorBufferSize, (PVOID*)&pszErrorBuffer); BAIL_ON_LWNET_ERROR(dwError); dwLen = LwGetErrorString(dwErrCode, pszErrorBuffer, dwErrorBufferSize); if ((dwLen == dwErrorBufferSize) && !IsNullOrEmptyString(pszErrorBuffer)) { dwError = LwAllocateStringPrintf( &pszErrorMsg, "Error: %s [error code: %d]", pszErrorBuffer, dwErrCode); BAIL_ON_LWNET_ERROR(dwError); } *ppszErrorMsg = pszErrorMsg; cleanup: LWNET_SAFE_FREE_STRING(pszErrorBuffer); return dwError; error: LWNET_SAFE_FREE_STRING(pszErrorMsg); *ppszErrorMsg = NULL; goto cleanup; }
DWORD LWNetDnsParseName( IN PDNS_RESPONSE_HEADER pHeader, IN PBYTE pData, OUT PDWORD pdwBytesToAdvance, OUT OPTIONAL PSTR* ppszName ) { DWORD dwError = 0; DWORD dwBytesToAdvance = 0; DWORD dwNameLen = 0; PSTR pszName = NULL; /* Figure out the size and how many bytes the parse will advance */ LWNetDnsParseNameWorker(pHeader, pData, &dwBytesToAdvance, &dwNameLen, NULL); if (ppszName) { /* Now allocate the memory, overallocating to ensure NULL termination in case the DNS packet does not termiante */ dwError = LWNetAllocateMemory((dwNameLen+3) * sizeof(CHAR), (PVOID*)&pszName); BAIL_ON_LWNET_ERROR(dwError); /* Fill in the name */ LWNetDnsParseNameWorker(pHeader, pData, NULL, NULL, pszName); /* Ensure NULL termination */ pszName[dwNameLen] = 0; } error: if (dwError) { LWNET_SAFE_FREE_MEMORY(pszName); } *pdwBytesToAdvance = dwBytesToAdvance; if (ppszName) { *ppszName = pszName; } return dwError; }
static DWORD LWNetNbResolveNameUdp( PSTR pszHostName, PSTR winsServer, UINT8 queryType, OUT struct in_addr **retAddrs, OUT PDWORD retAddrsLen) { DWORD dwError = 0; UINT16 transactionId = 0; UINT8 *NetBiosQuery = NULL; DWORD NetBiosQueryLen = 0; struct sockaddr_in dgAddr; struct timespec cvTimeout = {0}; struct timeval tp = {0}; struct in_addr *resAddrs = NULL; struct in_addr *tmpResAddrs = NULL; int sts = 0; DWORD i = 0; DWORD resAddrsLen = 0; DWORD resAddrsAllocLen = 128; DWORD commType = 0; BOOLEAN bLocked = FALSE; dwError = LWNetAllocateMemory( LWNB_NETBIOS_UDP_MAX, (PVOID*) &NetBiosQuery); BAIL_ON_LWNET_ERROR(dwError); memset(&dgAddr, 0, sizeof(dgAddr)); dgAddr.sin_family = AF_INET; dgAddr.sin_port = htons(137); if (winsServer && *winsServer) { sts = inet_aton(winsServer, &dgAddr.sin_addr); if (sts == -1) { dwError = LwErrnoToWin32Error(ERROR_INCORRECT_ADDRESS); BAIL_ON_LWNET_ERROR(dwError); } commType = LWNB_QUERY_WINS; dwError = LWNetNbConstructNameQuery( pszHostName, commType, queryType, &transactionId, NetBiosQuery, &NetBiosQueryLen); BAIL_ON_LWNET_ERROR(dwError); } else { dgAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); commType = LWNB_QUERY_BROADCAST; dwError = LWNetNbConstructNameQuery( pszHostName, commType, queryType, &transactionId, NetBiosQuery, &NetBiosQueryLen); BAIL_ON_LWNET_ERROR(dwError); } // If this is not enough memory, realloc below fixes that dwError = LWNetAllocateMemory( resAddrsAllocLen * sizeof(struct in_addr), (PVOID*) &resAddrs); BAIL_ON_LWNET_ERROR(dwError); pthread_mutex_lock(&gpNbCtx->mutex); bLocked = TRUE; sts = sendto(gpNbCtx->sock, NetBiosQuery, NetBiosQueryLen, 0, /* flags */ (struct sockaddr *) &dgAddr, sizeof(dgAddr)); if (sts == -1) { dwError = ERROR_NET_WRITE_FAULT; BAIL_ON_LWNET_ERROR(dwError); } do { gettimeofday(&tp, NULL); cvTimeout.tv_sec = tp.tv_sec + gpNbCtx->udpTimeout; cvTimeout.tv_nsec = tp.tv_usec * 1000; do { sts = pthread_cond_timedwait( &gpNbCtx->cv, &gpNbCtx->mutex, &cvTimeout); } while (!gpNbCtx->bNbRepsponse && sts != ETIMEDOUT); if (sts == 0 && gpNbCtx->transactionId == transactionId) { gpNbCtx->bNbRepsponse = FALSE; if ((commType == LWNB_QUERY_WINS && gpNbCtx->respError == 0) || (commType == LWNB_QUERY_BROADCAST)) { if ((gpNbCtx->addrsLen + resAddrsLen) > resAddrsAllocLen) { resAddrsAllocLen = resAddrsAllocLen * 2 + gpNbCtx->addrsLen; tmpResAddrs = LwRtlMemoryRealloc( resAddrs, resAddrsAllocLen); if (!tmpResAddrs) { dwError = ERROR_NOT_ENOUGH_MEMORY; BAIL_ON_LWNET_ERROR(dwError); } resAddrs = tmpResAddrs; tmpResAddrs = NULL; } for (i=0; i<gpNbCtx->addrsLen; i++) { memcpy(&resAddrs[resAddrsLen++], &gpNbCtx->addrs[i], sizeof(struct in_addr)); } } pthread_mutex_lock(&gpNbCtx->mutexAck); gpNbCtx->bAck = TRUE; pthread_mutex_unlock(&gpNbCtx->mutexAck); pthread_cond_signal(&gpNbCtx->cvAck); if (commType == LWNB_QUERY_WINS && gpNbCtx->respError) { // Bail when WINS query fails to resolve the hostname dwError = gpNbCtx->respError; BAIL_ON_LWNET_ERROR(dwError); } } } while (commType == LWNB_QUERY_BROADCAST && sts != ETIMEDOUT); bLocked = FALSE; pthread_mutex_unlock(&gpNbCtx->mutex); if (resAddrsLen == 0) { dwError = ERROR_BAD_NET_NAME; BAIL_ON_LWNET_ERROR(dwError); } *retAddrs = resAddrs; *retAddrsLen = resAddrsLen; cleanup: if (bLocked) { pthread_mutex_unlock(&gpNbCtx->mutex); } LWNET_SAFE_FREE_MEMORY(NetBiosQuery); return dwError; error: LWNET_SAFE_FREE_MEMORY(resAddrs); goto cleanup; }
/* "Second Level" NetBIOS name encoding */ DWORD LWNetNbStrToNbName2( PSTR Fqdn, UINT8 suffix, PBYTE *retNbNameL2, PDWORD retNbNameL2Len) { DWORD dwError = 0; DWORD len = 0; PSTR p = NULL; PSTR token = NULL; PSTR tokenPtr = NULL; UINT8 *retName = NULL; UINT8 *up = NULL; /* * 32=Max NetBIOS First Level encoding length * All . separators are replaced by 1 byte count, so * don't need to count number of dots to determine buffer length * 2=FirstLevel encoding length + ending length (by definition 0) */ len = LWNB_NETBIOS_ENCNAME_LEN + strlen(Fqdn) + 2; dwError = LWNetAllocateMemory(sizeof(CHAR) * len, (PVOID*)&retName); BAIL_ON_LWNET_ERROR(dwError); up = retName; dwError = LWNetAllocateString( Fqdn, &token); BAIL_ON_LWNET_ERROR(dwError); p = strchr(token, '.'); if (p) { *p++ = '\0'; Fqdn = token; } /* Store length of NetBIOS name component before that name */ *up++ = (UINT8) LWNB_NETBIOS_ENCNAME_LEN; LWNetNbStrToNbName(Fqdn, suffix, up); up += LWNB_NETBIOS_ENCNAME_LEN; /* Work through rest of Fqdn, replacing . with length of stuff before . */ if (p) { p = strtok_r(p, ".", &tokenPtr); } while (p) { len = strlen(p); *up++ = (UINT8) len; strncat((char *) up, p, sizeof(CHAR)*len - (up-retName)); up += len; p = strtok_r(NULL, ".", &tokenPtr); } *up++ = '\0'; *retNbNameL2 = (UINT8 *) retName; *retNbNameL2Len = up - retName; cleanup: LWNET_SAFE_FREE_MEMORY(token); return dwError; error: LWNET_SAFE_FREE_MEMORY(retName); goto cleanup; }
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 LWNetDnsSrvQuery( IN PCSTR pszDnsDomainName, IN OPTIONAL PCSTR pszSiteName, IN DWORD dwDsFlags, OUT PDNS_SERVER_INFO* ppServerArray, OUT PDWORD pdwServerCount ) // Call LWNET_SAFE_FREE_MEMORY on returned server array { DWORD dwError = 0; PSTR pszQuestion = NULL; const size_t dwBufferSize = (64 * 1024); PVOID pBuffer = NULL; PDNS_RESPONSE_HEADER pResponse = NULL; DWORD dwResponseSize = 0; PDLINKEDLIST pAnswersList = NULL; PDLINKEDLIST pAdditionalsList = NULL; PDLINKEDLIST pSRVRecordList = NULL; PDNS_SERVER_INFO pServerArray = NULL; DWORD dwServerCount = 0; // TODO - Handle trailing dot in domain; handle no dots in domain dwError = LWNetDnsGetSrvRecordQuestion(&pszQuestion, pszDnsDomainName, pszSiteName, dwDsFlags); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetAllocateMemory(dwBufferSize, &pBuffer); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetDnsQueryWithBuffer(pszQuestion, TRUE, FALSE, pBuffer, dwBufferSize, &dwResponseSize); BAIL_ON_LWNET_ERROR(dwError); pResponse = (PDNS_RESPONSE_HEADER) pBuffer; if (LWNetDnsIsTruncatedResponse(pResponse)) { dwError = LWNetDnsQueryWithBuffer(pszQuestion, FALSE, TRUE, pBuffer, dwBufferSize, &dwResponseSize); BAIL_ON_LWNET_ERROR(dwError); } // TODO: Add dwResponseSize validation to parsing. // Decode DNS response w/o taking into account record type dwError = LWNetDnsParseQueryResponse(pResponse, &pAnswersList, NULL, &pAdditionalsList); BAIL_ON_LWNET_ERROR(dwError); // Decode SRV records dwError = LWNetDnsBuildSRVRecordList(pResponse, pAnswersList, pAdditionalsList, &pSRVRecordList); BAIL_ON_LWNET_ERROR(dwError); // Create list of server names and addresses dwError = LWNetDnsBuildServerArray(pSRVRecordList, &pServerArray, &dwServerCount); BAIL_ON_LWNET_ERROR(dwError); error: LWNET_SAFE_FREE_STRING(pszQuestion); LWNET_SAFE_FREE_MEMORY(pBuffer); LWNET_SAFE_FREE_DNS_RECORD_LINKED_LIST(pAnswersList); LWNET_SAFE_FREE_DNS_RECORD_LINKED_LIST(pAdditionalsList); LWNET_SAFE_FREE_SRV_INFO_LINKED_LIST(pSRVRecordList); if (dwError) { LWNET_SAFE_FREE_MEMORY(pServerArray); dwServerCount = 0; } *ppServerArray= pServerArray; *pdwServerCount = dwServerCount; return dwError; }
VOID *LWNetSrvStartNetBiosThreadRoutine(VOID *ctx) { DWORD dwError = 0; struct pollfd pollfds[1]; int sts = 0; int sock = 0; UINT8 *NetBiosReply = NULL; struct sockaddr_in dgAddr; int NetBiosReplyAddrLen = 0; void *pNetBiosReplyAddrLen = &NetBiosReplyAddrLen; PLWNET_SRV_NETBIOS_CONTEXT pNbCtx = (PLWNET_SRV_NETBIOS_CONTEXT) ctx; int allowBroadcast = 1; UINT8 Flags = 0; UINT16 respTransactionId = 0; PSTR NbName = NULL; struct in_addr *addrs = NULL; DWORD addrsLen = 0; struct timeval tp = {0}; struct timespec cvTimeout = {0}; dwError = LWNetAllocateMemory( LWNB_NETBIOS_UDP_MAX, (PVOID*) &NetBiosReply); sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) { dwError = ERROR_INVALID_HANDLE; BAIL_ON_LWNET_ERROR(dwError); } sts = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &allowBroadcast, sizeof(allowBroadcast)); if (sts == -1) { dwError = ERROR_INVALID_HANDLE; BAIL_ON_LWNET_ERROR(dwError); } pthread_mutex_lock(&pNbCtx->mutex); pNbCtx->sock = sock; pthread_mutex_unlock(&pNbCtx->mutex); do { memset(pollfds, 0, sizeof(pollfds)); pollfds[0].fd = sock; pollfds[0].events = POLLIN; sts = poll(pollfds, 1, -1); if (sts > 0 && pollfds[0].revents) { sts = recvfrom( sock, NetBiosReply, LWNB_NETBIOS_UDP_MAX, 0, /* flags */ (struct sockaddr *) &dgAddr, pNetBiosReplyAddrLen); if (sts > 0) { dwError = LWNetNbParseNameQueryResponse( NetBiosReply, sts, 0, // remove this argument &respTransactionId, &NbName, NULL, &Flags, &addrs, &addrsLen); pthread_mutex_lock(&pNbCtx->mutex); pNbCtx->respError = dwError; pNbCtx->transactionId = respTransactionId; pNbCtx->addrs = addrs; pNbCtx->addrsLen = addrsLen; pNbCtx->bNbRepsponse = TRUE; pthread_mutex_lock(&pNbCtx->mutexAck); pthread_mutex_unlock(&pNbCtx->mutex); pthread_cond_broadcast(&pNbCtx->cv); // Wait for LWNetNbResolveName ack address received do { gettimeofday(&tp, NULL); cvTimeout.tv_sec = tp.tv_sec + pNbCtx->udpTimeout; cvTimeout.tv_nsec = tp.tv_usec * 1000; sts = pthread_cond_timedwait( &pNbCtx->cvAck, &pNbCtx->mutexAck, &cvTimeout); } while (!pNbCtx->bAck && sts != ETIMEDOUT); pNbCtx->bAck = FALSE; pthread_mutex_unlock(&pNbCtx->mutexAck); } } } while (!pNbCtx->bShutdown); LWNET_LOG_INFO("Stopping NetBIOS listener thread"); cleanup: LWNET_SAFE_FREE_MEMORY(NetBiosReply); LWNET_SAFE_FREE_STRING(NbName); if (sock != -1) { close(sock); } return NULL; error: goto cleanup; }
DWORD LWNetDnsParseRecord( IN PDNS_RESPONSE_HEADER pHeader, IN PBYTE pData, OUT PDNS_RECORD* ppRecord, OUT PDWORD pdwBytesToAdvance ) { DWORD dwError = 0; PBYTE pCurrent = pData; PSTR pszName = NULL; PDNS_RECORD pRecord = NULL; DWORD dwBytesToAdvance = 0; DWORD dwNameBytesToAdvance = 0; WORD wDataLen = 0; /* As read from DNS record response data */ dwError = LWNetDnsParseName(pHeader, pCurrent, &dwNameBytesToAdvance, &pszName); BAIL_ON_LWNET_ERROR(dwError); dwBytesToAdvance += dwNameBytesToAdvance; dwBytesToAdvance += sizeof(WORD); /* Type */ dwBytesToAdvance += sizeof(WORD); /* Class */ dwBytesToAdvance += sizeof(DWORD); /* TTL */ pCurrent += dwBytesToAdvance; wDataLen = LWNetDnsReadWORD( pCurrent ); dwBytesToAdvance += wDataLen + sizeof(WORD); dwError = LWNetAllocateMemory(sizeof(DNS_RECORD) + wDataLen, (PVOID*)&pRecord); BAIL_ON_LWNET_ERROR(dwError); // Fill in new record from buffer pCurrent = pData; pCurrent += dwNameBytesToAdvance; pRecord->pszName = pszName; pszName = NULL; pRecord->wType = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); pRecord->wClass = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); pRecord->dwTTL = LWNetDnsReadDWORD(pCurrent); pCurrent += sizeof(DWORD); pRecord->wDataLen = LWNetDnsReadWORD(pCurrent); pCurrent += sizeof(WORD); pRecord->pData = (PBYTE)pRecord + sizeof(DNS_RECORD); memcpy(pRecord->pData, pCurrent, pRecord->wDataLen); error: LWNET_SAFE_FREE_STRING(pszName); if (dwError) { if (pRecord) { LWNetDnsFreeRecord(pRecord); pRecord = NULL; } dwBytesToAdvance = 0; } *pdwBytesToAdvance = dwBytesToAdvance; *ppRecord = pRecord; return dwError; }
DWORD LWNetDnsGetNameServerList( OUT PSTR** pppszNameServerList, OUT PDWORD pdwNumServers ) // Call LWNET_SAFE_FREE_STRING_ARRAY on returned server list { DWORD dwError = 0; PSTR* ppszNameServerList = NULL; DWORD dwNumServers = 0; FILE* fp = NULL; BOOLEAN bFileExists = FALSE; PCSTR pszConfigFilePath = "/etc/resolv.conf"; const DWORD dwMaxLineLen = 1024; CHAR szBuf[dwMaxLineLen + 1]; regex_t rx; PDLINKEDLIST pNameServerList = NULL; PSTR pszNameServer = NULL; memset(&rx, 0, sizeof(rx)); if (regcomp(&rx, "^nameserver[[:space:]].*$", REG_EXTENDED) < 0) { dwError = ERROR_BAD_FORMAT; BAIL_ON_LWNET_ERROR(dwError); } dwError = LwCheckFileTypeExists( pszConfigFilePath, LWFILE_REGULAR, &bFileExists); BAIL_ON_LWNET_ERROR(dwError); if (!bFileExists) { *pppszNameServerList = NULL; *pdwNumServers = 0; goto cleanup; } if ((fp = fopen(pszConfigFilePath, "r")) == NULL) { dwError = LwMapErrnoToLwError(errno); BAIL_ON_LWNET_ERROR(dwError); } while (1) { if (fgets(szBuf, dwMaxLineLen, fp) == NULL) { if (!feof(fp)) { dwError = LwMapErrnoToLwError(errno); BAIL_ON_LWNET_ERROR(dwError); } else { break; } } LwStripWhitespace(szBuf, TRUE, TRUE); if (!LWNetDnsConfigLineIsComment(szBuf) && !regexec(&rx, szBuf, (size_t)0, NULL, 0)) { PSTR pszLocation = NULL; PCSTR pszSearchString = "nameserver"; if ((pszLocation = strstr(szBuf, pszSearchString))) { pszLocation += strlen(pszSearchString); if (!IsNullOrEmptyString(pszLocation)) { dwError = LWNetAllocateString(pszLocation, &pszNameServer); BAIL_ON_LWNET_ERROR(dwError); } dwError = LWNetDLinkedListAppend( &pNameServerList, pszNameServer); BAIL_ON_LWNET_ERROR(dwError); pszNameServer = NULL; dwNumServers++; } } } if (dwNumServers) { PDLINKEDLIST pListMember = NULL; DWORD iMember = 0; dwError = LWNetAllocateMemory(dwNumServers * sizeof(PSTR), (PVOID*)&ppszNameServerList); BAIL_ON_LWNET_ERROR(dwError); pListMember = pNameServerList; while (pListMember) { ppszNameServerList[iMember++] = (PSTR)pListMember->pItem; pListMember->pItem = NULL; pListMember = pListMember->pNext; } } *pppszNameServerList = ppszNameServerList; *pdwNumServers = dwNumServers; cleanup: regfree(&rx); LWNetDLinkedListFree(pNameServerList); LWNET_SAFE_FREE_STRING(pszNameServer); if (fp) { fclose(fp); } return dwError; error: *pppszNameServerList = NULL; *pdwNumServers = 0; if (ppszNameServerList) { LWNetFreeStringArray(ppszNameServerList, dwNumServers); } goto cleanup; }
/* Convert NetBIOS level 2 encoded name to a string */ DWORD LWNetNbName2ToStr( IN PBYTE buf, OUT PSTR *ppNbName, OUT PUINT8 pSuffix, OUT PDWORD dwBytesConsumed) { DWORD dwError = 0; PSTR *NbNameParts2 = NULL; PSTR NbName = NULL; CHAR netBiosName[LWNB_NAME_MAX_LENGTH] = {0}; DWORD addrsLen = 0; DWORD nbNameOffset = 0; DWORD i = 0; dwError = LWNetNbName2ToParts(&buf[i], &NbNameParts2, &addrsLen); BAIL_ON_LWNET_ERROR(dwError); dwError = LWNetAllocateMemory( addrsLen * sizeof(char), (PVOID*) &NbName); BAIL_ON_LWNET_ERROR(dwError); nbNameOffset = 0; for (i=0; NbNameParts2[i]; i++) { if (i==0) { /* * Convert first part of name, which is in NetBIOS layer 1 encoding * to a null-terminated string. */ LWNetNbNameToStr((UINT8 *) NbNameParts2[i], netBiosName, pSuffix); strncat(NbName, netBiosName, addrsLen - nbNameOffset); nbNameOffset += strlen(netBiosName); } else { /* The rest of the name is just text, so copy it */ strncat(NbName, ".", addrsLen - nbNameOffset); nbNameOffset++; strncat(NbName, NbNameParts2[i], addrsLen - nbNameOffset); nbNameOffset += strlen(NbNameParts2[i]); } } *ppNbName = NbName; *dwBytesConsumed = addrsLen; cleanup: for (i=0; NbNameParts2[i]; i++) { LWNET_SAFE_FREE_MEMORY(NbNameParts2[i]); } LWNET_SAFE_FREE_MEMORY(NbNameParts2); return dwError; error: LW_SAFE_FREE_MEMORY(NbName); goto cleanup; }
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; }
DWORD LWNetNbParseNameQueryResponse( IN PBYTE buf, IN DWORD len, IN UINT16 TransactionId, OUT UINT16 *retTransactionId, OUT PSTR *retNbName, OUT UINT32 *retTTL, OUT UINT8 *retFlags, OUT struct in_addr **retAddrs, OUT PDWORD retAddrsLen) { DWORD dwError = 0; DWORD i = 0; DWORD j = 0; UINT16 parseTransactionId = 0; UINT16 opCodeFlags = 0; UINT16 bResponse = 0; UINT16 nbQDCount = 0; UINT16 nbANCount = 0; UINT16 nbNSCount = 0; UINT16 nbARCount = 0; UINT16 nbNB = 0; UINT16 nbIN = 0; UINT32 nbTTL = 0; UINT16 nbNB_Flags = 0; UINT32 IpAddress = 0; UINT16 RDLength = 0; UINT8 queryType = 0; PSTR NetBiosName = NULL; PSTR NbName = NULL; DWORD numAddrs = 0; DWORD nbNameLen = 0; struct in_addr *addrs = NULL; memcpy(&parseTransactionId, &buf[i], sizeof(parseTransactionId)); i += sizeof(parseTransactionId); parseTransactionId = ntohs(parseTransactionId); if (retTransactionId) { *retTransactionId = parseTransactionId; } memcpy(&opCodeFlags, &buf[i], sizeof(opCodeFlags)); i += sizeof(opCodeFlags); opCodeFlags = ntohs(opCodeFlags); bResponse = opCodeFlags & (1 << LWNB_SHIFT_REQUEST_TYPE) ? 1 : 0; #if 0 /* Deal with other flags later... Not sure it matters at this point */ opCodeFlags |= (bBroadcast & 1) << LWNB_SHIFT_FLAG_BROADCAST; opCodeFlags |= 1 << LWNB_SHIFT_FLAG_RECURSION_DESIRED; opCodeFlags |= (opcode & 4) << LWNB_SHIFT_OPCODE; #endif if (bResponse != LWNB_OPCODE_RESPONSE) { /* Something is seriously wrong if this is not a NetBIOS response */ dwError = ERROR_BAD_NET_RESP; BAIL_ON_LWNET_ERROR(dwError); } memcpy(&nbQDCount, &buf[i], sizeof(nbQDCount)); i += sizeof(nbQDCount); nbQDCount = ntohs(nbQDCount); memcpy(&nbANCount, &buf[i], sizeof(nbANCount)); i += sizeof(nbANCount); nbANCount = ntohs(nbANCount); if (nbANCount != 1) { /* There must be exactly 1 answer to the sent query */ dwError = ERROR_BAD_NET_RESP; BAIL_ON_LWNET_ERROR(dwError); } memcpy(&nbNSCount, &buf[i], sizeof(nbNSCount)); i += sizeof(nbNSCount); nbNSCount = ntohs(nbNSCount); memcpy(&nbARCount, &buf[i], sizeof(nbARCount)); i += sizeof(nbARCount); nbARCount = ntohs(nbARCount); dwError = LWNetNbName2ToStr( &buf[i], &NetBiosName, &queryType, &nbNameLen); BAIL_ON_LWNET_ERROR(dwError); i += nbNameLen; memcpy(&nbNB, &buf[i], sizeof(nbNB)); i += sizeof(nbNB); nbNB = ntohs(nbNB); memcpy(&nbIN, &buf[i], sizeof(nbIN)); i += sizeof(nbIN); nbIN = ntohs(nbIN); if (nbNB != 0x0020 || nbIN != 0x0001) { /* RFC 1002 says these must be the values for these fields */ dwError = ERROR_BAD_NET_RESP; BAIL_ON_LWNET_ERROR(dwError); } memcpy(&nbTTL, &buf[i], sizeof(nbTTL)); i += sizeof(nbTTL); nbTTL = ntohl(nbTTL); if (retTTL) { *retTTL = nbTTL; } memcpy(&RDLength, &buf[i], sizeof(RDLength)); i += sizeof(RDLength); RDLength = ntohs(RDLength); numAddrs = RDLength / (sizeof(nbNB_Flags) + sizeof(IpAddress)); dwError = LWNetAllocateMemory( numAddrs * sizeof(struct in_addr), (PVOID*) &addrs); BAIL_ON_LWNET_ERROR(dwError); j = 0; do { memcpy(&nbNB_Flags, &buf[i], sizeof(nbNB_Flags)); i += sizeof(nbNB_Flags); nbNB_Flags = ntohs(nbNB_Flags); nbNB_Flags >>= 13; *retFlags = nbNB_Flags; memcpy(&IpAddress, &buf[i], sizeof(IpAddress)); i += sizeof(IpAddress); addrs[j++].s_addr = IpAddress; RDLength -= sizeof(nbNB_Flags) + sizeof(IpAddress); } while (RDLength > 0); *retNbName = NetBiosName; *retAddrs = addrs; *retAddrsLen = j; cleanup: return dwError; error: LWNET_SAFE_FREE_MEMORY(NetBiosName); LWNET_SAFE_FREE_MEMORY(NbName); LWNET_SAFE_FREE_MEMORY(addrs); goto cleanup; }
DWORD LWNetReadString( OUT PSTR *ppszDest, IN PACKED_ARRAY *pArray ) { // Decode a string according to RFC1035 DWORD dwError = 0; PSTR pszOut = NULL; size_t outOffset = 0; BYTE *followingCur; size_t followOffset; size_t copyLen; BYTE **pCur = &pArray->pCur; // A valid string can't be longer than the packet it comes from dwError = LWNetAllocateMemory(pArray->totalSize, (PVOID*)&pszOut); BAIL_ON_LWNET_ERROR(dwError); while (**pCur != '\0') { switch (**pCur & 0xC0) { case 0xC0: // This is a string reference followOffset = (((WORD)(*pCur)[0] << 8) | (*pCur)[1]) & 0x3FFF; if (followOffset >= pArray->totalSize) { //Offset out of bounds dwError = DNS_ERROR_BAD_PACKET; goto error; } *pCur += 2; pCur = &followingCur; followingCur = pArray->pStart + followOffset; if ((**pCur & 0xC0) == 0xC0) { // This is definitely redundant, and it could be an infinite // loop. dwError = DNS_ERROR_BAD_PACKET; goto error; } break; case 0x00: // This is a string component copyLen = (*pCur)[0]; (*pCur)++; if (copyLen + 2 + outOffset > pArray->totalSize) { // This goes out of bounds of the output buffer. There must be // an infinite loop in the encoding. dwError = DNS_ERROR_BAD_PACKET; goto error; } if (copyLen + 1 + *pCur - pArray->pStart > pArray->totalSize) { // The label goes out of bounds of the message dwError = DNS_ERROR_BAD_PACKET; goto error; } if (outOffset > 0) { //Add a dot to separate the components pszOut[outOffset++] = '.'; } memcpy(pszOut + outOffset, *pCur, copyLen); *pCur += copyLen; outOffset += copyLen; break; default: // Illegal reserved value dwError = DNS_ERROR_BAD_PACKET; goto error; } } *pCur += 1; pszOut[outOffset++] = '\0'; error: if (dwError) { LWNET_SAFE_FREE_STRING(pszOut); } *ppszDest = pszOut; return dwError; }
DWORD LWNetSrvNetBiosInit(VOID) { struct { unsigned mutexInit : 1; unsigned mutexAckInit : 1; unsigned cvInit : 1; unsigned cvAckInit : 1; } initialized = {0}; DWORD dwError = 0; PLWNET_SRV_NETBIOS_CONTEXT pNbCtx = NULL; dwError = LWNetAllocateMemory( sizeof(*pNbCtx), (PVOID*) &pNbCtx); BAIL_ON_LWNET_ERROR(dwError); dwError = LwErrnoToWin32Error(pthread_mutex_init(&pNbCtx->mutex, NULL)); BAIL_ON_LWNET_ERROR(dwError); initialized.mutexInit = TRUE; dwError = LwErrnoToWin32Error(pthread_cond_init(&pNbCtx->cv, NULL)); BAIL_ON_LWNET_ERROR(dwError); initialized.cvInit = TRUE; dwError = LwErrnoToWin32Error(pthread_mutex_init(&pNbCtx->mutexAck, NULL)); BAIL_ON_LWNET_ERROR(dwError); initialized.mutexAckInit = TRUE; dwError = LwErrnoToWin32Error(pthread_cond_init(&pNbCtx->cvAck, NULL)); BAIL_ON_LWNET_ERROR(dwError); initialized.cvAckInit = TRUE; dwError = LwErrnoToWin32Error(pthread_mutex_init( &pNbCtx->mutexTransactionId, NULL)); BAIL_ON_LWNET_ERROR(dwError); gpNbCtx = pNbCtx; cleanup: return dwError; error: if (initialized.mutexInit) { pthread_mutex_destroy(&pNbCtx->mutex); } if (initialized.mutexAckInit) { pthread_mutex_destroy(&pNbCtx->mutexAck); } if (initialized.cvInit) { pthread_cond_destroy(&pNbCtx->cv); } if (initialized.cvAckInit) { pthread_cond_destroy(&pNbCtx->cvAck); } LWNET_SAFE_FREE_MEMORY(pNbCtx); goto cleanup; }
DWORD LWNetDnsBuildServerArray( IN PDLINKEDLIST pSrvRecordList, OUT PDNS_SERVER_INFO* ppServerArray, OUT PDWORD pdwServerCount ) { DWORD dwError = 0; DWORD dwServerCount = 0; PDNS_SERVER_INFO pServerArray = NULL; PDLINKEDLIST pListMember = NULL; PDNS_SRV_INFO_RECORD pSrvRecord = NULL; DWORD dwServerIndex = 0; DWORD dwStringSize = 0; DWORD dwRequiredSize = 0; PSTR pStringLocation = NULL; for (pListMember = pSrvRecordList; pListMember; pListMember = pListMember->pNext) { pSrvRecord = (PDNS_SRV_INFO_RECORD)pListMember->pItem; dwStringSize += strlen(pSrvRecord->pszAddress) + 1; dwStringSize += strlen(pSrvRecord->pszTarget) + 1; dwServerCount++; } if (dwServerCount < 1) { // nothing to do, so we are done dwError = 0; goto error; } dwRequiredSize = dwServerCount * sizeof(DNS_SERVER_INFO) + dwStringSize; dwError = LWNetAllocateMemory(dwRequiredSize, (PVOID*)&pServerArray); BAIL_ON_LWNET_ERROR(dwError); pStringLocation = CT_PTR_ADD(pServerArray, dwServerCount * sizeof(DNS_SERVER_INFO)); dwServerIndex = 0; for (pListMember = pSrvRecordList; pListMember; pListMember = pListMember->pNext) { PSTR source; pSrvRecord = (PDNS_SRV_INFO_RECORD)pListMember->pItem; // Copy the strings into the buffer pServerArray[dwServerIndex].pszAddress = pStringLocation; for (source = pSrvRecord->pszAddress; source[0]; source++) { pStringLocation[0] = source[0]; pStringLocation++; } pStringLocation[0] = source[0]; pStringLocation++; pServerArray[dwServerIndex].pszName = pStringLocation; for (source = pSrvRecord->pszTarget; source[0]; source++) { pStringLocation[0] = source[0]; pStringLocation++; } pStringLocation[0] = source[0]; pStringLocation++; dwServerIndex++; } // TODO: Turns this into ASSERT if (CT_PTR_OFFSET(pServerArray, pStringLocation) != dwRequiredSize) { LWNET_LOG_ERROR("ASSERT - potential buffer overflow"); } error: if (dwError) { LWNET_SAFE_FREE_MEMORY(pServerArray); dwServerCount = 0; } *ppServerArray = pServerArray; *pdwServerCount = dwServerCount; return dwError; }