You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
823 lines
24 KiB
823 lines
24 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
Routines implementing Dynamic DNS registration of IPv6 addresses.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <windns.h>
|
|
#include <ntddip6.h>
|
|
|
|
//
|
|
// DHCP IPv4 addresses inside Microsoft have a default TTL of 20 minutes.
|
|
//
|
|
#define MAX_AAAA_TTL 1200 // Seconds.
|
|
|
|
//
|
|
// We must update the DNS records occasionally,
|
|
// or the DNS server might garbage-collect them.
|
|
// MSDN recommends a one-day interval.
|
|
//
|
|
#define MIN_UPDATE_INTERVAL (1*DAYS*1000) // Milliseconds.
|
|
|
|
__inline ULONG
|
|
MIN(ULONG a, ULONG b)
|
|
{
|
|
if (a < b)
|
|
return a;
|
|
else
|
|
return b;
|
|
}
|
|
|
|
|
|
SOCKET g_hIpv6Socket = INVALID_SOCKET;
|
|
WSAEVENT g_hIpv6AddressChangeEvent = NULL;
|
|
HANDLE g_hIpv6AddressChangeWait = NULL;
|
|
WSAOVERLAPPED g_hIpv6AddressChangeOverlapped;
|
|
//
|
|
// Stores the state from the last invocation of OnIpv6AddressChange. The only
|
|
// two fields used from the previous state are the site id
|
|
// (ZoneIndices[ScopeLevelSite]) and Mtu. The mtu field is overloaded to store
|
|
// information if the site id was manually changed for the interface or not. If
|
|
// the site id was manually change, Mtu is set to 1, otherwise 0. Once the site
|
|
// id has been manually changed, we do not try to override it. Also, this
|
|
// information is not persistent across reboots. If the site id is changed
|
|
// manually, on the next reboot, this information is lost and the 6to4 service
|
|
// might try to assign a new value. Secondly, there is no way to undo a manual
|
|
// setting. If a user sets the site id once, there is no way to go back to
|
|
// automatic configuration.
|
|
//
|
|
PIP_ADAPTER_ADDRESSES g_PreviousInterfaceState = NULL;
|
|
|
|
#define SITEID_MANUALLY_CHANGED Mtu
|
|
|
|
//
|
|
// Our caller uses StopIpv6AddressChangeNotification
|
|
// if we fail, so we don't need to cleanup.
|
|
//
|
|
DWORD
|
|
StartIpv6AddressChangeNotification()
|
|
{
|
|
ASSERT(g_hIpv6Socket == INVALID_SOCKET);
|
|
|
|
g_hIpv6Socket = WSASocket(AF_INET6, 0, 0,
|
|
NULL, 0,
|
|
WSA_FLAG_OVERLAPPED);
|
|
if (g_hIpv6Socket == INVALID_SOCKET)
|
|
return WSAGetLastError();
|
|
|
|
//
|
|
// We create an auto-reset event in the signalled state.
|
|
// So OnIpv6AddressChange will be executed initially.
|
|
//
|
|
|
|
ASSERT(g_hIpv6AddressChangeEvent == NULL);
|
|
g_hIpv6AddressChangeEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
|
|
if (g_hIpv6AddressChangeEvent == NULL)
|
|
return GetLastError();
|
|
|
|
//
|
|
// We specify a timeout, so that we update DNS
|
|
// at least that often. Otherwise the DNS server might
|
|
// garbage-collect our records.
|
|
//
|
|
|
|
IncEventCount("AC:StartIpv6AddressChangeNotification");
|
|
if (! RegisterWaitForSingleObject(&g_hIpv6AddressChangeWait,
|
|
g_hIpv6AddressChangeEvent,
|
|
OnIpv6AddressChange,
|
|
NULL,
|
|
MIN_UPDATE_INTERVAL,
|
|
WT_EXECUTELONGFUNCTION)) {
|
|
DecEventCount("AC:StartIpv6AddressChangeNotification");
|
|
return GetLastError();
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Assume that if the primary DNS server is the same, then that's
|
|
// good enough to combine the records.
|
|
//
|
|
BOOL
|
|
IsSameDNSServer(
|
|
PIP_ADAPTER_ADDRESSES pIf1,
|
|
PIP_ADAPTER_ADDRESSES pIf2
|
|
)
|
|
{
|
|
PIP_ADAPTER_DNS_SERVER_ADDRESS pDns1, pDns2;
|
|
|
|
pDns1 = pIf1->FirstDnsServerAddress;
|
|
while ((pDns1 != NULL) &&
|
|
(pDns1->Address.lpSockaddr->sa_family != AF_INET)) {
|
|
pDns1 = pDns1->Next;
|
|
}
|
|
|
|
pDns2 = pIf2->FirstDnsServerAddress;
|
|
while ((pDns2 != NULL) &&
|
|
(pDns2->Address.lpSockaddr->sa_family != AF_INET)) {
|
|
pDns2 = pDns2->Next;
|
|
}
|
|
|
|
if ((pDns1 == NULL) || (pDns2 == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(pDns1->Address.lpSockaddr->sa_family ==
|
|
pDns2->Address.lpSockaddr->sa_family);
|
|
|
|
return !memcmp(pDns1->Address.lpSockaddr,
|
|
pDns2->Address.lpSockaddr,
|
|
pDns1->Address.iSockaddrLength);
|
|
}
|
|
|
|
DNS_RECORD *
|
|
BuildRecordSetW(
|
|
WCHAR *hostname,
|
|
PIP_ADAPTER_ADDRESSES pFirstIf,
|
|
PIP4_ARRAY *ppServerList
|
|
)
|
|
{
|
|
DNS_RECORD *RSet, *pNext;
|
|
int i, iAddressCount = 0;
|
|
PIP_ADAPTER_UNICAST_ADDRESS Address;
|
|
PIP_ADAPTER_ADDRESSES pIf;
|
|
int ServerCount = 0;
|
|
PIP_ADAPTER_DNS_SERVER_ADDRESS DnsServer;
|
|
LPSOCKADDR_IN sin;
|
|
BOOL RegisterSiteLocals = ENABLED;
|
|
|
|
//
|
|
// Count DNS servers
|
|
//
|
|
for (DnsServer = pFirstIf->FirstDnsServerAddress;
|
|
DnsServer;
|
|
DnsServer = DnsServer->Next)
|
|
{
|
|
if (DnsServer->Address.lpSockaddr->sa_family != AF_INET) {
|
|
//
|
|
// DNS api currently only supports IPv4 addresses of servers
|
|
//
|
|
continue;
|
|
}
|
|
ServerCount++;
|
|
}
|
|
if (ServerCount == 0) {
|
|
*ppServerList = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Fill in DNS server array
|
|
//
|
|
*ppServerList = MALLOC(FIELD_OFFSET(IP4_ARRAY, AddrArray[ServerCount]));
|
|
if (*ppServerList == NULL) {
|
|
return NULL;
|
|
}
|
|
(*ppServerList)->AddrCount = ServerCount;
|
|
for (i = 0, DnsServer = pFirstIf->FirstDnsServerAddress;
|
|
DnsServer;
|
|
DnsServer = DnsServer->Next)
|
|
{
|
|
sin = (LPSOCKADDR_IN)DnsServer->Address.lpSockaddr;
|
|
if (sin->sin_family == AF_INET) {
|
|
(*ppServerList)->AddrArray[i++] = sin->sin_addr.s_addr;
|
|
}
|
|
}
|
|
ASSERT(i == ServerCount);
|
|
|
|
//
|
|
// Decide whether to register site locals in DNS.
|
|
//
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwErr;
|
|
|
|
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY_GLOBAL, 0,
|
|
KEY_QUERY_VALUE, &hKey);
|
|
if (dwErr == NO_ERROR) {
|
|
RegisterSiteLocals = GetInteger(hKey, L"EnableSiteLocalDdns",
|
|
ENABLED);
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Count eligible addresses
|
|
//
|
|
for (pIf=pFirstIf; pIf; pIf=pIf->Next) {
|
|
if (!(pIf->Flags & IP_ADAPTER_DDNS_ENABLED))
|
|
continue;
|
|
//
|
|
// Make sure interface has same DNS server
|
|
//
|
|
if ((pIf != pFirstIf) && !IsSameDNSServer(pFirstIf, pIf)) {
|
|
continue;
|
|
}
|
|
for (Address=pIf->FirstUnicastAddress; Address; Address=Address->Next) {
|
|
if ((Address->Address.lpSockaddr->sa_family == AF_INET6) &&
|
|
(Address->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) &&
|
|
((RegisterSiteLocals == ENABLED) ||
|
|
!IN6_IS_ADDR_SITELOCAL(&((LPSOCKADDR_IN6)
|
|
Address->Address.lpSockaddr)->sin6_addr))) {
|
|
iAddressCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Trace1(FSM, _T("DDNS building record set of %u addresses"), iAddressCount);
|
|
|
|
if (iAddressCount == 0) {
|
|
//
|
|
// Build a record set that specifies deletion.
|
|
//
|
|
|
|
RSet = MALLOC(sizeof *RSet);
|
|
if (RSet == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(RSet, 0, sizeof *RSet);
|
|
RSet->pName = (LPTSTR)hostname;
|
|
RSet->wType = DNS_TYPE_AAAA;
|
|
return RSet;
|
|
}
|
|
|
|
RSet = MALLOC(sizeof *RSet * iAddressCount);
|
|
if (RSet == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(RSet, 0, sizeof *RSet * iAddressCount);
|
|
|
|
pNext = NULL;
|
|
i = iAddressCount;
|
|
while (--i >= 0) {
|
|
RSet[i].pNext = pNext;
|
|
pNext = &RSet[i];
|
|
}
|
|
|
|
i=0;
|
|
for (pIf=pFirstIf; pIf; pIf=pIf->Next) {
|
|
if (!(pIf->Flags & IP_ADAPTER_DDNS_ENABLED))
|
|
continue;
|
|
|
|
if ((pIf != pFirstIf) && !IsSameDNSServer(pFirstIf, pIf)) {
|
|
continue;
|
|
}
|
|
for (Address=pIf->FirstUnicastAddress;
|
|
Address;
|
|
Address=Address->Next) {
|
|
if ((Address->Address.lpSockaddr->sa_family == AF_INET6) &&
|
|
(Address->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) &&
|
|
((RegisterSiteLocals == ENABLED) ||
|
|
!IN6_IS_ADDR_SITELOCAL(&((LPSOCKADDR_IN6)
|
|
Address->Address.lpSockaddr)->sin6_addr))) {
|
|
SOCKADDR_IN6 *sin6 = (SOCKADDR_IN6 *)
|
|
Address->Address.lpSockaddr;
|
|
|
|
RSet[i].pName = (LPTSTR)hostname;
|
|
|
|
//
|
|
// Using a large TTL is not good because it means
|
|
// any changes (adding a new address, removing an address)
|
|
// might not be visible for a long time.
|
|
//
|
|
RSet[i].dwTtl = MIN(MAX_AAAA_TTL,
|
|
MIN(Address->PreferredLifetime,
|
|
Address->LeaseLifetime));
|
|
|
|
RSet[i].wType = DNS_TYPE_AAAA;
|
|
RSet[i].wDataLength = sizeof RSet[i].Data.AAAA;
|
|
RSet[i].Data.AAAA.Ip6Address =
|
|
* (IP6_ADDRESS *) &sin6->sin6_addr;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
ASSERT(i == iAddressCount);
|
|
|
|
return RSet;
|
|
}
|
|
|
|
VOID
|
|
ReportDnsUpdateStatusW(
|
|
IN DNS_STATUS Status,
|
|
IN WCHAR *hostname,
|
|
IN DNS_RECORD *RSet
|
|
)
|
|
{
|
|
Trace3(ERR, _T("6to4svc: DnsReplaceRecordSet(%ls) %s: status %d"),
|
|
hostname,
|
|
RSet->wDataLength == 0 ? "delete" : "replace",
|
|
Status);
|
|
}
|
|
|
|
//
|
|
// This function adapted from net\tcpip\commands\ipconfig\info.c
|
|
//
|
|
VOID
|
|
GetInterfaceDeviceName(
|
|
IN ULONG Ipv4IfIndex,
|
|
IN PIP_INTERFACE_INFO InterfaceInfo,
|
|
OUT LPWSTR *IfDeviceName
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
//
|
|
// search the InterfaceInfo to get the devicename for this interface.
|
|
//
|
|
|
|
(*IfDeviceName) = NULL;
|
|
for( i = 0; i < (DWORD)InterfaceInfo->NumAdapters; i ++ ) {
|
|
if( InterfaceInfo->Adapter[i].Index != Ipv4IfIndex ) continue;
|
|
(*IfDeviceName) = InterfaceInfo->Adapter[i].Name + strlen(
|
|
"\\Device\\Tcpip_" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RegisterNameOnInterface(
|
|
PIP_ADAPTER_ADDRESSES pIf,
|
|
PWCHAR hostname,
|
|
DWORD namelen)
|
|
{
|
|
DNS_RECORD *RSet = NULL;
|
|
PIP4_ARRAY pServerList = NULL;
|
|
DWORD Status;
|
|
|
|
//
|
|
// Convert to a DNS record set.
|
|
//
|
|
|
|
RSet = BuildRecordSetW(hostname, pIf, &pServerList);
|
|
if ((RSet == NULL) || (pServerList == NULL)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Trace2(ERR, _T("DDNS registering %ls to server %d.%d.%d.%d"),
|
|
hostname, PRINT_IPADDR(pServerList->AddrArray[0]));
|
|
|
|
//
|
|
// REVIEW: We could (should?) compare the current record set
|
|
// to the previous record set, and only update DNS
|
|
// if there has been a change or if there was a timeout.
|
|
//
|
|
|
|
Status = DnsReplaceRecordSetW(
|
|
RSet,
|
|
DNS_UPDATE_CACHE_SECURITY_CONTEXT,
|
|
NULL,
|
|
pServerList,
|
|
NULL);
|
|
if (Status != NO_ERROR) {
|
|
Trace1(ERR, _T("Error: DnsReplaceRecordSet returned %d"), Status);
|
|
}
|
|
|
|
ReportDnsUpdateStatusW(Status, hostname, RSet);
|
|
|
|
Cleanup:
|
|
if (pServerList) {
|
|
FREE(pServerList);
|
|
}
|
|
if (RSet) {
|
|
FREE(RSet);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DoDdnsOnInterface(
|
|
PIP_ADAPTER_ADDRESSES pIf)
|
|
{
|
|
// Leave room to add a trailing "."
|
|
WCHAR hostname[NI_MAXHOST+1];
|
|
DWORD namelen;
|
|
|
|
//
|
|
// Get the fully-qualified DNS name for this machine
|
|
// and append a trailing dot.
|
|
//
|
|
namelen = NI_MAXHOST;
|
|
if (! GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified,
|
|
hostname, &namelen)) {
|
|
return;
|
|
}
|
|
|
|
namelen = (DWORD)wcslen(hostname);
|
|
|
|
hostname[namelen] = L'.';
|
|
hostname[namelen+1] = L'\0';
|
|
|
|
RegisterNameOnInterface(pIf, hostname, namelen);
|
|
|
|
//
|
|
// Also register the connection-specific name if configured to do so.
|
|
//
|
|
if (pIf->Flags & IP_ADAPTER_REGISTER_ADAPTER_SUFFIX) {
|
|
namelen = NI_MAXHOST;
|
|
if (! GetComputerNameExW(ComputerNamePhysicalDnsHostname,
|
|
hostname, &namelen)) {
|
|
return;
|
|
}
|
|
|
|
wcscat(hostname, L".");
|
|
wcscat(hostname, pIf->DnsSuffix);
|
|
|
|
namelen = (DWORD)wcslen(hostname);
|
|
|
|
hostname[namelen] = L'.';
|
|
hostname[namelen+1] = L'\0';
|
|
|
|
RegisterNameOnInterface(pIf, hostname, namelen);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the site id of a given interface.
|
|
//
|
|
VOID
|
|
SetSiteId(
|
|
IN PIP_ADAPTER_ADDRESSES pIf,
|
|
IN ULONG SiteId)
|
|
{
|
|
PIP_ADAPTER_DNS_SERVER_ADDRESS pDNS;
|
|
IPV6_INFO_INTERFACE Update;
|
|
DWORD Result;
|
|
PSOCKADDR_IN6 pAddr;
|
|
|
|
IPV6_INIT_INFO_INTERFACE(&Update);
|
|
|
|
Update.This.Index = pIf->Ipv6IfIndex;
|
|
Update.ZoneIndices[ADE_SITE_LOCAL] = SiteId;
|
|
|
|
Result = UpdateInterface(&Update);
|
|
|
|
Trace3(ERR, _T("SetSiteId if=%d site=%d result=%d"),
|
|
pIf->Ipv6IfIndex, SiteId, Result);
|
|
|
|
pIf->ZoneIndices[ScopeLevelSite] = SiteId;
|
|
|
|
//
|
|
// Site-local addresses of DNS servers may now have the wrong scope id.
|
|
// Fix them.
|
|
//
|
|
for (pDNS = pIf->FirstDnsServerAddress; pDNS != NULL; pDNS = pDNS->Next) {
|
|
pAddr = (PSOCKADDR_IN6)pDNS->Address.lpSockaddr;
|
|
if ((pAddr->sin6_family == AF_INET6) &&
|
|
(IN6_IS_ADDR_SITELOCAL(&pAddr->sin6_addr))) {
|
|
pAddr->sin6_scope_id = SiteId;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the site id of a given interface to an unused value.
|
|
//
|
|
VOID
|
|
NewSiteId(
|
|
IN PIP_ADAPTER_ADDRESSES pIf,
|
|
IN PIP_ADAPTER_ADDRESSES pAdapterAddresses)
|
|
{
|
|
PIP_ADAPTER_ADDRESSES CompareWithIf;
|
|
ULONG PotentialSiteId = 1;
|
|
|
|
//
|
|
// Find the lowest unused site id.
|
|
//
|
|
CompareWithIf = pAdapterAddresses;
|
|
while (CompareWithIf != NULL) {
|
|
if (CompareWithIf->ZoneIndices[ScopeLevelSite] == PotentialSiteId) {
|
|
PotentialSiteId++;
|
|
CompareWithIf = pAdapterAddresses;
|
|
} else {
|
|
CompareWithIf = CompareWithIf->Next;
|
|
}
|
|
}
|
|
|
|
SetSiteId(pIf, PotentialSiteId);
|
|
}
|
|
|
|
BOOL
|
|
SameSite(
|
|
IN PIP_ADAPTER_ADDRESSES A,
|
|
IN PIP_ADAPTER_ADDRESSES B)
|
|
{
|
|
IPV6_INFO_SITE_PREFIX PrefixA, PrefixB;
|
|
|
|
//
|
|
// If a connection-specific DNS suffix exists on either, then compare that.
|
|
//
|
|
// We do this first because it's more efficient than diving to the
|
|
// kernel to get the site prefixes. In addition, it's immune to
|
|
// whether the site prefix length is correct.
|
|
//
|
|
if (((A->DnsSuffix != NULL) && (A->DnsSuffix[0] != L'\0')) ||
|
|
((B->DnsSuffix != NULL) && (B->DnsSuffix[0] != L'\0'))) {
|
|
if ((A->DnsSuffix == NULL) || (B->DnsSuffix == NULL)) {
|
|
return FALSE;
|
|
}
|
|
return (wcscmp(A->DnsSuffix, B->DnsSuffix) == 0);
|
|
}
|
|
|
|
//
|
|
// No connection-specific DNS suffix exists on either interface.
|
|
// If an IPv6 site prefix exists on either, then compare that.
|
|
//
|
|
GetFirstSitePrefix(A->Ipv6IfIndex, &PrefixA);
|
|
GetFirstSitePrefix(B->Ipv6IfIndex, &PrefixB);
|
|
if ((PrefixA.Query.IF.Index != 0) || (PrefixB.Query.IF.Index != 0)) {
|
|
if ((PrefixA.Query.IF.Index == 0) || (PrefixB.Query.IF.Index == 0)) {
|
|
return FALSE;
|
|
}
|
|
if (PrefixA.Query.PrefixLength != PrefixB.Query.PrefixLength) {
|
|
return FALSE;
|
|
}
|
|
return (RtlEqualMemory(PrefixA.Query.Prefix.s6_addr,
|
|
PrefixB.Query.Prefix.s6_addr,
|
|
sizeof(IPv6Addr)));
|
|
}
|
|
|
|
//
|
|
// No site prefix exists on either interface.
|
|
// Default to saying they're in different sites.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
VOID CALLBACK
|
|
OnIpv6AddressChange(
|
|
IN PVOID lpParameter,
|
|
IN BOOLEAN TimerOrWaitFired)
|
|
{
|
|
PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL;
|
|
PIP_ADAPTER_ADDRESSES pIf, pIf2, PreviousInterfaceState;
|
|
BOOLEAN SetPreviousState = FALSE;
|
|
ULONG BytesNeeded = 0;
|
|
DWORD dwErr;
|
|
DWORD BytesReturned;
|
|
|
|
//
|
|
// Sleep for one second.
|
|
// Often there will be multiple address changes in a small time period,
|
|
// and we prefer to update DNS once.
|
|
//
|
|
Sleep(1000);
|
|
|
|
ENTER_API();
|
|
TraceEnter("OnIpv6AddressChange");
|
|
|
|
if (g_stService == DISABLED) {
|
|
Trace0(FSM, L"Service disabled");
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// First request another async notification.
|
|
// We must do this *before* getting the address list,
|
|
// to avoid missing an address change.
|
|
//
|
|
|
|
if (TimerOrWaitFired == FALSE) {
|
|
for (;;) {
|
|
ZeroMemory(&g_hIpv6AddressChangeOverlapped, sizeof(WSAOVERLAPPED));
|
|
g_hIpv6AddressChangeOverlapped.hEvent = g_hIpv6AddressChangeEvent;
|
|
|
|
dwErr = WSAIoctl(g_hIpv6Socket, SIO_ADDRESS_LIST_CHANGE,
|
|
NULL, 0,
|
|
NULL, 0, &BytesReturned,
|
|
&g_hIpv6AddressChangeOverlapped,
|
|
NULL);
|
|
if (dwErr != 0) {
|
|
dwErr = WSAGetLastError();
|
|
if (dwErr != WSA_IO_PENDING) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// The overlapped operation was initiated.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The overlapped operation completed immediately.
|
|
// Just try it again.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the address list.
|
|
//
|
|
|
|
for (;;) {
|
|
//
|
|
// GetAdaptersAddresses only returns addresses of the specified address
|
|
// family. To obtain both IPv4 DNS server addresses and IPv6 unicast
|
|
// addresses in the same call we need to pass AF_UNSPEC.
|
|
//
|
|
dwErr = GetAdaptersAddresses(
|
|
AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
|
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
|
NULL, pAdapterAddresses, &BytesNeeded);
|
|
if (dwErr == NO_ERROR) {
|
|
SetPreviousState = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (dwErr != ERROR_BUFFER_OVERFLOW) {
|
|
Trace1(ERR, _T("Error: GetAdaptersAddresses returned %d"), dwErr);
|
|
if (pAdapterAddresses != NULL) {
|
|
FREE(pAdapterAddresses);
|
|
pAdapterAddresses = NULL;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pAdapterAddresses == NULL)
|
|
pAdapterAddresses = MALLOC(BytesNeeded);
|
|
else {
|
|
PVOID Mem;
|
|
|
|
Mem = REALLOC(pAdapterAddresses, BytesNeeded);
|
|
if (Mem == NULL) {
|
|
FREE(pAdapterAddresses);
|
|
}
|
|
pAdapterAddresses = Mem;
|
|
}
|
|
if (pAdapterAddresses == NULL) {
|
|
Trace0(ERR, _T("Error: malloc failed"));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check for site id changes. At the start of each iteration, everything
|
|
// before 'pIf' will be fine. Everything after it will be untouched.
|
|
//
|
|
for (pIf = pAdapterAddresses; pIf != NULL; pIf = pIf->Next) {
|
|
//
|
|
// Don't change values for the 6to4 or ISATAP interfaces,
|
|
// since the site id is inherited off the underlying interface.
|
|
// Also we can't change values for IPv4-only interfaces.
|
|
//
|
|
if ((pIf->Ipv6IfIndex == V4_COMPAT_IFINDEX) ||
|
|
(pIf->Ipv6IfIndex == SIX_TO_FOUR_IFINDEX) ||
|
|
(pIf->Ipv6IfIndex == 0)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Try to find if we have state for this interface from a previous
|
|
// invocation of OnIpv6AddressChange.
|
|
//
|
|
for (PreviousInterfaceState = g_PreviousInterfaceState;
|
|
PreviousInterfaceState != NULL;
|
|
PreviousInterfaceState = PreviousInterfaceState->Next) {
|
|
if (PreviousInterfaceState->Ipv6IfIndex == pIf->Ipv6IfIndex) {
|
|
break;
|
|
}
|
|
}
|
|
if (PreviousInterfaceState != NULL) {
|
|
//
|
|
// There is already state present for this interface. If the site
|
|
// id has changed from the previous state, this is a manual
|
|
// configuration. Set the manually changed flag to 1 (note that the
|
|
// Mtu is overloaded for this purpose).
|
|
//
|
|
if (PreviousInterfaceState->ZoneIndices[ScopeLevelSite] !=
|
|
pIf->ZoneIndices[ScopeLevelSite]) {
|
|
pIf->SITEID_MANUALLY_CHANGED = 1;
|
|
} else {
|
|
pIf->SITEID_MANUALLY_CHANGED =
|
|
PreviousInterfaceState->SITEID_MANUALLY_CHANGED;
|
|
}
|
|
} else {
|
|
pIf->SITEID_MANUALLY_CHANGED = 0;
|
|
}
|
|
if (pIf->SITEID_MANUALLY_CHANGED == 1) {
|
|
continue;
|
|
}
|
|
|
|
for (pIf2 = pAdapterAddresses; pIf2 != pIf; pIf2 = pIf2->Next) {
|
|
if ((pIf2->Ipv6IfIndex == V4_COMPAT_IFINDEX) ||
|
|
(pIf2->Ipv6IfIndex == SIX_TO_FOUR_IFINDEX) ||
|
|
(pIf2->Ipv6IfIndex == 0) ||
|
|
(pIf2->SITEID_MANUALLY_CHANGED == 1)) {
|
|
continue;
|
|
}
|
|
|
|
if (SameSite(pIf, pIf2)) {
|
|
if (pIf->ZoneIndices[ScopeLevelSite] !=
|
|
pIf2->ZoneIndices[ScopeLevelSite]) {
|
|
//
|
|
// pIf has just moved into the same site as an earlier
|
|
// interface.
|
|
//
|
|
SetSiteId(pIf, pIf2->ZoneIndices[ScopeLevelSite]);
|
|
}
|
|
} else {
|
|
if (pIf->ZoneIndices[ScopeLevelSite] ==
|
|
pIf2->ZoneIndices[ScopeLevelSite]) {
|
|
//
|
|
// pIf has just moved out of its previous site.
|
|
// Pick a new unused site id.
|
|
//
|
|
NewSiteId(pIf, pAdapterAddresses);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (pIf=pAdapterAddresses; pIf; pIf=pIf->Next) {
|
|
if (pIf->Flags & IP_ADAPTER_DDNS_ENABLED) {
|
|
|
|
//
|
|
// See if we've already done this interface because it
|
|
// had the same DNS server as a previous one.
|
|
//
|
|
for (pIf2=pAdapterAddresses; pIf2 != pIf; pIf2 = pIf2->Next) {
|
|
if (!(pIf2->Flags & IP_ADAPTER_DDNS_ENABLED))
|
|
continue;
|
|
if (IsSameDNSServer(pIf2, pIf)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If not, go ahead and do DDNS.
|
|
//
|
|
if (pIf2 == pIf) {
|
|
DoDdnsOnInterface(pIf);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// A change in the set of IPv6 addresses might update the need for the
|
|
// different transition mechanisms.
|
|
//
|
|
UpdateServiceRequirements(pAdapterAddresses);
|
|
|
|
Cleanup:
|
|
//
|
|
// At this point, pAdapterAddresses is NULL, otherwise points to valid
|
|
// adapter data.
|
|
//
|
|
if (SetPreviousState) {
|
|
if (g_PreviousInterfaceState) {
|
|
FREE(g_PreviousInterfaceState);
|
|
}
|
|
g_PreviousInterfaceState = pAdapterAddresses;
|
|
} else {
|
|
ASSERT(pAdapterAddresses == NULL);
|
|
}
|
|
|
|
Done:
|
|
TraceLeave("OnIpv6AddressChange");
|
|
LEAVE_API();
|
|
}
|
|
|
|
VOID
|
|
StopIpv6AddressChangeNotification()
|
|
{
|
|
if (g_hIpv6AddressChangeWait != NULL) {
|
|
//
|
|
// Block until we're sure that the address change callback isn't
|
|
// still running.
|
|
//
|
|
LEAVE_API();
|
|
UnregisterWaitEx(g_hIpv6AddressChangeWait, INVALID_HANDLE_VALUE);
|
|
ENTER_API();
|
|
|
|
//
|
|
// Release the event we counted for RegisterWaitForSingleObject
|
|
//
|
|
DecEventCount("AC:StopIpv6AddressChangeNotification");
|
|
g_hIpv6AddressChangeWait = NULL;
|
|
}
|
|
|
|
if (g_PreviousInterfaceState != NULL) {
|
|
FREE(g_PreviousInterfaceState);
|
|
g_PreviousInterfaceState = NULL;
|
|
}
|
|
|
|
if (g_hIpv6AddressChangeEvent != NULL) {
|
|
CloseHandle(g_hIpv6AddressChangeEvent);
|
|
g_hIpv6AddressChangeEvent = NULL;
|
|
}
|
|
|
|
if (g_hIpv6Socket != INVALID_SOCKET) {
|
|
closesocket(g_hIpv6Socket);
|
|
g_hIpv6Socket = INVALID_SOCKET;
|
|
}
|
|
}
|