/*++ Copyright (c) 2000-2002 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) // // Test locality. // BOOL LocalIp_IsAddrLocal( IN PDNS_ADDR pAddr, IN PDNS_ADDR_ARRAY pLocalArray, OPTIONAL IN PDNS_NETINFO pNetInfo OPTIONAL ) /*++ Routine Description: Determine if IP is local. Arguments: pAddr -- ptr to IP to test pLocalArray -- local addresses to check pNetInfo -- network info to check Return Value: TRUE if local IP FALSE if remote --*/ { BOOL bresult = FALSE; PADDR_ARRAY parray; // // test for loopback // if ( DnsAddr_IsLoopback( pAddr, 0 // any family ) ) { return TRUE; } // // test against local addrs // - use addr list if provided // - use netinfo if provided // - otherwise query to get it // parray = pLocalArray; if ( !parray ) { parray = NetInfo_GetLocalAddrArray( pNetInfo, NULL, // no specific adapter 0, // no specific family 0, // no flags FALSE // no force ); } bresult = DnsAddrArray_ContainsAddr( parray, pAddr, DNSADDR_MATCH_IP ); if ( parray != pLocalArray ) { DnsAddrArray_Free( parray ); } return bresult; } // // Local address list // BOOL local_ScreenLocalAddrNotCluster( IN PDNS_ADDR pAddr, IN PDNS_ADDR pScreenAddr OPTIONAL ) /*++ Routine Description: Screen cluster out of local addrs. This is DnsAddrArray_ContainsAddrEx() screening function for use by GetLocalPtrRecord() to avoid matching cluster addresses. Arguments: pAddr -- address to screen pScreenAddr -- screening info; ignored for this function Return Value: TRUE if local addr passes screen -- is not cluster IP. FALSE if cluster. --*/ { // screen flags // - exact match on address type flag bits return ( !(pAddr->Flags & DNSADDR_FLAG_TRANSIENT) ); } PDNS_RECORD local_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. --*/ { DNS_ADDR addr; PDNS_ADDR paddr = &addr; PDNS_RECORD prr; PWSTR pnameHost = NULL; PWSTR pnameDomain; PDNS_ADAPTER padapter = NULL; DWORD iter; PWSTR pnameQuery = pBlob->pNameOrig; PDNS_NETINFO pnetInfo = pBlob->pNetInfo; DNSDBG( TRACE, ( "\nlocal_GetLocalPtrRecord( %S )\n", pnameQuery )); if ( !pnameQuery ) { return NULL; } // // convert reverse name to IP // if ( ! Dns_ReverseNameToDnsAddr_W( paddr, pnameQuery ) ) { DNSDBG( ANY, ( "WARNING: Ptr lookup name %S is not reverse name!\n", pnameQuery )); return NULL; } // // check for generic IP match // - skip for mcast response // - accept loopback or any on normal query // if ( !(pBlob->Flags & DNSP_QUERY_NO_GENERIC_NAMES) ) { if ( DnsAddr_IsLoopback( paddr, 0 ) ) { DNSDBG( QUERY, ( "Local PTR lookup matched loopback.\n" )); goto Matched; } else if ( DnsAddr_IsUnspec( paddr, 0 ) ) { DNSDBG( QUERY, ( "Local PTR lookup matched unspec.\n" )); goto Matched; } } // // check for specific IP match // NetInfo_AdapterLoopStart( pnetInfo ); while( padapter = NetInfo_GetNextAdapter( pnetInfo ) ) { // // have address match? // if server must use screening function to skip cluster IPs // // note: cluster IPs will be mapped back to virtual cluster // name by cache if ( DnsAddrArray_ContainsAddrEx( padapter->pLocalAddrs, paddr, DNSADDR_MATCH_IP, g_IsServer ? local_ScreenLocalAddrNotCluster : NULL, NULL // no screen address required ) ) { 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" // { PWCHAR pnameBuf = pBlob->NameBuffer; pnameHost = pnetInfo->pszHostName; if ( !pnameHost ) { pnameHost = L"localhost"; goto Build; } pnameDomain = pnetInfo->pszDomainName; if ( !pnameDomain ) { // use the adapter name even if NOT set for registration // if ( !padapter || // !(padapter->InfoFlags & AINFO_FLAG_REGISTER_DOMAIN_NAME) ) if ( !padapter ) { goto Build; } pnameDomain = padapter->pszAdapterDomain; if ( !pnameDomain ) { goto Build; } } if ( ! Dns_NameAppend_W( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnameHost, pnameDomain ) ) { DNS_ASSERT( FALSE ); goto Build; } pnameHost = pnameBuf; Build: // // create record // prr = Dns_CreatePtrRecordEx( paddr, (PDNS_NAME) pnameHost, LOCAL_IP_TTL, DnsCharSetUnicode, 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; } VOID localip_BuildRRListFromArray( IN OUT PDNS_RRSET pRRSet, IN PWSTR pNameRecord, IN WORD wType, IN PDNS_ADDR_ARRAY pAddrArray ) /*++ Routine Description: Build address record lists for local IP. Helper function as this same logic is in multiple places due to the tedious way we must put this together. Arguments: Return Value: --*/ { DWORD jter; PDNS_RECORD prr; INT fpass; DNSDBG( TRACE, ( "localip_BuildRRListFromArray()\n" "\tpname = %S\n" "\twtype = %d\n" "\tparray = %p\n", pNameRecord, wType, pAddrArray )); // // validate array // if ( !pAddrArray ) { DNSDBG( QUERY, ( "No addrs for record build -- NULL array!!!\n" )); return; } // // loop through adapter addresses // if ( pRRSet->pFirstRR != NULL ) { pNameRecord = NULL; } for ( jter = 0; jter < pAddrArray->AddrCount; jter++ ) { prr = Dns_CreateForwardRecord( pNameRecord, wType, &pAddrArray->AddrArray[jter], LOCAL_IP_TTL, DnsCharSetUnicode, DnsCharSetUnicode ); if ( prr ) { DNS_RRSET_ADD( *pRRSet, prr ); pNameRecord = NULL; } } } PDNS_RECORD local_GetLocalAddressRecord( IN OUT PQUERY_BLOB pBlob ) /*++ Routine Description: Get address record for local IP. Arguments: pBlob -- query blob Uses: pNameOrig wType pNetInfo fNoGenericNames Sets: fNoIpLocal TRUE -- no IP of type found, defaulted record FALSE -- records valid NameBuffer -- used as local storage fGenericNames -- accept local generic names (NULL, loopback, localhost) TRUE for DnsQuery() path FALSE for mcast queries Return Value: Ptr to record for query, if query name\type is IP. NULL if query not for IP. --*/ { DNS_ADDR addr; IP4_ADDRESS ip4; IP6_ADDRESS ip6; PDNS_ADDR_ARRAY parray = NULL; DWORD addrFlag; DWORD family; PDNS_RECORD prr; BOOL fmatchedName = FALSE; PWSTR pnameRecord = NULL; DWORD iter; DWORD bufLength; PWSTR pnameDomain; DNS_RRSET rrset; WORD wtype = pBlob->wType; PWSTR pnameBuf = pBlob->NameBuffer; PWSTR pnameQuery = pBlob->pNameOrig; PDNS_NETINFO pnetInfo = pBlob->pNetInfo; PDNS_ADAPTER padapter; DNSDBG( TRACE, ( "local_GetLocalAddressRecord( %S, %d )\n", pnameQuery, wtype )); // clear out param pBlob->fNoIpLocal = FALSE; // address types to include addrFlag = DNS_CONFIG_FLAG_ADDR_NON_CLUSTER; family = Family_GetFromDnsType( wtype ); if ( !family ) { DNS_ASSERT( FALSE ); return NULL; } // init record builder DNS_RRSET_INIT( rrset ); // // generic local names // - skip for doing MCAST matching // - NULL, empty, loopback, localhost accepted for regular query // if ( pBlob->Flags & DNSP_QUERY_NO_GENERIC_NAMES ) { if ( !pnameQuery ) { return NULL; } } else { // // NULL treated as local PDN // if ( !pnameQuery || !*pnameQuery ) { DNSDBG( QUERY, ( "Local lookup -- no query name, treat as PDN.\n" )); goto MatchedPdn; } // // "*" treated as all machine records // if ( Dns_NameCompare_W( pnameQuery, L"..localmachine" ) ) { DNSDBG( QUERY, ( "Local lookup -- * query name.\n" )); addrFlag |= DNS_CONFIG_FLAG_ADDR_CLUSTER; goto MatchedPdn; } // // loopback or localhost // if ( Dns_NameCompare_W( pnameQuery, L"loopback" ) || Dns_NameCompare_W( pnameQuery, L"localhost" ) ) { pnameRecord = 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 name // if ( ! Dns_NameCopyStandard_W( pnameBuf, pBlob->pNameOrig ) ) { DNSDBG( ANY, ( "Invalid name %S to local address query.\n", pnameQuery )); return NULL; } // split query name into hostname and domain name pnameDomain = Dns_SplitHostFromDomainNameW( pnameBuf ); // must have hostname match if ( !Dns_NameCompare_W( 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 // // check PDN match if ( !pnameDomain ) { DNSDBG( QUERY, ( "Local lookup -- no domain, treat as PDN!\n" )); goto MatchedPdn; } if ( Dns_NameCompare_W( pnameDomain, pnetInfo->pszDomainName ) ) { DNSDBG( QUERY, ( "Local lookup -- matched PDN!\n" )); goto MatchedPdn; } // // NO PDN match -- check adapter name match // for ( iter=0; iterAdapterCount; iter++ ) { padapter = NetInfo_GetAdapterByIndex( pnetInfo, iter ); if ( !(padapter->InfoFlags & AINFO_FLAG_REGISTER_DOMAIN_NAME) || ! padapter->pLocalAddrs || ! Dns_NameCompare_W( 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_W( 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 adapter // // note: we do NOT include cluster addrs for adapter name match // as we only must be able to provide in the // gethostbyname(NULL) type cases, which use PDN // // DCR: mem alloc failures building local records // getting no records built mapped properly to NO_MEMORY error // parray = NetInfo_CreateLocalAddrArray( pnetInfo, NULL, // no adapter name padapter, // just this adapter family, addrFlag ); if ( !parray ) { continue; } localip_BuildRRListFromArray( &rrset, pnameRecord, wtype, parray ); DnsAddrArray_Free( parray ); parray = NULL; } // // done with adapter name match // either // - no match // - match but didn't get IPs // - match if ( !fmatchedName ) { DNSDBG( QUERY, ( "Leave local_GetLocalAddressRecord() => no domain name match.\n" )); return NULL; } prr = rrset.pFirstRR; if ( prr ) { DNSDBG( QUERY, ( "Leave local_GetLocalAddressRecord() => %p matched adapter name.\n", prr )); return prr; } goto NoIp; MatchedPdn: // // matched PDN // // for gethostbyname() app-compat, must build in specific order // - first IP in each adapter // - remainder of IPs on adapters // if ( pnetInfo->pszHostName ) { if ( ! Dns_NameAppend_W( pnameBuf, DNS_MAX_NAME_BUFFER_LENGTH, pnetInfo->pszHostName, pnetInfo->pszDomainName ) ) { DNS_ASSERT( FALSE ); return NULL; } pnameRecord = pnameBuf; } else { pnameRecord = L"localhost"; } // // read addrs // // note: we don't add cluster flag above, as cluster addrs // not used on adapter name matches, just PDN for gethostbyname() // compat // if ( g_IsServer && (pBlob->Flags & DNSP_QUERY_INCLUDE_CLUSTER) ) { addrFlag |= DNS_CONFIG_FLAG_ADDR_CLUSTER; } // DCR: mem alloc failures building local records // getting no records built mapped properly to NO_MEMORY error // parray = NetInfo_CreateLocalAddrArray( pnetInfo, NULL, // no adapter name NULL, // no specific adatper family, addrFlag ); if ( !parray ) { return NULL; } localip_BuildRRListFromArray( &rrset, pnameRecord, wtype, parray ); DnsAddrArray_Free( parray ); // if successfully built -- done prr = rrset.pFirstRR; if ( prr ) { DNSDBG( QUERY, ( "Leave local_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 // 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 if ( wtype == DNS_TYPE_A ) { DnsAddr_BuildFromIp4( &addr, ip4, 0 // no port ); } else { DnsAddr_BuildFromIp6( &addr, & ip6, 0, // no scope 0 // no port ); } prr = Dns_CreateForwardRecord( (PDNS_NAME) pnameRecord, wtype, & addr, LOCAL_IP_TTL, DnsCharSetUnicode, DnsCharSetUnicode ); return prr; } DNS_STATUS Local_GetRecordsForLocalName( IN OUT PQUERY_BLOB pBlob ) /*++ Routine Description: Get local address info array. EXPORTED: called by resolver for MCAST 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 = local_GetLocalAddressRecord( pBlob ); } else if ( wtype == DNS_TYPE_PTR ) { prr = local_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 //