/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: hostent.c Abstract: Domain Name System (DNS) Library Hostent routines. Author: Jim Gilroy (jamesg) December 4, 2000 Revision History: --*/ #include "local.h" #include "ws2atm.h" // ATM address // // Max number of aliases // #define DNS_MAX_ALIAS_COUNT (8) // // Min size of hostent address buffer // - enough for one address of largest type // #define MIN_ADDR_BUF_SIZE (sizeof(ATM_ADDRESS)) // // String alignment in buffer // // DCR: string buffer alignment exposed globally // // Since address and string DATA (not ptrs) can be intermixed // as we build, we MUST size strings for DWORD (at minimum) so // to that addresses may be DWORD aligned. // However, when we build we can pack as tightly as desired // though obviously unicode strings must WCHAR align. // #define HOSTENT_STRING_ALIGN_DWORD(size) DWORD_ALIGN_DWORD(size) #define HOSTENT_STRING_ALIGN_PTR(ptr) DWORD_ALIGN(ptr) #define REQUIRED_HOSTENT_STRING_ALIGN_DWORD(size) WORD_ALIGN_DWORD(size) #define REQUIRED_HOSTENT_STRING_ALIGN_PTR(ptr) WORD_ALIGN(ptr) // // Hostent utilities // BOOL Hostent_IsSupportedAddrType( IN WORD wType ) /*++ Routine Description: Is this a supported address type for hostent. Arguments: wType -- type in question Return Value: TRUE if type supported FALSE otherwise --*/ { return ( wType == DNS_TYPE_A || wType == DNS_TYPE_AAAA || wType == DNS_TYPE_ATMA ); } DWORD Hostent_Size( IN PHOSTENT pHostent, IN DNS_CHARSET CharSetExisting, IN DNS_CHARSET CharSetTarget, IN PDWORD pAliasCount, IN PDWORD pAddrCount ) /*++ Routine Description: Find size of hostent. Arguments: pHostent -- hostent CharSetExisting -- char set of pHostent CharSetTarget -- char set to size to pAliasCount -- count of aliases pAddrCount -- count of addrs Return Value: Size in bytes of hostent. --*/ { DWORD sizeName = 0; DWORD sizeAliasNames = 0; DWORD sizeAliasPtr; DWORD sizeAddrPtr; DWORD sizeAddrs; DWORD sizeTotal; PCHAR palias; DWORD addrCount = 0; DWORD aliasCount = 0; DNSDBG( HOSTENT, ( "Hostent_Size( %p, %d, %d )\n", pHostent, CharSetExisting, CharSetTarget )); // // name // if ( pHostent->h_name ) { sizeName = Dns_GetBufferLengthForStringCopy( pHostent->h_name, 0, CharSetExisting, CharSetTarget ); sizeName = HOSTENT_STRING_ALIGN_DWORD( sizeName ); } // // aliases // if ( pHostent->h_aliases ) { while ( palias = pHostent->h_aliases[aliasCount] ) { sizeAliasNames += Dns_GetBufferLengthForStringCopy( palias, 0, CharSetExisting, CharSetTarget ); sizeAliasNames = HOSTENT_STRING_ALIGN_DWORD( sizeAliasNames ); aliasCount++; } } sizeAliasPtr = (aliasCount+1) * sizeof(PCHAR); // // addresses // if ( pHostent->h_addr_list ) { while ( pHostent->h_addr_list[addrCount] ) { addrCount++; } } sizeAddrPtr = (addrCount+1) * sizeof(PCHAR); sizeAddrs = addrCount * pHostent->h_length; // // calc total size // // note: be careful of alignment issues // our layout is // - hostent struct // - ptr arrays // - address + string data // // since address and string DATA (not ptrs) can be intermixed // as we build, we MUST size strings for DWORD (at minimum) so // to that addresses may be DWORD aligned // // in copying we can copy all addresses first and avoid intermix // but DWORD string alignment is still safe // sizeTotal = POINTER_ALIGN_DWORD( sizeof(HOSTENT) ) + sizeAliasPtr + sizeAddrPtr + sizeAddrs + sizeName + sizeAliasNames; if ( pAddrCount ) { *pAddrCount = addrCount; } if ( pAliasCount ) { *pAliasCount = aliasCount; } DNSDBG( HOSTENT, ( "Hostent sized:\n" "\tname = %d\n" "\talias ptrs = %d\n" "\talias names = %d\n" "\taddr ptrs = %d\n" "\taddrs = %d\n" "\ttotal = %d\n", sizeName, sizeAliasPtr, sizeAliasNames, sizeAddrPtr, sizeAddrs, sizeTotal )); return sizeTotal; } PHOSTENT Hostent_Init( IN OUT PBYTE * ppBuffer, //IN OUT PINT pBufSize, IN INT Family, IN INT AddrLength, IN DWORD AddrCount, IN DWORD AliasCount ) /*++ Routine Description: Init hostent struct. Assumes length is adequate. Arguments: ppBuffer -- addr to ptr to buffer to write hostent; on return contains next location in buffer Family -- address family AddrLength -- address length AddrCount -- address count AliasCount -- alias count Return Value: Ptr to hostent. --*/ { PBYTE pbuf = *ppBuffer; PHOSTENT phost; DWORD size; // // hostent // - must be pointer aligned // phost = (PHOSTENT) POINTER_ALIGN( pbuf ); phost->h_name = NULL; phost->h_length = (SHORT) AddrLength; phost->h_addrtype = (SHORT) Family; pbuf = (PBYTE) (phost + 1); // // init alias array // - set hostent ptr // - clear entire alias array; // since this count is often defaulted nice to clear it just // to avoid junk // pbuf = (PBYTE) POINTER_ALIGN( pbuf ); phost->h_aliases = (PCHAR *) pbuf; size = (AliasCount+1) * sizeof(PCHAR); RtlZeroMemory( pbuf, size ); pbuf += size; // // init addr array // - set hostent ptr // - clear first address entry // callers responsibility to NULL last addr pointer when done // *(PCHAR *)pbuf = NULL; phost->h_addr_list = (PCHAR *) pbuf; pbuf += (AddrCount+1) * sizeof(PCHAR); // // return next position in buffer // *ppBuffer = pbuf; return phost; } VOID Dns_PtrArrayToOffsetArray( IN OUT PCHAR * PtrArray, IN PCHAR pBase ) /*++ Routine Description: Change an array of pointers into array of offsets. This is used to convert aliases lists to offsets. Arguments: pPtrArray -- addr of ptr to array of pointers to convert to offsets the array must be terminated by NULL ptr pBase -- base address to offset from Return Value: None --*/ { PCHAR * pptr = PtrArray; PCHAR pdata; DNSDBG( TRACE, ( "Dns_PtrArrayToOffsetArray()\n" )); // // turn each pointer into offset // while( pdata = *pptr ) { *pptr++ = (PCHAR)( (PCHAR)pdata - (PCHAR)pBase ); } } VOID Hostent_ConvertToOffsets( IN OUT PHOSTENT pHostent ) /*++ Routine Description: Convert hostent to offsets. Arguments: pHostent -- hostent to convert to offsets Return Value: None --*/ { PBYTE ptr; DNSDBG( TRACE, ( "Hostent_ConvertToOffsets()\n" )); // // convert // - name // - alias array pointer // - address array pointer // if ( ptr = pHostent->h_name ) { pHostent->h_name = (PCHAR) (ptr - (PBYTE)pHostent); } // alias array // - convert array pointer // - convert pointers in array if ( ptr = (PBYTE)pHostent->h_aliases ) { pHostent->h_aliases = (PCHAR *) (ptr - (PBYTE)pHostent); Dns_PtrArrayToOffsetArray( (PCHAR *) ptr, (PCHAR) pHostent ); } // address array // - convert array pointer // - convert pointers in array if ( ptr = (PBYTE)pHostent->h_addr_list ) { pHostent->h_addr_list = (PCHAR *) (ptr - (PBYTE)pHostent); Dns_PtrArrayToOffsetArray( (PCHAR *) ptr, (PCHAR) pHostent ); } DNSDBG( TRACE, ( "Leave Hostent_ConvertToOffsets()\n" )); } PHOSTENT Hostent_Copy( IN OUT PBYTE * ppBuffer, IN OUT PINT pBufferSize, OUT PINT pHostentSize, IN PHOSTENT pHostent, IN DNS_CHARSET CharSetIn, IN DNS_CHARSET CharSetTarget, IN BOOL fOffsets, IN BOOL fAlloc ) /*++ Routine Description: Copy a hostent. Arguments: ppBuffer -- addr with ptr to buffer to write to; if no buffer then hostent is allocated updated with ptr to position in buffer after hostent pBufferSize -- addr containing size of buffer; updated with bytes left after hostent written (even if out of space, it contains missing number of bytes as negative number) pHostentSize -- addr to recv total size of hostent written pHostent -- existing hostent to copy CharSetIn -- charset of existing hostent CharSetTarget -- charset of target hostent fOffsets -- write hostent with offsets fAlloc -- allocate copy Return Value: Ptr to new hostent. NULL on error. See GetLastError(). --*/ { PBYTE pch; PHOSTENT phost = NULL; DWORD size; DWORD sizeTotal; DWORD bytesLeft; DWORD aliasCount; DWORD addrCount; DWORD addrLength; PCHAR * pptrArrayIn; PCHAR * pptrArrayOut; PCHAR pdataIn; DNSDBG( HOSTENT, ( "Hostent_Copy()\n" )); // // determine required hostent size // - allow sizing skip for already allocated buffers only // sizeTotal = Hostent_Size( pHostent, CharSetIn, CharSetTarget, & aliasCount, & addrCount ); // // alloc or reserve size in buffer // if ( fAlloc ) { pch = ALLOCATE_HEAP( sizeTotal ); if ( !pch ) { goto Failed; } } else { pch = FlatBuf_Arg_ReserveAlignPointer( ppBuffer, pBufferSize, sizeTotal ); if ( !pch ) { goto Failed; } } // // note: assuming from here on down that we have adequate space // // reason we aren't building with FlatBuf routines is that // a) i wrote this first // b) we believe we have adequate space // c) i haven't built FlatBuf string conversion routines // which we need below (for RnR unicode to ANSI) // // we could reset buf pointers here and build directly with FlatBuf // routines; this isn't directly necessary // // // init hostent struct // addrLength = pHostent->h_length; phost = Hostent_Init( & pch, pHostent->h_addrtype, addrLength, addrCount, aliasCount ); DNS_ASSERT( pch > (PBYTE)phost ); // // copy addresses // - no need to align as previous is address // pptrArrayIn = pHostent->h_addr_list; pptrArrayOut = phost->h_addr_list; if ( pptrArrayIn ) { while( pdataIn = *pptrArrayIn++ ) { *pptrArrayOut++ = pch; RtlCopyMemory( pch, pdataIn, addrLength ); pch += addrLength; } } *pptrArrayOut = NULL; // // copy the aliases // pptrArrayIn = pHostent->h_aliases; pptrArrayOut = phost->h_aliases; if ( pptrArrayIn ) { while( pdataIn = *pptrArrayIn++ ) { pch = REQUIRED_HOSTENT_STRING_ALIGN_PTR( pch ); *pptrArrayOut++ = pch; size = Dns_StringCopy( pch, NULL, // infinite size pdataIn, 0, // unknown length CharSetIn, CharSetTarget ); pch += size; } } *pptrArrayOut = NULL; // // copy the name // if ( pHostent->h_name ) { pch = REQUIRED_HOSTENT_STRING_ALIGN_PTR( pch ); phost->h_name = pch; size = Dns_StringCopy( pch, NULL, // infinite size pHostent->h_name, 0, // unknown length CharSetIn, CharSetTarget ); pch += size; } // // copy is complete // - verify our write functions work // ASSERT( (DWORD)(pch-(PBYTE)phost) <= sizeTotal ); if ( pHostentSize ) { *pHostentSize = (INT)( pch - (PBYTE)phost ); } if ( !fAlloc ) { PBYTE pnext = *ppBuffer; // if we sized too small -- // fix up the buf pointer and bytes left if ( pnext < pch ) { ASSERT( FALSE ); *ppBuffer = pch; *pBufferSize -= (INT)(pch - pnext); } } IF_DNSDBG( HOSTENT ) { DnsDbg_Hostent( "Hostent copy:", phost, (CharSetTarget == DnsCharSetUnicode) ); } // // convert to offsets? // if ( fOffsets ) { Hostent_ConvertToOffsets( phost ); } Failed: DNSDBG( TRACE, ( "Leave Hostent_Copy() => %p\n", phost )); return phost; } DWORD Hostent_WriteIp4Addrs( IN OUT PHOSTENT pHostent, OUT PCHAR pAddrBuf, IN DWORD MaxBufCount, IN PIP4_ADDRESS Ip4Array, IN DWORD ArrayCount, IN BOOL fScreenZero ) /*++ Routine Description: Write IP4 addresses to hostent. Arguments: pHostent -- hostent pAddrBuf -- buffer to hold addresses MaxBufCount -- max IPs buffer can hold Ip4Array -- array of IP4 addresses ArrayCount -- array count fScreenZero -- screen out zero addresses? Return Value: Count of addresses written --*/ { DWORD i = 0; DWORD stopCount = MaxBufCount; PIP4_ADDRESS pip = (PIP_ADDRESS) pAddrBuf; PIP4_ADDRESS * pipPtr = (PIP4_ADDRESS *) pHostent->h_addr_list; // // write IP addresses OR loopback if no IPs // if ( Ip4Array ) { if ( ArrayCount < stopCount ) { stopCount = ArrayCount; } for ( i=0; i < stopCount; ++i ) { IP4_ADDRESS ip = Ip4Array[i]; if ( ip != 0 || !fScreenZero ) { *pip = ip; *pipPtr++ = pip++; } } } *pipPtr = NULL; // count of addresses written return( i ); } DWORD Hostent_WriteLocalIp4Array( IN OUT PHOSTENT pHostent, OUT PCHAR pAddrBuf, IN DWORD MaxBufCount, IN PIP_ARRAY pIpArray ) /*++ Routine Description: Write local IP list into hostent. Arguments: pHostent -- hostent pAddrBuf -- buffer to hold addresses MaxBufCount -- max IPs buffer can hold pIpArray -- IP4 array of local addresses Return Value: Count of addresses written --*/ { DWORD count = 0; // // write array // if ( pIpArray ) { count = Hostent_WriteIp4Addrs( pHostent, pAddrBuf, MaxBufCount, pIpArray->AddrArray, pIpArray->AddrCount, TRUE // screen out zeros ); } // // if no addresses written, write loopback // if ( count==0 ) { pHostent->h_addr_list[0] = pAddrBuf; pHostent->h_addr_list[1] = NULL; *((IP4_ADDRESS*)pAddrBuf) = DNS_NET_ORDER_LOOPBACK; count = 1; } // count of addresses written return( count ); } BOOL Hostent_SetToSingleAddress( IN OUT PHOSTENT pHostent, IN PCHAR pAddr, IN DWORD AddrLength ) /*++ Routine Description: Set address in hostent. Arguments: pHostent -- hostent to check pAddr -- ptr to address to check AddrLength -- address length Return Value: TRUE if address successfully copied into hostent. FALSE otherwise (no hostent, wrong length, hostent empty) --*/ { PCHAR paddrHostent; // // validate // - must have hostent // - length must match // if ( !pHostent || AddrLength != (DWORD)pHostent->h_length ) { return FALSE; } // // slam address in on top of existing // - NULL 2nd addr pointer to terminate list // paddrHostent = pHostent->h_addr_list[0]; if ( !paddrHostent ) { return FALSE; } RtlCopyMemory( paddrHostent, pAddr, AddrLength ); pHostent->h_addr_list[1] = NULL; return TRUE; } BOOL Hostent_IsAddressInHostent( IN OUT PHOSTENT pHostent, IN PCHAR pAddr, IN DWORD AddrLength, IN INT Family OPTIONAL ) /*++ Routine Description: Does hostent contain this address. Arguments: pHostent -- hostent to check pAddr -- ptr to address to check AddrLength -- address length Family -- address family Return Value: TRUE if address is in hostent. FALSE otherwise. --*/ { BOOL freturn = FALSE; DWORD i; PCHAR paddrHostent; // // validate // - must have hostent // - must have address // - if family given, must match // - length must match // if ( !pHostent || !pAddr || AddrLength != (DWORD)pHostent->h_length || ( Family && Family != pHostent->h_addrtype ) ) { return freturn; } // // search for address -- if found return TRUE // i = 0; while ( paddrHostent = pHostent->h_addr_list[i++] ) { freturn = RtlEqualMemory( paddrHostent, pAddr, AddrLength ); if ( freturn ) { break; } } return freturn; } BOOL Hostent_IsIp4AddressInHostent( IN OUT PHOSTENT pHostent, IN IP4_ADDRESS Ip4Addr ) /*++ Routine Description: Does hostent contain this address. Arguments: pHostent -- hostent to check pAddr -- ptr to address to check AddrLength -- address length Family -- address family Return Value: TRUE if address is in hostent. FALSE otherwise. --*/ { DWORD i; PCHAR paddrHostent; // // validate // - must have hostent // - length must match // if ( !pHostent || sizeof(IP4_ADDRESS) != (DWORD)pHostent->h_length ) { return FALSE; } // // search for address -- if found return TRUE // i = 0; while ( paddrHostent = pHostent->h_addr_list[i++] ) { if ( Ip4Addr == *(PIP4_ADDRESS)paddrHostent ) { return TRUE; } } return FALSE; } // // Hostent building utilities // DNS_STATUS HostentBlob_Create( IN OUT PHOSTENT_BLOB * ppBlob, IN PHOSTENT_INIT pReq ) /*++ Routine Description: Initialize hostent (extending buffers as necessary) May allocate hostent buffer if existing too small. Returns required size. Arguments: ppBlob -- addr containing or to recv hostent object pReq -- hostent init request Return Value: --*/ { PHOSTENT_BLOB pblob = *ppBlob; PHOSTENT phost; PCHAR pbuf; BOOL funicode = FALSE; DWORD bytesLeft; DWORD addrSize; DWORD addrType; DWORD countAlias; DWORD countAddr; DWORD sizeChar; DWORD sizeHostent = 0; DWORD sizeAliasPtr; DWORD sizeAddrPtr; DWORD sizeAddrs; DWORD sizeName; DWORD sizeAliasNames; DWORD sizeTotal; DNSDBG( HOSTENT, ( "HostentBlob_Create()\n" )); // // calculate required size // // size for all char allocs // // note, we save CharSet info, if known, but the real // action in sizing or building or printing strings is simply // unicode\not-unicode sizeChar = sizeof(CHAR); if ( pReq->fUnicode || pReq->CharSet == DnsCharSetUnicode ) { sizeChar = sizeof(WCHAR); funicode = TRUE; } // limit alias count countAlias = pReq->AliasCount; if ( countAlias > DNS_MAX_ALIAS_COUNT ) { countAlias = DNS_MAX_ALIAS_COUNT; } sizeAliasPtr = (countAlias+1) * sizeof(PCHAR); // size address pointer array // - always size for at least one address // - write PTR address after record write // - write loopback or other local address // into local hostent countAddr = pReq->AddrCount; if ( countAddr == 0 ) { countAddr = 1; } sizeAddrPtr = (countAddr+1) * sizeof(PCHAR); // // determine address size and type // - may be specified directly // - or picked up from DNS type // // DCR: functionalize type-to\from-family and addr size // addrType = pReq->AddrFamily; if ( !addrType ) { WORD wtype = pReq->wType; if ( wtype == DNS_TYPE_A ) { addrType = AF_INET; } else if ( wtype == DNS_TYPE_AAAA || wtype == DNS_TYPE_A6 ) { addrType = AF_INET6; } else if ( wtype == DNS_TYPE_ATMA ) { addrType = AF_ATM; } } if ( addrType == AF_INET ) { addrSize = sizeof(IP4_ADDRESS); } else if ( addrType == AF_INET6 ) { addrSize = sizeof(IP6_ADDRESS ); } else if ( addrType == AF_ATM ) { addrSize = sizeof(ATM_ADDRESS); } else { // should have type and count or neither DNS_ASSERT( pReq->AddrCount == 0 ); addrSize = 0; } sizeAddrs = countAddr * addrSize; // always have buffer large enough for one // address of largest type if ( sizeAddrs < MIN_ADDR_BUF_SIZE ) { sizeAddrs = MIN_ADDR_BUF_SIZE; } // // namelength // - if actual name use it // (charset must match type we're building) // - if size, use it // - if absent use MAX // - round to DWORD if ( pReq->pName ) { if ( funicode ) { sizeName = wcslen( (PWSTR)pReq->pName ); } else { sizeName = strlen( pReq->pName ); } } else { sizeName = pReq->NameLength; } if ( sizeName ) { sizeName++; } else { sizeName = DNS_MAX_NAME_BUFFER_LENGTH; } sizeName = HOSTENT_STRING_ALIGN_DWORD( sizeChar*sizeName ); // // alias name lengths // - if absent use MAX for each string // - round to DWORD // sizeAliasNames = pReq->AliasNameLength; if ( sizeAliasNames ) { sizeAliasNames += pReq->AliasCount; } else { sizeAliasNames = DNS_MAX_NAME_BUFFER_LENGTH; } sizeAliasNames = HOSTENT_STRING_ALIGN_DWORD( sizeChar*sizeAliasNames ); // // calc total size // // note: be careful of alignment issues // our layout is // - hostent struct // - ptr arrays // - address + string data // // since address and string DATA (not ptrs) can be intermixed // as we build, we MUST size strings for DWORD (at minimum) so // to that addresses may be DWORD aligned // sizeTotal = POINTER_ALIGN_DWORD( sizeof(HOSTENT) ) + sizeAliasPtr + sizeAddrPtr + sizeAddrs + sizeName + sizeAliasNames; // // if no blob, allocate one along with buffer // if ( !pblob ) { pblob = (PHOSTENT_BLOB) ALLOCATE_HEAP( sizeTotal + sizeof(HOSTENT_BLOB) ); if ( !pblob ) { goto Failed; } RtlZeroMemory( pblob, sizeof(*pblob) ); pbuf = (PCHAR) (pblob + 1); pblob->pBuffer = pbuf; pblob->BufferLength = sizeTotal; pblob->fAllocatedBlob = TRUE; pblob->fAllocatedBuf = FALSE; } // // check existing buffer for size // - allocate new buffer if necessary // else { pbuf = pblob->pBuffer; if ( !pbuf || pblob->BufferLength < sizeTotal ) { if ( pbuf && pblob->fAllocatedBuf ) { FREE_HEAP( pbuf ); } pbuf = ALLOCATE_HEAP( sizeTotal ); pblob->pBuffer = pbuf; if ( pbuf ) { pblob->BufferLength = sizeTotal; pblob->fAllocatedBuf = TRUE; } // // DCR: alloc failure handling // - possibly keep previous buffers limitations // else // alloc failed { pblob->fAllocatedBuf = FALSE; return( DNS_ERROR_NO_MEMORY ); } } } // // init hostent and buffer subfields // bytesLeft = pblob->BufferLength; // // hostent // phost = (PHOSTENT) pbuf; pbuf += sizeof(HOSTENT); bytesLeft -= sizeof(HOSTENT); pblob->pHostent = phost; phost->h_name = NULL; phost->h_addr_list = NULL; phost->h_aliases = NULL; phost->h_length = (SHORT) addrSize; phost->h_addrtype = (SHORT) addrType; pblob->fWroteName = FALSE; pblob->AliasCount = 0; pblob->AddrCount = 0; pblob->CharSet = pReq->CharSet; pblob->fUnicode = funicode; if ( funicode ) { pblob->CharSet = DnsCharSetUnicode; } // // init alias array // - set hostent ptr // - clear entire alias array; // since this count is often defaulted nice to clear it just // to avoid junk // // #if 0 pwrite = FlatBuf_ReserveAlignPointer( & pbuf, & bytesLeft, sizeAliasPtr ); #endif if ( bytesLeft < sizeAliasPtr ) { DNS_ASSERT( FALSE ); goto Failed; } RtlZeroMemory( pbuf, sizeAliasPtr ); phost->h_aliases = (PCHAR *) pbuf; pbuf += sizeAliasPtr; bytesLeft -= sizeAliasPtr; pblob->MaxAliasCount = countAlias; // // init addr array // - set hostent ptr // - clear first address entry // callers responsibility to NULL last addr pointer when done // if ( bytesLeft < sizeAddrPtr ) { DNS_ASSERT( FALSE ); goto Failed; } * (PCHAR *)pbuf = NULL; phost->h_addr_list = (PCHAR *) pbuf; pbuf += sizeAddrPtr; bytesLeft -= sizeAddrPtr; pblob->MaxAddrCount = countAddr; // // set remaining buffer info // - save current buffer space // - save data on part of buffer available // for use by data // pblob->pAvailBuffer = pbuf; pblob->AvailLength = bytesLeft; pblob->pCurrent = pbuf; pblob->BytesLeft = bytesLeft; *ppBlob = pblob; IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "HostentBlob After Create:", pblob ); } return( ERROR_SUCCESS ); Failed: *ppBlob = pblob; if ( pblob && pblob->pBuffer && pblob->fAllocatedBuf ) { FREE_HEAP( pblob->pBuffer ); pblob->pBuffer = NULL; pblob->fAllocatedBuf = FALSE; } DNSDBG( HOSTENT, ( "Hostent Blob create failed!\n" )); return( DNS_ERROR_NO_MEMORY ); } PHOSTENT_BLOB HostentBlob_CreateAttachExisting( IN PHOSTENT pHostent, IN BOOL fUnicode ) /*++ Routine Description: Create hostent blob for existing hostent. This is a hack to allow existing RnR TLS hostents to be attached to hostent-blobs to smooth code transition. A full version would obviously require init structure and separate the sizing\init function from the creation function. Arguments: pHostent -- existing hostent fUnicode -- is unicode Return Value: Ptr to new hostent blob. NULL on alloc failure. GetLastError() has error. --*/ { PHOSTENT_BLOB pblob; DNSDBG( HOSTENT, ( "HostentBlob_CreateAttachExisting()\n" )); // // alloc // pblob = (PHOSTENT_BLOB) ALLOCATE_HEAP_ZERO( sizeof(HOSTENT_BLOB) ); if ( !pblob ) { SetLastError( DNS_ERROR_NO_MEMORY ); return NULL; } // // attach existing hostent // pblob->pHostent = pHostent; pblob->fUnicode = fUnicode; IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "Leaving AttachExisting:", pblob ); } return pblob; } VOID HostentBlob_Free( IN OUT PHOSTENT_BLOB pBlob ) /*++ Routine Description: Free hostent blob. Arguments: pBlob -- blob to free Return Value: None --*/ { // // free buffer? // if ( !pBlob ) { return; } if ( pBlob->fAllocatedBuf ) { FREE_HEAP( pBlob->pBuffer ); pBlob->pBuffer = NULL; pBlob->fAllocatedBuf = FALSE; } // // free blob itself? // if ( pBlob->fAllocatedBlob ) { FREE_HEAP( pBlob ); } } DNS_STATUS HostentBlob_WriteAddress( IN OUT PHOSTENT_BLOB pBlob, IN PVOID pAddress, IN DWORD AddrSize, IN DWORD AddrType ) /*++ Routine Description: Write IP4 address to hostent blob. Arguments: pBlob -- hostent build blob pAddress - address to write AddrSize - address size AddrType - address type (hostent type, e.g. AF_INET) Return Value: ERROR_SUCCESS if successful. ERROR_MORE_DATA if out of buffer space ERROR_INVALID_DATA if address doesn't match hostent --*/ { DWORD count = pBlob->AddrCount; PHOSTENT phost = pBlob->pHostent; PCHAR pcurrent; DWORD bytesLeft; // verify type // - set if empty or no addresses written if ( phost->h_addrtype != (SHORT)AddrType ) { if ( phost->h_addrtype != 0 ) { return( ERROR_INVALID_DATA ); } phost->h_addrtype = (SHORT) AddrType; phost->h_length = (SHORT) AddrSize; } // verify space if ( count >= pBlob->MaxAddrCount ) { return( ERROR_MORE_DATA ); } // align - to DWORD pcurrent = DWORD_ALIGN( pBlob->pCurrent ); bytesLeft = pBlob->BytesLeft; bytesLeft -= (DWORD)(pcurrent - pBlob->pCurrent); if ( bytesLeft < AddrSize ) { return( ERROR_MORE_DATA ); } // copy // - copy address to buffer // - set pointer in addr list // NULL following pointer RtlCopyMemory( pcurrent, pAddress, AddrSize ); phost->h_addr_list[count++] = pcurrent; phost->h_addr_list[count] = NULL; pBlob->AddrCount = count; pBlob->pCurrent = pcurrent + AddrSize; pBlob->BytesLeft = bytesLeft - AddrSize; return( NO_ERROR ); } DNS_STATUS HostentBlob_WriteAddressArray( IN OUT PHOSTENT_BLOB pBlob, IN PVOID pAddrArray, IN DWORD AddrCount, IN DWORD AddrSize, IN DWORD AddrType ) /*++ Routine Description: Write address array to hostent blob. Arguments: pBlob -- hostent build blob pAddrArray - address array to write AddrCount - address count AddrSize - address size AddrType - address type (hostent type, e.g. AF_INET) Return Value: ERROR_SUCCESS if successful. ERROR_MORE_DATA if out of buffer space ERROR_INVALID_DATA if address doesn't match hostent --*/ { DWORD count = AddrCount; PHOSTENT phost = pBlob->pHostent; PCHAR pcurrent; DWORD totalSize; DWORD i; DWORD bytesLeft; // verify type // - set if empty or no addresses written if ( phost->h_addrtype != (SHORT)AddrType ) { if ( phost->h_addrtype != 0 ) { return( ERROR_INVALID_DATA ); } phost->h_addrtype = (SHORT) AddrType; phost->h_length = (SHORT) AddrSize; } // verify space if ( count > pBlob->MaxAddrCount ) { return( ERROR_MORE_DATA ); } // align - to DWORD // // note: we are assuming that pAddrArray is internally // aligned adequately, otherwise we wouldn't be // getting an intact array and would have to add serially pcurrent = DWORD_ALIGN( pBlob->pCurrent ); bytesLeft = pBlob->BytesLeft; bytesLeft -= (DWORD)(pcurrent - pBlob->pCurrent); totalSize = count * AddrSize; if ( bytesLeft < totalSize ) { return( ERROR_MORE_DATA ); } // copy // - copy address array to buffer // - set pointer to each address in array // - NULL following pointer RtlCopyMemory( pcurrent, pAddrArray, totalSize ); for ( i=0; ih_addr_list[i] = pcurrent; pcurrent += AddrSize; } phost->h_addr_list[count] = NULL; pBlob->AddrCount = count; pBlob->pCurrent = pcurrent; pBlob->BytesLeft = bytesLeft - totalSize; return( NO_ERROR ); } DNS_STATUS HostentBlob_WriteNameOrAlias( IN OUT PHOSTENT_BLOB pBlob, IN PSTR pszName, IN BOOL fAlias, IN BOOL fUnicode ) /*++ Routine Description: Write name or alias to hostent Arguments: pBlob -- hostent build blob pszName -- name to write fAlias -- TRUE for alias; FALSE for name fUnicode -- name is unicode Return Value: ERROR_SUCCESS if successful. ERROR_MORE_DATA if out of buffer space ERROR_INVALID_DATA if address doesn't match hostent --*/ { DWORD count = pBlob->AliasCount; PHOSTENT phost = pBlob->pHostent; DWORD length; PCHAR pcurrent; DWORD bytesLeft; // // check length // if ( fUnicode ) { length = (wcslen( (PCWSTR)pszName ) + 1) * sizeof(WCHAR); } else { length = strlen( pszName ) + 1; } // // verify space // included ptr space // - skip if already written name // or exhausted alias array // if ( fAlias ) { if ( count >= pBlob->MaxAliasCount ) { return( ERROR_MORE_DATA ); } } else if ( pBlob->fWroteName ) { return( ERROR_MORE_DATA ); } // align pcurrent = REQUIRED_HOSTENT_STRING_ALIGN_PTR( pBlob->pCurrent ); bytesLeft = pBlob->BytesLeft; bytesLeft -= (DWORD)(pcurrent - pBlob->pCurrent); if ( bytesLeft < length ) { return( ERROR_MORE_DATA ); } // copy // - copy address to buffer // - set pointer in addr list // NULL following pointer RtlCopyMemory( pcurrent, pszName, length ); if ( fAlias ) { phost->h_aliases[count++] = pcurrent; phost->h_aliases[count] = NULL; pBlob->AliasCount = count; } else { phost->h_name = pcurrent; pBlob->fWroteName = TRUE; } length = REQUIRED_HOSTENT_STRING_ALIGN_DWORD( length ); pBlob->pCurrent = pcurrent + length; pBlob->BytesLeft = bytesLeft - length; return( NO_ERROR ); } DNS_STATUS HostentBlob_WriteRecords( IN OUT PHOSTENT_BLOB pBlob, IN PDNS_RECORD pRecords, IN BOOL fWriteName ) /*++ Routine Description: Write name or alias to hostent Arguments: pBlob -- hostent build blob pRecords -- records to convert to hostent fWriteName -- write name Return Value: ERROR_SUCCESS if successful. ERROR_MORE_DATA if out of buffer space ERROR_INVALID_DATA if address doesn't match hostent --*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prr = pRecords; DNSDBG( HOSTENT, ( "HostentBlob_WriteRecords( %p, %p, %d )\n", pBlob, pRecords, fWriteName )); // // write each record in turn to hostent // while ( prr ) { WORD wtype; if ( prr->Flags.S.Section != DNSREC_ANSWER && prr->Flags.S.Section != 0 ) { prr = prr->pNext; continue; } wtype = prr->wType; switch( wtype ) { case DNS_TYPE_A: status = HostentBlob_WriteAddress( pBlob, &prr->Data.A.IpAddress, sizeof(IP4_ADDRESS), AF_INET ); break; case DNS_TYPE_AAAA: status = HostentBlob_WriteAddress( pBlob, &prr->Data.AAAA.Ip6Address, sizeof(IP6_ADDRESS ), AF_INET6 ); break; case DNS_TYPE_ATMA: { ATM_ADDRESS atmAddr; // DCR: functionalize ATMA to ATM conversion // not sure this num of digits is correct // may have to actually parse address atmAddr.AddressType = prr->Data.ATMA.AddressType; atmAddr.NumofDigits = ATM_ADDR_SIZE; RtlCopyMemory( & atmAddr.Addr, prr->Data.ATMA.Address, ATM_ADDR_SIZE ); status = HostentBlob_WriteAddress( pBlob, & atmAddr, sizeof(ATM_ADDRESS), AF_ATM ); break; } case DNS_TYPE_CNAME: // record name is an alias status = HostentBlob_WriteNameOrAlias( pBlob, prr->pName, TRUE, // alias (prr->Flags.S.CharSet == DnsCharSetUnicode) ); break; case DNS_TYPE_PTR: // target name is the hostent name // but if already wrote name, PTR target becomes alias status = HostentBlob_WriteNameOrAlias( pBlob, prr->Data.PTR.pNameHost, pBlob->fWroteName ? TRUE // alias : FALSE, // name (prr->Flags.S.CharSet == DnsCharSetUnicode) ); break; default: DNSDBG( ANY, ( "Error record of type = %d while building hostent!\n", wtype )); status = ERROR_INVALID_DATA; } if ( status != ERROR_SUCCESS ) { DNSDBG( ANY, ( "ERROR: failed writing record to hostent!\n" "\tprr = %p\n" "\ttype = %d\n" "\tstatus = %d\n", prr, wtype, status )); } prr = prr->pNext; } IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "HostentBlob after WriteRecords():", pBlob ); } return( status ); } DNS_STATUS HostentBlob_CreateFromRecords( IN OUT PHOSTENT_BLOB * ppBlob, IN PDNS_RECORD pRecords, IN BOOL fWriteName, IN INT AddrFamily, OPTIONAL IN WORD wType OPTIONAL ) /*++ Routine Description: Create hostent from records Arguments: ppBlob -- ptr with or to recv hostent blob pRecords -- records to convert to hostent fWriteName -- write name to hostent AddrFamily -- addr family use if PTR records and no addr wType -- query type, if known Return Value: Ptr to blob if successful. NULL on error; GetLastError() has error. --*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrFirstAddr = NULL; PDNS_RECORD prr; DWORD addrCount = 0; WORD addrType = 0; HOSTENT_INIT request; PHOSTENT_BLOB pblob = *ppBlob; DNSDBG( HOSTENT, ( "HostentBlob_CreateFromRecords()\n" "\tpblob = %p\n" "\tprr = %p\n", pblob, pRecords )); // // count addresses // // DCR: fix up section hack when hosts file records get ANSWER section // prr = pRecords; while ( prr ) { if ( ( prr->Flags.S.Section == 0 || prr->Flags.S.Section == DNSREC_ANSWER ) && Hostent_IsSupportedAddrType( prr->wType ) ) { addrCount++; if ( !prrFirstAddr ) { prrFirstAddr = prr; addrType = prr->wType; } } prr = prr->pNext; } // // create or reinit hostent blob // RtlZeroMemory( &request, sizeof(request) ); request.AliasCount = DNS_MAX_ALIAS_COUNT; request.AddrCount = addrCount; request.wType = addrType; if ( !addrType ) { request.AddrFamily = AddrFamily; } request.CharSet = (pRecords) ? pRecords->Flags.S.CharSet : DnsCharSetUnicode; status = HostentBlob_Create( & pblob, & request ); if ( status != NO_ERROR ) { goto Done; } // // build hostent from answer records // // note: if manage to extract any useful data => continue // this protects against new unwriteable records breaking us // status = HostentBlob_WriteRecords( pblob, pRecords, TRUE // write name ); if ( status != NO_ERROR ) { if ( pblob->AddrCount || pblob->AliasCount || pblob->fWroteName ) { status = NO_ERROR; } else { goto Done; } } // // write address from PTR record // - first record PTR // OR // - queried for PTR and got CNAME answer, which can happen // in classless reverse lookup case // // DCR: add PTR address lookup to HostentBlob_WriteRecords() // - natural place // - but would have to figure out handling of multiple PTRs // if ( pRecords && ( pRecords->wType == DNS_TYPE_PTR || ( wType == DNS_TYPE_PTR && pRecords->wType == DNS_TYPE_CNAME && pRecords->Flags.S.Section == DNSREC_ANSWER ) ) ) { IP6_ADDRESS ip6; DWORD addrLength = sizeof(IP6_ADDRESS); INT family = 0; DNSDBG( HOSTENT, ( "Writing address for PTR record %S\n", pRecords->pName )); // convert reverse name to IP if ( Dns_StringToAddressEx( (PCHAR) & ip6, & addrLength, (PCSTR) pRecords->pName, & family, IS_UNICODE_RECORD(pRecords), TRUE // reverse lookup name ) ) { status = HostentBlob_WriteAddress( pblob, (PCHAR) &ip6, addrLength, family ); ASSERT( status == NO_ERROR ); status = ERROR_SUCCESS; } } // // write name? // if ( !pblob->fWroteName && fWriteName && prrFirstAddr ) { status = HostentBlob_WriteNameOrAlias( pblob, prrFirstAddr->pName, FALSE, // name (prrFirstAddr->Flags.S.CharSet == DnsCharSetUnicode) ); } IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "HostentBlob after CreateFromRecords():", pblob ); } Done: if ( status != NO_ERROR && pblob ) { HostentBlob_Free( pblob ); pblob = NULL; } *ppBlob = pblob; DNSDBG( HOSTENT, ( "Leave HostentBlob_CreateFromRecords() => status = %d\n", status )); return( status ); } // // Hostent Query // PHOSTENT_BLOB HostentBlob_Query( IN PWSTR pwsName, IN WORD wType, IN DWORD Flags, IN OUT PVOID * ppMsg, OPTIONAL IN INT AddrFamily OPTIONAL ) /*++ Routine Description: Query DNS to create hostent. Arguments: pwsName -- name to query wType -- query type Flags -- query flags ppMsg -- addr to recv ptr to message AddrType -- address type (family) to reserve space for if querying for PTR records Return Value: Ptr to blob if successful. NULL on error; GetLastError() has error. --*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrQuery = NULL; PHOSTENT_BLOB pblob = NULL; DNSDBG( HOSTENT, ( "HostentBlob_Query()\n" "\tname = %S\n" "\ttype = %d\n" "\tflags = %08x\n" "\tmsg out = %p\n", pwsName, wType, Flags, ppMsg )); // // query // - if fails, dump any message before return // status = DnsQuery_W( pwsName, wType, Flags, NULL, &prrQuery, ppMsg ); // if failed, dump any message if ( status != NO_ERROR ) { if ( ppMsg && *ppMsg ) { DnsApiFree( *ppMsg ); *ppMsg = NULL; } if ( status == RPC_S_SERVER_UNAVAILABLE ) { status = WSATRY_AGAIN; } goto Done; } if ( !prrQuery ) { ASSERT( FALSE ); status = DNS_ERROR_RCODE_NAME_ERROR; goto Done; } // // build hostent // status = HostentBlob_CreateFromRecords( & pblob, prrQuery, TRUE, // write name from first answer AddrFamily, wType ); if ( status != NO_ERROR ) { goto Done; } // // for address query must get answer // // DCR: DnsQuery() should convert to no-records on empty CNAME chain? // DCR: should we go ahead and build hostent? // if ( pblob->AddrCount == 0 && Hostent_IsSupportedAddrType(wType) ) { status = DNS_INFO_NO_RECORDS; } Done: if ( prrQuery ) { DnsRecordListFree( prrQuery, DnsFreeRecordListDeep ); } if ( status != NO_ERROR && pblob ) { HostentBlob_Free( pblob ); pblob = NULL; } DNSDBG( HOSTENT, ( "Leave HostentBlob_Query()\n" "\tpblob = %p\n" "\tstatus = %d\n", pblob, status )); SetLastError( status ); return( pblob ); } // // Special hostents // PHOSTENT_BLOB HostentBlob_Localhost( IN INT Family ) /*++ Routine Description: Create hostent from records Arguments: AddrFamily -- address family Return Value: Ptr to blob if successful. NULL on error; GetLastError() has error. --*/ { DNS_STATUS status = NO_ERROR; PDNS_RECORD prrFirstAddr = NULL; PDNS_RECORD prr; DWORD addrCount = 0; DWORD addrSize; CHAR addrBuf[ sizeof(IP6_ADDRESS ) ]; HOSTENT_INIT request; PHOSTENT_BLOB pblob = NULL; DNSDBG( HOSTENT, ( "HostentBlob_Localhost()\n" )); // // create hostent blob // RtlZeroMemory( &request, sizeof(request) ); request.AliasCount = 1; request.AddrCount = 1; request.AddrFamily = Family; request.fUnicode = TRUE; status = HostentBlob_Create( & pblob, & request ); if ( status != NO_ERROR ) { goto Done; } // // write in loopback address // if ( Family == AF_INET ) { * (PIP4_ADDRESS) addrBuf = DNS_NET_ORDER_LOOPBACK; addrSize = sizeof(IP4_ADDRESS); } else if ( Family == AF_INET6 ) { IP6_SET_ADDR_LOOPBACK( (PIP6_ADDRESS)addrBuf ); addrSize = sizeof(IN6_ADDR); } else { status = DNS_ERROR_INVALID_DATA; goto Done; } status = HostentBlob_WriteAddress( pblob, addrBuf, addrSize, Family ); if ( status != NO_ERROR ) { goto Done; } // // write localhost // status = HostentBlob_WriteNameOrAlias( pblob, (PSTR) L"localhost", FALSE, // name TRUE // unicode ); IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "HostentBlob after CreateFromRecords():", pblob ); } Done: if ( status != NO_ERROR && pblob ) { HostentBlob_Free( pblob ); pblob = NULL; } SetLastError( status ); DNSDBG( HOSTENT, ( "Leave Hostent_Localhost() => status = %d\n", status )); return( pblob ); } DNS_STATUS HostentBlob_CreateFromIpArray( IN OUT PHOSTENT_BLOB * ppBlob, IN INT AddrFamily, IN INT AddrSize, IN INT AddrCount, IN PCHAR pArray, IN PSTR pName, IN BOOL fUnicode ) /*++ Routine Description: Create hostent from records Arguments: ppBlob -- ptr with or to recv hostent blob AddrFamily -- addr family use if PTR records and no addr pArray -- array of addresses pName -- name for hostent fUnicode -- TRUE if name is and hostent will be in unicode FALSE for narrow name and hostent Return Value: Ptr to blob if successful. NULL on error; GetLastError() has error. --*/ { DNS_STATUS status = NO_ERROR; HOSTENT_INIT request; PHOSTENT_BLOB pblob = *ppBlob; DNSDBG( HOSTENT, ( "HostentBlob_CreateFromIpArray()\n" "\tppBlob = %p\n" "\tfamily = %d\n" "\tsize = %d\n" "\tcount = %d\n" "\tpArray = %p\n", ppBlob, AddrFamily, AddrSize, AddrCount, pArray )); // // create or reinit hostent blob // RtlZeroMemory( &request, sizeof(request) ); request.AliasCount = DNS_MAX_ALIAS_COUNT; request.AddrCount = AddrCount; request.AddrFamily = AddrFamily; request.fUnicode = fUnicode; request.pName = pName; status = HostentBlob_Create( & pblob, & request ); if ( status != NO_ERROR ) { goto Done; } // // write in array // if ( AddrCount ) { status = HostentBlob_WriteAddressArray( pblob, pArray, AddrCount, AddrSize, AddrFamily ); if ( status != NO_ERROR ) { goto Done; } } // // write name? // if ( pName ) { status = HostentBlob_WriteNameOrAlias( pblob, pName, FALSE, // name not alias fUnicode ); } IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "Leaving HostentBlob_CreateFromIpArray():", pblob ); } Done: if ( status != NO_ERROR && pblob ) { HostentBlob_Free( pblob ); pblob = NULL; } *ppBlob = pblob; DNSDBG( HOSTENT, ( "Leave HostentBlob_CreateFromIpArray() => status = %d\n", status )); return( status ); } DNS_STATUS HostentBlob_CreateLocal( IN OUT PHOSTENT_BLOB * ppBlob, IN INT AddrFamily, IN BOOL fLoopback, IN BOOL fZero, IN BOOL fHostnameOnly ) /*++ Routine Description: Create hostent from records Arguments: ppBlob -- ptr with or to recv hostent blob AddrFamily -- addr family use if PTR records and no addr Return Value: Ptr to blob if successful. NULL on error; GetLastError() has error. --*/ { DNS_STATUS status = NO_ERROR; PHOSTENT_BLOB pblob = NULL; WORD wtype; INT size; IP6_ADDRESS ip; DNSDBG( HOSTENT, ( "HostentBlob_CreateLocal()\n" "\tppBlob = %p\n" "\tfamily = %d\n" "\tfLoopback = %d\n" "\tfZero = %d\n" "\tfHostname = %d\n", ppBlob, AddrFamily, fLoopback, fZero, fHostnameOnly )); // // get family info // - start with override IP = 0 // - if loopback switch to appropriate loopback // RtlZeroMemory( &ip, sizeof(ip) ); if ( AddrFamily == AF_INET ) { wtype = DNS_TYPE_A; size = sizeof(IP4_ADDRESS); if ( fLoopback ) { * (PIP4_ADDRESS) &ip = DNS_NET_ORDER_LOOPBACK; } } else if ( AddrFamily == AF_INET6 ) { wtype = DNS_TYPE_AAAA; size = sizeof(IP6_ADDRESS); if ( fLoopback ) { IP6_SET_ADDR_LOOPBACK( &ip ); } } else { status = ERROR_INVALID_PARAMETER; goto Done; } // // query for local host info // pblob = HostentBlob_Query( NULL, // NULL name gets local host data wtype, 0, // standard query NULL, // no message AddrFamily ); if ( !pblob ) { DNS_ASSERT( FALSE ); status = GetLastError(); goto Done; } // // overwrite with specific address // if ( fLoopback || fZero ) { if ( ! Hostent_SetToSingleAddress( pblob->pHostent, (PCHAR) &ip, size ) ) { DNS_ASSERT( pblob->AddrCount == 0 ); pblob->AddrCount = 0; status = HostentBlob_WriteAddress( pblob, & ip, size, AddrFamily ); if ( status != NO_ERROR ) { DNS_ASSERT( status!=NO_ERROR ); goto Done; } } } // // for gethostname() // - chop name down to just hostname // - kill off aliases // if ( fHostnameOnly ) { PWSTR pname = (PWSTR) pblob->pHostent->h_name; PWSTR pdomain; DNS_ASSERT( pname ); if ( pname ) { pdomain = Dns_GetDomainName_W( pname ); if ( pdomain ) { DNS_ASSERT( pdomain > pname+1 ); DNS_ASSERT( *(pdomain-1) == L'.' ); *(pdomain-1) = 0; } } pblob->pHostent->h_aliases = NULL; } IF_DNSDBG( HOSTENT ) { DnsDbg_HostentBlob( "Leaving HostentBlob_CreateLocal():", pblob ); } Done: if ( status != NO_ERROR && pblob ) { HostentBlob_Free( pblob ); pblob = NULL; } *ppBlob = pblob; DNSDBG( HOSTENT, ( "Leave HostentBlob_CreateLocal() => status = %d\n", status )); return( status ); } // // End hostent.c //