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