/*++ Copyright (c) 1996-2000 Microsoft Corporation Module Name: update.c Abstract: Domain Name System (DNS) Library Update client routines. Author: Jim Gilroy (jamesg) October, 1996 Environment: User Mode - Win32 Revision History: --*/ #include "local.h" // // 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 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, FALSE ); 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, FALSE ); DNSDBG( UPDATE, ( "Leave Dns_UpdateHostAddrs() status=%p %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 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 = %p\n" "\tpRecord = %p\n" "\t\towner = %s\n", dwFlags, pRecord, pRecord ? pRecord->pName : NULL )); // // if not a UPDATE compatibile adapter list -- no action // if ( !IS_UPDATE_NETWORK_INFO(pNetworkInfo) ) { return( ERROR_INVALID_PARAMETER ); } // // suck info from adapter list // pszzone = pNetworkInfo->pSearchList->pszDomainOrZoneName; parrayServers = Dns_ConvertNetworkInfoToIpArray( pNetworkInfo ); pszserverName = pNetworkInfo->aAdapterInfoList[0]->pszAdapterDomain; 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; } 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 = %p %s.\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 pNetworkInfo; DNS_STATUS status = NO_ERROR; // // convert params into UPDATE compatible adapter list // pNetworkInfo = Dns_CreateUpdateNetworkInfo( pszZone, pszServerName, aipServers, 0 ); if ( !pNetworkInfo ) { return( ERROR_INVALID_PARAMETER ); } // // call real update function // status = Dns_UpdateLib( pRecord, dwFlags, pNetworkInfo, hCreds, ppMsgRecv ); Dns_FreeNetworkInfo( pNetworkInfo ); return status; } // // End update.c //