reactos/sdk/lib/dnslib/sablob.c

640 lines
18 KiB
C

/*
* 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;
}