|
|
/*++
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
//
|