windows-nt/Source/XPSP1/NT/ds/dns/dnslib/rralloc.c

1178 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996-2001 Microsoft Corporation
Module Name:
rralloc.c
Abstract:
Domain Name System (DNS) Library
Resource record allocation \ creation routines.
Author:
Jim Gilroy (jamesg) January, 1997
Environment:
User Mode - Win32
Revision History:
--*/
#include "local.h"
#define SET_FLAGS(Flags, value) \
( *(PWORD)&(Flags) = value )
PDNS_RECORD
WINAPI
Dns_AllocateRecord(
IN WORD wBufferLength
)
/*++
Routine Description:
Allocate record structure.
Arguments:
wBufferLength - desired buffer length (beyond structure header)
Return Value:
Ptr to message buffer.
NULL on error.
--*/
{
PDNS_RECORD prr;
prr = ALLOCATE_HEAP( SIZEOF_DNS_RECORD_HEADER + wBufferLength );
if ( prr == NULL )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
RtlZeroMemory(
prr,
SIZEOF_DNS_RECORD_HEADER );
// as first cut, set datalength to buffer length
prr->wDataLength = wBufferLength;
return( prr );
}
VOID
WINAPI
Dns_RecordFree(
IN OUT PDNS_RECORD pRecord
)
/*++
Routine Description:
Free a record
Arguments:
pRecord -- record list to free
Return Value:
None.
--*/
{
DNSDBG( HEAP, ( "Dns_RecordFree( %p )\n", pRecord ));
// handle NULL for convenience
if ( !pRecord )
{
return;
}
// free owner name?
if ( FLAG_FreeOwner( pRecord ) )
{
FREE_HEAP( pRecord->pName );
}
//
// free data -- but only if flag set
//
// note: even if we fix copy functions to do atomic
// allocations, we'll still have to have free to
// handle RPC allocations
// (unless we very cleverly, treated RPC as flat blob, then
// did fix up (to offsets before and afterward)
//
if ( FLAG_FreeData( pRecord ) )
{
switch( pRecord->wType )
{
case DNS_TYPE_A:
break;
case DNS_TYPE_PTR:
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_MB:
case DNS_TYPE_MD:
case DNS_TYPE_MF:
case DNS_TYPE_MG:
case DNS_TYPE_MR:
if ( pRecord->Data.PTR.pNameHost )
{
FREE_HEAP( pRecord->Data.PTR.pNameHost );
}
break;
case DNS_TYPE_SOA:
if ( pRecord->Data.SOA.pNamePrimaryServer )
{
FREE_HEAP( pRecord->Data.SOA.pNamePrimaryServer );
}
if ( pRecord->Data.SOA.pNameAdministrator )
{
FREE_HEAP( pRecord->Data.SOA.pNameAdministrator );
}
break;
case DNS_TYPE_MINFO:
case DNS_TYPE_RP:
if ( pRecord->Data.MINFO.pNameMailbox )
{
FREE_HEAP( pRecord->Data.MINFO.pNameMailbox );
}
if ( pRecord->Data.MINFO.pNameErrorsMailbox )
{
FREE_HEAP( pRecord->Data.MINFO.pNameErrorsMailbox );
}
break;
case DNS_TYPE_MX:
case DNS_TYPE_AFSDB:
case DNS_TYPE_RT:
if ( pRecord->Data.MX.pNameExchange )
{
FREE_HEAP( pRecord->Data.MX.pNameExchange );
}
break;
case DNS_TYPE_HINFO:
case DNS_TYPE_ISDN:
case DNS_TYPE_TEXT:
case DNS_TYPE_X25:
{
DWORD iter;
DWORD count = pRecord->Data.TXT.dwStringCount;
for ( iter = 0; iter < count; iter++ )
{
if ( pRecord->Data.TXT.pStringArray[iter] )
{
FREE_HEAP( pRecord->Data.TXT.pStringArray[iter] );
}
}
break;
}
case DNS_TYPE_SRV:
if ( pRecord->Data.SRV.pNameTarget )
{
FREE_HEAP( pRecord->Data.SRV.pNameTarget );
}
break;
case DNS_TYPE_WINSR:
if ( pRecord->Data.WINSR.pNameResultDomain )
{
FREE_HEAP( pRecord->Data.WINSR.pNameResultDomain );
}
break;
default:
// other types -- A, AAAA, ATMA, WINS, NULL,
// have no internal pointers
break;
}
}
// for catching heap problems
pRecord->pNext = DNS_BAD_PTR;
pRecord->pName = DNS_BAD_PTR;
FREE_HEAP( pRecord );
}
VOID
WINAPI
Dns_RecordListFree(
IN OUT PDNS_RECORD pRecord
)
/*++
Routine Description:
Free list of records.
Arguments:
pRecord -- record list to free
Return Value:
None.
--*/
{
PDNS_RECORD pnext;
DNSDBG( TRACE, (
"Dns_RecordListFree( %p )\n",
pRecord ));
//
// loop through and free every RR in list
//
while ( pRecord )
{
pnext = pRecord->pNext;
Dns_RecordFree( pRecord );
pRecord = pnext;
}
}
VOID
WINAPI
Dns_RecordListFreeEx(
IN OUT PDNS_RECORD pRecord,
IN BOOL fFreeOwner
)
/*++
Routine Description:
Free list of records.
DCR: RecordListFreeEx (no free owner option) is probably useless
Note: owner name is freed ONLY when indicated by flag;
other ptrs are considered to be either
1) internal as when records read from wire or copied
2) external and to be freed by record creator
Arguments:
pRecord -- record list to free
fFreeOwner -- flag indicating owner name should be freed
Return Value:
None.
--*/
{
PDNS_RECORD pnext;
DNSDBG( TRACE, (
"Dns_RecordListFreeEx( %p, %d )\n",
pRecord,
fFreeOwner ));
//
// loop through and free every RR in list
//
while ( pRecord )
{
pnext = pRecord->pNext;
// free owner name?
// - if "FreeOwner" flag NOT set, then don't free
if ( !fFreeOwner )
{
FLAG_FreeOwner( pRecord ) = FALSE;
}
// free record
Dns_RecordFree( pRecord );
pRecord = pnext;
}
}
//
// Special record type creation routines
//
PDNS_RECORD
CreateRecordBasic(
IN PDNS_NAME pOwnerName,
IN BOOL fCopyName,
IN WORD wType,
IN WORD wDataLength,
IN DWORD AllocLength,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create record of arbitary type.
Helper function to wrap up
- record alloc
- name alloc
- basic setup
Arguments:
pOwnerName -- owner name
fCopyName -- TRUE - make copy of owner name
FALSE - use directly
wType -- type
AllocLength -- allocaction length, including any imbedded data
wDataLength -- data length to set
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PDNS_RECORD precord;
PDNS_RECORD prr;
PCHAR pname;
DWORD bufLength;
//
// alloc record
//
prr = Dns_AllocateRecord( (WORD)AllocLength );
if ( !prr )
{
return( NULL );
}
//
// copy owner name
//
if ( fCopyName && pOwnerName )
{
pname = Dns_NameCopyAllocate(
pOwnerName,
0, // length unknown
NameCharSet,
RecordCharSet );
if ( !pname )
{
FREE_HEAP( prr );
return( NULL );
}
}
else
{
pname = pOwnerName;
}
//
// set fields
// - name, type and charset
// - TTL, section left zero
// - FreeData is specifically off
//
prr->pName = pname;
prr->wType = wType;
prr->wDataLength = wDataLength;
SET_FREE_OWNER(prr);
prr->Flags.S.CharSet = RecordCharSet;
prr->dwTtl = Ttl;
return( prr );
}
PDNS_RECORD
Dns_CreateFlatRecord(
IN PDNS_NAME pOwnerName,
IN WORD wType,
IN PCHAR pData,
IN DWORD DataLength,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create flat record.
Arguments:
pOwnerName -- owner name
wType -- record type
pData -- ptr to data for record
DataLength -- length (in bytes) of data
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PDNS_RECORD prr;
//
// determine record size
// - record buffer will include hostname
//
prr = CreateRecordBasic(
pOwnerName,
TRUE, // copy name
wType,
(WORD) DataLength, // datalength
DataLength, // alloc datalength
Ttl,
NameCharSet,
RecordCharSet );
if ( !prr )
{
return( NULL );
}
//
// copy in data
//
RtlCopyMemory(
(PBYTE) &prr->Data,
pData,
DataLength );
return( prr );
}
PDNS_RECORD
Dns_CreatePtrTypeRecord(
IN PDNS_NAME pOwnerName,
IN BOOL fCopyName,
IN PDNS_NAME pTargetName,
IN WORD wType,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create PTR type (single-indirection) record.
This can be used to create any "PTR-type" record:
PTR, CNAME, NS, etc.
Arguments:
pOwnerName -- owner name
fCopyName -- TRUE - make copy of owner name
FALSE - use directly
pTargetName -- target name
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PDNS_RECORD precord;
PDNS_RECORD prr;
PCHAR pname;
DWORD bufLength;
//
// determine record size
// - record buffer will include hostname
//
bufLength = Dns_GetBufferLengthForNameCopy(
pTargetName,
0, // length unknown
NameCharSet,
RecordCharSet );
if ( !bufLength )
{
return( NULL );
}
//
// create record
//
prr = CreateRecordBasic(
pOwnerName,
fCopyName,
wType,
sizeof(DNS_PTR_DATA), // data length
(sizeof(DNS_PTR_DATA) + bufLength), // alloc length
Ttl,
NameCharSet,
RecordCharSet );
if ( !prr )
{
return( NULL );
}
//
// write target name into buffer, immediately following PTR data struct
//
prr->Data.PTR.pNameHost = (PCHAR)&prr->Data + sizeof(DNS_PTR_DATA);
Dns_NameCopy(
prr->Data.PTR.pNameHost,
NULL,
pTargetName,
0,
NameCharSet,
RecordCharSet
);
return( prr );
}
PDNS_RECORD
Dns_CreatePtrRecordEx(
IN PIP_UNION pIp,
IN PDNS_NAME pszHostName,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create PTR record from IP address and hostname.
Arguments:
pIp -- IP union (IP4 or IP6)
pszHostName -- host name, FULL FQDN
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PCHAR pname;
//
// create reverse lookup name
// - note this is external allocation
//
if ( IPUNION_IS_IP4( pIp ) )
{
IP4_ADDRESS ip = IPUNION_GET_IP4(pIp);
if ( RecordCharSet == DnsCharSetUnicode )
{
pname = (PCHAR) Dns_Ip4AddressToReverseNameAlloc_W( ip );
}
else
{
pname = Dns_Ip4AddressToReverseNameAlloc_A( ip );
}
}
else
{
IP6_ADDRESS ip = IPUNION_GET_IP6(pIp);
if ( RecordCharSet == DnsCharSetUnicode )
{
pname = (PCHAR) Dns_Ip6AddressToReverseNameAlloc_W( ip );
}
else
{
pname = Dns_Ip6AddressToReverseNameAlloc_A( ip );
}
}
if ( !pname )
{
return( NULL );
}
//
// build record
//
return Dns_CreatePtrTypeRecord(
pname,
FALSE, // don't copy IP
pszHostName, // target name
DNS_TYPE_PTR,
Ttl,
NameCharSet,
RecordCharSet );
}
PDNS_RECORD
Dns_CreatePtrRecordExEx(
IN PIP_UNION pIp,
IN PSTR pszHostName,
IN PSTR pszDomainName,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create PTR record from hostname and domain name.
Helper function for DHCP registrations when hostname
and domain name are separate and both required.
Arguments:
pIp -- IP union (IP4 or IP6)
pszHostName -- host name (single label)
pszDomainName -- domain name
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
WCHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
DNSDBG( TRACE, (
"Dns_CreatePtrRecordExEx()\n" ));
//
// build appended name
//
// DCR: could require just host name and check that
// either domain exists or hostname is full
//
if ( !pszHostName || !pszDomainName )
{
return NULL;
}
if ( NameCharSet != DnsCharSetUnicode )
{
if ( ! Dns_NameAppend_A(
(PCHAR) nameBuffer,
DNS_MAX_NAME_BUFFER_LENGTH,
pszHostName,
pszDomainName ) )
{
DNS_ASSERT( FALSE );
return NULL;
}
}
else
{
if ( ! Dns_NameAppend_W(
(PWCHAR) nameBuffer,
DNS_MAX_NAME_BUFFER_LENGTH,
(PWSTR) pszHostName,
(PWSTR) pszDomainName ) )
{
DNS_ASSERT( FALSE );
return NULL;
}
}
//
// build record
//
return Dns_CreatePtrRecordEx(
pIp,
(PCHAR) nameBuffer,
Ttl,
NameCharSet,
RecordCharSet
);
}
PDNS_RECORD
Dns_CreateARecord(
IN PDNS_NAME pOwnerName,
IN IP_ADDRESS Ip4Addr,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create A record.
Arguments:
pOwnerName -- owner name
Ip4Addr -- IP address
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PDNS_RECORD prr;
//
// determine record size
// - record buffer will include hostname
//
prr = CreateRecordBasic(
pOwnerName,
TRUE, // copy name
DNS_TYPE_A,
sizeof(DNS_A_DATA),
sizeof(DNS_A_DATA),
Ttl,
NameCharSet,
RecordCharSet );
if ( !prr )
{
return( NULL );
}
//
// set IP
//
prr->Data.A.IpAddress = Ip4Addr;
return( prr );
}
PDNS_RECORD
Dns_CreateAAAARecord(
IN PDNS_NAME pOwnerName,
IN IP6_ADDRESS Ip6Addr,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create A record.
Arguments:
pOwnerName -- owner name
Ip6Addr -- IP6 address
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PDNS_RECORD prr;
//
// determine record size
// - record buffer will include hostname
//
prr = CreateRecordBasic(
pOwnerName,
TRUE, // copy name
DNS_TYPE_AAAA,
sizeof(DNS_AAAA_DATA),
sizeof(DNS_AAAA_DATA),
Ttl,
NameCharSet,
RecordCharSet );
if ( !prr )
{
return( NULL );
}
//
// set IP
//
prr->Data.AAAA.Ip6Address = Ip6Addr;
return( prr );
}
PDNS_RECORD
Dns_CreateForwardRecord(
IN PDNS_NAME pOwnerName,
IN PIP_UNION pIp,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create forward lookup record.
This is just a shim to avoid duplicating selection logic.
Arguments:
pOwnerName -- owner name
pIp -- IP address union
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
//
// build desired type
//
// DCR: must add\choose A6
//
if ( IPUNION_IS_IP4( pIp ) )
{
return Dns_CreateARecord(
pOwnerName,
IPUNION_GET_IP4(pIp),
Ttl,
NameCharSet,
RecordCharSet );
}
else
{
return Dns_CreateAAAARecord(
pOwnerName,
IPUNION_GET_IP6(pIp),
Ttl,
NameCharSet,
RecordCharSet );
}
}
PDNS_RECORD
Dns_CreateForwardRecordForSockaddr(
IN PDNS_NAME pOwnerName,
IN PSOCKADDR pSockaddr,
IN DWORD Ttl,
IN DNS_CHARSET NameCharSet,
IN DNS_CHARSET RecordCharSet
)
/*++
Routine Description:
Create forward lookup record.
This is just a shim to avoid duplicating selection logic.
Arguments:
pOwnerName -- owner name
pSockaddr -- ptr to sockaddr
Ttl -- TTL
NameCharSet -- character set of name
RecordCharSet -- character set for resulting record
Return Value:
Ptr to PTR record.
NULL on error.
--*/
{
PFAMILY_INFO pinfo;
DNSDBG( TRACE, (
"Dns_CreateForwardRecordForSockaddr()\n" ));
pinfo = FamilyInfo_GetForSockaddr( pSockaddr );
if ( !pinfo )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
//
// build flat record of desired type
//
return Dns_CreateFlatRecord(
pOwnerName,
pinfo->DnsType,
(PBYTE)pSockaddr + pinfo->OffsetToAddrInSockaddr,
pinfo->LengthAddr,
Ttl,
NameCharSet,
RecordCharSet );
}
PDNS_RECORD
Dns_CreateRecordForIpString_W(
IN PCWSTR pwsName,
IN WORD wType,
IN DWORD Ttl
)
/*++
Routine Description:
Create record for IP string query.
Arguments:
pwsName -- name that may be IP string query
wType -- type of query
Return Value:
Ptr to record for query, if query name\type is IP.
NULL if query not for IP.
--*/
{
IP_UNION ipUnion;
PDNS_RECORD prr;
DNSDBG( TRACE, (
"\nDns_CreateRecordForIpString( %S, wType = %d )\n",
pwsName,
wType ));
if ( !pwsName )
{
return NULL;
}
//
// support A or AAAA queries for IP strings
// - IP4 strings must be in w.x.y.z form otherwise
// we convert the all numeric names also
//
// DCR: need A6 support for direct query
//
if ( wType == DNS_TYPE_A )
{
IP4_ADDRESS ip4;
PCWSTR pdot;
DWORD count;
if ( ! Dns_Ip4StringToAddress_W(
& ip4,
(PWSTR) pwsName ) )
{
return NULL;
}
// verify three dot form w.x.y.z
pdot = pwsName;
count = 3;
while ( count-- )
{
pdot = wcschr( pdot, L'.' );
if ( !pdot || !*++pdot )
{
return( NULL );
}
}
IPUNION_SET_IP4( &ipUnion, ip4 );
}
else if ( wType == DNS_TYPE_AAAA )
{
IP6_ADDRESS ip6;
if ( ! Dns_Ip6StringToAddress_W(
& ip6,
(PWSTR) pwsName ) )
{
return NULL;
}
IPUNION_SET_IP6( &ipUnion, ip6 );
}
else
{
return NULL;
}
//
// name is IP string -- build record
//
prr = Dns_CreateForwardRecord(
(PDNS_NAME) pwsName,
& ipUnion,
Ttl,
DnsCharSetUnicode,
DnsCharSetUnicode );
DNSDBG( TRACE, (
"Create record %p for IP string %S type %d.\n",
prr,
pwsName,
wType ));
return prr;
}
//
// End rralloc.c
//