|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
localip.c
Abstract:
Local IP address routines.
Author:
Jim Gilroy October 2000
Revision History:
--*/
#include "local.h"
//
// TTL on local records
//
// Use registration TTL
//
#define LOCAL_IP_TTL (g_RegistrationTtl)
PDNS_ADDR_ARRAY GetLocalAddrArrayFromResolver( VOID ) /*++
Routine Description:
Get local address info from resolver.
Arguments:
None
Return Value:
Ptr to address info array from resolver. NULL on failure.
--*/ { DNS_STATUS rpcStatus; PDNS_ADDR_ARRAY paddrArray = NULL; ENVAR_DWORD_INFO filterInfo;
//
// get config including environment variable
//
Reg_ReadDwordEnvar( RegIdFilterClusterIp, &filterInfo );
//
// query resolver
//
RpcTryExcept { rpcStatus = NO_ERROR; R_ResolverGetLocalAddrInfoArray( NULL, & paddrArray, filterInfo ); } RpcExcept( DNS_RPC_EXCEPTION_FILTER ) { rpcStatus = RpcExceptionCode(); } RpcEndExcept
// return array
DNS_ASSERT( !rpcStatus || !paddrArray );
return paddrArray; }
PIP4_ARRAY LocalIp_GetIp4Array( VOID ) /*++
Routine Description:
Get local IP4 address array.
Arguments:
None
Return Value:
Ptr to IP4 addresses. NULL on failure.
--*/ { PDNS_ADDR_ARRAY paddrArray = NULL; PIP4_ARRAY pipArray = NULL; DWORD count; DWORD i;
//
// addr info array from resolver
// => build IP array from IPs
//
paddrArray = GetLocalAddrArrayFromResolver(); if ( paddrArray ) { count = paddrArray->AddrCount; if ( count > 0 ) { pipArray = (PIP4_ARRAY) DnsCreateIpArray( count ); if ( pipArray ) { for ( i=0; i < count; i++ ) { pipArray->AddrArray[i] = paddrArray->AddrArray[i].IpAddr; } goto Done; } } }
//
// no array from resolver -- build directly
// - no chance to filter
//
count = 0; pipArray = (PIP4_ARRAY) Dns_GetLocalIpAddressArray(); if ( pipArray ) { count = pipArray->AddrCount; if ( count == 0 ) { FREE_HEAP( pipArray ); pipArray = NULL; } } Done:
// free blob from resolver
if ( paddrArray ) { FREE_HEAP( paddrArray ); }
// set out param
return( pipArray ); }
DWORD DnsGetIpAddressInfoList( OUT PDNS_ADDRESS_INFO * ppAddrInfo ) /*++
Routine Description:
Get local IP4 address info -- include IP and subnet mask.
DCR: good to get rid of old DNS_ADDRESS_INFO function
Only use of this function is in DHCP, which is calling here rather than IP help API for some reason.
Arguments:
ppAddrInfo -- addr to recv ptr to addr info array; caller must free
Return Value:
Count of IP addresses in array. Zero on failure.
--*/ { PDNS_ADDR_ARRAY paddrArray = NULL; PDNS_ADDRESS_INFO pnew = NULL; PDNS_ADDRESS_INFO pinfo; DWORD count; DNS_ADDRESS_INFO infoBuffer[256];
//
// addr info array from resolver
// => build IP array from IPs
//
// DCR_WARNING: ADDR_INFO to ADDRESS_INFO conversion
// if keep this function and change ADDR_INFO for IP6
// then this quicky hack breaks
//
paddrArray = GetLocalAddrArrayFromResolver(); if ( paddrArray ) { count = paddrArray->AddrCount; pinfo = (PDNS_ADDRESS_INFO) paddrArray->AddrArray; }
//
// no array from resolver -- build directly
//
else { count = Dns_GetIpAddresses( infoBuffer, 256 ); pinfo = infoBuffer; }
//
// allocate result buffer
// copy addr info array to buffer
//
if ( count ) { DWORD size = count * sizeof(DNS_ADDRESS_INFO);
pnew = (PDNS_ADDRESS_INFO) ALLOCATE_HEAP( size ); if ( !pnew ) { count = 0; goto Done; } RtlCopyMemory( pnew, pinfo, size ); }
Done:
// free blob from resolver
if ( paddrArray ) { FREE_HEAP( paddrArray ); }
// set out param
*ppAddrInfo = pnew; return( count ); }
PDNS_ADDR_ARRAY DnsGetLocalAddrArray( VOID ) /*++
Routine Description:
Get local address info array.
Arguments:
None.
Return Value:
Ptr to new addr info array. Caller MUST free. NULL on error.
--*/ { PDNS_ADDR_ARRAY paddrArray;
DNSDBG( TRACE, ( "DnsGetLocalAddrArray()\n" ));
//
// addr info array from resolver
// => build IP array from IPs
//
paddrArray = GetLocalAddrArrayFromResolver(); if ( paddrArray ) { return paddrArray; }
//
// no array from resolver -- build directly
//
return DnsGetLocalAddrArrayDirect(); }
//
// Direct routines -- build the IP info
//
PDNS_ADDR_ARRAY DnsGetLocalAddrArrayDirect( VOID ) /*++
Routine Description:
Get local address info array.
Arguments:
None.
Return Value:
Ptr to addr info array. NULL on failure.
--*/ { PDNS_ADDRESS_INFO pqueryBuf; PDNS_ADDR_ARRAY pnew = NULL; DWORD count; DWORD size;
DNSDBG( TRACE, ( "DnsGetLocalAddrArrayDirect()\n" ));
//
// create big buffer IP help call
//
pqueryBuf = ALLOCATE_HEAP( sizeof(DNS_ADDRESS_INFO) * DNS_MAX_IP_INTERFACE_COUNT ); if ( !pqueryBuf ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); }
//
// get IP addresses
// - if zero, determine is error or really no IPs
//
count = Dns_GetIpAddresses( pqueryBuf, DNS_MAX_IP_INTERFACE_COUNT ); #if 0
// don't really need this
// if can't get count -- it's zero
// to use this on Win2K would have to change
// Dns_GetIpAddresses() which is in lib
if ( count == 0 ) { if ( GetLastError() != NO_ERROR ) { goto Cleanup; } } #endif
//
// build correctly sized array
//
size = SIZE_FOR_ADDR_ARRAY( count ); pnew = (PDNS_ADDR_ARRAY) ALLOCATE_HEAP( size ); if ( !pnew ) { SetLastError( DNS_ERROR_NO_MEMORY ); goto Cleanup; }
pnew->AddrCount = count;
RtlCopyMemory( pnew->AddrArray, pqueryBuf, count * sizeof(DNS_ADDR_INFO) );
Cleanup:
FREE_HEAP( pqueryBuf );
return( pnew ); }
//
// Build local records
//
PDNS_RECORD GetLocalPtrRecord( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Get pointer record for local IP.
Arguments:
pBlob -- query blob
Uses: pNameOrig wType pNetInfo
Sets: NameBufferWide -- used as local storage
Return Value:
Ptr to record for query, if query name\type is IP. NULL if query not for IP.
--*/ { IP_UNION ipUnion; PDNS_RECORD prr; IP4_ADDRESS ip4; PSTR pnameHost = NULL; PSTR pnameDomain; PDNS_ADAPTER padapter = NULL; DWORD iter; DWORD jter; DWORD bufLength; INT family; PSTR pnameQuery = pBlob->pNameOrigWire; PDNS_NETINFO pnetInfo = pBlob->pNetworkInfo;
DNSDBG( TRACE, ( "\nGetLocalPtrRecord( %s )\n", pnameQuery ));
if ( !pnameQuery ) { return NULL; }
//
// convert reverse name to IP
//
bufLength = sizeof(IP6_ADDRESS); family = 0;
if ( ! Dns_ReverseNameToAddress_A( (PCHAR) & ipUnion.Addr, & bufLength, pnameQuery, & family ) ) { DNSDBG( ANY, ( "WARNING: Ptr lookup name %s is not reverse name!\n", pnameQuery )); return NULL; } ipUnion.IsIp6 = (family == AF_INET6 );
//
// check for IP match
// - first loopback or any
//
if ( ipUnion.IsIp6 ) { if ( IP6_IS_ADDR_UNSPECIFIED( (PIP6_ADDRESS)&ipUnion.Addr ) || IP6_IS_ADDR_LOOPBACK( (PIP6_ADDRESS)&ipUnion.Addr ) ) { goto Matched; }
// DCR: no IP6 local addresses
DNSDBG( QUERY, ( "Local PTR lookup -- no local IP6 info -- quiting.\n" )); return NULL; }
ip4 = ipUnion.Addr.Ip4;
if ( ip4 == DNS_NET_ORDER_LOOPBACK || ip4 == 0 ) { DNSDBG( QUERY, ( "Local PTR lookup matched loopback or any.\n" ));
goto Matched; }
//
// check for cluster match
//
// if cluster match, allow query to go to wire
//
// DCR: cluster record PTR build for cluster name
//
if ( pBlob->pfnIsClusterIp ) { if ( (pBlob->pfnIsClusterIp)( pBlob, &ipUnion ) ) { return NULL; } }
//
// check for specific IP match
//
for ( iter = 0; iter < pnetInfo->AdapterCount; iter ++ ) { PIP_ARRAY parray;
padapter = pnetInfo->AdapterArray[iter]; if ( !padapter ) { continue; } parray = padapter->pAdapterIPAddresses; if ( !parray ) { DNSDBG( QUERY, ( "Local PTR lookup -- no IPs for adapter name (%s).\n", padapter->pszAdapterDomain )); continue; }
for ( jter = 0; jter < parray->AddrCount; jter++ ) { if ( parray->AddrArray[jter] == ip4 ) { goto Matched; } } }
//
// no IP match
//
DNSDBG( QUERY, ( "Leave local PTR lookup. No local IP match.\n" "\treverse name = %s\n", pnameQuery ));
return NULL;
Matched:
//
// create hostname
// preference order:
// - full PDN
// - full adapter domain name from adapter with IP
// - hostname (single label)
// - "localhost"
//
{ PCHAR pnameBuf = (PCHAR) pBlob->NameBufferWide;
pnameHost = pnetInfo->pszHostName; if ( !pnameHost ) { pnameHost = "localhost"; goto Build; } pnameDomain = pnetInfo->pszDomainName; if ( !pnameDomain ) { // use the adapter name even if NOT set for registration
// if ( !padapter ||
// !(padapter->InfoFlags & DNS_FLAG_REGISTER_DOMAIN_NAME) )
if ( !padapter ) { goto Build; } pnameDomain = padapter->pszAdapterDomain; if ( !pnameDomain ) { goto Build; } } if ( ! Dns_NameAppend_A( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnameHost, pnameDomain ) ) { DNS_ASSERT( FALSE ); goto Build; } pnameHost = pnameBuf;
Build: //
// create record
//
prr = Dns_CreatePtrRecordEx( & ipUnion, pnameHost, LOCAL_IP_TTL, DnsCharSetUtf8, DnsCharSetUnicode ); if ( !prr ) { DNSDBG( ANY, ( "Local PTR record creation failed for name %s!\n", pnameHost )); return NULL; } }
DNSDBG( QUERY, ( "Created local PTR record %p with hostname %s.\n" "\treverse name = %S\n", prr, pnameHost, pnameQuery ));
return prr; }
PDNS_RECORD BuildRecordForLocalIp4( IN PSTR pszName, IN IP4_ADDRESS IpAddr, IN IS_CLUSTER_IP_FUNC pfnIsClusterIp ) /*++
Routine Description:
Build local IP record.
Wraps up default cluster filtering and record building.
Arguments:
pszName -- name of record
IpAddr -- IP4 address
pfnIsClusterIp -- filtering function Return Value:
TRUE if created record. FALSE on error (cluster IP or mem alloc failure).
--*/ { PDNS_RECORD prr;
DNSDBG( TRACE, ( "BuildRecordForLocalIp4( %s, %s )\n", pszName, IP_STRING(IpAddr) ));
// filter off cluster IP -- if desired
if ( pfnIsClusterIp ) { IP_UNION ipUnion;
IPUNION_SET_IP4( &ipUnion, IpAddr );
if ( pfnIsClusterIp( NULL, // no blob required
&ipUnion ) ) { return NULL; } }
// create the record
return Dns_CreateARecord( pszName, IpAddr, LOCAL_IP_TTL, DnsCharSetUtf8, DnsCharSetUnicode ); }
PDNS_RECORD GetLocalAddressRecord( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Get address record for local IP.
Arguments:
pBlob -- query blob
Uses: pNameOrig wType pNetInfo
Sets: fNoIpLocal TRUE -- no IP of type found, defaulted record FALSE -- records valid
NameBufferWide -- used as local storage
Return Value:
Ptr to record for query, if query name\type is IP. NULL if query not for IP.
--*/ { IP_UNION ipUnion; IP4_ADDRESS ip4; IP6_ADDRESS ip6; PDNS_RECORD prr; BOOL fmatchedName = FALSE; PSTR pnameRecord = NULL; PWSTR pnameRecordWide = NULL; DWORD iter; DWORD jter; DWORD bufLength; PSTR pnameDomain; DNS_RRSET rrset; WORD wtype = pBlob->wType; PSTR pnameBuf = (PCHAR) pBlob->NameBufferWide; PWSTR pnameQuery = pBlob->pNameOrig; PDNS_NETINFO pnetInfo = pBlob->pNetworkInfo; IS_CLUSTER_IP_FUNC pfnClusterFilter;
DNSDBG( TRACE, ( "GetLocalAddressRecord( %S, %d )\n", pnameQuery, wtype ));
// clear out param
pBlob->fNoIpLocal = FALSE;
//
// NULL treated as local PDN
//
if ( !pnameQuery ) { DNSDBG( QUERY, ( "Local lookup -- no query name, treat as PDN.\n" )); goto MatchedPdn; }
//
// loopback or localhost
//
if ( Dns_NameCompare_W( pnameQuery, L"loopback" ) || Dns_NameCompare_W( pnameQuery, L"localhost" ) ) { pnameRecord = (PSTR) pnameQuery, IP6_SET_ADDR_LOOPBACK( &ip6 ); ip4 = DNS_NET_ORDER_LOOPBACK; goto SingleIp; }
//
// if no hostname -- done
//
if ( !pnetInfo->pszHostName ) { DNSDBG( QUERY, ( "No hostname configured!\n" )); return NULL; }
//
// copy wire format name
//
if ( ! Dns_NameCopyStandard_A( pnameBuf, pBlob->pNameOrigWire ) ) { DNSDBG( ANY, ( "Invalid name %S to local address query.\n", pnameQuery )); return NULL; }
// split query name into hostname and domain name
pnameDomain = Dns_SplitHostFromDomainName_A( pnameBuf );
// must have hostname match
if ( !Dns_NameCompare_UTF8( pnameBuf, pnetInfo->pszHostName ) ) { DNSDBG( ANY, ( "Local lookup, failed hostname match!\n", pnameQuery )); return NULL; }
//
// hostname's match
// - no domain name => PDN equivalent
// - match PDN => all addresses
// - match adapter name => adapter addresses
// - no match
//
// first setup
// - RR set builder
// - filtering function
//
DNS_RRSET_INIT( rrset );
pfnClusterFilter = pBlob->pfnIsClusterIp; if ( pfnClusterFilter && !(pBlob->Flags & DNSP_QUERY_FILTER_CLUSTER) ) { pfnClusterFilter = NULL; }
if ( !pnameDomain ) { DNSDBG( QUERY, ( "Local lookup -- no domain, treat as PDN!\n" )); goto MatchedPdn; }
// check PDN match
if ( Dns_NameCompare_UTF8( pnameDomain, pnetInfo->pszDomainName ) ) { DNSDBG( QUERY, ( "Local lookup -- matched PDN!\n" )); goto MatchedPdn; }
//
// check adapter name match
//
for ( iter = 0; iter < pnetInfo->AdapterCount; iter ++ ) { PDNS_ADAPTER padapter = pnetInfo->AdapterArray[iter]; PIP_ARRAY parray;
if ( !padapter || !(padapter->InfoFlags & DNS_FLAG_REGISTER_DOMAIN_NAME) ) { continue; } if ( ! Dns_NameCompare_UTF8( pnameDomain, padapter->pszAdapterDomain ) ) { continue; }
// build name if we haven't built it before
// we stay in the loop in case more than one
// adapter has the same domain name
if ( !fmatchedName ) { DNSDBG( QUERY, ( "Local lookup -- matched adapter name %s\n", padapter->pszAdapterDomain ));
if ( ! Dns_NameAppend_A( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnetInfo->pszHostName, padapter->pszAdapterDomain ) ) { DNS_ASSERT( FALSE ); return NULL; } pnameRecord = pnameBuf; fmatchedName = TRUE; }
// build forward records for all IPs in list
//
// DCR: IP6 addresses missing
if ( wtype == DNS_TYPE_AAAA ) { goto NoIp; }
// no IP for adapter?
parray = padapter->pAdapterIPAddresses; if ( !parray ) { DNSDBG( QUERY, ( "Local lookup -- no IPs for adapter name (%s) matched!\n", padapter->pszAdapterDomain )); }
// build records for adapter IPs
for ( jter = 0; jter < parray->AddrCount; jter++ ) { prr = BuildRecordForLocalIp4( pnameRecord, parray->AddrArray[jter], pfnClusterFilter ); if ( prr ) { pnameRecord = NULL; DNS_RRSET_ADD( rrset, prr ); } } }
// done with adapter name
// either
// - no match
// - match but didn't get IPs
// - match
if ( !fmatchedName ) { DNSDBG( QUERY, ( "Leave GetLocalAddressRecord() => no domain name match.\n" )); return NULL; }
prr = rrset.pFirstRR; if ( prr ) { DNSDBG( QUERY, ( "Leave GetLocalAddressRecord() => %p matched adapter name.\n", prr )); return prr; } goto NoIp;
MatchedPdn:
//
// matched PDN
//
// must build in specific order
// - first IP in each adapter
// - remainder of IPs on adapters
//
fmatchedName = TRUE;
if ( ! Dns_NameAppend_A( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnetInfo->pszHostName, pnetInfo->pszDomainName ) ) { DNS_ASSERT( FALSE ); return NULL; } pnameRecord = pnameBuf;
// DCR: IP6 addresses missing
if ( wtype == DNS_TYPE_AAAA ) { goto NoIp; }
// get first IP in each adapter
for ( iter = 0; iter < pnetInfo->AdapterCount; iter ++ ) { PDNS_ADAPTER padapter = pnetInfo->AdapterArray[iter]; PIP_ARRAY parray;
if ( !padapter || !(parray = padapter->pAdapterIPAddresses) ) { continue; } prr = BuildRecordForLocalIp4( pnameRecord, parray->AddrArray[0], pfnClusterFilter ); if ( prr ) { pnameRecord = NULL; DNS_RRSET_ADD( rrset, prr ); } }
// get rest of IPs in each adapter
for ( iter = 0; iter < pnetInfo->AdapterCount; iter ++ ) { PDNS_ADAPTER padapter = pnetInfo->AdapterArray[iter]; PIP_ARRAY parray;
if ( !padapter || !(parray = padapter->pAdapterIPAddresses) ) { continue; } for ( jter = 1; jter < parray->AddrCount; jter++ ) { prr = BuildRecordForLocalIp4( pnameRecord, parray->AddrArray[jter], pfnClusterFilter ); if ( prr ) { pnameRecord = NULL; DNS_RRSET_ADD( rrset, prr ); } } }
// if successfully built -- done
prr = rrset.pFirstRR; if ( prr ) { DNSDBG( QUERY, ( "Leave GetLocalAddressRecord() => %p matched PDN name.\n", prr )); return prr; }
// matched name but found no records
// fall through to NoIp section
//
//goto NoIp;
NoIp:
//
// matched name -- but no IP
// use loopback address; assume this is a lookup prior to
// connect which happens to be the local name, rather than an
// explict local lookup to get binding IPs
//
// DCR: fix IP6 hack for local names
DNSDBG( ANY, ( "WARNING: local name match but no IP -- using loopback\n" ));
IP6_SET_ADDR_LOOPBACK( &ip6 ); ip4 = DNS_NET_ORDER_LOOPBACK; pBlob->fNoIpLocal = TRUE;
// fall through to single IP
SingleIp:
// single IP
// - loopback address and be unicode queried name
// - no IP failure (zero IP) and be UTF8 PDN name
if ( wtype == DNS_TYPE_A ) { IPUNION_SET_IP4( &ipUnion, ip4 ); } else { IPUNION_SET_IP6( &ipUnion, ip6 ); }
prr = Dns_CreateForwardRecord( (PSTR) pnameRecord, & ipUnion, LOCAL_IP_TTL, (pnameRecord == (PSTR)pnameQuery) ? DnsCharSetUnicode : DnsCharSetUtf8, DnsCharSetUnicode );
return prr; }
DNS_STATUS GetRecordsForLocalName( IN OUT PQUERY_BLOB pBlob ) /*++
Routine Description:
Get local address info array.
Arguments:
pBlob -- query blob
Uses: pNameOrig wType pNetInfo
Sets: pLocalRecords fNoIpLocal if local name without records
Return Value:
ERROR_SUCCESS if successful. DNS_ERROR_RCODE_NAME_ERROR on failure.
--*/ { WORD wtype = pBlob->wType; PDNS_RECORD prr = NULL;
if ( wtype == DNS_TYPE_A || wtype == DNS_TYPE_AAAA ) { prr = GetLocalAddressRecord( pBlob ); }
else if ( wtype == DNS_TYPE_PTR ) { prr = GetLocalPtrRecord( pBlob ); }
// set local records
// - if not NO IP situation then this
// is final query result also
if ( prr ) { pBlob->pLocalRecords = prr; if ( !pBlob->fNoIpLocal ) { pBlob->pRecords = prr; } return ERROR_SUCCESS; }
return DNS_ERROR_RCODE_NAME_ERROR; }
//
// End localip.c
//
|