windows-nt/Source/XPSP1/NT/ds/dns/server/client/sam.c
2020-09-26 16:20:57 +08:00

1054 lines
23 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
sam.c
Abstract:
Domain Name System (DNS) Server -- Admin Client API
Functions developed for SAM as simplified interfaces \ examples.
Author:
Jim Gilroy (jamesg) September 1997
Environment:
User Mode - Win32
Revision History:
--*/
#include "dnsclip.h"
#define MAX_SAM_BACKOFF (32000) // wait up to 32 seconds
//
// Type specific update functions.
//
VOID
DNS_API_FUNCTION
DnssrvFillRecordHeader(
IN OUT PDNS_RPC_RECORD pRecord,
IN DWORD dwTtl,
IN DWORD dwTimeStamp,
IN BOOL fSuppressNotify
)
{
pRecord->dwTtlSeconds = dwTtl;
pRecord->dwTimeStamp = dwTimeStamp;
pRecord->dwFlags = 0;
if ( fSuppressNotify )
{
pRecord->dwFlags |= DNS_RPC_FLAG_SUPPRESS_NOTIFY;
}
}
DWORD
DNS_API_FUNCTION
DnssrvWriteNameToFlatBuffer(
IN OUT PCHAR pchWrite,
IN LPCSTR pszName
)
/*++
Routine Description:
Write DNS name (or string) to flat buffer.
Arguments:
pchWrite -- location to write name at
pszName -- name or string to write
Return Value:
Length of name written, including count byte. Caller may countinue in
buffer at pchWrite + returned length.
0 on name error.
--*/
{
DWORD length;
//
// get name length
// whether name or string, must be 255 or less to fit
// counted character format
length = strlen( pszName );
if ( length > DNS_MAX_NAME_LENGTH )
{
return( 0 );
}
//
// write name to desired location
// - count byte first
// - then name itself
* (PUCHAR) pchWrite = (UCHAR) length;
pchWrite++;
RtlCopyMemory(
pchWrite,
pszName,
length );
return( length+1 );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvFillOutSingleIndirectionRecord(
IN OUT PDNS_RPC_RECORD pRecord,
IN WORD wType,
IN LPCSTR pszName
)
{
PCHAR pwriteName;
DWORD length;
DWORD dataLength = 0;
//
// find name write position and final data length for various types
//
switch( wType )
{
case DNS_TYPE_MX:
pwriteName = (PCHAR) &pRecord->Data.MX.nameExchange;
dataLength += sizeof(WORD);
break;
case DNS_TYPE_SRV:
pwriteName = (PCHAR) &pRecord->Data.SRV.nameTarget;
dataLength += 3*sizeof(WORD);
break;
default:
// all plain single-indirection types (CNAME, NS, PTR, etc)
pwriteName = (PCHAR) &pRecord->Data.PTR.nameNode;
}
//
// write name
// - note name's datalength includes count character
//
length = DnssrvWriteNameToFlatBuffer( pwriteName, pszName );
if ( !length )
{
return( ERROR_INVALID_DATA );
}
dataLength += length;
// set record header fields
pRecord->wType = wType;
pRecord->wDataLength = (WORD)dataLength;
ASSERT( (PCHAR)pRecord + SIZEOF_DNS_RPC_RECORD_HEADER + dataLength
== pwriteName + length );
return( ERROR_SUCCESS );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvAddARecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN IP_ADDRESS ipAddress,
IN DWORD dwTtl,
IN DWORD dwTimeout,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
dwTtl,
dwTimeout,
fSuppressNotify );
record.wType = DNS_TYPE_A;
record.wDataLength = sizeof(IP_ADDRESS);
record.Data.A.ipAddress = ipAddress;
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
&record,
NULL );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvAddCnameRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszCannonicalName,
IN DWORD dwTtl,
IN DWORD dwTimeout,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
dwTtl,
dwTimeout,
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_CNAME,
pszCannonicalName );
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
&record,
NULL );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvAddMxRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszMailExchangeHost,
IN WORD wPreference,
IN DWORD dwTtl,
IN DWORD dwTimeout,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
dwTtl,
dwTimeout,
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_MX,
pszMailExchangeHost );
record.Data.MX.wPreference = wPreference;
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
&record,
NULL );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvAddNsRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszNsHostName,
IN DWORD dwTtl,
IN DWORD dwTimeout,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
DWORD length;
// pack up data and send
DnssrvFillRecordHeader(
& record,
dwTtl,
dwTimeout,
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_NS,
pszNsHostName );
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
&record,
NULL );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvConcatDnsNames(
OUT PCHAR pszResult,
IN LPCSTR pszDomain,
IN LPCSTR pszName
)
/*++
Routine Description:
Concatenate two DNS names.
Result is FQDN DNS name -- dot terminated.
Note, currently no validity check is done on names being appended.
If they are invalid DNS names then result is invalid DNS name.
Arguments:
pszResult -- result name buffer; should be DNS_MAX_NAME_BUFFER_LEN to
be protected from name overwrite
pszDomain -- domain name to write
pszName -- name (like a host name) to prepend to domain name
Return Value:
ERROR_SUCCESS if successful. pszResult then contains FQDN
DNS_ERROR_INVALID_NAME on failure.
--*/
{
DWORD lengthDomain;
DWORD lengthName;
// handle NULL name case
if ( !pszName )
{
strcpy( pszResult, pszDomain );
return( ERROR_SUCCESS );
}
//
// build combined name
// - verify combined length within DNS limit
// - put dot between names
// - dot terminate combined name (make FQDN)
//
lengthDomain = strlen( pszDomain );
lengthName = strlen( pszName );
if ( lengthDomain + lengthName + 2 > DNS_MAX_NAME_LENGTH )
{
return( DNS_ERROR_INVALID_NAME );
}
strcpy( pszResult, pszName );
if ( pszDomain[lengthName-1] != '.' )
{
strcat( pszResult, "." );
}
strcat( pszResult, pszDomain );
if ( pszDomain[lengthDomain-1] != '.' )
{
strcat( pszResult, "." );
}
return( ERROR_SUCCESS );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvSbsAddClientToIspZone(
IN LPCWSTR pwszServer,
IN LPCSTR pszIspZone,
IN LPCSTR pszClient,
IN LPCSTR pszClientHost,
IN IP_ADDRESS ipClientHost,
IN DWORD dwTtl
)
{
DNS_STATUS status;
INT recordCount = (-1);
INT backoff = 0;
CHAR szdomain[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR szhost[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR sztarget[ DNS_MAX_NAME_BUFFER_LENGTH ];
//
// to register in ISP zone need to register
// - MX
// - CNAME for web server
// - host for web and mail server
//
// build FQDN for domain and host and cname
// - do this now so we know names are valid
//
status = DnssrvConcatDnsNames(
szdomain,
pszIspZone,
pszClient );
if ( status != ERROR_SUCCESS )
{
return( status );
}
status = DnssrvConcatDnsNames(
szhost,
szdomain,
pszClientHost );
if ( status != ERROR_SUCCESS )
{
return( status );
}
status = DnssrvConcatDnsNames(
sztarget,
szdomain,
"www" );
if ( status != ERROR_SUCCESS )
{
return( status );
}
//
// do registrations, looping in case server is unable to complete
// immediately but is open for update
//
while ( 1 )
{
// if retrying backoff, but continue trying
if ( backoff )
{
if ( backoff > MAX_SAM_BACKOFF )
{
break;
}
Sleep( backoff );
}
backoff += 1000;
//
// remove any old entries at client domain
//
if ( recordCount < 0 )
{
status = DnssrvDeleteNode(
pwszServer,
pszIspZone,
szdomain,
1 // delete subtree
);
if ( status == DNS_ERROR_NAME_DOES_NOT_EXIST )
{
status = ERROR_SUCCESS;
}
}
// register A record
else if ( recordCount < 1 )
{
status = DnssrvAddARecord(
pwszServer,
szhost,
ipClientHost,
dwTtl,
0, // no timeout
TRUE ); // suppress notify
}
// register CNAME for WEB server
else if ( recordCount < 2 )
{
status = DnssrvAddCnameRecord(
pwszServer,
sztarget,
szhost,
dwTtl,
0, // no timeout
TRUE ); // suppress notify
}
// register MX at client's domain root
// then at wildcard
else if ( recordCount < 3 )
{
status = DnssrvAddMxRecord(
pwszServer,
szdomain,
szhost,
10, // preference
dwTtl,
0, // no timeout
TRUE ); // suppress notify
}
else if ( recordCount < 4 )
{
// prepare *.<client>.isp name for wildcard MX record
status = DnssrvConcatDnsNames(
sztarget,
szdomain,
"*" );
if ( status != ERROR_SUCCESS )
{
ASSERT( FALSE );
break;
}
status = DnssrvAddMxRecord(
pwszServer,
sztarget,
szhost,
10, // preference
dwTtl,
0, // no timeout
TRUE ); // suppress notify
}
// all desired records registered
else
{
ASSERT( recordCount == 4 );
break;
}
//
// check status on operations
// - if successful, inc count and reset backoff to move
// onto next operation
// - if zone locked, continue after backoff
// - other errors are terminal
//
if ( status == ERROR_SUCCESS ||
status == DNS_ERROR_RECORD_ALREADY_EXISTS )
{
recordCount++;
backoff = 0;
}
else if ( status == DNS_ERROR_ZONE_LOCKED )
{
continue;
}
else
{
break;
}
}
return( status );
}
//
// Record deleting functions.
//
// This example uses A records.
// Could be cloned to handle MX or CNAME or NS.
// OR could expand this function to choose type
//
BOOL
DNS_API_FUNCTION
DnssrvMatchDnsRpcName(
IN PDNS_RPC_NAME pRpcName,
IN LPCSTR pszName
)
{
CHAR nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ] = "";
RtlCopyMemory(
nameBuf,
pRpcName->achName,
pRpcName->cchNameLength );
return Dns_NameCompare_UTF8( nameBuf, (LPSTR)pszName );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvDeleteARecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN IP_ADDRESS ipAddress,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
DNSDBG( RPC2, ( "DnssrvDeleteARecord()\n" ));
// pack up data and send
DnssrvFillRecordHeader(
& record,
0, // TTL irrelevant for delete
0, // timeout irrelevant
fSuppressNotify );
record.wType = DNS_TYPE_A;
record.wDataLength = sizeof(IP_ADDRESS);
record.Data.A.ipAddress = ipAddress;
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
NULL, // no add
&record );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvDeleteCnameRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszCannonicalName,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
0, // TTL irrelevant for delete
0, // timeout irrelevant
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_CNAME,
pszCannonicalName );
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
NULL,
&record );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvDeleteMxRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszMailExchangeHost,
IN WORD wPreference,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
0, // TTL irrelevant for delete
0, // timeout irrelevant
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_MX,
pszMailExchangeHost );
record.Data.MX.wPreference = wPreference;
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
NULL,
&record );
}
DNS_STATUS
DNS_API_FUNCTION
DnssrvDeleteNsRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszNodeName,
IN LPCSTR pszNsHostName,
IN BOOL fSuppressNotify
)
{
DNS_RPC_RECORD record;
// pack up data and send
DnssrvFillRecordHeader(
& record,
0, // TTL irrelevant for delete
0, // timeout irrelevant
fSuppressNotify );
DnssrvFillOutSingleIndirectionRecord(
& record,
DNS_TYPE_NS,
pszNsHostName );
return DnssrvUpdateRecord(
pwszServer,
NULL, // zone not specified
pszNodeName,
NULL,
&record );
}
#if 0
DNS_STATUS
DNS_API_FUNCTION
DnssrvSbsDeleteRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszZone,
IN LPCSTR pszDomain,
IN LPCSTR pszOwner,
IN WORD wType,
IN LPCSTR pszDataName,
IN IP_ADDRESS ipHost
)
{
PDNS_RPC_RECORD prpcRecord;
DNS_STATUS status;
BOOL ffound;
INT countRecords;
PBYTE pbyte;
PBYTE pstopByte;
PBYTE pbuffer;
DWORD bufferLength;
CHAR szdomain[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR szhost[ DNS_MAX_NAME_BUFFER_LENGTH ];
DNSDBG( RPC2, ( "DnssrvSbsDeleteRecord()\n" ));
//
// to register in ISP zone need to register
// - MX
// - CNAME for web server
// - host for web and mail server
//
// build FQDN for domain and host and cname
// - do this now so we know names are valid
//
status = DnssrvConcatDnsNames(
szdomain,
pszZone,
pszDomain );
if ( status != ERROR_SUCCESS )
{
return( status );
}
status = DnssrvConcatDnsNames(
szhost,
szdomain,
pszOwner );
if ( status != ERROR_SUCCESS )
{
return( status );
}
//
// enumerate records at a particular node
//
status = DnssrvEnumRecords(
pwszServer,
szhost,
NULL,
wType,
( DNS_RPC_VIEW_ALL_DATA | DNS_RPC_VIEW_NO_CHILDREN ),
& bufferLength,
& pbuffer );
if ( status != ERROR_SUCCESS )
{
DNSDBG( RPC2, ( "DnssrvEnumRecord() failed %p.\n", status ));
return( status );
}
pstopByte = pbuffer + bufferLength;
pbyte = pbuffer;
//
// read node info
// - extract record count
//
countRecords = ((PDNS_RPC_NODE)pbyte)->wRecordCount;
pbyte += ((PDNS_RPC_NODE)pbyte)->wLength;
pbyte = DNS_NEXT_DWORD_PTR(pbyte);
//
// loop through all records in node, delete appropriate one
//
DNSDBG( RPC2, (
"Checking %d records for matching record.\n",
countRecords ));
while ( countRecords-- )
{
prpcRecord = (PDNS_RPC_RECORD) pbyte;
if ( !DNS_IS_RPC_RECORD_WITHIN_BUFFER( prpcRecord, pstopByte ) )
{
DNS_PRINT((
"ERROR: Bogus buffer at %p\n"
"\tRecord leads past buffer end at %p\n"
"\twith %d records remaining.\n",
prpcRecord,
pstopByte,
countRecords+1 ));
DNS_ASSERT( FALSE );
return( DNS_ERROR_INVALID_DATA );
}
// if type not desired type, then not interesting
if ( prpcRecord->wType != wType )
{
DNS_ASSERT( FALSE );
return( DNS_ERROR_INVALID_DATA );
}
DNSDBG( RPC2, (
"Checking record at %p for matching data of type %d.\n",
prpcRecord,
wType ));
//
// check for data match, delete if match
//
switch ( wType )
{
case DNS_TYPE_A:
ffound = ( prpcRecord->Data.A.ipAddress == ipHost );
DNSDBG( RPC2, (
"%s match between A record %lx and desired IP %lx\n",
ffound ? "Found" : "No",
prpcRecord->Data.A.ipAddress,
ipHost ));
break;
case DNS_TYPE_MX:
ffound = DnssrvMatchDnsRpcName(
& prpcRecord->Data.MX.nameExchange,
pszDataName );
break;
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_PTR:
ffound = DnssrvMatchDnsRpcName(
& prpcRecord->Data.MX.nameExchange,
pszDataName );
break;
default:
return( DNS_ERROR_INVALID_DATA );
}
if ( ffound )
{
DNSDBG( RPC2, (
"Found record (handle = %p) with desired data\n"
"\t... deleting record\n",
prpcRecord->hRecord ));
status = DnssrvDeleteRecord(
pwszServer,
szhost,
prpcRecord->hRecord );
if ( status != ERROR_SUCCESS )
{
return( status );
}
// shouldn't need to continue, as no duplicates allowed in general case
// however to rule out glue or WINS cached data, continue compare\delete
// until node is clear
}
// position ourselves at next record
pbyte = (PCHAR) DNS_GET_NEXT_RPC_RECORD( prpcRecord );
// continue looking for matching records
}
return( ERROR_SUCCESS );
}
#endif
DNS_STATUS
DNS_API_FUNCTION
DnssrvSbsDeleteRecord(
IN LPCWSTR pwszServer,
IN LPCSTR pszZone,
IN LPCSTR pszDomain,
IN LPCSTR pszOwner,
IN WORD wType,
IN LPCSTR pszDataName,
IN IP_ADDRESS ipHost
)
{
DNS_STATUS status;
CHAR szdomain[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR szhost[ DNS_MAX_NAME_BUFFER_LENGTH ];
DNSDBG( RPC2, ( "DnssrvSbsDeleteRecord()\n" ));
//
// to register in ISP zone need to register
// - MX
// - CNAME for web server
// - host for web and mail server
//
// build FQDN for domain and host and cname
// - do this now so we know names are valid
//
status = DnssrvConcatDnsNames(
szdomain,
pszZone,
pszDomain );
if ( status != ERROR_SUCCESS )
{
return( status );
}
status = DnssrvConcatDnsNames(
szhost,
szdomain,
pszOwner );
if ( status != ERROR_SUCCESS )
{
return( status );
}
//
// dispatch to appropriate type delete routine
//
switch ( wType )
{
case DNS_TYPE_A:
return DnssrvDeleteARecord(
pwszServer,
szhost,
ipHost,
FALSE // no notify suppress
);
case DNS_TYPE_NS:
return DnssrvDeleteNsRecord(
pwszServer,
szhost,
pszDataName,
FALSE // no notify suppress
);
case DNS_TYPE_CNAME:
return DnssrvDeleteCnameRecord(
pwszServer,
szhost,
pszDataName,
FALSE // no notify suppress
);
case DNS_TYPE_MX:
return DnssrvDeleteMxRecord(
pwszServer,
szhost,
pszDataName,
(WORD) ipHost,
FALSE // no notify suppress
);
default:
return( DNS_ERROR_INVALID_DATA );
}
return( ERROR_SUCCESS );
}
//
// End sam.c
//