/*++ Copyright (c) 1996-2001 Microsoft Corporation Module Name: rralloc.c Abstract: Domain Name System (DNS) Library Resource record allocation \ creation routines. Author: Jim Gilroy (jamesg) January, 1997 Environment: User Mode - Win32 Revision History: --*/ #include "local.h" #define SET_FLAGS(Flags, value) \ ( *(PWORD)&(Flags) = value ) PDNS_RECORD WINAPI Dns_AllocateRecord( IN WORD wBufferLength ) /*++ Routine Description: Allocate record structure. Arguments: wBufferLength - desired buffer length (beyond structure header) Return Value: Ptr to message buffer. NULL on error. --*/ { PDNS_RECORD prr; prr = ALLOCATE_HEAP( SIZEOF_DNS_RECORD_HEADER + wBufferLength ); if ( prr == NULL ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); } RtlZeroMemory( prr, SIZEOF_DNS_RECORD_HEADER ); // as first cut, set datalength to buffer length prr->wDataLength = wBufferLength; return( prr ); } VOID WINAPI Dns_RecordFree( IN OUT PDNS_RECORD pRecord ) /*++ Routine Description: Free a record Arguments: pRecord -- record list to free Return Value: None. --*/ { DNSDBG( HEAP, ( "Dns_RecordFree( %p )\n", pRecord )); // handle NULL for convenience if ( !pRecord ) { return; } // free owner name? if ( FLAG_FreeOwner( pRecord ) ) { FREE_HEAP( pRecord->pName ); } // // free data -- but only if flag set // // note: even if we fix copy functions to do atomic // allocations, we'll still have to have free to // handle RPC allocations // (unless we very cleverly, treated RPC as flat blob, then // did fix up (to offsets before and afterward) // if ( FLAG_FreeData( pRecord ) ) { switch( pRecord->wType ) { case DNS_TYPE_A: break; case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: case DNS_TYPE_MB: case DNS_TYPE_MD: case DNS_TYPE_MF: case DNS_TYPE_MG: case DNS_TYPE_MR: if ( pRecord->Data.PTR.pNameHost ) { FREE_HEAP( pRecord->Data.PTR.pNameHost ); } break; case DNS_TYPE_SOA: if ( pRecord->Data.SOA.pNamePrimaryServer ) { FREE_HEAP( pRecord->Data.SOA.pNamePrimaryServer ); } if ( pRecord->Data.SOA.pNameAdministrator ) { FREE_HEAP( pRecord->Data.SOA.pNameAdministrator ); } break; case DNS_TYPE_MINFO: case DNS_TYPE_RP: if ( pRecord->Data.MINFO.pNameMailbox ) { FREE_HEAP( pRecord->Data.MINFO.pNameMailbox ); } if ( pRecord->Data.MINFO.pNameErrorsMailbox ) { FREE_HEAP( pRecord->Data.MINFO.pNameErrorsMailbox ); } break; case DNS_TYPE_MX: case DNS_TYPE_AFSDB: case DNS_TYPE_RT: if ( pRecord->Data.MX.pNameExchange ) { FREE_HEAP( pRecord->Data.MX.pNameExchange ); } break; case DNS_TYPE_HINFO: case DNS_TYPE_ISDN: case DNS_TYPE_TEXT: case DNS_TYPE_X25: { DWORD iter; DWORD count = pRecord->Data.TXT.dwStringCount; for ( iter = 0; iter < count; iter++ ) { if ( pRecord->Data.TXT.pStringArray[iter] ) { FREE_HEAP( pRecord->Data.TXT.pStringArray[iter] ); } } break; } case DNS_TYPE_SRV: if ( pRecord->Data.SRV.pNameTarget ) { FREE_HEAP( pRecord->Data.SRV.pNameTarget ); } break; case DNS_TYPE_WINSR: if ( pRecord->Data.WINSR.pNameResultDomain ) { FREE_HEAP( pRecord->Data.WINSR.pNameResultDomain ); } break; default: // other types -- A, AAAA, ATMA, WINS, NULL, // have no internal pointers break; } } // for catching heap problems pRecord->pNext = DNS_BAD_PTR; pRecord->pName = DNS_BAD_PTR; FREE_HEAP( pRecord ); } VOID WINAPI Dns_RecordListFree( IN OUT PDNS_RECORD pRecord ) /*++ Routine Description: Free list of records. Arguments: pRecord -- record list to free Return Value: None. --*/ { PDNS_RECORD pnext; DNSDBG( TRACE, ( "Dns_RecordListFree( %p )\n", pRecord )); // // loop through and free every RR in list // while ( pRecord ) { pnext = pRecord->pNext; Dns_RecordFree( pRecord ); pRecord = pnext; } } VOID WINAPI Dns_RecordListFreeEx( IN OUT PDNS_RECORD pRecord, IN BOOL fFreeOwner ) /*++ Routine Description: Free list of records. DCR: RecordListFreeEx (no free owner option) is probably useless Note: owner name is freed ONLY when indicated by flag; other ptrs are considered to be either 1) internal as when records read from wire or copied 2) external and to be freed by record creator Arguments: pRecord -- record list to free fFreeOwner -- flag indicating owner name should be freed Return Value: None. --*/ { PDNS_RECORD pnext; DNSDBG( TRACE, ( "Dns_RecordListFreeEx( %p, %d )\n", pRecord, fFreeOwner )); // // loop through and free every RR in list // while ( pRecord ) { pnext = pRecord->pNext; // free owner name? // - if "FreeOwner" flag NOT set, then don't free if ( !fFreeOwner ) { FLAG_FreeOwner( pRecord ) = FALSE; } // free record Dns_RecordFree( pRecord ); pRecord = pnext; } } // // Special record type creation routines // PDNS_RECORD CreateRecordBasic( IN PDNS_NAME pOwnerName, IN BOOL fCopyName, IN WORD wType, IN WORD wDataLength, IN DWORD AllocLength, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create record of arbitary type. Helper function to wrap up - record alloc - name alloc - basic setup Arguments: pOwnerName -- owner name fCopyName -- TRUE - make copy of owner name FALSE - use directly wType -- type AllocLength -- allocaction length, including any imbedded data wDataLength -- data length to set Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD precord; PDNS_RECORD prr; PCHAR pname; DWORD bufLength; // // alloc record // prr = Dns_AllocateRecord( (WORD)AllocLength ); if ( !prr ) { return( NULL ); } // // copy owner name // if ( fCopyName && pOwnerName ) { pname = Dns_NameCopyAllocate( pOwnerName, 0, // length unknown NameCharSet, RecordCharSet ); if ( !pname ) { FREE_HEAP( prr ); return( NULL ); } } else { pname = pOwnerName; } // // set fields // - name, type and charset // - TTL, section left zero // - FreeData is specifically off // prr->pName = pname; prr->wType = wType; prr->wDataLength = wDataLength; SET_FREE_OWNER(prr); prr->Flags.S.CharSet = RecordCharSet; prr->dwTtl = Ttl; return( prr ); } PDNS_RECORD Dns_CreateFlatRecord( IN PDNS_NAME pOwnerName, IN WORD wType, IN PCHAR pData, IN DWORD DataLength, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create flat record. Arguments: pOwnerName -- owner name wType -- record type pData -- ptr to data for record DataLength -- length (in bytes) of data Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD prr; // // determine record size // - record buffer will include hostname // prr = CreateRecordBasic( pOwnerName, TRUE, // copy name wType, (WORD) DataLength, // datalength DataLength, // alloc datalength Ttl, NameCharSet, RecordCharSet ); if ( !prr ) { return( NULL ); } // // copy in data // RtlCopyMemory( (PBYTE) &prr->Data, pData, DataLength ); return( prr ); } // // Reverse record creation // PDNS_RECORD Dns_CreatePtrTypeRecord( IN PDNS_NAME pOwnerName, IN BOOL fCopyName, IN PDNS_NAME pTargetName, IN WORD wType, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create PTR type (single-indirection) record. This can be used to create any "PTR-type" record: PTR, CNAME, NS, etc. Arguments: pOwnerName -- owner name fCopyName -- TRUE - make copy of owner name FALSE - use directly pTargetName -- target name Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD precord; PDNS_RECORD prr; PCHAR pname; DWORD bufLength; // // determine record size // - record buffer will include hostname // bufLength = Dns_GetBufferLengthForNameCopy( pTargetName, 0, // length unknown NameCharSet, RecordCharSet ); if ( !bufLength ) { return( NULL ); } // // create record // prr = CreateRecordBasic( pOwnerName, fCopyName, wType, sizeof(DNS_PTR_DATA), // data length (sizeof(DNS_PTR_DATA) + bufLength), // alloc length Ttl, NameCharSet, RecordCharSet ); if ( !prr ) { return( NULL ); } // // write target name into buffer, immediately following PTR data struct // prr->Data.PTR.pNameHost = (PCHAR)&prr->Data + sizeof(DNS_PTR_DATA); Dns_NameCopy( prr->Data.PTR.pNameHost, NULL, pTargetName, 0, NameCharSet, RecordCharSet ); return( prr ); } PDNS_RECORD Dns_CreatePtrRecordEx( IN PDNS_ADDR pAddr, IN PDNS_NAME pszHostName, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create PTR record from IP address and hostname. Arguments: pAddr -- addr (IP4 or IP6) to map into PTR pszHostName -- host name, FULL FQDN Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PCHAR pname = NULL; DWORD family; // // create reverse lookup name // - note this is external allocation // family = DnsAddr_Family( pAddr ); if ( family == AF_INET ) { IP4_ADDRESS ip = DnsAddr_GetIp4(pAddr); if ( RecordCharSet == DnsCharSetUnicode ) { pname = (PCHAR) Dns_Ip4AddressToReverseNameAlloc_W( ip ); } else { pname = Dns_Ip4AddressToReverseNameAlloc_A( ip ); } } else if ( family == AF_INET6 ) { PIP6_ADDRESS p6 = DnsAddr_GetIp6Ptr(pAddr); if ( RecordCharSet == DnsCharSetUnicode ) { pname = (PCHAR) Dns_Ip6AddressToReverseNameAlloc_W( *p6 ); } else { pname = Dns_Ip6AddressToReverseNameAlloc_A( *p6 ); } } if ( !pname ) { return( NULL ); } // // build record // return Dns_CreatePtrTypeRecord( pname, FALSE, // don't copy IP pszHostName, // target name DNS_TYPE_PTR, Ttl, NameCharSet, RecordCharSet ); } PDNS_RECORD Dns_CreatePtrRecordExEx( IN PDNS_ADDR pAddr, IN PSTR pszHostName, IN PSTR pszDomainName, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create PTR record from hostname and domain name. Helper function for DHCP registrations when hostname and domain name are separate and both required. Arguments: pAddr -- addr (IP4 or IP6) to map into PTR pszHostName -- host name (single label) pszDomainName -- domain name Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { WCHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ]; DNSDBG( TRACE, ( "Dns_CreatePtrRecordExEx()\n" )); // // build appended name // // DCR: could require just host name and check that // either domain exists or hostname is full // if ( !pszHostName || !pszDomainName ) { return NULL; } if ( NameCharSet != DnsCharSetUnicode ) { if ( ! Dns_NameAppend_A( (PCHAR) nameBuffer, DNS_MAX_NAME_BUFFER_LENGTH, pszHostName, pszDomainName ) ) { DNS_ASSERT( FALSE ); return NULL; } } else { if ( ! Dns_NameAppend_W( (PWCHAR) nameBuffer, DNS_MAX_NAME_BUFFER_LENGTH, (PWSTR) pszHostName, (PWSTR) pszDomainName ) ) { DNS_ASSERT( FALSE ); return NULL; } } // // build record // return Dns_CreatePtrRecordEx( pAddr, (PCHAR) nameBuffer, Ttl, NameCharSet, RecordCharSet ); } // // Forward record creation // PDNS_RECORD Dns_CreateARecord( IN PDNS_NAME pOwnerName, IN IP4_ADDRESS Ip4Addr, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create A record. Arguments: pOwnerName -- owner name Ip4Addr -- IP address Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD prr; // // determine record size // - record buffer will include hostname // prr = CreateRecordBasic( pOwnerName, TRUE, // copy name DNS_TYPE_A, sizeof(DNS_A_DATA), sizeof(DNS_A_DATA), Ttl, NameCharSet, RecordCharSet ); if ( !prr ) { return( NULL ); } // // set IP // prr->Data.A.IpAddress = Ip4Addr; return( prr ); } PDNS_RECORD Dns_CreateAAAARecord( IN PDNS_NAME pOwnerName, IN IP6_ADDRESS Ip6Addr, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create A record. Arguments: pOwnerName -- owner name Ip6Addr -- IP6 address Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD prr; // // determine record size // - record buffer will include hostname // prr = CreateRecordBasic( pOwnerName, TRUE, // copy name DNS_TYPE_AAAA, sizeof(DNS_AAAA_DATA), sizeof(DNS_AAAA_DATA), Ttl, NameCharSet, RecordCharSet ); if ( !prr ) { return( NULL ); } // // set IP // prr->Data.AAAA.Ip6Address = Ip6Addr; return( prr ); } PDNS_RECORD Dns_CreateAAAARecordFromDnsAddr( IN PDNS_NAME pOwnerName, IN PDNS_ADDR pAddr, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create A record. Arguments: pOwnerName -- owner name pAddr -- Ptr to DNS_ADDR Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PDNS_RECORD prr; // // determine record size // - record buffer will include hostname // prr = Dns_CreateAAAARecord( pOwnerName, * (PIP6_ADDRESS) &pAddr->SockaddrIn6.sin6_addr, Ttl, NameCharSet, RecordCharSet ); if ( !prr ) { return( NULL ); } // // slap scope into reserved field // prr->dwReserved = pAddr->SockaddrIn6.sin6_scope_id; return( prr ); } PDNS_RECORD Dns_CreateForwardRecord( IN PDNS_NAME pOwnerName, IN WORD wType, OPTIONAL IN PDNS_ADDR pAddr, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create forward lookup record. This is just a shim to avoid duplicating selection logic. Arguments: pOwnerName -- owner name wType -- type, if specified; zero for getting type from pAddr pAddr -- ptr to DNS_ADDR Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { // // build desired type // if ( DnsAddr_IsIp4( pAddr ) ) { if ( wType == 0 || wType == DNS_TYPE_A ) { return Dns_CreateARecord( pOwnerName, DnsAddr_GetIp4( pAddr ), Ttl, NameCharSet, RecordCharSet ); } } else if ( DnsAddr_IsIp6( pAddr ) ) { if ( wType == 0 || wType == DNS_TYPE_AAAA ) { return Dns_CreateAAAARecordFromDnsAddr( pOwnerName, pAddr, Ttl, NameCharSet, RecordCharSet ); } } return NULL; } PDNS_RECORD Dns_CreateForwardRecordFromIp6( IN PDNS_NAME pOwnerName, IN PIP6_ADDRESS pIp, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create forward lookup record. This is just a shim to avoid duplicating selection logic. Arguments: pOwnerName -- owner name pIp -- IP6 address Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { // // build desired type // if ( IP6_IS_ADDR_V4MAPPED( pIp ) ) { return Dns_CreateARecord( pOwnerName, IP6_GET_V4_ADDR( pIp ), Ttl, NameCharSet, RecordCharSet ); } else { return Dns_CreateAAAARecord( pOwnerName, *pIp, Ttl, NameCharSet, RecordCharSet ); } } PDNS_RECORD Dns_CreateForwardRecordForSockaddr( IN PDNS_NAME pOwnerName, IN PSOCKADDR pSockaddr, IN DWORD Ttl, IN DNS_CHARSET NameCharSet, IN DNS_CHARSET RecordCharSet ) /*++ Routine Description: Create forward lookup record. This is just a shim to avoid duplicating selection logic. Arguments: pOwnerName -- owner name pSockaddr -- ptr to sockaddr Ttl -- TTL NameCharSet -- character set of name RecordCharSet -- character set for resulting record Return Value: Ptr to PTR record. NULL on error. --*/ { PFAMILY_INFO pinfo; DNSDBG( TRACE, ( "Dns_CreateForwardRecordForSockaddr()\n" )); pinfo = FamilyInfo_GetForSockaddr( pSockaddr ); if ( !pinfo ) { SetLastError( ERROR_INVALID_DATA ); return NULL; } // // build flat record of desired type // return Dns_CreateFlatRecord( pOwnerName, pinfo->DnsType, (PBYTE)pSockaddr + pinfo->OffsetToAddrInSockaddr, pinfo->LengthAddr, Ttl, NameCharSet, RecordCharSet ); } PDNS_RECORD Dns_CreateRecordForIpString_W( IN PCWSTR pwsName, IN WORD wType, OPTIONAL IN DWORD Ttl ) /*++ Routine Description: Create record for IP string query. Arguments: pwsName -- name that may be IP string query wType -- type of query; OPTIONAL, if zero type derived from string Return Value: Ptr to record for query, if query name\type is IP. NULL if query not for IP. --*/ { DNS_ADDR addr; PDNS_RECORD prr; DNSDBG( TRACE, ( "\nDns_CreateRecordForIpString( %S, wType = %d )\n", pwsName, wType )); if ( !pwsName ) { return NULL; } // // support A or AAAA queries for IP strings // - IP4 strings must be in w.x.y.z form otherwise // we convert the all numeric names also // if ( wType == DNS_TYPE_A || wType == 0 ) { IP4_ADDRESS ip4; PCWSTR pdot; DWORD count; if ( ! Dns_Ip4StringToAddress_W( & ip4, (PWSTR) pwsName ) ) { goto Try6; } // verify three dot form w.x.y.z pdot = pwsName; count = 3; while ( count-- ) { pdot = wcschr( pdot, L'.' ); if ( !pdot || !*++pdot ) { goto Try6; } } DnsAddr_BuildFromIp4( &addr, ip4, 0 ); wType = DNS_TYPE_A; goto Build; } Try6: if ( wType == DNS_TYPE_AAAA || wType == 0 ) { // this will convert any form, // but set type==AAAA to fail record build if // did NOT build IP6 if ( Dns_StringToDnsAddr_W( & addr, (PWSTR) pwsName ) ) { wType = DNS_TYPE_AAAA; goto Build; } } return NULL; // no match Build: // // name is IP string -- build record // prr = Dns_CreateForwardRecord( (PDNS_NAME) pwsName, wType, & addr, Ttl, DnsCharSetUnicode, DnsCharSetUnicode ); DNSDBG( TRACE, ( "Create record %p for IP string %S type %d.\n", prr, pwsName, wType )); return prr; } // // End rralloc.c //