mirror of
https://github.com/reactos/reactos.git
synced 2024-12-30 19:14:31 +00:00
640 lines
18 KiB
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;
|
|
}
|
|
|