+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS DNS Shared Library
+ * FILE: lib/dnslib/sablob.c
+ * PURPOSE: Functions for the Saved Answer Blob Implementation
+ */
+
+/* INCLUDES ******************************************************************/
+#include "precomp.h"
+
+/* DATA **********************************************************************/
+
+/* FUNCTIONS *****************************************************************/
+
+PVOID
+WINAPI
+FlatBuf_Arg_ReserveAlignPointer(IN PVOID Position,
+ IN PSIZE_T FreeSize,
+ IN SIZE_T Size)
+{
+ /* Just a little helper that we use */
+ return FlatBuf_Arg_Reserve(Position, FreeSize, Size, sizeof(PVOID));
+}
+
+PDNS_BLOB
+WINAPI
+SaBlob_Create(IN ULONG Count)
+{
+ PDNS_BLOB Blob;
+ PDNS_ARRAY DnsAddrArray;
+
+ /* Allocate the blob */
+ Blob = Dns_AllocZero(sizeof(DNS_BLOB));
+ if (Blob)
+ {
+ /* Check if it'll hold any addresses */
+ if (Count)
+ {
+ /* Create the DNS Address Array */
+ DnsAddrArray = DnsAddrArray_Create(Count);
+ if (!DnsAddrArray)
+ {
+ /* Failure, free the blob */
+ SaBlob_Free(Blob);
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ else
+ {
+ /* Link it with the blob */
+ Blob->DnsAddrArray = DnsAddrArray;
+ }
+ }
+ }
+
+ /* Return the blob */
+ return Blob;
+}
+
+PDNS_BLOB
+WINAPI
+SaBlob_CreateFromIp4(IN LPWSTR Name,
+ IN ULONG Count,
+ IN PIN_ADDR AddressArray)
+{
+ PDNS_BLOB Blob;
+ LPWSTR NameCopy;
+ ULONG i;
+
+ /* Create the blob */
+ Blob = SaBlob_Create(Count);
+ if (!Blob) goto Quickie;
+
+ /* If we have a name */
+ if (Name)
+ {
+ /* Create a copy of it */
+ NameCopy = Dns_CreateStringCopy_W(Name);
+ if (!NameCopy) goto Quickie;
+
+ /* Save the pointer to the name */
+ Blob->Name = NameCopy;
+ }
+
+ /* Loop all the addresses */
+ for (i = 0; i < Count; i++)
+ {
+ /* Add an entry for this address */
+ DnsAddrArray_AddIp4(Blob->DnsAddrArray, AddressArray[i], IpV4Address);
+ }
+
+ /* Return the blob */
+ return Blob;
+
+Quickie:
+ /* Free the blob, set error and fail */
+ SaBlob_Free(Blob);
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+}
+
+VOID
+WINAPI
+SaBlob_Free(IN PDNS_BLOB Blob)
+{
+ /* Make sure we got a blob */
+ if (Blob)
+ {
+ /* Free the name */
+ Dns_Free(Blob->Name);
+
+ /* Loop the aliases */
+ while (Blob->AliasCount)
+ {
+ /* Free the alias */
+ Dns_Free(Blob->Aliases[Blob->AliasCount]);
+
+ /* Decrease number of aliases */
+ Blob->AliasCount--;
+ }
+
+ /* Free the DNS Address Array */
+ DnsAddrArray_Free(Blob->DnsAddrArray);
+
+ /* Free the blob itself */
+ Dns_Free(Blob);
+ }
+}
+
+PHOSTENT
+WINAPI
+SaBlob_CreateHostent(IN OUT PULONG_PTR BufferPosition,
+ IN OUT PSIZE_T FreeBufferSpace,
+ IN OUT PSIZE_T HostEntrySize,
+ IN PDNS_BLOB Blob,
+ IN DWORD StringType,
+ IN BOOLEAN Relative,
+ IN BOOLEAN BufferAllocated)
+{
+ PDNS_ARRAY DnsAddrArray = Blob->DnsAddrArray;
+ ULONG AliasCount = Blob->AliasCount;
+ WORD AddressFamily = AF_UNSPEC;
+ ULONG AddressCount = 0, AddressSize = 0, TotalSize, NamePointerSize;
+ ULONG AliasPointerSize;
+ PDNS_FAMILY_INFO FamilyInfo = NULL;
+ ULONG StringLength = 0;
+ ULONG i;
+ ULONG HostentSize = 0;
+ PHOSTENT Hostent = NULL;
+ ULONG_PTR HostentPtr;
+ PVOID CurrentAddress;
+
+ /* Check if we actually have any addresses */
+ if (DnsAddrArray)
+ {
+ /* Get the address family */
+ AddressFamily = DnsAddrArray->Addresses[0].AddressFamily;
+
+ /* Get family information */
+ FamilyInfo = FamilyInfo_GetForFamily(AddressFamily);
+
+ /* Save the current address count and their size */
+ AddressCount = DnsAddrArray->UsedAddresses;
+ AddressSize = FamilyInfo->AddressSize;
+ }
+
+ /* Calculate total size for all the addresses, and their pointers */
+ TotalSize = AddressSize * AddressCount;
+ NamePointerSize = AddressCount * sizeof(PVOID) + sizeof(PVOID);
+
+ /* Check if we have a name */
+ if (Blob->Name)
+ {
+ /* Find out the size we'll need for a copy */
+ StringLength = (Dns_GetBufferLengthForStringCopy(Blob->Name,
+ 0,
+ UnicodeString,
+ StringType) + 1) & ~1;
+ }
+
+ /* Now do the same for the aliases */
+ for (i = AliasCount; i; i--)
+ {
+ /* Find out the size we'll need for a copy */
+ HostentSize += (Dns_GetBufferLengthForStringCopy(Blob->Aliases[i],
+ 0,
+ UnicodeString,
+ StringType) + 1) & ~1;
+ }
+
+ /* Find out how much the pointers will take */
+ AliasPointerSize = AliasCount * sizeof(PVOID) + sizeof(PVOID);
+
+ /* Calculate Hostent Size */
+ HostentSize += TotalSize +
+ NamePointerSize +
+ AliasPointerSize +
+ StringLength +
+ sizeof(HOSTENT);
+
+ /* Check if we already have a buffer */
+ if (!BufferAllocated)
+ {
+ /* We don't, allocate space ourselves */
+ HostentPtr = (ULONG_PTR)Dns_AllocZero(HostentSize);
+ }
+ else
+ {
+ /* We do, so allocate space in the buffer */
+ HostentPtr = (ULONG_PTR)FlatBuf_Arg_ReserveAlignPointer(BufferPosition,
+ FreeBufferSpace,
+ HostentSize);
+ }
+
+ /* Make sure we got space */
+ if (HostentPtr)
+ {
+ /* Initialize it */
+ Hostent = Hostent_Init((PVOID)&HostentPtr,
+ AddressFamily,
+ AddressSize,
+ AddressCount,
+ AliasCount);
+ }
+
+ /* Loop the addresses */
+ for (i = 0; i < AddressCount; i++)
+ {
+ /* Get the pointer of the current address */
+ CurrentAddress = (PVOID)((ULONG_PTR)&DnsAddrArray->Addresses[i] +
+ FamilyInfo->AddressOffset);
+
+ /* Write the pointer */
+ Hostent->h_addr_list[i] = (PCHAR)HostentPtr;
+
+ /* Copy the address */
+ RtlCopyMemory((PVOID)HostentPtr, CurrentAddress, AddressSize);
+
+ /* Advance the buffer */
+ HostentPtr += AddressSize;
+ }
+
+ /* Check if we have a name */
+ if (Blob->Name)
+ {
+ /* Align our current position */
+ HostentPtr += 1 & ~1;
+
+ /* Save our name here */
+ Hostent->h_name = (LPSTR)HostentPtr;
+
+ /* Now copy it in the blob */
+ HostentPtr += Dns_StringCopy((PVOID)HostentPtr,
+ NULL,
+ Blob->Name,
+ 0,
+ UnicodeString,
+ StringType);
+ }
+
+ /* Loop the Aliases */
+ for (i = AliasCount; i; i--)
+ {
+ /* Align our current position */
+ HostentPtr += 1 & ~1;
+
+ /* Save our alias here */
+ Hostent->h_aliases[i] = (LPSTR)HostentPtr;
+
+ /* Now copy it in the blob */
+ HostentPtr += Dns_StringCopy((PVOID)HostentPtr,
+ NULL,
+ Blob->Aliases[i],
+ 0,
+ UnicodeString,
+ StringType);
+ }
+
+ /* Check if the caller didn't have a buffer */
+ if (!BufferAllocated)
+ {
+ /* Return the size; not needed if we had a blob, since it's internal */
+ *HostEntrySize = *BufferPosition - (ULONG_PTR)HostentPtr;
+ }
+
+ /* Convert to Offsets if requested */
+ if(Relative) Hostent_ConvertToOffsets(Hostent);
+
+ /* Return the full, complete, hostent */
+ return Hostent;
+}
+
+INT
+WINAPI
+SaBlob_WriteNameOrAlias(IN PDNS_BLOB Blob,
+ IN LPWSTR String,
+ IN BOOLEAN IsAlias)
+{
+ /* Check if this is an alias */
+ if (!IsAlias)
+ {
+ /* It's not. Simply create a copy of the string */
+ Blob->Name = Dns_CreateStringCopy_W(String);
+ if (!Blob->Name) return GetLastError();
+ }
+ else
+ {
+ /* Does it have a name, and less then 8 aliases? */
+ if ((Blob->Name) && (Blob->AliasCount <= 8))
+ {
+ /* Yup, create a copy of the string and increase the alias count */
+ Blob->Aliases[Blob->AliasCount] = Dns_CreateStringCopy_W(String);
+ Blob->AliasCount++;
+ }
+ else
+ {
+ /* Invalid request! */
+ return ERROR_MORE_DATA;
+ }
+ }
+
+ /* Return Success */
+ return ERROR_SUCCESS;
+}
+
+INT
+WINAPI
+SaBlob_WriteAddress(IN PDNS_BLOB Blob,
+ OUT PDNS_ADDRESS DnsAddr)
+{
+ /* Check if we have an array yet */
+ if (!Blob->DnsAddrArray)
+ {
+ /* Allocate one! */
+ Blob->DnsAddrArray = DnsAddrArray_Create(1);
+ if (!Blob->DnsAddrArray) return ERROR_OUTOFMEMORY;
+ }
+
+ /* Add this address */
+ return DnsAddrArray_AddAddr(Blob->DnsAddrArray, DnsAddr, AF_UNSPEC, 0) ?
+ ERROR_SUCCESS:
+ ERROR_MORE_DATA;
+}
+
+BOOLEAN
+WINAPI
+SaBlob_IsSupportedAddrType(WORD DnsType)
+{
+ /* Check for valid Types that we support */
+ return (DnsType == DNS_TYPE_A ||
+ DnsType == DNS_TYPE_ATMA ||
+ DnsType == DNS_TYPE_AAAA);
+}
+
+INT
+WINAPI
+SaBlob_WriteRecords(OUT PDNS_BLOB Blob,
+ IN PDNS_RECORD DnsRecord,
+ IN BOOLEAN DoAlias)
+{
+ DNS_ADDRESS DnsAddress;
+ INT ErrorCode = STATUS_INVALID_PARAMETER;
+ BOOLEAN WroteOnce = FALSE;
+
+ /* Zero out the Address */
+ RtlZeroMemory(&DnsAddress, sizeof(DnsAddress));
+
+ /* Loop through all the Records */
+ while (DnsRecord)
+ {
+ /* Is this not an answer? */
+ if (DnsRecord->Flags.S.Section != DNSREC_ANSWER)
+ {
+ /* Then simply move on to the next DNS Record */
+ DnsRecord = DnsRecord->pNext;
+ continue;
+ }
+
+ /* Check the type of thsi record */
+ switch(DnsRecord->wType)
+ {
+ /* Regular IPv4, v6 or ATM Record */
+ case DNS_TYPE_A:
+ case DNS_TYPE_AAAA:
+ case DNS_TYPE_ATMA:
+
+ /* Create a DNS Address from the record */
+ DnsAddr_BuildFromDnsRecord(DnsRecord, &DnsAddress);
+
+ /* Add it to the DNS Blob */
+ ErrorCode = SaBlob_WriteAddress(Blob, &DnsAddress);
+
+ /* Add the name, if needed */
+ if ((DoAlias) &&
+ (!WroteOnce) &&
+ (!Blob->Name) &&
+ (DnsRecord->pName))
+ {
+ /* Write the name from the DNS Record */
+ ErrorCode = SaBlob_WriteNameOrAlias(Blob,
+ DnsRecord->pName,
+ FALSE);
+ WroteOnce = TRUE;
+ }
+ break;
+
+ case DNS_TYPE_CNAME:
+
+ /* Just write the alias name */
+ ErrorCode = SaBlob_WriteNameOrAlias(Blob,
+ DnsRecord->pName,
+ TRUE);
+ break;
+
+ case DNS_TYPE_PTR:
+
+ /* Check if we already have a name */
+ if (Blob->Name)
+ {
+ /* We don't, so add this as a name */
+ ErrorCode = SaBlob_WriteNameOrAlias(Blob,
+ DnsRecord->pName,
+ FALSE);
+ }
+ else
+ {
+ /* We do, so add it as an alias */
+ ErrorCode = SaBlob_WriteNameOrAlias(Blob,
+ DnsRecord->pName,
+ TRUE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Next record */
+ DnsRecord = DnsRecord->pNext;
+ }
+
+ /* Return error code */
+ return ErrorCode;
+}
+
+PDNS_BLOB
+WINAPI
+SaBlob_CreateFromRecords(IN PDNS_RECORD DnsRecord,
+ IN BOOLEAN DoAliases,
+ IN DWORD DnsType)
+{
+ PDNS_RECORD LocalDnsRecord;
+ ULONG ProcessedCount = 0;
+ PDNS_BLOB DnsBlob;
+ INT ErrorCode;
+ DNS_ADDRESS DnsAddress;
+
+ /* Find out how many DNS Addresses to allocate */
+ LocalDnsRecord = DnsRecord;
+ while (LocalDnsRecord)
+ {
+ /* Make sure this record is an answer */
+ if ((LocalDnsRecord->Flags.S.Section == DNSREC_ANSWER) &&
+ (SaBlob_IsSupportedAddrType(LocalDnsRecord->wType)))
+ {
+ /* Increase number of records to process */
+ ProcessedCount++;
+ }
+
+ /* Move to the next record */
+ LocalDnsRecord = LocalDnsRecord->pNext;
+ }
+
+ /* Create the DNS Blob */
+ DnsBlob = SaBlob_Create(ProcessedCount);
+ if (!DnsBlob)
+ {
+ /* Fail */
+ ErrorCode = GetLastError();
+ goto Quickie;
+ }
+
+ /* Write the record to the DNS Blob */
+ ErrorCode = SaBlob_WriteRecords(DnsBlob, DnsRecord, TRUE);
+ if (ErrorCode != NO_ERROR)
+ {
+ /* We failed... but do we still have valid data? */
+ if ((DnsBlob->Name) || (DnsBlob->AliasCount))
+ {
+ /* We'll just assume success then */
+ ErrorCode = NO_ERROR;
+ }
+ else
+ {
+ /* Ok, last chance..do you have a DNS Address Array? */
+ if ((DnsBlob->DnsAddrArray) &&
+ (DnsBlob->DnsAddrArray->UsedAddresses))
+ {
+ /* Boy are you lucky! */
+ ErrorCode = NO_ERROR;
+ }
+ }
+
+ /* Buh-bye! */
+ goto Quickie;
+ }
+
+ /* Check if this is a PTR record */
+ if ((DnsRecord->wType == DNS_TYPE_PTR) ||
+ ((DnsType == DNS_TYPE_PTR) &&
+ (DnsRecord->wType == DNS_TYPE_CNAME) &&
+ (DnsRecord->Flags.S.Section == DNSREC_ANSWER)))
+ {
+ /* Get a DNS Address Structure */
+ if (Dns_ReverseNameToDnsAddr_W(&DnsAddress, DnsRecord->pName))
+ {
+ /* Add it to the Blob */
+ if (SaBlob_WriteAddress(DnsBlob, &DnsAddress)) ErrorCode = NO_ERROR;
+ }
+ }
+
+ /* Ok...do we still not have a name? */
+ if (!(DnsBlob->Name) && (DoAliases) && (LocalDnsRecord))
+ {
+ /* We have an local DNS Record, so just use it to write the name */
+ ErrorCode = SaBlob_WriteNameOrAlias(DnsBlob,
+ LocalDnsRecord->pName,
+ FALSE);
+ }
+
+Quickie:
+ /* Check error code */
+ if (ErrorCode != NO_ERROR)
+ {
+ /* Free the blob and set the error */
+ SaBlob_Free(DnsBlob);
+ DnsBlob = NULL;
+ SetLastError(ErrorCode);
+ }
+
+ /* Return */
+ return DnsBlob;
+}
+
+PDNS_BLOB
+WINAPI
+SaBlob_Query(IN LPWSTR Name,
+ IN WORD DnsType,
+ IN ULONG Flags,
+ IN PVOID *Reserved,
+ IN DWORD AddressFamily)
+{
+ PDNS_RECORD DnsRecord = NULL;
+ INT ErrorCode;
+ PDNS_BLOB DnsBlob = NULL;
+ LPWSTR LocalName, LocalNameCopy;
+
+ /* If they want reserved data back, clear it out in case we fail */
+ if (Reserved) *Reserved = NULL;
+
+ /* Query DNS */
+ ErrorCode = DnsQuery_W(Name,
+ DnsType,
+ Flags,
+ NULL,
+ &DnsRecord,
+ Reserved);
+ if (ErrorCode != ERROR_SUCCESS)
+ {
+ /* We failed... did the caller use reserved data? */
+ if (Reserved && *Reserved)
+ {
+ /* He did, and it was valid. Free it */
+ DnsApiFree(*Reserved);
+ *Reserved = NULL;
+ }
+
+ /* Normalize error code */
+ if (ErrorCode == RPC_S_SERVER_UNAVAILABLE) ErrorCode = WSATRY_AGAIN;
+ goto Quickie;
+ }
+
+ /* Now create the Blob from the DNS Records */
+ DnsBlob = SaBlob_CreateFromRecords(DnsRecord, TRUE, DnsType);
+ if (!DnsBlob)
+ {
+ /* Failed, get error code */
+ ErrorCode = GetLastError();
+ goto Quickie;
+ }
+
+ /* Make sure it has a name */
+ if (!DnsBlob->Name)
+ {
+ /* It doesn't, fail */
+ ErrorCode = DNS_INFO_NO_RECORDS;
+ goto Quickie;
+ }
+
+ /* Check if the name is local or loopback */
+ if (!(DnsNameCompare_W(DnsBlob->Name, L"localhost")) &&
+ !(DnsNameCompare_W(DnsBlob->Name, L"loopback")))
+ {
+ /* Nothing left to do, exit! */
+ goto Quickie;
+ }
+
+ /* This is a local name...query it */
+ LocalName = DnsQueryConfigAllocEx(DnsConfigFullHostName_W, NULL, NULL);
+ if (LocalName)
+ {
+ /* Create a copy for the caller */
+ LocalNameCopy = Dns_CreateStringCopy_W(LocalName);
+ if (LocalNameCopy)
+ {
+ /* Overwrite the one in the blob */
+ DnsBlob->Name = LocalNameCopy;
+ }
+ else
+ {
+ /* We failed to make a copy, free memory */
+ DnsApiFree(LocalName);
+ }
+ }
+
+Quickie:
+ /* Free the DNS Record if we have one */
+ if (DnsRecord) DnsRecordListFree(DnsRecord, DnsFreeRecordList);
+
+ /* Check if this is a failure path with an active blob */
+ if ((ErrorCode != ERROR_SUCCESS) && (DnsBlob))
+ {
+ /* Free the blob */
+ SaBlob_Free(DnsBlob);
+ DnsBlob = NULL;
+ }
+
+ /* Set the last error and return */
+ SetLastError(ErrorCode);
+ return DnsBlob;
+}
+