Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2637 lines
50 KiB

/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
rrbuild.c
Abstract:
Domain Name System (DNS) Library
Build resource record routines.
Author:
Jim Gilroy (jamesg) January, 1997
Revision History:
Jing Chen (t-jingc) June, 1998
--*/
#include "local.h"
//
// Type specific record build routines
//
PDNS_RECORD
A_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build A record from string data.
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_A_DATA) );
if ( !precord )
{
return( NULL );
}
if ( ! Dns_Ip4StringToAddress_A(
&precord->Data.A.IpAddress,
Argv[0] ) )
{
Dns_RecordFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
return( precord );
}
PDNS_RECORD
A_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build A record from string data.
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_A_DATA) );
if ( !precord )
{
return( NULL );
}
if ( ! Dns_Ip4StringToAddress_W(
&precord->Data.A.IpAddress,
Argv[0] ) )
{
Dns_RecordFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
return( precord );
}
PDNS_RECORD
Ptr_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build PTR compatible record from string data.
Includes: NS, PTR, CNAME, MB, MR, MG, MD, MF
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_PTR_DATA) );
if ( !precord )
{
return( NULL );
}
precord->Data.PTR.pNameHost = Argv[0];
return( precord );
}
PDNS_RECORD
Ptr_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build PTR compatible record from string data.
Includes: NS, PTR, CNAME, MB, MR, MG, MD, MF
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_PTR_DATA) );
if ( !precord )
{
return( NULL );
}
precord->Data.PTR.pNameHost = (PDNS_NAME) Argv[0];
return( precord );
}
PDNS_RECORD
Mx_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build MX compatible record from string data.
Includes: MX, RT, AFSDB
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD temp;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_MX_DATA) );
if ( !precord )
{
return( NULL );
}
//
// MX preference value
// RT preference
// AFSDB subtype
//
temp = strtoul( Argv[0], NULL, 10 );
if ( temp > MAXWORD )
{
temp = MAXWORD;
}
precord->Data.MX.wPreference = (USHORT) temp;
//
// MX exchange
// RT exchange
// AFSDB hostname
//
precord->Data.MX.pNameExchange = Argv[1];
return( precord );
}
PDNS_RECORD
Mx_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build MX compatible record from string data.
Includes: MX, RT, AFSDB
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD temp;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_MX_DATA) );
if ( !precord )
{
return( NULL );
}
//
// MX preference value
// RT preference
// AFSDB subtype
//
temp = wcstoul( Argv[0], NULL, 10 );
if ( temp > MAXWORD )
{
temp = MAXWORD;
}
precord->Data.MX.wPreference = (USHORT) temp;
//
// MX exchange
// RT exchange
// AFSDB hostname
//
precord->Data.MX.pNameExchange = (PDNS_NAME) Argv[1];
return( precord );
}
PDNS_RECORD
Soa_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build SOA record from string data.
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PDWORD pdword;
if ( Argc != 7 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_SOA_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read primary server and responsible party
//
precord->Data.SOA.pNamePrimaryServer = Argv[0];
Argc--;
Argv++;
precord->Data.SOA.pNameAdministrator = Argv[0];
Argc--;
Argv++;
//
// read integer data
//
pdword = &precord->Data.SOA.dwSerialNo;
while( Argc-- )
{
*pdword = strtoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Soa_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build SOA record from string data.
Arguments:
Argc -- count of data arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PDWORD pdword;
if ( Argc != 7 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_SOA_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read primary server and responsible party
//
precord->Data.SOA.pNamePrimaryServer = (PDNS_NAME) Argv[0];
Argc--;
Argv++;
precord->Data.SOA.pNameAdministrator = (PDNS_NAME) Argv[0];
Argc--;
Argv++;
//
// read integer data
//
pdword = &precord->Data.SOA.dwSerialNo;
while( Argc-- )
{
*pdword = wcstoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Minfo_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build MINFO and RP records from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_MINFO_DATA) );
if ( !precord )
{
return( NULL );
}
//
// MINFO responsible mailbox
// RP responsible person mailbox
precord->Data.MINFO.pNameMailbox = Argv[0];
Argc--;
Argv++;
//
// MINFO errors to mailbox
// RP text RR location
precord->Data.MINFO.pNameErrorsMailbox = Argv[0];
Argc--;
Argv++;
return( precord );
}
PDNS_RECORD
Minfo_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build MINFO and RP records from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_MINFO_DATA) );
if ( !precord )
{
return( NULL );
}
//
// MINFO responsible mailbox
// RP responsible person mailbox
precord->Data.MINFO.pNameMailbox = (PDNS_NAME) Argv[0];
Argc--;
Argv++;
//
// MINFO errors to mailbox
// RP text RR location
precord->Data.MINFO.pNameErrorsMailbox = (PDNS_NAME) Argv[0];
Argc--;
Argv++;
return( precord );
}
PDNS_RECORD
Txt_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build TXT compatible records from string data.
Includes: TXT, X25, HINFO, ISDN
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
WORD dataLength;
PCHAR * pstringPtr;
if ( Argc < 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
//
// allocate space for a pointer for each data string
//
precord = Dns_AllocateRecord( (WORD)DNS_TEXT_RECORD_LENGTH(Argc) );
if ( !precord )
{
return( NULL );
}
precord->Data.TXT.dwStringCount = Argc;
//
// read as many strings as we have
//
// DCR_FIX: no checking for string limits
// - string count limits on HINFO, X25, ISDN
// - 256 length on strings
// - 64K on overall size
//
pstringPtr = (PCHAR *) precord->Data.TXT.pStringArray;
while ( Argc-- )
{
*pstringPtr = Argv[0];
pstringPtr++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Txt_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build TXT compatible records from string data.
Includes: TXT, X25, HINFO, ISDN
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
WORD dataLength;
LPWSTR * pstringPtr;
if ( Argc < 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
//
// allocate space for a pointer for each data string
//
precord = Dns_AllocateRecord( (WORD)DNS_TEXT_RECORD_LENGTH(Argc) );
if ( !precord )
{
return( NULL );
}
precord->Data.TXT.dwStringCount = Argc;
//
// read as many strings as we have
//
// DCR_FIX: no checking for string limits
// - string count limits on HINFO, X25, ISDN
// - 256 length on strings
// - 64K on overall size
//
pstringPtr = (LPWSTR *) precord->Data.TXT.pStringArray;
while ( Argc-- )
{
*pstringPtr = Argv[0];
pstringPtr++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Aaaa_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build AAAA record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_AAAA_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read IP6 address
//
if ( ! Dns_Ip6StringToAddress_A(
(PIP6_ADDRESS) &precord->Data.AAAA.Ip6Address,
Argv[0] ) )
{
Dns_RecordFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
return( precord );
}
PDNS_RECORD
Aaaa_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build AAAA record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
if ( Argc != 1 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_AAAA_DATA) );
if ( !precord )
{
return( NULL );
}
//
// convert IPv6 string to address
//
if ( ! Dns_Ip6StringToAddress_W(
&precord->Data.AAAA.Ip6Address,
Argv[0]
) )
{
SetLastError( ERROR_INVALID_DATA );
Dns_RecordFree( precord );
return NULL;
}
return( precord );
}
PDNS_RECORD
Srv_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build SRV record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PWORD pword;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_SRV_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
pword = &precord->Data.SRV.wPriority;
while( Argc-- > 1 )
{
DWORD temp;
temp = strtoul( Argv[0], NULL, 10 );
if ( temp > MAXWORD )
{
temp = MAXWORD;
}
*pword++ = (WORD) temp;
Argv++;
}
//
// target host
//
precord->Data.SRV.pNameTarget = Argv[0];
return( precord );
}
PDNS_RECORD
Srv_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build SRV record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PWORD pword;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_SRV_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
pword = &precord->Data.SRV.wPriority;
while( Argc-- > 1 )
{
DWORD temp;
temp = wcstoul( Argv[0], NULL, 10 );
if ( temp > MAXWORD )
{
temp = MAXWORD;
}
*pword++ = (WORD) temp;
Argv++;
}
//
// target host
//
precord->Data.SRV.pNameTarget = (PDNS_NAME) Argv[0];
return( precord );
}
PDNS_RECORD
Atma_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build ATMA record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PBYTE pbyte;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_ATMA_DATA) +
DNS_ATMA_MAX_ADDR_LENGTH );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
pbyte = &precord->Data.ATMA.AddressType;
*pbyte = (BYTE) strtoul( Argv[0], NULL, 10 );
pbyte++;
Argv++;
if ( precord->Data.ATMA.AddressType == DNS_ATMA_FORMAT_E164 )
{
UINT length = strlen( Argv[0] );
UINT iter;
if ( length > DNS_ATMA_MAX_ADDR_LENGTH )
{
length = DNS_ATMA_MAX_ADDR_LENGTH;
}
for ( iter = 0; iter < length; iter++ )
{
precord->Data.ATMA.Address[iter] = Argv[0][iter];
}
precord->wDataLength = (WORD) length;
}
else
{
UINT length = strlen( Argv[0] );
UINT iter;
length /= 2;
if ( length != DNS_ATMA_MAX_ADDR_LENGTH )
{
Dns_RecordListFree( precord );
return NULL;
}
for ( iter = 0; iter < length; iter++ )
{
char temp[3];
temp[0] = Argv[0][(2*iter)];
temp[1] = Argv[0][(2*iter) + 1];
temp[2] = 0;
precord->Data.ATMA.Address[iter] = (char) strtoul( temp, NULL, 16 );
}
precord->wDataLength = (WORD) length;
}
return( precord );
}
PDNS_RECORD
Atma_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build ATMA record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PBYTE pbyte;
CHAR addrBuffer[256];
DWORD bufLength;
if ( Argc != 2 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_ATMA_DATA) +
DNS_ATMA_MAX_ADDR_LENGTH );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
pbyte = &precord->Data.ATMA.AddressType;
*pbyte = (BYTE) wcstoul( Argv[0], NULL, 10 );
pbyte++;
Argv++;
//
// copy ATMA address string to wire
//
bufLength = DNS_ATMA_MAX_ADDR_LENGTH+1;
if ( ! Dns_StringCopy(
addrBuffer,
& bufLength,
(PCHAR) Argv[0],
0, // length unknown
DnsCharSetUnicode,
DnsCharSetWire
) )
{
Dns_RecordListFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
//
// read address into record buffer
//
// DCR_CLEANUP: this is duplicate code with above function,
// functionalize and fix; also remove this loop
// and do a memcopy
//
if ( precord->Data.ATMA.AddressType == DNS_ATMA_FORMAT_E164 )
{
UINT length = strlen( addrBuffer );
UINT iter;
if ( length > DNS_ATMA_MAX_ADDR_LENGTH )
{
length = DNS_ATMA_MAX_ADDR_LENGTH;
}
for ( iter = 0; iter < length; iter++ )
{
precord->Data.ATMA.Address[iter] = addrBuffer[iter];
}
precord->wDataLength = (WORD) length;
}
else
{
UINT length = strlen( addrBuffer );
UINT iter;
length /= 2;
if ( length != DNS_ATMA_MAX_ADDR_LENGTH )
{
Dns_RecordListFree( precord );
return NULL;
}
for ( iter = 0; iter < length; iter++ )
{
char temp[3];
temp[0] = addrBuffer[(2*iter)];
temp[1] = addrBuffer[(2*iter) + 1];
temp[2] = 0;
precord->Data.ATMA.Address[iter] = (char) strtoul( temp, NULL, 16 );
}
precord->wDataLength = (WORD) length;
}
return( precord );
}
PDNS_RECORD
Wins_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build WINS record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD ipCount = Argc - 3;
PDWORD pdword;
PIP4_ADDRESS pip;
if ( Argc < 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( (WORD) DNS_WINS_RECORD_LENGTH((WORD) ipCount) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
// DCR_ENHANCE: could check for non-conversion in strtoul
//
pdword = &precord->Data.WINS.dwMappingFlag;
while ( Argc > ipCount )
{
*pdword = (DWORD) strtoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
Argc--;
}
*pdword = ipCount;
//
// convert IP addresses
//
pip = precord->Data.WINS.WinsServers;
while ( Argc-- )
{
if ( ! Dns_Ip4StringToAddress_A(
pip,
Argv[0] ) )
{
Dns_RecordFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
pip++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Wins_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build WINS record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD ipCount = Argc - 3;
PDWORD pdword;
PIP4_ADDRESS pip;
char szAddr[256];
if ( Argc < 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( (WORD) DNS_WINS_RECORD_LENGTH((WORD) ipCount) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
// DCR_ENHANCE: could check for non-conversion in strtoul
//
pdword = &precord->Data.WINS.dwMappingFlag;
while ( Argc-- > 1 )
{
*pdword = (DWORD) wcstoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
}
*pdword = ipCount;
//
// convert IP addresses
//
pip = precord->Data.WINS.WinsServers;
while ( Argc-- )
{
if ( ! Dns_Ip4StringToAddress_W(
pip,
Argv[0] ) )
{
Dns_RecordFree( precord );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
pip++;
Argv++;
}
return( precord );
}
PDNS_RECORD
Winsr_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build WINSR record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PDWORD pdword;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_WINSR_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
// DCR_ENHANCE: could check for non-conversion in strtoul
//
pdword = &precord->Data.WINSR.dwMappingFlag;
while( Argc-- > 1 )
{
*pdword = (WORD) strtoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
}
//
// result domain
//
precord->Data.WINSR.pNameResultDomain = Argv[0];
return( precord );
}
PDNS_RECORD
Winsr_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build WINSR record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
PDWORD pdword;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
precord = Dns_AllocateRecord( sizeof(DNS_WINSR_DATA) );
if ( !precord )
{
return( NULL );
}
//
// read integer data
//
// DCR_ENHANCE: could check for non-conversion in strtoul
//
pdword = &precord->Data.WINSR.dwMappingFlag;
while( Argc-- > 1 )
{
*pdword = (WORD) wcstoul( Argv[0], NULL, 10 );
pdword++;
Argv++;
}
//
// result domain
//
precord->Data.WINSR.pNameResultDomain = (PDNS_NAME) Argv[0];
return( precord );
}
PDNS_RECORD
Wks_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build WKS record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD byteCount = 0;
DWORD i;
PCHAR pch;
WSADATA wsaData;
DNS_STATUS status;
struct protoent * pProtoent;
if ( Argc < 3 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
i = 2;
while ( i < Argc)
{
byteCount += strlen( Argv[i] ) + 1;
i++;
}
byteCount++; //bBitMasks[0] : string length
//
// allocate space for WKS
//
precord = Dns_AllocateRecord( (WORD)DNS_WKS_RECORD_LENGTH(byteCount) );
if ( !precord )
{
return( NULL );
}
//
// get protocol number:
//
// start winsock:
//
// DCR: this is busted, winsock should be started by now
status = WSAStartup( DNS_WINSOCK_VERSION, &wsaData );
if ( status == SOCKET_ERROR )
{
Dns_RecordFree( precord );
status = WSAGetLastError();
SetLastError( status );
return( NULL );
}
pProtoent = getprotobyname( Argv[0] );
if ( ! pProtoent || pProtoent->p_proto >= MAXUCHAR )
{
Dns_RecordFree( precord );
status = WSAGetLastError();
SetLastError( status );
return( NULL );
}
precord->Data.WKS.chProtocol = (UCHAR) pProtoent->p_proto;
//
// get ipAddresss:
//
precord->Data.WKS.IpAddress = inet_addr( Argv[1] );
//
// get the services, put all in one string
//
pch = precord->Data.WKS.BitMask;
(UCHAR) *pch = (UCHAR) byteCount-1; //string length
pch++;
i = 2;
strcpy( pch, Argv[i] );
while ( ++i < Argc )
{
strcat( pch, " " );
strcat( pch, Argv[i] );
}
return( precord );
}
PDNS_RECORD
Wks_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build WKS record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD precord;
DWORD byteCount = 0;
DWORD i;
PWCHAR pch;
WSADATA wsaData;
DNS_STATUS status;
struct protoent * pProtoent;
char szAddr[256];
WCHAR tcpStr[4], udpStr[4], space[2];
if ( Argc < 3 )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
i = 2;
while ( i < Argc)
{
byteCount += wcslen( Argv[i] ) + 1;
i++;
}
byteCount++; //bBitMasks[0] : string length
//
// allocate space for WKS
//
precord = Dns_AllocateRecord( (WORD)DNS_WKS_RECORD_LENGTH(byteCount) );
if ( !precord )
{
return( NULL );
}
//
// get protocol number
//
status = WSAStartup( DNS_WINSOCK_VERSION, &wsaData );
if ( status == SOCKET_ERROR )
{
Dns_RecordFree( precord );
status = WSAGetLastError();
SetLastError( status );
return( NULL );
}
#if 0
//
// DCR_FIX: WKS build
//
if ( ! Dns_CopyStringEx( szAddr, 0, (PCHAR) Argv[0], 0, TRUE, FALSE ) )
{
Dns_RecordListFree( precord );
return NULL;
}
pProtoent = getprotobyname( szAddr );
if ( ! pProtoent || pProtoent->p_proto >= MAXUCHAR )
{
Dns_RecordFree( precord );
status = WSAGetLastError();
SetLastError( status );
return( NULL );
}
precord->Data.WKS.chProtocol = (UCHAR) pProtoent->p_proto;
//
// IP Address
//
if ( ! Dns_CopyStringEx( szAddr, 0, (PCHAR) Argv[0], 0, TRUE, FALSE ) )
{
Dns_RecordListFree( precord );
return NULL;
}
precord->Data.WKS.IpAddress = inet_addr( szAddr );
//
// get the services, put all in one string
//
pch = (PWCHAR) precord->Data.WKS.bBitMask;
(UCHAR) *pch = (UCHAR) byteCount-1;
pch++;
i = 2;
if ( ! Dns_NameCopy(
(PBYTE) space,
0,
" ",
0,
DnsCharSetUnicode,
DnsCharSetWire ) )
{
Dns_RecordListFree( precord );
return NULL;
}
wcscpy( pch, Argv[i] );
while ( ++i < Argc )
{
wcscat( pch, space );
wcscat( pch, Argv[i] );
}
#endif
return( precord );
}
PDNS_RECORD
Key_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build KEY record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int keyStringLength;
DWORD keyLength = 0;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
keyStringLength = strlen( Argv[ 3 ] );
prec = Dns_AllocateRecord( (WORD)
sizeof( DNS_KEY_DATA ) + keyStringLength );
if ( !prec )
{
return NULL;
}
prec->Data.KEY.wFlags = (WORD) strtoul( *( Argv++ ), NULL, 0 );
prec->Data.KEY.chProtocol = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.KEY.chAlgorithm = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
Argc -= 3;
Dns_SecurityBase64StringToKey(
prec->Data.KEY.Key,
&keyLength,
*Argv,
keyStringLength );
Argc--;
Argv++;
prec->wDataLength = (WORD) ( SIZEOF_KEY_FIXED_DATA + keyLength );
return prec;
} // Key_RecordBuild
PDNS_RECORD
Key_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build KEY record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int keyStringLength;
DWORD keyLength = 0;
if ( Argc != 4 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
keyStringLength = wcslen( Argv[ 3 ] );
prec = Dns_AllocateRecord( (WORD)
sizeof( DNS_KEY_DATA ) + keyStringLength / 2 );
if ( !prec )
{
return NULL;
}
prec->Data.KEY.wFlags = (WORD) wcstoul( *( Argv++ ), NULL, 0 );
prec->Data.KEY.chProtocol = (BYTE) wcstoul( *( Argv++ ), NULL, 10 );
prec->Data.KEY.chAlgorithm = (BYTE) wcstoul( *( Argv++ ), NULL, 10 );
Argc -= 3;
#if 0
// JJW: MUST COPY BUFFER???
Dns_SecurityBase64StringToKey(
prec->Data.KEY.Key,
&keyLength,
*Argv,
keyStringLength );
#endif
Argc--;
Argv++;
prec->wDataLength = (WORD) ( SIZEOF_KEY_FIXED_DATA + keyLength );
return prec;
} // Key_RecordBuildW
PDNS_RECORD
Sig_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build SIG record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int sigStringLength;
DWORD sigLength = 0;
if ( Argc != 9 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
sigStringLength = strlen( Argv[8] );
prec = Dns_AllocateRecord( (WORD)
( sizeof(DNS_SIG_DATA) + sigStringLength ) );
if ( !prec )
{
return NULL;
}
prec->Data.SIG.wTypeCovered = Dns_RecordTypeForName( *( Argv++ ), 0 );
prec->Data.SIG.chAlgorithm = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.Sig.chLabelCount = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.dwOriginalTtl = ( DWORD ) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.dwExpiration = Dns_ParseSigTime( *( Argv++ ), 0 );
prec->Data.SIG.dwTimeSigned = Dns_ParseSigTime( *( Argv++ ), 0 );
prec->Data.SIG.wKeyTag = (WORD) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.pNameSigner = *( Argv++ );
Argc -= 8;
//
// Validate signature times.
//
if ( prec->Data.SIG.dwExpiration == 0 ||
prec->Data.SIG.dwTimeSigned == 0 ||
prec->Data.SIG.dwTimeSigned >= prec->Data.SIG.dwExpiration )
{
Dns_RecordFree( prec );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
//
// Parse signature.
//
if ( Dns_SecurityBase64StringToKey(
prec->Data.SIG.Signature,
&sigLength,
*Argv,
sigStringLength ) != ERROR_SUCCESS )
{
Dns_RecordFree( prec );
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
Argc--;
Argv++;
prec->wDataLength = (WORD) ( sizeof( DNS_SIG_DATA ) - 4 + sigLength );
return prec;
} // Sig_RecordBuild
PDNS_RECORD
Sig_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build SIG record from string data.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int sigStringLength;
DWORD sigLength = 0;
PCHAR pch;
if ( Argc != 8 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
sigStringLength = wcslen( Argv[ 7 ] );
prec = Dns_AllocateRecord( (WORD)
( sizeof( DNS_SIG_DATA ) + sigStringLength ) );
if ( !prec )
{
return NULL;
}
#if 0
// JJW: how to convert all args here???
prec->Data.SIG.wTypeCovered = Dns_RecordTypeForName( *( Argv++ ), 0 );
prec->Data.SIG.chAlgorithm = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.Sig.chLabelCount = (BYTE) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.dwOriginalTtl = ( DWORD ) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.dwExpiration = Dns_ParseSigTime( *( Argv++ ), 0 );
prec->Data.SIG.dwTimeSigned = Dns_ParseSigTime( *( Argv++ ), 0 );
prec->Data.SIG.wKeyTag = (WORD) strtoul( *( Argv++ ), NULL, 10 );
prec->Data.SIG.pNameSigner = *( Argv++ );
Argc -= 8;
Dns_SecurityBase64StringToKey(
prec->Data.SIG.Signature,
&sigLength,
*Argv,
sigStringLength );
#endif
Argc--;
Argv++;
prec->wDataLength = (WORD) ( sizeof( DNS_SIG_DATA ) - 4 + sigLength );
return prec;
} // Sig_RecordBuildW
PDNS_RECORD
Nxt_RecordBuild(
IN DWORD Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build NXT record from string data.
First arg is next name, followed by list of record types at that name.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int typeIdx = 0;
if ( Argc < 2 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
prec = Dns_AllocateRecord( (WORD) (
sizeof( LPTSTR ) + sizeof(WORD) * Argc ) );
if ( !prec )
{
return NULL;
}
prec->Data.NXT.pNameNext = *( Argv++ );
--Argc;
prec->Data.NXT.wNumTypes = 0;
while ( Argc-- )
{
++prec->Data.NXT.wNumTypes;
prec->Data.NXT.wTypes[ typeIdx++ ] =
Dns_RecordTypeForName( *( Argv++ ), 0 );
}
return prec;
} // Nxt_RecordBuild
PDNS_RECORD
Nxt_RecordBuildW(
IN DWORD Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build NXT record from string data.
First arg is next name, followed by list of record types at that name.
Arguments:
Argc -- count of data Arguments
Argv -- argv array of data string pointers
Return Value:
Ptr to new record if successful.
NULL on failure.
--*/
{
PDNS_RECORD prec;
int typeIdx = 0;
if ( Argc < 2 )
{
SetLastError( ERROR_INVALID_DATA );
return NULL;
}
prec = Dns_AllocateRecord( (WORD) (
sizeof( LPTSTR ) + sizeof(WORD) * ( Argc - 1 ) ) );
if ( !prec )
{
return NULL;
}
prec->Data.NXT.pNameNext = ( PDNS_NAME ) ( *( Argv++ ) );
--Argc;
#if 0
// JJW: convert type string???
while ( Argc-- )
{
prec->Data.NXT.wTypes[ typeIdx++ ] =
Dns_RecordTypeForName( *( Argv++ ), 0 );
}
#endif
return prec;
} // Nxt_RecordBuildW
//
// RR build routines jump table
//
typedef PDNS_RECORD (* RR_BUILD_FUNCTION)(
DWORD,
PCHAR * );
//extern RR_BUILD_FUNCTION RRBuildTable[];
typedef PDNS_RECORD (* RR_BUILD_FUNCTION_W)(
DWORD,
PWCHAR * );
//extern RR_BUILD_FUNCTION_W RRBuildTableW[];
RR_BUILD_FUNCTION RRBuildTable[] =
{
NULL, // ZERO
A_RecordBuild, // A
Ptr_RecordBuild, // NS
Ptr_RecordBuild, // MD
Ptr_RecordBuild, // MF
Ptr_RecordBuild, // CNAME
Soa_RecordBuild, // SOA
Ptr_RecordBuild, // MB
Ptr_RecordBuild, // MG
Ptr_RecordBuild, // MR
NULL, // NULL
Wks_RecordBuild, // WKS
Ptr_RecordBuild, // PTR
Txt_RecordBuild, // HINFO
Minfo_RecordBuild, // MINFO
Mx_RecordBuild, // MX
Txt_RecordBuild, // TXT
Minfo_RecordBuild, // RP
Mx_RecordBuild, // AFSDB
Txt_RecordBuild, // X25
Txt_RecordBuild, // ISDN
Mx_RecordBuild, // RT
NULL, // NSAP
NULL, // NSAPPTR
Sig_RecordBuild, // SIG
Key_RecordBuild, // KEY
NULL, // PX
NULL, // GPOS
Aaaa_RecordBuild, // AAAA
NULL, // LOC
Nxt_RecordBuild, // NXT
NULL, // EID
NULL, // NIMLOC
Srv_RecordBuild, // SRV
Atma_RecordBuild, // ATMA
NULL, // NAPTR
NULL, // KX
NULL, // CERT
NULL, // A6
NULL, // DNAME
NULL, // SINK
NULL, // OPT
NULL, // 42
NULL, // 43
NULL, // 44
NULL, // 45
NULL, // 46
NULL, // 47
NULL, // 48
//
// NOTE: last type indexed by type ID MUST be set
// as MAX_SELF_INDEXED_TYPE #define in record.h
// (see note above in record info table)
//
// Pseudo record types
//
NULL, // TKEY
NULL, // TSIG
//
// MS only types
//
Wins_RecordBuild, // WINS
Winsr_RecordBuild, // WINSR
};
RR_BUILD_FUNCTION_W RRBuildTableW[] =
{
NULL, // ZERO
A_RecordBuildW, // A
Ptr_RecordBuildW, // NS
Ptr_RecordBuildW, // MD
Ptr_RecordBuildW, // MF
Ptr_RecordBuildW, // CNAME
Soa_RecordBuildW, // SOA
Ptr_RecordBuildW, // MB
Ptr_RecordBuildW, // MG
Ptr_RecordBuildW, // MR
NULL, // NULL
Wks_RecordBuildW, // WKS
Ptr_RecordBuildW, // PTR
Txt_RecordBuildW, // HINFO
Minfo_RecordBuildW, // MINFO
Mx_RecordBuildW, // MX
Txt_RecordBuildW, // TXT
Minfo_RecordBuildW, // RP
Mx_RecordBuildW, // AFSDB
Txt_RecordBuildW, // X25
Txt_RecordBuildW, // ISDN
Mx_RecordBuildW, // RT
NULL, // NSAP
NULL, // NSAPPTR
Sig_RecordBuildW, // SIG
Key_RecordBuildW, // KEY
NULL, // PX
NULL, // GPOS
Aaaa_RecordBuildW, // AAAA
NULL, // LOC
Nxt_RecordBuildW, // NXT
NULL, // EID
NULL, // NIMLOC
Srv_RecordBuildW, // SRV
Atma_RecordBuildW, // ATMA
NULL, // NAPTR
NULL, // KX
NULL, // CERT
NULL, // A6
NULL, // DNAME
NULL, // SINK
NULL, // OPT
NULL, // 42
NULL, // 43
NULL, // 44
NULL, // 45
NULL, // 46
NULL, // 47
NULL, // 48
//
// NOTE: last type indexed by type ID MUST be set
// as MAX_SELF_INDEXED_TYPE #define in record.h
// (see note above in record info table)
//
// Pseudo record types
//
NULL, // TKEY
NULL, // TSIG
//
// MS only types
//
Wins_RecordBuildW, // WINS
Winsr_RecordBuildW, // WINSR
};
//
// Public build routine
//
PDNS_RECORD
Dns_RecordBuild_A(
IN OUT PDNS_RRSET pRRSet,
IN LPSTR pszOwner,
IN WORD wType,
IN BOOL fAdd,
IN UCHAR Section,
IN INT Argc,
IN PCHAR * Argv
)
/*++
Routine Description:
Build record from data strings.
Arguments:
pRRSet -- ptr to RR set structure being built
pszOwner -- DNS name of RR owner
wType -- record type
fAdd -- add\delete, exist\no-exist flag
Section -- RR section for record
Argc -- count of data strings
Argv -- argv array of ptrs to data strings
Return Value:
Ptr to record built.
NULL on error.
--*/
{
PDNS_RECORD precord;
WORD index;
IF_DNSDBG( INIT )
{
DNS_PRINT((
"Dns_RecordBuild()\n"
"\trrset = %p\n"
"\towner = %s\n"
"\ttype = %d\n"
"\tfAdd = %d\n"
"\tsection = %d\n"
"\targc = %d\n",
pRRSet,
pszOwner,
wType,
fAdd,
Section,
Argc ));
}
//
// every record MUST have owner name
//
if ( !pszOwner )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
//
// if no data, no dispatch required
//
if ( Argc == 0 )
{
precord = Dns_AllocateRecord( 0 );
if ( ! precord )
{
return( NULL );
}
}
// have data, dispatch to type specific build routine
else
{
index = INDEX_FOR_TYPE( wType );
DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
if ( !index || !RRBuildTable[ index ] )
{
// can NOT build unknown types
SetLastError( DNS_ERROR_INVALID_TYPE );
DNS_PRINT((
"ERROR: can not build record of type %d\n",
wType ));
return( NULL );
}
precord = RRBuildTable[ index ](
Argc,
Argv );
if ( ! precord )
{
DNS_PRINT((
"ERROR: Record build routine failure for record type %d.\n"
"\tstatus = %d\n\n",
wType,
GetLastError() ));
if ( !GetLastError() )
{
SetLastError( ERROR_INVALID_DATA );
}
return( NULL );
}
}
//
// fill out record structure
//
precord->pName = pszOwner;
precord->wType = wType;
precord->Flags.S.Section = Section;
precord->Flags.S.Delete = !fAdd;
precord->Flags.S.CharSet = DnsCharSetAnsi;
IF_DNSDBG( INIT )
{
DnsDbg_Record(
"New record built\n",
precord );
}
//
// link into existing RR set (if any)
//
if ( pRRSet )
{
DNS_RRSET_ADD( *pRRSet, precord );
}
return( precord );
}
PDNS_RECORD
Dns_RecordBuild_W(
IN OUT PDNS_RRSET pRRSet,
IN LPWSTR pszOwner,
IN WORD wType,
IN BOOL fAdd,
IN UCHAR Section,
IN INT Argc,
IN PWCHAR * Argv
)
/*++
Routine Description:
Build record from data strings.
Arguments:
pRRSet -- ptr to RR set structure being built
pszOwner -- DNS name of RR owner
wType -- record type
fAdd -- add\delete, exist\no-exist flag
Section -- RR section for record
Argc -- count of data strings
Argv -- argv array of ptrs to data strings
Return Value:
Ptr to record built.
NULL on error.
--*/
{
PDNS_RECORD precord;
WORD index;
DNSDBG( INIT, (
"Dns_RecordBuild()\n"
"\trrset = %p\n"
"\towner = %S\n"
"\ttype = %d\n"
"\tfAdd = %d\n"
"\tsection = %d\n"
"\targc = %d\n",
pRRSet,
pszOwner,
wType,
fAdd,
Section,
Argc ));
//
// every record MUST have owner name
//
if ( !pszOwner )
{
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
//
// if no data, no dispatch required
//
if ( Argc == 0 )
{
precord = Dns_AllocateRecord( 0 );
if ( ! precord )
{
return( NULL );
}
}
// have data, dispatch to type specific build routine
else
{
index = INDEX_FOR_TYPE( wType );
DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
if ( !index || !RRBuildTableW[ index ] )
{
// can NOT build unknown types
SetLastError( DNS_ERROR_INVALID_TYPE );
DNS_PRINT((
"ERROR: can not build record of type %d\n",
wType ));
return( NULL );
}
precord = RRBuildTableW[ index ](
Argc,
Argv );
if ( ! precord )
{
DNS_PRINT((
"ERROR: Record build routine failure for record type %d.\n"
"\tstatus = %d\n\n",
wType,
GetLastError() ));
if ( !GetLastError() )
{
SetLastError( ERROR_INVALID_DATA );
}
return( NULL );
}
}
//
// fill out record structure
//
precord->pName = (PDNS_NAME) pszOwner;
precord->wType = wType;
precord->Flags.S.Section = Section;
precord->Flags.S.Delete = !fAdd;
precord->Flags.S.CharSet = DnsCharSetUnicode;
IF_DNSDBG( INIT )
{
DnsDbg_Record(
"New record built\n",
precord );
}
//
// link into existing RR set (if any)
//
if ( pRRSet )
{
DNS_RRSET_ADD( *pRRSet, precord );
}
return( precord );
}
//
// End rrbuild.c
//