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

4682 lines
107 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) 1996-2001 Microsoft Corporation
Module Name:
update.c
Abstract:
Domain Name System (DNS) API
Update client routines.
Author:
Jim Gilroy (jamesg) October, 1996
Revision History:
--*/
#include "local.h"
//
// Security flag check
//
#define UseSystemDefaultForSecurity(flag) \
( ((flag) & DNS_UPDATE_SECURITY_CHOICE_MASK) \
== DNS_UPDATE_SECURITY_USE_DEFAULT )
//
// Local update flag
// - must make sure this is in UPDATE_RESERVED space
//
#define DNS_UPDATE_LOCAL_COPY (0x00010000)
//
// DCR_DELETE: this is stupid
//
#define DNS_UNACCEPTABLE_UPDATE_OPTIONS \
(~ \
( DNS_UPDATE_SHARED | \
DNS_UPDATE_SECURITY_OFF | \
DNS_UPDATE_CACHE_SECURITY_CONTEXT | \
DNS_UPDATE_SECURITY_ON | \
DNS_UPDATE_FORCE_SECURITY_NEGO | \
DNS_UPDATE_TRY_ALL_MASTER_SERVERS | \
DNS_UPDATE_LOCAL_COPY | \
DNS_UPDATE_SECURITY_ONLY ))
//
// Update Timeouts
//
// note, max is a little longer than might be expected as DNS server
// may have to contact primary and wait for primary to do update (inc.
// disk access) then response
//
#define INITIAL_UPDATE_TIMEOUT (4) // 4 seconds
#define MAX_UPDATE_TIMEOUT (60) // 60 seconds
//
// Private prototypes
//
DNS_STATUS
DoQuickUpdate(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN BOOL fUpdateTestMode,
IN PIP_ARRAY pServerList OPTIONAL,
IN HANDLE hCreds OPTIONAL
);
DNS_STATUS
DoQuickUpdateEx(
OUT PDNS_MSG_BUF * ppMsgRecv, OPTIONAL
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN PDNS_NETINFO pNetworkInfo,
IN HANDLE hCreds OPTIONAL
);
DNS_STATUS
DoMultiMasterUpdate(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN HANDLE hCreds OPTIONAL,
IN LPSTR pszDomain,
IN IP_ADDRESS BadIp,
IN PIP_ARRAY pServerList
);
VOID
SetLastFailedUpdateInfo(
IN PDNS_MSG_BUF pMsg,
IN DNS_STATUS Status
);
PCHAR
Dns_WriteNoDataUpdateRecordToMessage(
IN PCHAR pch,
IN PCHAR pchStop,
IN WORD wClass,
IN WORD wType
)
/*++
Routine Description:
No data RR cases:
This includes prereqs and deletes except for specific record cases.
Arguments:
pch - ptr to next byte in packet buffer
pchStop - end of packet buffer
wClass - class
wType - desired RR type
Return Value:
Ptr to next postion in buffer, if successful.
NULL on error.
--*/
{
PDNS_WIRE_RECORD pdnsRR;
DNSDBG( WRITE, (
"Writing update RR to packet buffer at %p.\n",
pch ));
//
// out of space
//
pdnsRR = (PDNS_WIRE_RECORD) pch;
pch += sizeof( DNS_WIRE_RECORD );
if ( pch >= pchStop )
{
DNS_PRINT(( "ERROR out of space writing record to packet.\n" ));
return( NULL );
}
//
// set type and class
//
*(UNALIGNED WORD *) &pdnsRR->RecordType = htons( wType );
*(UNALIGNED WORD *) &pdnsRR->RecordClass = htons( wClass );
//
// TTL and datalength zero for all no data cases
// - prereqs except specific record delete
// - deletes except specific record delete
//
*(UNALIGNED DWORD *) &pdnsRR->TimeToLive = 0;
*(UNALIGNED WORD *) &pdnsRR->DataLength = 0;
return( pch );
}
PCHAR
Dns_WriteDataUpdateRecordToMessage(
IN PCHAR pch,
IN PCHAR pchStop,
IN WORD wClass,
IN WORD wType,
IN DWORD dwTtl,
IN WORD wDataLength
)
/*++
Routine Description:
No data RR cases:
This includes prereqs and deletes except for specific record cases.
Arguments:
pch - ptr to next byte in packet buffer
pchStop - end of packet buffer
wClass - class
wType - desired RR type
dwTtl - time to live
wDataLength - data length
Return Value:
Ptr to next postion in buffer, if successful.
NULL on error.
--*/
{
PDNS_WIRE_RECORD pdnsRR;
DNSDBG( WRITE2, (
"Writing RR to packet buffer at %p.\n",
pch ));
//
// out of space
//
pdnsRR = (PDNS_WIRE_RECORD) pch;
pch += sizeof( DNS_WIRE_RECORD );
if ( pch + wDataLength >= pchStop )
{
DNS_PRINT(( "ERROR out of space writing record to packet.\n" ));
return( NULL );
}
//
// set type and class
//
*(UNALIGNED WORD *) &pdnsRR->RecordType = htons( wType );
*(UNALIGNED WORD *) &pdnsRR->RecordClass = htons( wClass );
//
// TTL and datalength zero for all no data cases
// - prereqs except specific record delete
// - deletes except specific record delete
//
*(UNALIGNED DWORD *) &pdnsRR->TimeToLive = htonl( dwTtl );
*(UNALIGNED WORD *) &pdnsRR->DataLength = htons( wDataLength );
return( pch );
}
//
// Host update routines
//
#if 0
PDNS_MSG_BUF
Dns_BuildHostUpdateMessage(
IN OUT PDNS_MSG_BUF pMsg,
IN LPSTR pszZone,
IN LPSTR pszName,
IN PIP_ARRAY aipAddresses,
IN DWORD dwTtl
)
/*++
Routine Description:
Build server update message.
Arguments:
pMsg -- existing message buffer, to use; NULL to allocate new one
pszZone -- zone name for update
pszName -- full DNS hostname being updated
aipAddresses -- IP addresses to be updated
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
PDNS_HEADER pdnsMsg;
PCHAR pch;
PCHAR pchstop;
DWORD i;
WORD nameOffset;
IF_DNSDBG( UPDATE )
{
DNS_PRINT((
"Enter Dns_BuildHostUpdateMessage()\n"
"\tpMsg = %p\n"
"\tpszZone = %s\n"
"\tpszName = %s\n"
"\tdwTtl = %d\n",
pMsg,
pszZone,
pszName,
dwTtl ));
DnsDbg_IpArray(
"\tHost IP array\n",
"host",
aipAddresses );
}
//
// create message buffer
//
if ( !pMsg )
{
DNS_PRINT(( "Allocating new UPDATE message buffer.\n" ));
pMsg = ALLOCATE_HEAP( DNS_UDP_ALLOC_LENGTH );
if ( !pMsg )
{
return( NULL );
}
RtlZeroMemory(
pMsg,
DNS_UDP_ALLOC_LENGTH );
pMsg->BufferLength = DNS_UDP_MAX_PACKET_LENGTH;
pMsg->pBufferEnd = (PCHAR)&pMsg->MessageHead + pMsg->BufferLength;
//
// set default sockaddr info
// - caller MUST choose remote IP address
pMsg->RemoteAddress.sin_family = AF_INET;
pMsg->RemoteAddress.sin_port = NET_ORDER_DNS_PORT;
pMsg->RemoteAddressLength = sizeof( SOCKADDR_IN );
// set header for update
pMsg->MessageHead.Opcode = DNS_OPCODE_UPDATE;
}
//
// existing message, just verify
//
ELSE_ASSERT( pMsg->MessageHead.Opcode == DNS_OPCODE_UPDATE );
//
// reset current pointer after header
// - note: send length is set based on this ptr
//
pMsg->pCurrent = pMsg->MessageBody;
//
// build message
//
pch = pMsg->pCurrent;
pchstop = pMsg->pBufferEnd;
//
// zone section
//
pMsg->MessageHead.QuestionCount = 1;
pch = Dns_WriteDottedNameToPacket(
pch,
pchstop,
pszZone,
NULL,
0,
FALSE );
if ( !pch )
{
return( NULL );
}
*(UNALIGNED WORD *) pch = DNS_RTYPE_SOA;
pch += sizeof(WORD);
*(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET;
pch += sizeof(WORD);
//
// prerequisites -- no records
//
pMsg->MessageHead.AnswerCount = 0;
//
// update
// - delete A records at name
// - add new A records
//
// save offset to host name for future writes
nameOffset = (WORD)(pch - (PCHAR) &pMsg->MessageHead);
pch = Dns_WriteDottedNameToPacket(
pch,
pchstop,
pszName,
pszZone,
DNS_OFFSET_TO_QUESTION_NAME,
FALSE );
if ( !pch )
{
DNS_PRINT(( "ERROR writing dotted name to packet.\n" ));
return( NULL );
}
pch = Dns_WriteNoDataUpdateRecordToMessage(
pch,
pchstop,
DNS_CLASS_ALL, // delete all
DNS_TYPE_A // A records
);
DNS_ASSERT( pch );
//
// add A record for each address in array
// - use offset for name
// - write IP
for ( i=0; i<aipAddresses->AddrCount; i++ )
{
*(UNALIGNED WORD *) pch = htons( (WORD)(nameOffset|(WORD)0xC000) );
pch += sizeof( WORD );
pch = Dns_WriteDataUpdateRecordToMessage(
pch,
pchstop,
DNS_CLASS_INTERNET,
DNS_TYPE_A, // A records
dwTtl,
sizeof(IP_ADDRESS)
);
DNS_ASSERT( pch );
*(UNALIGNED DWORD *) pch = aipAddresses->AddrArray[i];
pch += sizeof(DWORD);
}
// total update sections RRs
// one delete RR, plus one for each new IP
pMsg->MessageHead.NameServerCount = (USHORT)(aipAddresses->AddrCount + 1);
//
// additional section - no records
//
pMsg->MessageHead.AdditionalCount = 0;
//
// reset current ptr -- need for send routine
//
pMsg->pCurrent = pch;
IF_DNSDBG( SEND )
{
DnsDbg_Message(
"UPDATE packet built",
pMsg );
}
return( pMsg );
}
#endif
PDNS_RECORD
Dns_HostUpdateRRSet(
IN LPSTR pszHostName,
IN PIP_ARRAY AddrArray,
IN DWORD dwTtl
)
/*++
Routine Description:
Create records for host update:
-- whack of all A records
-- add of all A records in new set
Arguments:
pszHostName -- host name, FULL FQDN
AddrArray -- new IPs of host
dwTtl -- TTL for records
Return Value:
Ptr to record list.
NULL on error.
--*/
{
DNS_RRSET rrset;
PDNS_RECORD prr;
DWORD i;
//
// create whack
//
prr = Dns_AllocateRecord( 0 );
if ( ! prr )
{
return( NULL );
}
prr->pName = pszHostName;
prr->wType = DNS_TYPE_A;
prr->Flags.S.Section = DNSREC_UPDATE;
prr->Flags.S.Delete = TRUE;
//
// create update record for each address
//
if ( !AddrArray )
{
return( prr );
}
DNS_RRSET_INIT( rrset );
DNS_RRSET_ADD( rrset, prr );
for ( i=0; i<AddrArray->AddrCount; i++ )
{
prr = Dns_AllocateRecord( sizeof(DNS_A_DATA) );
if ( ! prr )
{
Dns_RecordListFree( rrset.pFirstRR );
return( NULL );
}
prr->pName = pszHostName;
prr->wType = DNS_TYPE_A;
prr->Flags.S.Section = DNSREC_UPDATE;
prr->dwTtl = dwTtl;
prr->Data.A.IpAddress = AddrArray->AddrArray[i];
DNS_RRSET_ADD( rrset, prr );
}
// return ptr to first record in list
return( rrset.pFirstRR );
}
//
// DCR: dead code, remove
//
DNS_STATUS
Dns_UpdateHostAddrs(
IN LPSTR pszName,
IN PIP_ARRAY aipAddresses,
IN PIP_ARRAY aipServers,
IN DWORD dwTtl
)
/*++
Routine Description:
Updates client's A records registered with DNS server.
Arguments:
pszName -- name (FQDN) of client to update
aipAddresses -- counted array of new client IP addrs
aipServers -- counted array of DNS server IP addrs
dwTtl -- TTL for new A records
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
PDNS_RECORD prr;
DNS_STATUS status;
IF_DNSDBG( UPDATE )
{
DNS_PRINT((
"Enter Dns_UpdateHostAddrs()\n"
"\tpszName = %s\n"
"\tdwTtl = %d\n",
pszName,
dwTtl ));
DnsDbg_IpArray(
"\tHost IP array\n",
"\tHost",
aipAddresses );
DnsDbg_IpArray(
"\tNS IP array\n",
"\tNS",
aipServers );
}
//
// never let anyone set a TTL longer than 1 hour
//
// DCR: define policy on what our clients should use for TTL
// one hour is not bad
// could key off type of address
// RAS -- 15 minutes (as may be back up again quickly)
// DHCP -- one hour (may move)
// static -- one day (machines may be reconfigured)
//
if ( dwTtl > 3600 )
{
dwTtl = 3600;
}
//
// build update RR set
//
prr = Dns_HostUpdateRRSet(
pszName,
aipAddresses,
dwTtl );
if ( ! prr )
{
status = GetLastError();
DNS_ASSERT( status == DNS_ERROR_NO_MEMORY );
return status;
}
//
// do the update
//
status = Dns_UpdateLib(
prr,
0, // no flags
NULL, // no adapter list
NULL, // use default credentials
NULL // response not desired
);
Dns_RecordListFree( prr );
DNSDBG( UPDATE, (
"Leave Dns_UpdateHostAddrs() status=%d %s\n",
status,
Dns_StatusString(status) ));
return( status );
}
DNS_STATUS
Dns_UpdateLib(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN PDNS_NETINFO pNetworkInfo,
IN HANDLE hCreds OPTIONAL,
OUT PDNS_MSG_BUF * ppMsgRecv OPTIONAL
)
/*++
Routine Description:
Send DNS update.
Arguments:
pRecord -- list of records to send in update
dwFlags -- update flags; primarily security
pNetworkInfo -- adapter list with necessary info for update
- zone name
- primary name server name
- primary name server IP
hCreds -- credentials handle returned from
ppMsgRecv -- OPTIONAL addr to recv ptr to response message
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
PDNS_MSG_BUF pmsgSend = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
DNS_STATUS status = NO_ERROR;
WORD length;
PIP_ARRAY parrayServers = NULL;
LPSTR pszzone;
LPSTR pszserverName;
BOOL fsecure = FALSE;
BOOL fswitchToTcp = FALSE;
DNS_HEADER header;
PCHAR pCreds=NULL;
DNSDBG( UPDATE, (
"Dns_UpdateLib()\n"
"\tflags = %08x\n"
"\tpRecord = %p\n"
"\t\towner = %s\n",
dwFlags,
pRecord,
pRecord ? pRecord->pName : NULL ));
//
// if not a UPDATE compatibile adapter list -- no action
//
if ( ! NetInfo_IsForUpdate(pNetworkInfo) )
{
return( ERROR_INVALID_PARAMETER );
}
//
// suck info from adapter list
//
pszzone = NetInfo_UpdateZoneName( pNetworkInfo );
parrayServers = NetInfo_ConvertToIpArray( pNetworkInfo );
pszserverName = NetInfo_UpdateServerName( pNetworkInfo );
DNS_ASSERT( pszzone && parrayServers );
//
// build recv message buffer
// - must be big enough for TCP
//
pmsgRecv = Dns_AllocateMsgBuf( DNS_TCP_DEFAULT_PACKET_LENGTH );
if ( !pmsgRecv )
{
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
//
// build update packet
// note currently this function allocates TCP sized buffer if records
// given; if this changes need to alloc TCP buffer here
//
CLEAR_DNS_HEADER_FLAGS_AND_XID( &header );
header.Opcode = DNS_OPCODE_UPDATE;
pmsgSend = Dns_BuildPacket(
&header, // copy header
TRUE, // ... but not header counts
pszzone, // question zone\type SOA
DNS_TYPE_SOA,
pRecord,
0, // no other flags
TRUE // building an update packet
);
if ( !pmsgSend)
{
DNS_PRINT(( "ERROR: failed send buffer allocation.\n" ));
status = DNS_ERROR_NO_MEMORY;
goto Cleanup;
}
//
// try non-secure first unless explicitly secure only
//
fsecure = (dwFlags & DNS_UPDATE_SECURITY_ONLY);
if ( !fsecure )
{
status = Dns_SendAndRecv(
pmsgSend,
& pmsgRecv,
NULL, // no response records
dwFlags,
parrayServers,
pNetworkInfo );
if ( status == ERROR_SUCCESS )
{
status = Dns_MapRcodeToStatus( pmsgRecv->MessageHead.ResponseCode );
}
if ( status != DNS_ERROR_RCODE_REFUSED ||
dwFlags & DNS_UPDATE_SECURITY_OFF )
{
goto Cleanup;
}
DNSDBG( UPDATE, (
"Failed unsecure update, switching to secure!\n"
"\tcurrent time (ms) = %d\n",
GetCurrentTime() ));
fsecure = TRUE;
}
//
// security
// - must have server name
// - must start package
//
if ( fsecure )
{
if ( !pszserverName )
{
status = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
status = Dns_StartSecurity( FALSE );
if ( status != ERROR_SUCCESS )
{
goto Cleanup;
}
//
// DCR: hCreds doesn't return security context
// - idea of something beyond just standard security
// credentials was we'd been able to return the context
// handle
//
pCreds = Dns_GetApiContextCredentials(hCreds);
status = Dns_DoSecureUpdate(
pmsgSend,
pmsgRecv,
NULL,
dwFlags,
pNetworkInfo,
parrayServers,
pszserverName,
pCreds, // initialized in DnsAcquireContextHandle
NULL // default context name
);
if ( status == ERROR_SUCCESS )
{
status = Dns_MapRcodeToStatus( pmsgRecv->MessageHead.ResponseCode );
}
}
Cleanup:
// free server array sucked from adapter list
if ( parrayServers )
{
FREE_HEAP( parrayServers );
}
// return recv message buffer
if ( ppMsgRecv )
{
*ppMsgRecv = pmsgRecv;
}
else
{
FREE_HEAP( pmsgRecv );
}
FREE_HEAP( pmsgSend);
DNSDBG( UPDATE, (
"Dns_UpdateLib() completed, status = %d %s.\n\n",
status,
Dns_StatusString(status) ));
return( status );
}
DNS_STATUS
Dns_UpdateLibEx(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN PDNS_NAME pszZone,
IN PDNS_NAME pszServerName,
IN PIP_ARRAY aipServers,
IN HANDLE hCreds OPTIONAL,
OUT PDNS_MSG_BUF * ppMsgRecv OPTIONAL
)
/*++
Routine Description:
Send DNS update.
This routine builds an UPDATE compatible pNetworkInfo from the
information given. Then calls Dns_Update().
Arguments:
pRecord -- list of records to send in update
pszZone -- zone name for update
pszServerName -- server name
aipServers -- DNS servers to send update to
hCreds -- Optional Credentials info
ppMsgRecv -- addr for ptr to recv buffer, if desired
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
PDNS_NETINFO pnetInfo;
DNS_STATUS status = NO_ERROR;
DNSDBG( UPDATE, ( "Dns_UpdateLibEx()\n" ));
//
// convert params into UPDATE compatible adapter list
//
pnetInfo = NetInfo_CreateForUpdate(
pszZone,
pszServerName,
aipServers,
0 );
if ( !pnetInfo )
{
return( ERROR_INVALID_PARAMETER );
}
//
// call real update function
//
status = Dns_UpdateLib(
pRecord,
dwFlags,
pnetInfo,
hCreds,
ppMsgRecv );
NetInfo_Free( pnetInfo );
return status;
}
//
// Update credentials
//
//
// Credentials are an optional future parameter to allow the caller
// to set the context handle to that of a given NT account. This
// structure will most likely be the following as defined in rpcdce.h:
//
// #define SEC_WINNT_AUTH_IDENTITY_ANSI 0x1
//
// typedef struct _SEC_WINNT_AUTH_IDENTITY_A {
// unsigned char __RPC_FAR *User;
// unsigned long UserLength;
// unsigned char __RPC_FAR *Domain;
// unsigned long DomainLength;
// unsigned char __RPC_FAR *Password;
// unsigned long PasswordLength;
// unsigned long Flags;
// } SEC_WINNT_AUTH_IDENTITY_A, *PSEC_WINNT_AUTH_IDENTITY_A;
//
// #define SEC_WINNT_AUTH_IDENTITY_UNICODE 0x2
//
// typedef struct _SEC_WINNT_AUTH_IDENTITY_W {
// unsigned short __RPC_FAR *User;
// unsigned long UserLength;
// unsigned short __RPC_FAR *Domain;
// unsigned long DomainLength;
// unsigned short __RPC_FAR *Password;
// unsigned long PasswordLength;
// unsigned long Flags;
// } SEC_WINNT_AUTH_IDENTITY_W, *PSEC_WINNT_AUTH_IDENTITY_W;
//
DNS_STATUS
WINAPI
DnsAcquireContextHandle_W(
IN DWORD CredentialFlags,
IN PVOID Credentials OPTIONAL,
OUT PHANDLE pContext
)
/*++
Routine Description:
Get credentials handle to security context for update.
The handle can for the default process credentials (user account or
system machine account) or for a specified set of credentials
identified by Credentials.
Arguments:
CredentialFlags -- flags
Credentials -- a PSEC_WINNT_AUTH_IDENTITY_W
(explicit definition skipped to avoid requiring rpcdec.h)
pContext -- addr to receive credentials handle
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on failure.
--*/
{
if ( ! pContext )
{
return ERROR_INVALID_PARAMETER;
}
*pContext = Dns_CreateAPIContext(
CredentialFlags,
Credentials,
TRUE // unicode
);
if ( ! *pContext )
{
return DNS_ERROR_NO_MEMORY;
}
else
{
return NO_ERROR;
}
}
DNS_STATUS
WINAPI
DnsAcquireContextHandle_A(
IN DWORD CredentialFlags,
IN PVOID Credentials OPTIONAL,
OUT PHANDLE pContext
)
/*++
Routine Description:
Get credentials handle to security context for update.
The handle can for the default process credentials (user account or
system machine account) or for a specified set of credentials
identified by Credentials.
Arguments:
CredentialFlags -- flags
Credentials -- a PSEC_WINNT_AUTH_IDENTITY_A
(explicit definition skipped to avoid requiring rpcdec.h)
pContext -- addr to receive credentials handle
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on failure.
--*/
{
if ( ! pContext )
{
return ERROR_INVALID_PARAMETER;
}
*pContext = Dns_CreateAPIContext(
CredentialFlags,
Credentials,
FALSE );
if ( ! *pContext )
{
return DNS_ERROR_NO_MEMORY;
}
else
{
return NO_ERROR;
}
}
VOID
WINAPI
DnsReleaseContextHandle(
IN HANDLE ContextHandle
)
/*++
Routine Description:
Frees context handle created by DnsAcquireContextHandle_X() routines.
Arguments:
ContextHandle - Handle to be closed.
Return Value:
None.
--*/
{
if ( ContextHandle )
{
//
// free any cached security context handles
//
// DCR_FIX0: should delete all contexts associated with this
// context (credentials handle) not all
//
// DCR: to be robust, user "ContextHandle" should be ref counted
// it should be set one on create; when in use, incremented
// then dec when done; then this Free could not collide with
// another thread's use
//
//Dns_TimeoutSecurityContextListEx( TRUE, ContextHandle );
Dns_TimeoutSecurityContextList( TRUE );
Dns_FreeAPIContext( ContextHandle );
}
}
//
// Utilities
//
DWORD
prepareUpdateRecordSet(
IN OUT PDNS_RECORD pRRSet,
IN BOOL fClearTtl,
IN BOOL fSetFlags,
IN WORD wFlags
)
/*++
Routine Description:
Validate and prepare record set for update.
- record set is single RR set
- sets record flags for update
Arguments:
pRRSet -- record set; MUST be in UTF8
note: pRRSet is not touched (not OUT param)
IF fClearTtl AND fSetFlags are both FALSE
fClearTtl -- clear TTL in records; TRUE for delete set
fSetFlags -- set section and delete flags
wFlags -- flags field to set
(should contain desired section and delete flags)
Return Value:
ERROR_SUCCESS if successful.
ERROR_INVALID_PARAMETER if record set is not acceptable.
--*/
{
PDNS_RECORD prr;
PSTR pname;
WORD type;
DNSDBG( TRACE, ( "prepareUpdateRecordSet()\n" ));
// validate
if ( !pRRSet )
{
return ERROR_INVALID_PARAMETER;
}
type = pRRSet->wType;
//
// note: could do an "update-type" check here, but that just
// A) burns unnecessary memory and cycles
// B) makes it harder to test bogus records sent in updates
// to the server
//
pname = (PSTR) pRRSet->pName;
if ( !pname )
{
return ERROR_INVALID_PARAMETER;
}
//
// check each RR in set
// - validate RR is in set
// - set RR flags
//
prr = pRRSet;
while ( prr )
{
if ( fSetFlags )
{
prr->Flags.S.Section = 0;
prr->Flags.S.Delete = 0;
prr->Flags.DW |= wFlags;
}
if ( fClearTtl )
{
prr->dwTtl = 0;
}
// check current RR in set
// - matches name and type
if ( prr != pRRSet )
{
if ( prr->wType != type ||
! prr->pName ||
! Dns_NameCompare_UTF8( pname, prr->pName ) )
{
return ERROR_INVALID_PARAMETER;
}
}
prr = prr->pNext;
}
return ERROR_SUCCESS;
}
PDNS_RECORD
DnsBuildUpdateSet(
IN OUT PDNS_RECORD pPrereqSet,
IN OUT PDNS_RECORD pAddSet,
IN OUT PDNS_RECORD pDeleteSet
)
/*++
Routine Description:
Build combined record list for update.
Combines prereq, delete and add records.
Note: record sets MUST be in UTF8.
Arguments:
pPrereqSet -- prerequisite records; note this does NOT
include delete preqs (see note below)
pAddSet -- records to add
pDeleteSet -- records to delete
Return Value:
Ptr to combined record list for update.
--*/
{
PDNS_RECORD plast = NULL;
PDNS_RECORD pfirst = NULL;
DNSDBG( TRACE, ( "DnsBuildUpdateSet()\n" ));
//
// append prereq set
//
// DCR: doesn't handle delete prereqs
// this is fine because we roll our own, but if
// later expand the function, then need them
//
// note, I could add flag==PREREQ datalength==0
// test in prepareUpdateRecordSet() function, then
// set Delete flag; however, we'd still have the
// question of how to distinguish existence (class==ANY)
// prereq from delete (class==NONE) prereq -- without
// directly exposing the record Delete flag
//
if ( pPrereqSet )
{
plast = pPrereqSet;
pfirst = pPrereqSet;
prepareUpdateRecordSet(
pPrereqSet,
FALSE, // no TTL clear
TRUE, // set flags
DNSREC_PREREQ // prereq section
);
while ( plast->pNext )
{
plast = plast->pNext;
}
}
//
// append delete records
// do before Add records so that delete\add of same record
// leaves it in place
//
if ( pDeleteSet )
{
if ( !plast )
{
plast = pDeleteSet;
pfirst = pDeleteSet;
}
else
{
plast->pNext = pDeleteSet;
}
prepareUpdateRecordSet(
pDeleteSet,
TRUE, // clear TTL
TRUE, // set flags
DNSREC_UPDATE | DNSREC_DELETE // update section, delete bit
);
while ( plast->pNext )
{
plast = plast->pNext;
}
}
//
// append add records
//
if ( pAddSet )
{
if ( !plast )
{
plast = pAddSet;
pfirst = pAddSet;
}
else
{
plast->pNext = pAddSet;
}
prepareUpdateRecordSet(
pAddSet,
FALSE, // no TTL change
TRUE, // set flags
DNSREC_UPDATE // update section
);
}
return pfirst;
}
BOOL
IsPtrUpdate(
IN PDNS_RECORD pRecordList
)
/*++
Routine Description:
Check if update is PTR update.
Arguments:
pRecordList -- update record list
Return Value:
TRUE if PTR update.
FALSE otherwise.
--*/
{
PDNS_RECORD prr = pRecordList;
BOOL bptrUpdate = FALSE;
//
// find, then test first record in update section
//
while ( prr )
{
if ( prr->Flags.S.Section == DNSREC_UPDATE )
{
if ( prr->wType == DNS_TYPE_PTR )
{
bptrUpdate = TRUE;
}
break;
}
prr = prr->pNext;
}
return bptrUpdate;
}
//
// Replace functions
//
DNS_STATUS
WINAPI
replaceRecordsInSetPrivate(
IN PDNS_RECORD pReplaceSet,
IN DWORD Options,
IN HANDLE hCredentials, OPTIONAL
IN PIP_ARRAY pServerList, OPTIONAL
IN PVOID pReserved,
IN DNS_CHARSET CharSet
)
/*++
Routine Description:
Replace record set routine handling all character sets.
Arguments:
pReplaceSet - replacement record set
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
CharSet - character set of incoming records
Return Value:
ERROR_SUCCESS if update successful.
ErrorCode from server if server rejects update.
ERROR_INVALID_PARAMETER if bad param.
--*/
{
DNS_STATUS status;
PDNS_RECORD preplaceCopy = NULL;
PDNS_RECORD pupdateList = NULL;
BOOL btypeDelete;
DNS_RECORD rrNoCname;
DNS_RECORD rrDeleteType;
BOOL fcnameUpdate;
DNSDBG( TRACE, (
"replaceRecordsInSetPrivate()\n"
"\tpReplaceSet = %p\n"
"\tOptions = %08x\n"
"\thCredentials = %p\n"
"\tpServerList = %p\n"
"\tCharSet = %d\n",
pReplaceSet,
Options,
hCredentials,
pServerList,
CharSet
));
//
// read update config
//
Reg_RefreshUpdateConfig();
//
// make local copy in UTF8
//
if ( !pReplaceSet )
{
return ERROR_INVALID_PARAMETER;
}
preplaceCopy = Dns_RecordSetCopyEx(
pReplaceSet,
CharSet,
DnsCharSetUtf8 );
if ( !preplaceCopy )
{
return ERROR_INVALID_PARAMETER;
}
//
// validate arguments
// - must have single RR set
// - mark them for update
//
status = prepareUpdateRecordSet(
preplaceCopy,
FALSE, // no TTL clear
TRUE, // set flags
DNSREC_UPDATE // flag as update
);
if ( status != ERROR_SUCCESS )
{
status = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// check if simple type delete
//
btypeDelete = ( preplaceCopy->wDataLength == 0 &&
preplaceCopy->pNext == NULL );
//
// set security for update
//
if ( UseSystemDefaultForSecurity( Options ) )
{
Options |= g_UpdateSecurityLevel;
}
if ( hCredentials )
{
Options |= DNS_UPDATE_CACHE_SECURITY_CONTEXT;
}
//
// type delete record
//
// if have replace records -- this goes in front
// if type delete -- then ONLY need this record
//
RtlZeroMemory( &rrDeleteType, sizeof(DNS_RECORD) );
rrDeleteType.pName = (PDNS_NAME) preplaceCopy->pName;
rrDeleteType.wType = preplaceCopy->wType;
rrDeleteType.wDataLength = 0;
rrDeleteType.Flags.DW = DNSREC_UPDATE | DNSREC_DELETE;
if ( btypeDelete )
{
rrDeleteType.pNext = NULL;
}
else
{
rrDeleteType.pNext = preplaceCopy;
}
pupdateList = &rrDeleteType;
//
// CNAME does not exist precondition record
// - for all updates EXCEPT CNAME
//
fcnameUpdate = ( preplaceCopy->wType == DNS_TYPE_CNAME );
if ( !fcnameUpdate )
{
RtlZeroMemory( &rrNoCname, sizeof(DNS_RECORD) );
rrNoCname.pName = (PDNS_NAME) preplaceCopy->pName;
rrNoCname.wType = DNS_TYPE_CNAME;
rrNoCname.wDataLength = 0;
rrNoCname.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
rrNoCname.pNext = &rrDeleteType;
pupdateList = &rrNoCname;
}
//
// do the update
//
status = DoQuickUpdate(
pupdateList,
Options,
FALSE,
pServerList,
hCredentials);
if ( status == NO_ERROR )
{
DnsFlushResolverCacheEntry_UTF8( (LPSTR) preplaceCopy->pName );
}
//
// CNAME collision test
//
// if replacing CNAME may have gotten silent ignore
// - first check if successfully replaced CNAME
// - if still not sure, check that no other records
// at name -- if NON-CNAME found then treat silent ignore
// as YXRRSET error
//
if ( fcnameUpdate &&
! btypeDelete &&
status == NO_ERROR )
{
PDNS_RECORD pqueryRR = NULL;
BOOL fsuccess = FALSE;
// DCR: need to query update server list here to
// avoid intermediate caching
status = DnsQuery_UTF8(
preplaceCopy->pName,
DNS_TYPE_CNAME,
DNS_QUERY_BYPASS_CACHE,
pServerList,
& pqueryRR,
NULL );
if ( status == NO_ERROR &&
Dns_RecordCompare(
preplaceCopy,
pqueryRR ) )
{
fsuccess = TRUE;
}
Dns_RecordListFree( pqueryRR );
if ( fsuccess )
{
goto Cleanup;
}
// query for any type at CNAME
// if found then assume we got a silent update
// success
status = DnsQuery_UTF8(
preplaceCopy->pName,
DNS_TYPE_ALL,
DNS_QUERY_BYPASS_CACHE,
pServerList,
& pqueryRR,
NULL );
if ( status == ERROR_SUCCESS )
{
PDNS_RECORD prr = pqueryRR;
while ( prr )
{
if ( pReplaceSet->wType != prr->wType &&
Dns_NameCompare_UTF8(
preplaceCopy->pName,
prr->pName ) )
{
status = DNS_ERROR_RCODE_YXRRSET;
break;
}
prr = prr->pNext;
}
}
else
{
status = ERROR_SUCCESS;
}
Dns_RecordListFree( pqueryRR );
}
Cleanup:
Dns_RecordListFree( preplaceCopy );
return status;
}
DNS_STATUS
WINAPI
DnsReplaceRecordSetUTF8(
IN PDNS_RECORD pReplaceSet,
IN DWORD Options,
IN HANDLE hCredentials OPTIONAL,
IN PIP_ARRAY aipServers OPTIONAL,
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to replace record set on DNS server.
Arguments:
pReplaceSet - new record set for name and type
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given securit5y credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
None.
--*/
{
DNSDBG( TRACE, ( "DnsReplaceRecordSetUTF8()\n" ));
return replaceRecordsInSetPrivate(
pReplaceSet,
Options,
hCredentials,
aipServers,
pReserved,
DnsCharSetUtf8
);
}
DNS_STATUS
WINAPI
DnsReplaceRecordSetW(
IN PDNS_RECORD pReplaceSet,
IN DWORD Options,
IN HANDLE hCredentials OPTIONAL,
IN PIP_ARRAY aipServers OPTIONAL,
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to replace record set on DNS server.
Arguments:
pReplaceSet - new record set for name and type
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
None.
--*/
{
DNSDBG( TRACE, ( "DnsReplaceRecordSetW()\n" ));
return replaceRecordsInSetPrivate(
pReplaceSet,
Options,
hCredentials,
aipServers,
pReserved,
DnsCharSetUnicode
);
}
DNS_STATUS
WINAPI
DnsReplaceRecordSetA(
IN PDNS_RECORD pReplaceSet,
IN DWORD Options,
IN HANDLE hCredentials OPTIONAL,
IN PIP_ARRAY aipServers OPTIONAL,
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to replace record set on DNS server.
Arguments:
pReplaceSet - new record set for name and type
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
None.
--*/
{
DNSDBG( TRACE, ( "DnsReplaceRecordSetA()\n" ));
return replaceRecordsInSetPrivate(
pReplaceSet,
Options,
hCredentials,
aipServers,
pReserved,
DnsCharSetAnsi
);
}
//
// Modify functions
//
DNS_STATUS
WINAPI
modifyRecordsInSetPrivate(
IN PDNS_RECORD pAddRecords,
IN PDNS_RECORD pDeleteRecords,
IN DWORD Options,
IN HANDLE hCredentials, OPTIONAL
IN PIP_ARRAY pServerList, OPTIONAL
IN PVOID pReserved,
IN DNS_CHARSET CharSet
)
/*++
Routine Description:
Dynamic update routine to replace record set on DNS server.
Arguments:
pAddRecords - records to register on server
pDeleteRecords - records to remove from server
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
CharSet - character set of incoming records
Return Value:
ERROR_SUCCESS if update successful.
ErrorCode from server if server rejects update.
ERROR_INVALID_PARAMETER if bad param.
--*/
{
DNS_STATUS status;
PDNS_RECORD paddCopy = NULL;
PDNS_RECORD pdeleteCopy = NULL;
PDNS_RECORD pupdateSet = NULL;
DNSDBG( TRACE, (
"modifyRecordsInSetPrivate()\n"
"\tpAddSet = %p\n"
"\tpDeleteSet = %p\n"
"\tOptions = %08x\n"
"\thCredentials = %p\n"
"\tpServerList = %p\n"
"\tCharSet = %d\n",
pAddRecords,
pDeleteRecords,
Options,
hCredentials,
pServerList,
CharSet
));
//
// read update config
//
Reg_RefreshUpdateConfig();
//
// make local copy in UTF8
//
if ( pAddRecords )
{
paddCopy = Dns_RecordSetCopyEx(
pAddRecords,
CharSet,
DnsCharSetUtf8 );
}
if ( pDeleteRecords )
{
pdeleteCopy = Dns_RecordSetCopyEx(
pDeleteRecords,
CharSet,
DnsCharSetUtf8 );
}
//
// validate arguments
// - add and delete must be for single RR set
// and must be for same RR set
//
if ( !paddCopy && !pdeleteCopy )
{
return ERROR_INVALID_PARAMETER;
}
if ( paddCopy )
{
status = prepareUpdateRecordSet(
paddCopy,
FALSE, // no TTL clear
FALSE, // no flag clear
0 // no flags to set
);
if ( status != ERROR_SUCCESS )
{
status = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
if ( pdeleteCopy )
{
status = prepareUpdateRecordSet(
pdeleteCopy,
FALSE, // no TTL clear
FALSE, // no flag clear
0 // no flags to set
);
if ( status != ERROR_SUCCESS )
{
status = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
if ( paddCopy &&
pdeleteCopy &&
! Dns_NameCompare_UTF8( paddCopy->pName, pdeleteCopy->pName ) )
{
status = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// set security for update
//
if ( UseSystemDefaultForSecurity( Options ) )
{
Options |= g_UpdateSecurityLevel;
}
if ( hCredentials )
{
Options |= DNS_UPDATE_CACHE_SECURITY_CONTEXT;
}
//
// create update RRs
// - no prereqs
// - delete RRs set for delete
// - add RRs appended
//
pupdateSet = DnsBuildUpdateSet(
NULL, // no precons
paddCopy,
pdeleteCopy );
//
// do the update
//
status = DoQuickUpdate(
pupdateSet,
Options,
FALSE,
pServerList,
hCredentials );
//
// flush cache entry for update
//
if ( status == ERROR_SUCCESS )
{
DnsFlushResolverCacheEntry_UTF8( (LPSTR) pupdateSet->pName );
}
//
// cleanup local copy
//
Dns_RecordListFree( pupdateSet );
DNSDBG( TRACE, ( "Leave modifyRecordsInSetPrivate()\n" ));
return status;
Cleanup:
//
// cleanup copies on failure before combined list
//
Dns_RecordListFree( paddCopy );
Dns_RecordListFree( pdeleteCopy );
DNSDBG( TRACE, ( "Leave modifyRecordsInSetPrivate()\n" ));
return status;
}
DNS_STATUS
WINAPI
DnsModifyRecordsInSet_W(
IN PDNS_RECORD pAddRecords,
IN PDNS_RECORD pDeleteRecords,
IN DWORD Options,
IN HANDLE hCredentials, OPTIONAL
IN PIP_ARRAY pServerList, OPTIONAL
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to modify record set on DNS server.
Arguments:
pAddRecords - records to register on server
pDeleteRecords - records to remove from server
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
ERROR_SUCCESS if update successful.
ErrorCode from server if server rejects update.
ERROR_INVALID_PARAMETER if bad param.
--*/
{
return modifyRecordsInSetPrivate(
pAddRecords,
pDeleteRecords,
Options,
hCredentials,
pServerList,
pReserved,
DnsCharSetUnicode
);
}
DNS_STATUS
WINAPI
DnsModifyRecordsInSet_A(
IN PDNS_RECORD pAddRecords,
IN PDNS_RECORD pDeleteRecords,
IN DWORD Options,
IN HANDLE hCredentials, OPTIONAL
IN PIP_ARRAY pServerList, OPTIONAL
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to modify record set on DNS server.
Arguments:
pAddRecords - records to register on server
pDeleteRecords - records to remove from server
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
ERROR_SUCCESS if update successful.
ErrorCode from server if server rejects update.
ERROR_INVALID_PARAMETER if bad param.
--*/
{
return modifyRecordsInSetPrivate(
pAddRecords,
pDeleteRecords,
Options,
hCredentials,
pServerList,
pReserved,
DnsCharSetAnsi
);
}
DNS_STATUS
WINAPI
DnsModifyRecordsInSet_UTF8(
IN PDNS_RECORD pAddRecords,
IN PDNS_RECORD pDeleteRecords,
IN DWORD Options,
IN HANDLE hCredentials, OPTIONAL
IN PIP_ARRAY pServerList, OPTIONAL
IN PVOID pReserved
)
/*++
Routine Description:
Dynamic update routine to modify record set on DNS server.
Arguments:
pAddRecords - records to register on server
pDeleteRecords - records to remove from server
Options - update options
pServerList - list of DNS servers to go to; if not given, machines
default servers are queried to find correct servers to send update to
hCredentials - handle to credentials to be used for update; optional,
if not given security credentials of this process are used in update
pReserved - reserved; should be NULL
Return Value:
ERROR_SUCCESS if update successful.
ErrorCode from server if server rejects update.
ERROR_INVALID_PARAMETER if bad param.
--*/
{
return modifyRecordsInSetPrivate(
pAddRecords,
pDeleteRecords,
Options,
hCredentials,
pServerList,
pReserved,
DnsCharSetUtf8
);
}
//
// Full scale update API
//
DNS_STATUS
DnsUpdate(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN PDNS_NETINFO pNetworkInfo,
IN HANDLE hCreds, OPTIONAL
OUT PDNS_MSG_BUF * ppMsgRecv OPTIONAL
)
/*++
Routine Description:
Send DNS update.
Note if pNetworkInfo is not specified or not a valid UPDATE adapter list,
then a FindAuthoritativeZones (FAZ) query is done prior to the update.
Arguments:
pRecord -- list of records to send in update
dwFlags -- flags to update
pNetworkInfo -- DNS servers to send update to
ppMsgRecv -- addr for ptr to recv buffer, if desired
Return Value:
ERROR_SUCCESS if successful.
Error status on failure.
--*/
{
DNS_STATUS status;
PDNS_NETINFO plocalNetworkInfo = NULL;
DNSDBG( TRACE, ( "DnsUpdate()\n" ));
//
// read update config
//
Reg_RefreshUpdateConfig();
//
// need to build update adapter list from FAZ
// - only pass on BYPASS_CACHE flag
// - note DnsFindAuthoritativeZone() will append
// DNS_QUERY_ALLOW_EMPTY_AUTH_RESP flag yet
// DnsQuery_UTF8 will die on that flag without
// BYPASS_CACHE also set, so just set BYPASS_CACHE
// flag here;
// bogus, but that's how it is now
//
if ( ! NetInfo_IsForUpdate(pNetworkInfo) )
{
status = DnsFindAuthoritativeZone(
pRecord->pName,
//(dwFlags & DNS_QUERY_BYPASS_CACHE),
DNS_QUERY_BYPASS_CACHE,
NULL, // no specified servers
& plocalNetworkInfo );
if ( status != ERROR_SUCCESS )
{
return( status );
}
pNetworkInfo = plocalNetworkInfo;
}
//
// call the real update routine in dnslib
//
status = Dns_UpdateLib(
pRecord,
dwFlags,
pNetworkInfo,
hCreds,
ppMsgRecv );
// if there was an error sending the update, flush the resolver
// cache entry for the zone name to possibly pick up an alternate
// DNS server for the next retry attempt of a similar update.
//
// DCR_QUESTION: is this the correct error code?
// maybe ERROR_TIMED_OUT?
//
if ( status == DNS_ERROR_RECORD_TIMED_OUT )
{
PSTR pzoneName;
if ( pNetworkInfo &&
(pzoneName = NetInfo_UpdateZoneName( pNetworkInfo )) )
{
DnsFlushResolverCacheEntry_UTF8( pzoneName );
DnsFlushResolverCacheEntry_UTF8( pRecord->pName );
}
}
// cleanup local adapter list if used
if ( plocalNetworkInfo )
{
NetInfo_Free( plocalNetworkInfo );
}
return( status );
}
//
// Private update functions
//
// These calls are used only inside dnsapi.dll to do the client
// host updates. They are exposed in dnsapi.dll only for test
// purposes.
//
//
// DCR: eliminate nonsense "unique modify" routines -- they add no value
// moral -- don't let your PM talk to your developers
//
// DCR: get rid of old GlennC update routines
//
DNS_STATUS
DnsRegisterRRSet_Ex (
IN OUT PDNS_RECORD pRegisterSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL,
IN HANDLE hCreds OPTIONAL
);
DNS_STATUS
DnsModifyRRSet_Ex (
IN PDNS_RECORD pCurrentSet,
IN PDNS_RECORD pNewSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL,
IN HANDLE hCreds OPTIONAL
)
/*++
Routine Description:
Modify record set of known existing records.
Note: this is not my idea of "Modify". It is really "Register"
where you specify both what you think the existing set is and
what you want it to be.
Like most of these routines -- it's a big waste of time.
Arguments:
None.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_RECORD pCopyOfCurrentSet = NULL;
PDNS_RECORD pCopyOfNewSet = NULL;
PDNS_RECORD pAddSet = NULL;
PDNS_RECORD pDeleteSet = NULL;
PDNS_RECORD pUpdateSet = NULL;
PDNS_NETINFO pNetworkInfo = NULL;
DNSDBG( TRACE, ( "DnsModifyRRSet_Ex()\n" ));
IF_DNSDBG( UPDATE )
{
DnsDbg_RecordSet(
"DnsModifyRRSet_Ex() -- current set",
pCurrentSet );
DnsDbg_RecordSet(
"DnsModifyRRSet_Ex() -- new set",
pNewSet );
}
//
// Validate arguments ...
//
if ( ( fOptions != DNS_UPDATE_UNIQUE ) &&
( fOptions & DNS_UNACCEPTABLE_UPDATE_OPTIONS ) )
{
return ERROR_INVALID_PARAMETER;
}
if ( prepareUpdateRecordSet( pCurrentSet,
FALSE,
FALSE,
0 ) != NO_ERROR )
{
return ERROR_INVALID_PARAMETER;
}
if ( prepareUpdateRecordSet( pNewSet,
FALSE,
FALSE,
0 ) != NO_ERROR )
{
return ERROR_INVALID_PARAMETER;
}
if ( !Dns_NameCompare_UTF8( pCurrentSet->pName, pNewSet->pName ) )
{
return ERROR_INVALID_PARAMETER;
}
//
// Make a copy of the RRSets, to preserve the original RRSets ...
//
pCopyOfCurrentSet = Dns_RecordSetCopyEx( pCurrentSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfCurrentSet )
{
status = DNS_ERROR_NO_MEMORY;
goto Exit;
}
pCopyOfNewSet = Dns_RecordSetCopyEx( pNewSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfNewSet )
{
status = DNS_ERROR_NO_MEMORY;
goto Exit;
}
//
// pAddSet = pCopyOfNewSet - pCopyOfCurrentSet
// pDeleteSet = pCopyOfCurrentSet - pCopyOfNewSet
//
//
// no change from previous registration?
// - touch DNS server to make sure it's in sync
//
if ( Dns_RecordSetCompare( pCopyOfNewSet,
pCopyOfCurrentSet,
&pAddSet,
&pDeleteSet ) )
{
status = DnsRegisterRRSet_Ex(
pCopyOfNewSet,
fOptions,
aipServers,
hCreds );
goto Exit;
}
//
// shared update - do simple modify
// - delete what was registered before, that don't want now
// - add everything in new set for robustness
//
if ( fOptions & DNS_UPDATE_SHARED )
{
//
// Update with the following:
// Prerequisites: Nothing
// Add: pCopyOfNewSet
// Delete: pDeleteSet
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( NULL,
pCopyOfNewSet,
pDeleteSet );
//
// Call update ...
//
status = DoQuickUpdate( pUpdateSet,
fOptions,
FALSE,
aipServers,
hCreds);
//
// Free memory used ...
//
Dns_RecordListFree( pUpdateSet );
pUpdateSet = NULL;
pCopyOfNewSet = NULL;
pDeleteSet = NULL;
goto Exit;
}
//
// Unique case
//
//
// Update with the following:
// Prerequisites: pCopyOfCurrentSet
// Add: pAddSet
// Delete: pDeleteSet
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( pCopyOfCurrentSet,
pAddSet,
pDeleteSet );
//
// Call update ...
//
status = DoQuickUpdate( pUpdateSet,
fOptions,
FALSE,
aipServers,
hCreds);
//
// Free memory used ...
//
Dns_RecordListFree( pUpdateSet );
pUpdateSet = NULL;
pCopyOfCurrentSet = NULL;
pAddSet = NULL;
pDeleteSet = NULL;
if ( status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
PDNS_RECORD pActualCurrentSet = NULL;
PDNS_RECORD pSharedSet = NULL;
PDNS_RECORD pNotUsedSet = NULL;
DNS_RECORD Record;
IP_ARRAY ipArray;
IP_ADDRESS serverIp = DnsGetLastServerUpdateIP();
if ( serverIp )
{
ipArray.AddrCount = 1;
ipArray.AddrArray[0] = serverIp;
}
pNetworkInfo = NetInfo_CreateFromIpArray(
aipServers,
serverIp,
FALSE, // no search info
NULL );
//
// Make another copy of the Current RRSet ...
//
pCopyOfCurrentSet = Dns_RecordSetCopyEx(
pCurrentSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfCurrentSet )
{
status = DNS_ERROR_NO_MEMORY;
goto Exit;
}
//
// Query server for pActualCurrentSet ...
//
status = QueryDirectEx(
NULL, // no response message
& pActualCurrentSet,
NULL, // no header
0, // no header counts
(LPSTR) pCopyOfCurrentSet->pName,
pCopyOfCurrentSet->wType,
NULL, // no input records
DNS_QUERY_TREAT_AS_FQDN,
NULL, // no DNS server list
pNetworkInfo );
if ( IsEmptyDnsResponse( pActualCurrentSet ) )
{
Dns_RecordListFree( pActualCurrentSet );
pActualCurrentSet = NULL;
if ( status == NO_ERROR )
{
status = DNS_ERROR_RCODE_NAME_ERROR;
}
}
if ( status == DNS_ERROR_RCODE_NAME_ERROR ||
status == DNS_INFO_NO_RECORDS )
{
DNS_RECORD Record;
//
// Query failed to find any entry for given name and type.
// We now need to assume that the pCurrentSet is that there
// isn't anything registered.
//
RtlZeroMemory( &Record, sizeof(DNS_RECORD) );
Record.pName = (PDNS_NAME) pCopyOfNewSet->pName;
Record.wType = pCopyOfNewSet->wType;
Record.wDataLength = 0; // Meaning all structures not present.
Record.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// Do an update with the following:
// Prerequisites: None - will be Record above.
// Add: pRegisterSet
// Delete: None
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( NULL,
pCopyOfNewSet,
NULL );
Record.pNext = pCopyOfNewSet;
status = DoQuickUpdate( &Record,
fOptions,
FALSE,
serverIp ? &ipArray : aipServers,
hCreds);
if ( status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
status = DNS_ERROR_TRY_AGAIN_LATER;
}
goto Exit;
}
if ( status )
{
goto Exit;
}
//
// Sometimes when DnsQuery is called, the returned record set
// contains additional records of different types than what
// was queried for. Need to strip off the additional records
// from the query results.
//
pNotUsedSet = DnsRecordSetDetach( pActualCurrentSet );
if ( pNotUsedSet )
{
Dns_RecordListFree( pNotUsedSet );
pNotUsedSet = NULL;
}
//
// pAddSet = pNewSet - pActualCurrentSet
// pDeleteSet = pActualCurrentSet - pNewSet
//
(void) Dns_RecordSetCompare( pCopyOfNewSet,
pActualCurrentSet,
&pAddSet,
&pDeleteSet );
//
// pSharedSet = pDeleteSet - pCopyOfCurrentSet
// pNotUsedSet = pCopyOfCurrentSet - pDeleteSet
//
(void) Dns_RecordSetCompare( pDeleteSet,
pCopyOfCurrentSet,
&pSharedSet,
&pNotUsedSet );
Dns_RecordListFree( pDeleteSet );
pDeleteSet = NULL;
Dns_RecordListFree( pCopyOfCurrentSet );
pCopyOfCurrentSet = NULL;
Dns_RecordListFree( pNotUsedSet );
pNotUsedSet = NULL;
Dns_RecordListFree( pActualCurrentSet );
pActualCurrentSet = NULL;
//
// unaccounted for records indicate other updater of name
//
// records we don't want (delete set) and did NOT previously
// register mean some other user is adding records
//
if ( pSharedSet )
{
Dns_RecordListFree( pSharedSet );
pSharedSet = NULL;
status = DNS_ERROR_NOT_UNIQUE;
goto Exit;
}
RtlZeroMemory( &Record, sizeof(DNS_RECORD) );
Record.pName = (PDNS_NAME) pCopyOfNewSet->pName;
Record.wType = pCopyOfNewSet->wType;
Record.wDataLength = 0; // Meaning all data.
Record.Flags.DW = DNSREC_UPDATE | DNSREC_DELETE;
//
// Do an update with the following:
// Prerequisites: None
// Add: pCopyOfNewSet
// Delete: All
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( NULL,
pCopyOfNewSet,
NULL );
Record.pNext = pCopyOfNewSet;
status = DoQuickUpdate( &Record,
fOptions,
FALSE,
serverIp ? &ipArray : aipServers,
hCreds);
goto Exit;
}
Exit :
if ( pCopyOfCurrentSet )
{
Dns_RecordListFree( pCopyOfCurrentSet );
}
if ( pCopyOfNewSet )
{
Dns_RecordListFree( pCopyOfNewSet );
}
if ( pAddSet )
{
Dns_RecordListFree( pAddSet );
}
if ( pDeleteSet )
{
Dns_RecordListFree( pDeleteSet );
}
if ( pNetworkInfo )
{
NetInfo_Free( pNetworkInfo );
}
DNSDBG( TRACE, ( "Leave DnsModifyRRSet_Ex()\n" ));
return status;
}
DNS_STATUS
DnsRegisterRRSet_Ex(
IN OUT PDNS_RECORD pRegisterSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL,
IN HANDLE hCreds OPTIONAL
)
/*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_RECORD pCurrentSet = NULL;
PDNS_RECORD pUpdateSet = NULL;
PDNS_NETINFO pNetworkInfo = NULL;
DNS_RECORD Record;
DNS_RECORD NoCName;
IP_ARRAY ipArray;
IP_ADDRESS serverIp = 0;
DNSDBG( TRACE, ( "DnsRegisterRRSet_Ex()\n" ));
IF_DNSDBG( UPDATE )
{
DnsDbg_RecordSet(
"DnsRegisterRRSet_Ex()",
pRegisterSet );
}
//
// Validate arguments ...
//
if ( ( fOptions != DNS_UPDATE_UNIQUE ) &&
( fOptions & DNS_UNACCEPTABLE_UPDATE_OPTIONS ) )
{
return ERROR_INVALID_PARAMETER;
}
//
// Are all the RRSet structures of the same type and name?
// Mark them all as Updates (add) ...
//
if ( prepareUpdateRecordSet(
pRegisterSet,
FALSE,
TRUE,
DNSREC_UPDATE ) != NO_ERROR )
{
return ERROR_INVALID_PARAMETER;
}
//
// Prepare CName does not exist record
//
RtlZeroMemory( &NoCName, sizeof(DNS_RECORD) );
NoCName.pName = (PDNS_NAME) pRegisterSet->pName;
NoCName.wType = DNS_TYPE_CNAME;
NoCName.wDataLength = 0; // Meaning all structures not present.
NoCName.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// shared update
// - just throw our records out there (with CNAME no-exist)
//
// DCR: this case is just ModifyRecordsInSet
//
if ( fOptions & DNS_UPDATE_SHARED &&
pRegisterSet->wType != DNS_TYPE_CNAME )
{
NoCName.pNext = pRegisterSet;
status = DoQuickUpdate( &NoCName,
fOptions,
FALSE,
aipServers,
hCreds);
goto Exit;
}
//
// From here on down the code is handling the non-shared case ....
//
//
// Do an update with the following:
// Prerequisites: pRegisterSet
// Add: None
// Delete: None
//
// Build the update request with Pre, Add, Del sets ...
//
//
// DCR_PERF: prereq only update is wasteful
// we need to query AND we are trusting query
// -- supposed to find last update server -- so
// just do it
//
// - faz => find target DNS server
// - direct query for our set
// - compare
// - full match => done
// - unresolveable conflict => fail
// - ok mismatch => send replace
//
pUpdateSet = DnsBuildUpdateSet( pRegisterSet,
NULL,
NULL );
//
// Call update ...
//
if ( pUpdateSet->wType != DNS_TYPE_CNAME )
{
NoCName.pNext = pUpdateSet;
status = DoQuickUpdate( &NoCName,
fOptions,
FALSE,
aipServers,
hCreds);
}
else
{
status = DoQuickUpdate( pUpdateSet,
fOptions,
FALSE,
aipServers,
hCreds);
}
if ( status == NO_ERROR )
{
//
// We are done, no need to reregister since the server has
// what it should have.
//
goto Exit;
}
if ( status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
//
// For these errors, we just need to figure out what the server
// has and send the correct update info.
//
serverIp = DnsGetLastServerUpdateIP();
ipArray.AddrCount = 1;
ipArray.AddrArray[0] = serverIp;
status = NO_ERROR;
}
if ( status )
{
//
// For all other update errors, don't bother trying anything more.
//
goto Exit;
}
// get network info for
pNetworkInfo = NetInfo_CreateFromIpArray(
aipServers,
serverIp,
FALSE, // no search info
NULL );
//
// Let's see if the DNS server doesn't know about this set at all
// and just try to add it with the prerequisite that there is nothing
// there already for the given record name.
//
if ( pRegisterSet->wType == DNS_TYPE_CNAME )
{
//
// Handle CNAME type here as special case
//
RtlZeroMemory( &Record, sizeof(DNS_RECORD) );
Record.pName = (PDNS_NAME) pRegisterSet->pName;
Record.wType = DNS_TYPE_ANY;
Record.wDataLength = 0; // Meaning all structures not present.
Record.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// Do an update with the following:
// Prerequisites: None - will be Record above.
// Add: pRegisterSet
// Delete: None
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( NULL,
pRegisterSet,
NULL );
Record.pNext = pRegisterSet;
status = DoQuickUpdate( &Record,
fOptions,
FALSE,
serverIp ? &ipArray : aipServers,
hCreds);
if ( status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
status = DNS_ERROR_TRY_AGAIN_LATER;
}
goto Exit;
}
else
{
RtlZeroMemory( &Record, sizeof(DNS_RECORD) );
Record.pName = (PDNS_NAME) pRegisterSet->pName;
Record.wType = pRegisterSet->wType;
Record.wDataLength = 0; // Meaning all structures not present.
Record.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// Do an update with the following:
// Prerequisites: None - will be Record above and CNAME does not exist.
// Add: pRegisterSet
// Delete: All
//
// Build the update request with Pre, Add, Del sets ...
//
pUpdateSet = DnsBuildUpdateSet( NULL,
pRegisterSet,
NULL );
Record.pNext = pRegisterSet;
NoCName.pNext = &Record;
status = DoQuickUpdate( &NoCName,
fOptions,
FALSE,
serverIp ? &ipArray : aipServers,
hCreds);
}
if ( status == NO_ERROR )
{
//
// We are done, no need to reregister since the server has
// what it should have.
//
goto Exit;
}
if ( status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
//
// For these errors, we just need to figure out what the server
// has and send the correct update info.
//
status = NO_ERROR;
}
if ( status )
{
//
// For all other update errors, don't bother trying anything more.
//
goto Exit;
}
//
// Query server for pCurrentSet ...
//
status = QueryDirectEx(
NULL, // no response message
& pCurrentSet,
NULL, // no header
0, // no header counts
(LPSTR) pRegisterSet->pName,
pRegisterSet->wType,
NULL, // no input records
DNS_QUERY_TREAT_AS_FQDN,
NULL, // no DNS server list
pNetworkInfo
);
if ( IsEmptyDnsResponse( pCurrentSet ) )
{
Dns_RecordListFree( pCurrentSet );
pCurrentSet = NULL;
}
if ( pCurrentSet )
{
PDNS_RECORD pOtherSet = NULL;
PDNS_RECORD pNotUsedSet = NULL;
//
// Sometimes when DnsQuery is called, the returned record set contains
// additional records of different types than what was queried for.
// Need to strip off the additional records from the query results.
//
pNotUsedSet = DnsRecordSetDetach( pCurrentSet );
if ( pNotUsedSet )
{
Dns_RecordListFree( pNotUsedSet );
pNotUsedSet = NULL;
}
//
// There are records on the server, make sure that these are ones
// that we know about, otherwise this call should return
// DNS_ERROR_NOT_UNIQUE.
//
if ( Dns_RecordSetCompare( pCurrentSet,
pRegisterSet,
&pOtherSet,
&pNotUsedSet ) )
{
//
// The two RRSets are the same. Weird! We just did a PreReq
// update looking for just this and it said the records weren't
// there, now when we query they are! This should never happen
// theoretically. Assert if otherwise . . .
//
DNS_ASSERT( FALSE );
status = NO_ERROR;
goto Exit;
}
if ( pNotUsedSet )
{
Dns_RecordListFree( pNotUsedSet );
pNotUsedSet = NULL;
}
//
// Test to see if any of the records up one the server are not the same
// as any in our registration set. If so return error . . .
//
if ( pOtherSet )
{
Dns_RecordListFree( pOtherSet );
pOtherSet = NULL;
status = DNS_ERROR_NOT_UNIQUE;
goto Exit;
}
//
// Call ModifyRecordSet with the Current and Register RR sets . . .
//
status = DnsModifyRRSet_Ex( pCurrentSet,
pRegisterSet,
fOptions,
serverIp ? &ipArray : aipServers,
hCreds );
}
else
{
//
// This is a confused DNS server! We've already tried a prerequisite
// update that there should be nothing, and add the registration
// RR set. No need to do it again, assume for now that the server
// is busy updating the particular RR set.
//
status = DNS_ERROR_TRY_AGAIN_LATER;
}
Exit :
if ( pCurrentSet )
{
Dns_RecordListFree( pCurrentSet );
}
if ( pNetworkInfo )
{
NetInfo_Free( pNetworkInfo );
}
DNSDBG( TRACE, ( "Leave DnsRegisterRRSet_Ex()\n" ));
return status;
}
//
// DCR_CLEANUP: ModifyRecordSet() functions?
// decide what to do with these
// if to be exposed -- then all three
// if only used by async reg, then just
// need UTF8 routine
//
DNS_STATUS
WINAPI
DnsModifyRecordSet_UTF8(
IN HANDLE hCredentials OPTIONAL,
IN PDNS_RECORD pCurrentSet,
IN PDNS_RECORD pNewSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL )
/*++
Routine Description:
Dynamic DNS routine to update records. This function figures out the
records that need to be added and/or removed to modify an existing
record set in the DNS domain name space. This routine will not remove
any records that may have been added by another process for the given
record set name. In the case where "other" records are detected,
DNS_ERROR_NOT_UNIQUE will be returned.
Arguments:
hCredentials - handle to credentials to be used for update.
pCurrentSet - the set that the caller currently thinks is registered
in the DNS domain name space.
pNewSet - the set that should be registered in the DNS domain name
space. The current records will be modified to get to this
set.
fOptions - the Dynamic DNS update options that the caller may wish to
use (see dnsapi.h).
aipServers - a specific list of servers to goto to figure out the
authoritative DNS server(s) for the given record set
domain zone name.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_RECORD pCopyOfCurrentSet = NULL;
PDNS_RECORD pCopyOfNewSet = NULL;
DWORD dwFlags = fOptions;
DNSDBG( TRACE, ( "DnsModifyRecordSet_UTF8()\n" ));
//
// if just new set -- do an add
//
if ( pNewSet && !pCurrentSet )
{
return DnsAddRecordSet_UTF8( hCredentials,
pNewSet,
fOptions,
aipServers );
}
//
// if just current set -- do a delete
//
if ( !pNewSet && pCurrentSet )
{
return DnsModifyRecordsInSet_UTF8(
NULL, // no add records
pCurrentSet, // delete current set
fOptions,
hCredentials,
aipServers, // DNS servers
NULL // reserved
);
}
if ( !pNewSet && !pCurrentSet )
{
return ERROR_INVALID_PARAMETER;
}
if ( UseSystemDefaultForSecurity( dwFlags ) )
{
dwFlags |= g_UpdateSecurityLevel;
}
if ( hCredentials )
{
dwFlags |= DNS_UPDATE_CACHE_SECURITY_CONTEXT;
}
//
// build record sets for update
//
// DCR_CLEANUP: unnecessary RR copy here
// DCR_FIX0: unnecessary RR copy here
// ModifyRRSet_Ex() does it's own copy
// and doesn't touch input records
//
pCopyOfCurrentSet = Dns_RecordSetCopyEx(
pCurrentSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfCurrentSet )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
pCopyOfNewSet = Dns_RecordSetCopyEx(
pNewSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfNewSet )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
status = DnsModifyRRSet_Ex(
pCopyOfCurrentSet,
pCopyOfNewSet,
dwFlags | DNS_UPDATE_UNIQUE,
aipServers,
hCredentials );
if ( status == NO_ERROR )
{
if ( pCopyOfCurrentSet )
{
DnsFlushResolverCacheEntry_UTF8( (LPSTR) pCopyOfCurrentSet->pName );
}
else
{
DnsFlushResolverCacheEntry_UTF8( (LPSTR) pCopyOfNewSet->pName );
}
}
Done:
Dns_RecordListFree( pCopyOfCurrentSet );
Dns_RecordListFree( pCopyOfNewSet );
DNSDBG( TRACE, ( "Leave DnsModifyRecordSet_UTF8()\n" ));
return status;
}
DNS_STATUS WINAPI
DnsModifyRecordSet_A (
IN HANDLE hCredentials OPTIONAL,
IN PDNS_RECORD pCurrentSet,
IN PDNS_RECORD pNewSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL )
/*++
Routine Description:
ANSI version of ModifyRecordSet.
Note, there is NO use of this function it exists only for Elena's test
scripts. Once the test scripts are changed this function can be deleted.
Arguments:
same as above
Return Value:
None.
--*/
{
DNS_STATUS status;
PDNS_RECORD pcopyCurrent;
PDNS_RECORD pcopyNew;
DNSDBG( TRACE, ( "DnsModifyRecordSet_A()\n" ));
//
// copy record sets to UTF8
//
pcopyCurrent = Dns_RecordSetCopyEx(
pCurrentSet,
DnsCharSetAnsi,
DnsCharSetUtf8 );
pcopyNew = Dns_RecordSetCopyEx(
pNewSet,
DnsCharSetAnsi,
DnsCharSetUtf8 );
//
// modify in UTF8
//
status = DnsModifyRecordSet_UTF8(
hCredentials,
pcopyCurrent,
pcopyNew,
fOptions,
aipServers );
//
// cleanup local copies
//
Dns_RecordListFree( pcopyCurrent );
Dns_RecordListFree( pcopyNew );
return status;
}
DNS_STATUS
WINAPI
DnsModifyRecordSet_W(
IN HANDLE hCredentials OPTIONAL,
IN PDNS_RECORD pCurrentSet,
IN PDNS_RECORD pNewSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL )
/*++
Routine Description:
Unicode version of ModifyRecordSet.
Note, there is NO use of this function it exists only for Elena's test
scripts. Once the test scripts are changed this function can be deleted.
Arguments:
same as above
Return Value:
None.
--*/
{
DNS_STATUS status;
PDNS_RECORD pcopyCurrent;
PDNS_RECORD pcopyNew;
DNSDBG( TRACE, ( "DnsModifyRecordSet_W()\n" ));
//
// copy record sets to UTF8
//
pcopyCurrent = Dns_RecordSetCopyEx(
pCurrentSet,
DnsCharSetUnicode,
DnsCharSetUtf8 );
pcopyNew = Dns_RecordSetCopyEx(
pNewSet,
DnsCharSetUnicode,
DnsCharSetUtf8 );
//
// modify in UTF8
//
status = DnsModifyRecordSet_UTF8(
hCredentials,
pcopyCurrent,
pcopyNew,
fOptions,
aipServers );
//
// cleanup local copies
//
Dns_RecordListFree( pcopyCurrent );
Dns_RecordListFree( pcopyNew );
return status;
}
//
// DCR_CLEANUP: DnsAddRecordSet() functions?
// decide what to do with these
// if to be exposed -- then all three
// if only used by async reg, then just
// need UTF8 routine
//
DNS_STATUS
WINAPI
DnsAddRecordSet_UTF8(
IN HANDLE hCredentials OPTIONAL,
IN OUT PDNS_RECORD pRegisterSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL )
/*++
Routine Description:
Dynamic DNS routine to update records. This routine adds the
record set described by pRegisterSet to the DNS domain name
space. This routine fails with error DNS_ERROR_NOT_UNIQUE if
there are any pre-existing records in the DNS name space that are
not part of the set described by pRegisterSet.
Arguments:
hCredentials - handle to credentials to be used for update.
pRegisterSet - the set that should be registered in the DNS domain name
space.
fOptions - the Dynamic DNS update options that the caller may wish to
use (see dnsapi.h).
aipServers - a specific list of servers to goto to figure out the
authoritative DNS server(s) for the given record set
domain zone name.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_RECORD pCopyOfRegisterSet = NULL;
DWORD dwFlags = fOptions;
DNSDBG( TRACE, ( "DnsAddRecordSet_UTF8()\n" ));
IF_DNSDBG( UPDATE )
{
DnsDbg_RecordSet(
"DnsAddRecordSet_UTF8()",
pRegisterSet );
}
//
// Validate arguments ...
//
if ( !pRegisterSet )
{
return ERROR_INVALID_PARAMETER;
}
if ( UseSystemDefaultForSecurity( dwFlags ) )
{
dwFlags |= g_UpdateSecurityLevel;
}
if ( hCredentials )
{
dwFlags |= DNS_UPDATE_CACHE_SECURITY_CONTEXT;
}
pCopyOfRegisterSet = Dns_RecordSetCopyEx( pRegisterSet,
DnsCharSetUtf8,
DnsCharSetUtf8 );
if ( !pCopyOfRegisterSet )
{
return DNS_ERROR_NO_MEMORY;
}
status = DnsRegisterRRSet_Ex( pCopyOfRegisterSet,
dwFlags | DNS_UPDATE_UNIQUE,
aipServers,
hCredentials);
if ( status == NO_ERROR )
{
DnsFlushResolverCacheEntry_UTF8( (LPSTR) pCopyOfRegisterSet->pName );
}
Dns_RecordListFree( pCopyOfRegisterSet );
DNSDBG( TRACE, ( "Leave DnsAddRecordSet_UTF8()\n" ));
return status;
}
DNS_STATUS
WINAPI
DnsAddRecordSet_A(
IN HANDLE hCredentials OPTIONAL,
IN OUT PDNS_RECORD pRegisterSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL
)
/*++
Routine Description:
ANSI version of AddRecordSet.
Note, there is NO use of this function it exists only for Elena's test
scripts. Once the test scripts are changed this function can be deleted.
Arguments:
same as above
Return Value:
None.
--*/
{
DNS_STATUS status;
PDNS_RECORD pcopySet;
DNSDBG( TRACE, ( "DnsAddRecordSet_A()\n" ));
//
// copy set to UTF8
//
pcopySet = Dns_RecordSetCopyEx(
pRegisterSet,
DnsCharSetAnsi,
DnsCharSetUtf8 );
//
// add set
//
status = DnsAddRecordSet_UTF8(
hCredentials,
pcopySet,
fOptions,
aipServers );
// cleanup local copy
Dns_RecordListFree( pcopySet );
return status;
}
DNS_STATUS
WINAPI
DnsAddRecordSet_W(
IN HANDLE hCredentials OPTIONAL,
IN OUT PDNS_RECORD pRegisterSet,
IN DWORD fOptions,
IN PIP_ARRAY aipServers OPTIONAL )
/*++
Routine Description:
ANSI version of AddRecordSet.
Note, there is NO use of this function it exists only for Elena's test
scripts. Once the test scripts are changed this function can be deleted.
Arguments:
same as above
Return Value:
None.
--*/
{
DNS_STATUS status;
PDNS_RECORD pcopySet;
DNSDBG( TRACE, ( "DnsAddRecordSet_W()\n" ));
//
// copy set to UTF8
//
pcopySet = Dns_RecordSetCopyEx(
pRegisterSet,
DnsCharSetUnicode,
DnsCharSetUtf8 );
//
// add set
//
status = DnsAddRecordSet_UTF8(
hCredentials,
pcopySet,
fOptions,
aipServers );
// cleanup local copy
Dns_RecordListFree( pcopySet );
return status;
}
//
// Update test functions are called by system components
//
DNS_STATUS
WINAPI
DnsUpdateTest_UTF8(
IN HANDLE hCredentials OPTIONAL,
IN PCSTR pszName,
IN DWORD Flags,
IN PIP_ARRAY pDnsServers OPTIONAL
)
/*++
Routine Description:
Dynamic DNS routine to test whether the caller can update the
records in the DNS domain name space for the given record name.
Arguments:
hCredentials - handle to credentials to be used for update.
pszName - the record set name that the caller wants to test.
Flags - the Dynamic DNS update options that the caller may wish to
use (see dnsapi.h).
pDnsServers - a specific list of servers to goto to figure out the
authoritative DNS server(s) for the given record set
domain zone name.
Return Value:
None.
--*/
{
PWSTR pnameWide = NULL;
DNS_STATUS status = NO_ERROR;
DNSDBG( TRACE, (
"DnsUpdateTest_UTF8( %s )\n",
pszName ));
if ( !pszName )
{
return ERROR_INVALID_PARAMETER;
}
pnameWide = Dns_NameCopyAllocate(
(PCHAR) pszName,
0,
DnsCharSetUtf8,
DnsCharSetUnicode );
if ( !pnameWide )
{
return ERROR_INVALID_NAME;
}
status = DnsUpdateTest_W(
hCredentials,
(PCWSTR) pnameWide,
Flags,
pDnsServers );
FREE_HEAP( pnameWide );
return status;
}
DNS_STATUS
WINAPI
DnsUpdateTest_A(
IN HANDLE hCredentials OPTIONAL,
IN PCSTR pszName,
IN DWORD Flags,
IN PIP_ARRAY pDnsServers OPTIONAL
)
/*++
Routine Description:
Dynamic DNS routine to test whether the caller can update the
records in the DNS domain name space for the given record name.
Arguments:
hCredentials - handle to credentials to be used for update.
pszName - the record set name that the caller wants to test.
Flags - the Dynamic DNS update options that the caller may wish to
use (see dnsapi.h).
pDnsServers - a specific list of servers to goto to figure out the
authoritative DNS server(s) for the given record set
domain zone name.
Return Value:
None.
--*/
{
PWSTR pnameWide = NULL;
DNS_STATUS status = NO_ERROR;
DNSDBG( TRACE, (
"DnsUpdateTest_UTF8( %s )\n",
pszName ));
if ( !pszName )
{
return ERROR_INVALID_PARAMETER;
}
pnameWide = Dns_NameCopyAllocate(
(PCHAR) pszName,
0,
DnsCharSetUtf8,
DnsCharSetUnicode );
if ( !pnameWide )
{
return ERROR_INVALID_NAME;
}
status = DnsUpdateTest_W(
hCredentials,
(PCWSTR) pnameWide,
Flags,
pDnsServers );
FREE_HEAP( pnameWide );
return status;
}
DNS_STATUS
WINAPI
DnsUpdateTest_W(
IN HANDLE hCredentials OPTIONAL,
IN PCWSTR pszName,
IN DWORD Flags,
IN PIP_ARRAY pDnsServers OPTIONAL
)
/*++
Routine Description:
Dynamic DNS routine to test whether the caller can update the
records in the DNS domain name space for the given record name.
Arguments:
hCredentials - handle to credentials to be used for update.
pszName - the record set name that the caller wants to test.
Flags - the Dynamic DNS update options that the caller may wish to
use (see dnsapi.h).
pDnsServers - a specific list of servers to goto to figure out the
authoritative DNS server(s) for the given record set
domain zone name.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
DNS_RECORD record;
DWORD flags = Flags;
LPSTR pnameUtf8 = NULL;
DNSDBG( TRACE, (
"DnsUpdateTest_W( %S )\n",
pszName ));
//
// validation
//
if ( flags & DNS_UNACCEPTABLE_UPDATE_OPTIONS )
{
return ERROR_INVALID_PARAMETER;
}
if ( !pszName )
{
return ERROR_INVALID_PARAMETER;
}
//
// try resolver
//
if ( flags & DNS_UPDATE_TEST_USE_LOCAL_SYS_ACCT )
{
DWORD rpcStatus = NO_ERROR;
if ( !pDnsServers ||
pDnsServers->AddrCount != 1 )
{
return ERROR_INVALID_PARAMETER;
}
RpcTryExcept
{
status = CRrUpdateTest(
NULL,
(PWSTR) pszName,
0,
pDnsServers->AddrArray[0] );
}
RpcExcept( DNS_RPC_EXCEPTION_FILTER )
{
rpcStatus = RpcExceptionCode();
}
RpcEndExcept
if ( rpcStatus == NO_ERROR )
{
return status;
}
return rpcStatus;
}
//
// direct update attempt
//
//
// read update config
//
Reg_RefreshUpdateConfig();
if ( UseSystemDefaultForSecurity( flags ) )
{
flags |= g_UpdateSecurityLevel;
}
if ( hCredentials )
{
flags |= DNS_UPDATE_CACHE_SECURITY_CONTEXT;
}
//
// build record
// - NOEXIST prerequisite
//
pnameUtf8 = Dns_NameCopyAllocate(
(PCHAR) pszName,
0,
DnsCharSetUnicode,
DnsCharSetUtf8 );
if ( ! pnameUtf8 )
{
return DNS_ERROR_NO_MEMORY;
}
RtlZeroMemory( &record, sizeof(DNS_RECORD) );
record.pName = (PDNS_NAME) pnameUtf8;
record.wType = DNS_TYPE_ANY;
record.wDataLength = 0;
record.Flags.DW = DNSREC_PREREQ | DNSREC_NOEXIST;
//
// do the prereq update
//
status = DoQuickUpdate(
&record,
flags,
TRUE,
pDnsServers,
hCredentials );
FREE_HEAP( pnameUtf8 );
return status;
}
//
// Update execution functions
//
DNS_STATUS
DoQuickUpdate(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN BOOL fUpdateTestMode,
IN PIP_ARRAY pServerList OPTIONAL,
IN HANDLE hCreds OPTIONAL
)
/*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PSTR pzoneName;
PDNS_NAME pname = pRecord->pName;
PDNS_NETINFO pnetInfo = NULL;
DNSDBG( UPDATE, (
"DoQuickUpdate( rr=%p, flag=%08x )\n",
pRecord,
dwFlags ));
IF_DNSDBG( UPDATE )
{
DnsDbg_RecordSet(
"Entering DoQuickUpdate():",
pRecord );
}
//
// caller has particular server list
//
if ( pServerList )
{
IP_ADDRESS serverIp;
PIP_ARRAY pserverListCopy = Dns_CreateIpArrayCopy( pServerList );
if ( !pserverListCopy )
{
return DNS_ERROR_NO_MEMORY;
}
status = DoQuickFAZ(
&pnetInfo,
pname,
pserverListCopy );
if ( status != ERROR_SUCCESS )
{
FREE_HEAP( pserverListCopy );
pserverListCopy = NULL;
return status;
}
//
// FAZ should always produce update network info blob
//
DNS_ASSERT( NetInfo_IsForUpdate(pnetInfo) );
pzoneName = NetInfo_UpdateZoneName( pnetInfo );
if ( dwFlags & DNS_UPDATE_TRY_ALL_MASTER_SERVERS )
{
status = DoMultiMasterUpdate(
pRecord,
dwFlags,
hCreds,
pzoneName,
0,
pserverListCopy );
}
else
{
status = DoQuickUpdateEx(
NULL,
pRecord,
dwFlags,
pnetInfo,
hCreds );
if ( status == ERROR_TIMEOUT )
{
serverIp = pnetInfo->AdapterArray[0]->ServerArray[0].IpAddress;
#if DBG
if ( g_IsDnsServer &&
IsLocalIpAddress( serverIp ) )
{
DnsDbg_PrintfToDebugger(
"DNSAPI: Note that DDNS update to local server\n"
" with IP address 0x%.8x timed out,\n"
" now trying other DNS servers\n"
" authoritative for zone (%s)\n",
serverIp,
pzoneName );
}
#endif
status = DoMultiMasterUpdate(
pRecord,
dwFlags,
hCreds,
pzoneName,
serverIp,
pserverListCopy );
}
}
NetInfo_Free( pnetInfo );
FREE_HEAP( pserverListCopy );
pserverListCopy = NULL;
return status;
}
//
// server list unspecified
// - use FAZ to figure it out
//
else
{
PIP_ARRAY serverListArray[ UPDATE_ADAPTER_LIMIT ];
PDNS_NETINFO networkInfoArray[ UPDATE_ADAPTER_LIMIT ];
DWORD netCount = UPDATE_ADAPTER_LIMIT;
DWORD iter;
BOOL bsuccess = FALSE;
//
// build server list for update
// - collapse adapters on same network into single adapter
// - FAZ to find update servers
// - collapse results from same network into single target
//
netCount = GetDnsServerListsForUpdate(
serverListArray,
netCount,
dwFlags
);
status = CollapseDnsServerListsForUpdate(
serverListArray,
networkInfoArray,
& netCount,
pname );
DNS_ASSERT( netCount <= UPDATE_ADAPTER_LIMIT );
if ( netCount == 0 )
{
if ( status == ERROR_SUCCESS )
{
status = DNS_ERROR_NO_DNS_SERVERS;
}
return status;
}
//
// do update on all distinct (disjoint) networks
//
for ( iter = 0;
iter < netCount;
iter++ )
{
PIP_ARRAY pdnsArray = serverListArray[ iter ];
pnetInfo = networkInfoArray[ iter ];
if ( !pnetInfo )
{
ASSERT( FALSE );
FREE_HEAP( pdnsArray );
continue;
}
DNS_ASSERT( NetInfo_IsForUpdate(pnetInfo) );
pzoneName = NetInfo_UpdateZoneName( pnetInfo );
//
// multimater update?
// - if flag set
// - or simple update (best net) times out
//
if ( dwFlags & DNS_UPDATE_TRY_ALL_MASTER_SERVERS )
{
status = DoMultiMasterUpdate(
pRecord,
dwFlags,
hCreds,
pzoneName,
0,
pdnsArray );
}
else
{
status = DoQuickUpdateEx(
NULL,
pRecord,
dwFlags,
pnetInfo,
hCreds );
if ( status == ERROR_TIMEOUT )
{
IP_ADDRESS serverIp;
serverIp = pnetInfo->AdapterArray[0]
->ServerArray[0].IpAddress;
status = DoMultiMasterUpdate(
pRecord,
dwFlags,
hCreds,
pzoneName,
serverIp,
pdnsArray );
}
}
NetInfo_Free( pnetInfo );
FREE_HEAP( pdnsArray );
if ( status == NO_ERROR ||
( fUpdateTestMode &&
( status == DNS_ERROR_RCODE_YXDOMAIN ||
status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET ) ) )
{
bsuccess = TRUE;
}
}
//
// successful update on any network counts as success
//
// DCR_QUESTION: not sure why don't just NO_ERROR all bsuccess,
// only case would be this fUpdateTestMode thing above
// on single network
//
if ( bsuccess )
{
if ( netCount != 1 )
{
return NO_ERROR;
}
}
return status;
}
}
DNS_STATUS
DoQuickUpdateEx(
OUT PDNS_MSG_BUF * ppMsgRecv, OPTIONAL
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN PDNS_NETINFO pNetworkInfo,
IN HANDLE hCreds OPTIONAL
)
/*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PDNS_MSG_BUF pmsg = NULL;
DNSDBG( TRACE, ( "DoQuickUpdateEx()\n" ));
//
// do update
// - optionally specify update context through net adapter list
// otherwise DnsUpdateEx() will use FAZ
//
status = DnsUpdate(
pRecord,
dwFlags,
pNetworkInfo,
hCreds,
&pmsg
);
if ( pmsg )
{
//
// DCR: why LastDnsServerUpdated requires these errors?
//
if ( status == NO_ERROR ||
status == DNS_ERROR_RCODE_SERVER_FAILURE ||
status == DNS_ERROR_RCODE_NOT_IMPLEMENTED ||
status == DNS_ERROR_RCODE_REFUSED ||
status == DNS_ERROR_RCODE_YXRRSET ||
status == DNS_ERROR_RCODE_NXRRSET )
{
IP4_ADDRESS ip = MSG_REMOTE_IP4(pmsg);
DNSDBG( TRACE, ( "DNS Update sent to server 0x%x\n", ip ));
g_LastDNSServerUpdated = ip;
}
//
// build last update error blob here
//
if ( status != NO_ERROR )
{
SetLastFailedUpdateInfo(
pmsg,
status );
}
}
if ( ppMsgRecv )
{
*ppMsgRecv = pmsg;
}
else
{
if ( pmsg )
{
FREE_HEAP( pmsg );
}
}
GUI_MODE_SETUP_WS_CLEANUP( g_InNTSetupMode );
return status;
}
DNS_STATUS
DoMultiMasterUpdate(
IN PDNS_RECORD pRecord,
IN DWORD dwFlags,
IN HANDLE hCreds OPTIONAL,
IN LPSTR pszDomain,
IN IP_ADDRESS BadIp,
IN PIP_ARRAY pServerList
)
/*++
Routine Description:
Do update to multi-master DNS primary.
Arguments:
pRecord -- record list to update
Flags -- update options
hCreds -- update credentials
pszDomain -- domain (zone) name to update
BadIp -- IP of server that didn't response to previous update attempt
pServerList -- list of DNS servers
Return Value:
ERROR_SUCCESS if successful.
ErrorCode on failure.
--*/
{
PDNS_NETINFO pnetInfo = NULL;
PIP_ARRAY pnsList = NULL;
PIP_ARRAY pbadServerList = NULL;
DNS_STATUS status = DNS_ERROR_NO_DNS_SERVERS;
DWORD iter;
//
// Get the full NS server list for the domain name and x out BadIp
// if present.
//
pnsList = GetNameServersListForDomain( pszDomain, pServerList );
if ( !pnsList )
{
return status;
}
if ( pnsList->AddrCount == 1 &&
Dns_IsAddressInIpArray( pnsList, BadIp ) )
{
status = ERROR_TIMEOUT;
goto Done;
}
//
// Create and initialize bad servers list with BadIp
//
pbadServerList = Dns_CreateIpArray( pnsList->AddrCount + 1 );
if ( !pbadServerList )
{
status = DNS_ERROR_NO_MEMORY;
goto Done;
}
if ( BadIp )
{
Dns_AddIpToIpArray( pbadServerList, BadIp );
}
//
// attempt update against each multi-master DNS server
//
// identify multi-master servers as those which return their themselves
// as the authoritative server when do FAZ query
//
for ( iter = 0; iter < pnsList->AddrCount; iter++ )
{
IP_ARRAY ipArray;
IP_ADDRESS serverIp = pnsList->AddrArray[iter];
//
// If the current server that we are about to FAZ to is in
// the ignore list, skip it . . .
//
if ( Dns_IsAddressInIpArray( pbadServerList, serverIp ) )
{
continue;
}
ipArray.AddrCount = 1;
ipArray.AddrArray[0] = serverIp;
status = DoQuickFAZ(
&pnetInfo,
pszDomain,
&ipArray );
if ( status != ERROR_SUCCESS )
{
continue;
}
DNS_ASSERT( pnetInfo->AdapterCount == 1 );
DNS_ASSERT( pnetInfo->AdapterArray[0]->ServerCount != 0 );
if ( serverIp != pnetInfo->AdapterArray[0]->ServerArray[0].IpAddress )
{
serverIp = pnetInfo->AdapterArray[0]->ServerArray[0].IpAddress;
//
// If the current server that we are about to FAZ to is in
// the ignore list, skip it . . .
//
if ( Dns_IsAddressInIpArray( pbadServerList, serverIp ) )
{
NetInfo_Free( pnetInfo );
continue;
}
}
status = DoQuickUpdateEx(
NULL,
pRecord,
dwFlags,
pnetInfo,
hCreds
);
NetInfo_Free( pnetInfo );
if ( status == ERROR_TIMEOUT )
{
//
// Add serverIp to ignore list
//
Dns_AddIpToIpArray( pbadServerList, serverIp );
}
else
{
if ( dwFlags & DNS_UPDATE_TRY_ALL_MASTER_SERVERS )
{
Dns_AddIpToIpArray( pbadServerList, serverIp );
}
else
{
goto Done;
}
}
}
Done:
FREE_HEAP( pnsList );
FREE_HEAP( pbadServerList );
return status;
}
//
// DCR_DELETE: not sure of the point of GetLastServerUpdateIp()
//
IP_ADDRESS
DnsGetLastServerUpdateIP(
void )
/*++
Routine Description:
None.
Arguments:
None.
Return Value:
None.
--*/
{
return g_LastDNSServerUpdated;
}
//
// Last failed update info
//
DNS_FAILED_UPDATE_INFO g_FailedUpdateInfo = { 0, 0, 0, 0 };
VOID
DnsGetLastFailedUpdateInfo(
OUT PDNS_FAILED_UPDATE_INFO pInfo
)
/*++
Routine Description:
Retrieve failed update information.
Arguments:
pInfo -- ptr to receive failed info blob
Return Value:
None.
--*/
{
// fill in last info
RtlCopyMemory(
pInfo,
& g_FailedUpdateInfo,
sizeof(*pInfo) );
}
VOID
SetLastFailedUpdateInfo(
IN PDNS_MSG_BUF pMsg,
IN DNS_STATUS Status
)
/*++
Routine Description:
Set last failed update info.
Arguments:
pMsg -- message with update failure
Status -- status
Return Value:
None.
--*/
{
g_FailedUpdateInfo.Ip4Address = MSG_REMOTE_IP4(pMsg);
//g_FailedUpdateInfo.Ip6Address = MSG_REMOTE_IP6(pMsg);
g_FailedUpdateInfo.Status = Status;
g_FailedUpdateInfo.Rcode = pMsg->MessageHead.ResponseCode;
}
//
// DCR: include client secure update here
// to do so, will have to expose
// in dnslib some of the security utilities
// but then can REMOVE exports of a bunch
// of the send\recv\socket routines
//
//
// End of udpate.c
//