//+---------------------------------------------------------------------------
    //
    //  Microsoft Windows
    //  Copyright (C) Microsoft Corporation, 2001.
    //
    //  File:       G P N L A . C P P
    //
    //  Contents:   Class for Handling NLA Changes that affect Group Policies
    //
    //  Notes:
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //----------------------------------------------------------------------------

    #include "pch.h"
    #pragma hdrstop
    #include "trace.h"
    #include "gpnla.h"
    #include <winsock2.h>
    #include <mswsock.h>
    #include "nmbase.h"
    #include <userenv.h>
    #include <userenvp.h>
    #include <ncstl.h>
    #include <ncstlstr.h>
    #include <stlalgor.h>
    #include <lancmn.h>
    #include <lm.h>
    #include <ipnathlp.h>
    #include "ncmisc.h"
    #include "ipifcons.h"
    #include "ncexcept.h"
    #include "conman.h"

    GUID g_WsMobilityServiceClassGuid = NLA_SERVICE_CLASS_GUID;

    extern CGroupPolicyNetworkLocationAwareness* g_pGPNLA;

    bool operator == (const GPNLAPAIR& rpair1, const GPNLAPAIR& rpair2)
    {
        return IsEqualGUID(rpair1.first, rpair2.first) == TRUE;
    }

    LONG CGroupPolicyNetworkLocationAwareness::m_lBusyWithReconfigure = 0;

    CGroupPolicyNetworkLocationAwareness::CGroupPolicyNetworkLocationAwareness()
    {
        m_fSameNetwork = FALSE;
        m_fShutdown = FALSE;
        m_lRefCount = 0;
        m_fErrorShutdown = FALSE;
        m_hGPWait = INVALID_HANDLE_VALUE;
        m_hNLAWait = INVALID_HANDLE_VALUE;
        m_lBusyWithReconfigure = 0;
    }

    CGroupPolicyNetworkLocationAwareness::~CGroupPolicyNetworkLocationAwareness()
    {
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::Initialize
    //
    //  Purpose:    To initialize the different components required for detecting
    //              changes to the network and creating the different
    //              synchronization objects required.
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating SUCCESS or FAILURE
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::Initialize()
    {
        HRESULT hr;

        TraceTag(ttidGPNLA, "Initializing Group Policy Handler");

        InitializeCriticalSection(&m_csList);

        // Init Winsock
        if (ERROR_SUCCESS == WSAStartup(MAKEWORD(2, 2), &m_wsaData))
        {
            m_hEventNLA = CreateEvent(NULL, FALSE, FALSE, NULL);
            if (m_hEventNLA)
            {
                m_hEventExit = CreateEvent(NULL, FALSE, FALSE, NULL);
                if (m_hEventExit)
                {
                    m_hEventGP = CreateEvent(NULL, FALSE, FALSE, NULL);
                    if (m_hEventGP)
                    {
                        hr = RegisterWait();
                        if (SUCCEEDED(hr))
                        {
                            if (RegisterGPNotification(m_hEventGP, TRUE))
                            {
                                ZeroMemory(&m_wsaCompletion,sizeof(m_wsaCompletion));
                                ZeroMemory(&m_wsaOverlapped,sizeof(m_wsaOverlapped));

                                m_wsaOverlapped.hEvent = m_hEventNLA;

                                m_wsaCompletion.Type = NSP_NOTIFY_EVENT;
                                m_wsaCompletion.Parameters.Event.lpOverlapped = &m_wsaOverlapped;

                                ZeroMemory(&m_wqsRestrictions, sizeof(m_wqsRestrictions));
                                m_wqsRestrictions.dwSize = sizeof(m_wqsRestrictions);
                                m_wqsRestrictions.lpServiceClassId = &g_WsMobilityServiceClassGuid;
                                m_wqsRestrictions.dwNameSpace = NS_NLA;

                                hr = LookupServiceBegin(LUP_NOCONTAINERS);

                                if (SUCCEEDED(hr))
                                {
                                    // Loop through once and get all the data to begin with.
                                    hr = EnumChanges();
                                }
                                return hr;
                            }
                            else
                            {
                                hr = HrFromLastWin32Error();
                                DeregisterWait();
                            }
                        }
                        CloseHandle(m_hEventGP);
                    }
                    else
                    {
                        hr = HrFromLastWin32Error();
                    }
                }
                else
                {
                    hr = HrFromLastWin32Error();
                }
                CloseHandle(m_hEventExit);
            }
            else
            {
                hr = HrFromLastWin32Error();
            }
            CloseHandle(m_hEventNLA);
        }
        else
        {
            int nError;

            nError  = WSAGetLastError();

            hr = HRESULT_FROM_WIN32(nError);
        }

        TraceError("CGroupPolicyNetworkLocationAwareness::Initialize failed", hr);

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::Uninitialize
    //
    //  Purpose:    This is used to ensure that no threads are currently running
    //              when they should be stopped.  If the refcount is >0 then it
    //              waits for the last busy thread to terminate and set the event
    //              marking its termination so that shutdown can proceed.
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::Uninitialize()
    {
        HRESULT hr = S_OK;
        DWORD dwRet = WAIT_OBJECT_0;
        int nCount = 0;

        TraceTag(ttidGPNLA, "Unitializing Group Policy Handler");

        m_fShutdown = TRUE;

        Unreference();

        // LookupServiceEnd should cause an event to fire which will make us exit (unless NLA was already stopped).
        hr = LookupServiceEnd();

        if ((0 != m_lRefCount) && SUCCEEDED(hr) && !m_fErrorShutdown)
        {
            dwRet = WaitForSingleObject(m_hEventExit, 30000L);
        }

        do
        {
            hr = DeregisterWait();
            if (SUCCEEDED(hr))
            {
                break;
            }
        } while ((nCount++ < 3) && FAILED(hr));

        TraceError("DeregisterWait returned", hr);

        if (SUCCEEDED(hr))
        {
            CloseHandle(m_hEventExit);
            CloseHandle(m_hEventNLA);

            DeleteCriticalSection(&m_csList);

            WSACleanup();
        }

        TraceTag(ttidGPNLA, "NLA was uninitialized");

         return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::RegisterWait
    //
    //  Purpose:    Registers the Wait Object so that we don't require any threads
    //              of our own.
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::RegisterWait()
    {
        TraceFileFunc(ttidGPNLA);

        HRESULT hr = S_OK;
        NTSTATUS Status;

        Reference();  // Make sure that we're referenced so that we don't accidentally kill the service while it's still busy.

        Status = RtlRegisterWait(&m_hNLAWait, m_hEventNLA, &CGroupPolicyNetworkLocationAwareness::EventHandler, this, INFINITE, WT_EXECUTEINLONGTHREAD);

        if (!NT_SUCCESS(Status))
        {
            m_hNLAWait = INVALID_HANDLE_VALUE;
            hr = HRESULT_FROM_NT(Status);
        }
        else
        {
            Status = RtlRegisterWait(&m_hGPWait, m_hEventGP, &CGroupPolicyNetworkLocationAwareness::GroupPolicyChange, this, INFINITE, WT_EXECUTEINLONGTHREAD);
            if (!NT_SUCCESS(Status))
            {
                hr = HRESULT_FROM_NT(Status);
                RtlDeregisterWaitEx(m_hNLAWait, INVALID_HANDLE_VALUE);
                m_hGPWait = INVALID_HANDLE_VALUE;
                m_hNLAWait = INVALID_HANDLE_VALUE;
            }
        }

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::DeregisterWait
    //
    //  Purpose:    Deregisters the wait so that we can shutdown and not have
    //              any new threads spawned.
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::DeregisterWait()
    {
        TraceFileFunc(ttidGPNLA);

        HRESULT hr,hr1,hr2 = S_OK;
        NTSTATUS Status1, Status2;

        if (INVALID_HANDLE_VALUE != m_hNLAWait)
        {
            Status1 = RtlDeregisterWaitEx(m_hNLAWait, INVALID_HANDLE_VALUE);

            if (!NT_SUCCESS(Status1))
            {
                hr1 = HRESULT_FROM_NT(Status1);
            }

            if (INVALID_HANDLE_VALUE != m_hGPWait)
            {
                Status2 = RtlDeregisterWaitEx(m_hGPWait, INVALID_HANDLE_VALUE);
                if (!NT_SUCCESS(Status2))
                {
                    hr2 = HRESULT_FROM_NT(Status2);
                }
            }

            if (FAILED(hr1))
            {
                hr = hr1;
            }
            else if (FAILED(hr2))
            {
                hr = hr2;
            }
        }
        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::IsJoinedToDomain
    //
    //  Purpose:    Checks to see if this machine belongs to an NT Domain
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    BOOL.  TRUE = Joined to a Domain, FALSE = not...
    //
    //  Author:     sjkhan   29 Jan 2002
    //
    //  Notes:
    //
    BOOL CGroupPolicyNetworkLocationAwareness::IsJoinedToDomain()
    {
        static DWORD dwDomainMember = 0xffffffff; // Unconfigured

        TraceTag(ttidGPNLA, "Entering IsJoinedToDomain");

        if (0xffffffff == dwDomainMember)
        {
            dwDomainMember = FALSE;

            LPWSTR pszDomain;
            NETSETUP_JOIN_STATUS njs = NetSetupUnknownStatus;
            if (NERR_Success == NetGetJoinInformation(NULL, &pszDomain, &njs))
            {
                NetApiBufferFree(pszDomain);
                if (NetSetupDomainName == njs)
                {
                    dwDomainMember = TRUE;
                    TraceTag(ttidGPNLA, "We're  on a domain (NLA policies apply)");
                }
                else
                {
                    TraceTag(ttidGPNLA, "We're not on a domain (No NLA policies will apply)");
                }
            }
            else
            {
                TraceTag(ttidGPNLA, "We're not on a domain (No NLA policies will apply)");
            }
        }
        else
        {
            TraceTag(ttidGPNLA, "IsJoinedToDomain: We're already configured...");
        }

        TraceTag(ttidGPNLA, "Leaving IsJoinedToDomain");

        return static_cast<BOOL>(dwDomainMember);
    }



    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::IsSameNetworkAsGroupPolicies
    //
    //  Purpose:    Used to determine our current network location with respect to
    //              the network from which the Group Policies came from.
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    BOOL.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    BOOL CGroupPolicyNetworkLocationAwareness::IsSameNetworkAsGroupPolicies()
    {
        BOOL fNetworkMatch = FALSE;  // Assume we are on a different network.
        WCHAR pszName[256] = {0};
        DWORD dwSize = 256;
        DWORD dwErr;

        TraceTag(ttidGPNLA, "Entering IsSameNetworkAsGroupPolicies");

        // Get the network Name.
        dwErr = GetGroupPolicyNetworkName(pszName, &dwSize);

        TraceTag(ttidGPNLA, "NetworkName: %S", pszName);

        if (ERROR_SUCCESS == dwErr)
        {
            if (IsJoinedToDomain())
            {
                CExceptionSafeLock esLock(&m_csList);  // Protecting list
                GPNLAPAIR nlapair;

                // We need to look at all of the adapters to check that at least 1
                // is on the same network from which the Group Policies came.
                for (GPNLAITER iter = m_listAdapters.begin(); iter != m_listAdapters.end(); iter++)
                {
                    LPCSTR pStr = NULL;
                    nlapair = *iter;

                    TraceTag(ttidGPNLA,  "Network Name: %S", nlapair.second.strNetworkName);
                    TraceTag(ttidGPNLA,  "Network Status: %s", DbgNcs(nlapair.second.ncsStatus));

                    if (
                            (nlapair.second.strNetworkName == pszName)
                            &&
                            (
                                (NCS_CONNECTED == nlapair.second.ncsStatus) ||
                                (NCS_AUTHENTICATING == nlapair.second.ncsStatus) ||
                                (NCS_AUTHENTICATION_SUCCEEDED == nlapair.second.ncsStatus) ||
                                (NCS_AUTHENTICATION_FAILED == nlapair.second.ncsStatus) ||
                                (NCS_CREDENTIALS_REQUIRED == nlapair.second.ncsStatus)
                            )
                        )
                    {
                        // Yes, we're still on the network so we need to enforce group policies.
                        fNetworkMatch = TRUE;
                    }
                }
            }
            else
            {
                TraceTag(ttidGPNLA, "We're not on a domain, exiting...");
            }

            if (fNetworkMatch != m_fSameNetwork)
            {
                LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
                m_fSameNetwork = fNetworkMatch;
                ReconfigureHomeNet();
            }
        }

        return fNetworkMatch;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::Reference
    //
    //  Purpose:    Increments our reference count.
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    The current Refcount (note this may not be 100% accurate,
    //              but will never be 0 unless we're really shutting down).
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    LONG CGroupPolicyNetworkLocationAwareness::Reference()
    {
        InterlockedIncrement(&m_lRefCount);

        TraceTag(ttidGPNLA, "Reference() - Count: %d", m_lRefCount);

        return m_lRefCount;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::Unreference
    //
    //  Purpose:    Decrements our reference countand sets and event if it reaches
    //              zero and we're shutting down.
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    The current Refcount (note this may not be 100% accurate,
    //              but will never be 0 unless we're really shutting down).
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    LONG CGroupPolicyNetworkLocationAwareness::Unreference()
    {
        if ((0 == InterlockedDecrement(&m_lRefCount)) && m_fShutdown)
        {
            SetEvent(m_hEventExit);
        }

        TraceTag(ttidGPNLA, "Unreference() - Count: %d", m_lRefCount);

        return m_lRefCount;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::LookupServiceBegin
    //
    //  Purpose:    Wraps the WSA function using our class members.
    //
    //  Arguments:
    //      DWORD dwControlFlags - WSA Control Flags
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::LookupServiceBegin(DWORD dwControlFlags)
    {
        HRESULT hr = S_OK;

        if (SOCKET_ERROR == WSALookupServiceBegin(&m_wqsRestrictions, dwControlFlags, &m_hQuery))
        {
            int nError;

            nError = WSAGetLastError();

            hr = HRESULT_FROM_WIN32(nError);

            TraceError("WSALookupServiceBegin() failed", hr);

            m_hQuery = NULL;
        }

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::LookupServiceNext
    //
    //  Purpose:    Wraps the WSA function using our class members.
    //
    //  Arguments:
    //      DWORD dwControlFlags [in]           - WSA Control Flags
    //      LPDWORD lpdwBufferLength [in/out]   - Buffer Length sent/required.
    //      LPWSAQUERYSET lpqsResults [out]     - Actual Query Results.
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::LookupServiceNext(DWORD dwControlFlags, LPDWORD lpdwBufferLength, LPWSAQUERYSET lpqsResults)
    {
        HRESULT hr = S_OK;
        int nError;

        INT nRet = WSALookupServiceNext(m_hQuery, dwControlFlags, lpdwBufferLength, lpqsResults);
        if (SOCKET_ERROR == nRet)
        {
            BOOL fTraceError;

            nError = WSAGetLastError();
            hr = HRESULT_FROM_WIN32(nError);

            fTraceError = (!lpqsResults || (hr == HRESULT_FROM_WIN32(WSA_E_NO_MORE))) ? TRUE : FALSE;

            TraceErrorOptional("LookupServiceNext", hr, fTraceError);
        }

        TraceTag(ttidGPNLA, "LookupServiceNext terminated with %x", nRet);

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::LookupServiceEnd
    //
    //  Purpose:    Wraps the WSA function using our class members.
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating success/failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::LookupServiceEnd()
    {
        HRESULT hr = S_OK;
        int nError;

        if (SOCKET_ERROR == WSALookupServiceEnd(m_hQuery))
        {
            nError = WSAGetLastError();
            hr = HRESULT_FROM_WIN32(nError);
        }

        m_hQuery = NULL;

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::QueueEvent
    //
    //  Purpose:    Queue's an event to notify netshell of a change.
    //
    //  Arguments:
    //      CONMAN_EVENTTYPE cmEventType [in]   - Type of Event.
    //      LPGUID pguidAdapter [in]            - Guid for the adapter.
    //      NETCON_STATUS ncsStatus [in]        - Status for Connection.
    //
    //  Returns:    HRESULT.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::QueueEvent(CONMAN_EVENTTYPE cmEventType, LPGUID pguidAdapter, NETCON_STATUS ncsStatus)
    {
        HRESULT hr = S_OK;

        if ( (CONNECTION_STATUS_CHANGE == cmEventType) ||
             (CONNECTION_ADDRESS_CHANGE == cmEventType) )
        {
            CONMAN_EVENT* pEvent = new CONMAN_EVENT;

            if (pEvent)
            {
                ZeroMemory(pEvent, sizeof(CONMAN_EVENT));
                pEvent->Type = cmEventType;
                pEvent->guidId = *pguidAdapter;
                pEvent->Status = ncsStatus;
                pEvent->ConnectionManager = CONMAN_LAN;

                if (NCS_HARDWARE_NOT_PRESENT == ncsStatus) // Not too useful for LAN connections. We can delete the device instead.
                {
                    // This will happen during PnP undock.
                    TraceTag(ttidGPNLA, "Sending delete for NCS_HARDWARE_NOT_PRESENT instead");
                    pEvent->Type = CONNECTION_DELETED;
                }

                if (!QueueUserWorkItemInThread(LanEventWorkItem, reinterpret_cast<PVOID>(pEvent), EVENTMGR_CONMAN))
                {
                    FreeConmanEvent(pEvent);
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
        else
        {
            hr = E_INVALIDARG;
        }

        TraceHr(ttidError, FAL, hr, FALSE, "CGroupPolicyNetworkLocationAwareness::QueueEvent");

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::EnumChanges
    //
    //  Purpose:    Enumerates all the changes that have occurred to the network.
    //
    //  Arguments:
    //      (none)
    //
    //  Returns:    HRESULT indicating success or failure.
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:      This will re-increment the reference count if m_fShutdown is not set.
    //              Doesn't allow it to go to Zero though.
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::EnumChanges()
    {
        HRESULT hr = S_OK;
        BOOL fRet = FALSE;
        BOOL fNoNetwork = TRUE;
        BOOL fNetworkMatch = FALSE;
        PWSAQUERYSET wqsResult = NULL;
        DWORD dwLen;
        WCHAR pszName[256] = {0};
        DWORD dwSize = 256;

        TraceTag(ttidGPNLA, "Entering EnumChanges");

        BOOL bDomainMember = IsJoinedToDomain();

        if (!m_hQuery)
        {
            // For some reason we didn't get this ealier.
            // Possibly TCP/IP wasn't installed.  We can add this now and
            // it will have the desired effect.
            LookupServiceBegin(LUP_NOCONTAINERS);
        }

        if (!m_hQuery)
        {
            return E_UNEXPECTED;
        }

        while (fRet == FALSE)
        {
            dwLen = 0;
            // Do call twice, first to get dwSize of buffer for second call
            hr = LookupServiceNext(0, &dwLen, NULL);
            if (FAILED(hr) && hr != HRESULT_FROM_WIN32(WSA_E_NO_MORE) && hr != HRESULT_FROM_WIN32(WSAEFAULT))
            {
                TraceError("LookupServiceNext", hr);
                fRet = FALSE;
                break;
            }

            wqsResult = reinterpret_cast<PWSAQUERYSET>(new BYTE[dwLen]);

            if (!wqsResult)
            {
                hr = HrFromLastWin32Error();
                TraceError("Error: malloc() failed", hr);
                fRet = TRUE;
                break;
            }

            if (S_OK == (hr = LookupServiceNext(0, &dwLen, wqsResult)))
            {
                fNoNetwork = FALSE;
                if (wqsResult->lpBlob != NULL)
                {
                    NLA_BLOB *blob = reinterpret_cast<NLA_BLOB *>(wqsResult->lpBlob->pBlobData);
                    int next;
                    do
                    {
                        // We are looking for the blob containing the network GUID
                        if (blob->header.type == NLA_INTERFACE)
                        {
                            WCHAR strAdapter[MAX_PATH];
                            DWORD dwErr;

                            ZeroMemory(strAdapter, MAX_PATH * sizeof(WCHAR));

                            wcscpy(strAdapter, wqsResult->lpszServiceInstanceName);

                            // Get the network Name. We ignore failure since we still need to know other details.
                            dwErr = GetGroupPolicyNetworkName(pszName, &dwSize);

                            // matching pszName and interface type is ATM/LAN etc, but not RAS
                            if(blob->data.interfaceData.dwType != IF_TYPE_PPP && blob->data.interfaceData.dwType != IF_TYPE_SLIP)
                            {
                                CExceptionSafeLock esLock(&m_csList);   // Protecting list
                                GUID guidAdapter;
                                WCHAR strAdapterGuid[39];
                                GPNLAPAIR nlapair;
                                GPNLAITER iter;
                                NETCON_STATUS ncsStatus;

                                ZeroMemory(strAdapterGuid, 39 * sizeof(WCHAR));

                                TraceTag(ttidGPNLA, "AdapterName: %s", blob->data.interfaceData.adapterName);

                                mbstowcs(strAdapterGuid, blob->data.interfaceData.adapterName, 39);

                                CLSIDFromString(strAdapterGuid, &guidAdapter);

                                nlapair.first = guidAdapter;

                                iter = find(m_listAdapters.begin(), m_listAdapters.end(), nlapair);

                                if (iter == m_listAdapters.end())
                                {
                                    // We didn't find the adapter in the list that we currently have.
                                    // So we need to add it to the list.
                                    hr = HrGetPnpDeviceStatus(&guidAdapter, &ncsStatus);

                                    nlapair.second.strNetworkName = strAdapter;
                                    nlapair.second.ncsStatus = ncsStatus;

                                    if (SUCCEEDED(hr))
                                    {
                                        // If we got a valid status, we go ahead and add the adapter to
                                        // the list.
                                        m_listAdapters.insert(m_listAdapters.begin(), nlapair);
                                    }

                                    // Send the initial address status info:
                                    QueueEvent(CONNECTION_STATUS_CHANGE,  &guidAdapter, ncsStatus);
                                    QueueEvent(CONNECTION_ADDRESS_CHANGE, &guidAdapter, ncsStatus);
                                }
                                else
                                {
                                    // We found the adapter, so update its status.
                                    GPNLAPAIR& rnlapair = *iter;

                                    if (rnlapair.second.strNetworkName != strAdapter)
                                    {
                                        rnlapair.second.strNetworkName = strAdapter;
                                    }

                                    hr = HrGetPnpDeviceStatus(&guidAdapter, &ncsStatus);

                                    if (SUCCEEDED(hr))
                                    {
                                        if (ncsStatus != rnlapair.second.ncsStatus)
                                        {
                                            // The status is different so we need to send an event to the connections folder.
                                            rnlapair.second.ncsStatus = ncsStatus;
                                        }

                                        // [Deon] We need to always send this as we don't really know what the current
                                        // status of the adapter is. We only know the NLA part.
                                        //
                                        // If we make the above check it could happen somebody else moves the address over
                                        // to NCS_INVALID_ADDRESS and then we don't send the NCS_CONNECTED once it changes.
                                        QueueEvent(CONNECTION_STATUS_CHANGE,  &guidAdapter, ncsStatus);
                                        QueueEvent(CONNECTION_ADDRESS_CHANGE, &guidAdapter, ncsStatus);
                                    }
                                }

                                if (strAdapter != pszName)
                                {
                                    // If this adapter is not on the same network, then we need to look at all others and
                                    // ensure that at least 1 is on the same network from which the Group Policies came.
                                    for (GPNLAITER iter = m_listAdapters.begin(); iter != m_listAdapters.end(); iter++)
                                    {
                                        LPCSTR pStr = NULL;
                                        nlapair = *iter;

                                        TraceTag(ttidGPNLA,  "Network Name: %S", nlapair.second.strNetworkName);
                                        TraceTag(ttidGPNLA,  "Network Status: %s", DbgNcs(nlapair.second.ncsStatus));

                                        if (nlapair.second.strNetworkName == pszName)
                                        {
                                            // Yes, we're still on the network so we need to enforce group policies.
                                            fNetworkMatch = TRUE;
                                        }
                                    }
                                }

                                if (fNetworkMatch)
                                {
                                    break;
                                }
                            }
                        }
                        // There may be multiple blobs for each interface so make sure we find them all
                        next = blob->header.nextOffset;
                        blob = (NLA_BLOB *)(((char *)blob) + next);
                    } while(next != 0);
                }
                else
                {
                    TraceTag(ttidGPNLA, "Blob is NULL");
                    fRet = TRUE;
                }

                free(wqsResult);
                wqsResult = NULL;
            }
            else
            {
                if (hr != HRESULT_FROM_WIN32(WSA_E_NO_MORE))
                {
                    TraceError("LookupServiceNext failed\n", hr);
                    fRet = FALSE;
                }
                free(wqsResult);
                break;
            }
        }

        BOOL fFireRefreshAll = FALSE;

        if (bDomainMember)
        {
            if (!fNoNetwork)
            {   // We have a Network
                if (fNetworkMatch)
                {
                    // Enforce Policies.
                    if (!m_fSameNetwork)
                    {
                        // We are changing the network - we need to refresh all the connectoids in the folder to
                        // update their icons to reflect policy.
                        fFireRefreshAll = TRUE;
                        m_fSameNetwork  = TRUE;
                    }

                    TraceTag(ttidGPNLA, "Network Match");
                }
                else
                {
                    // Removed Policy Enforcement.
                    if (m_fSameNetwork)
                    {
                        // We are changing the network - we need to refresh all the connectoids in the folder to
                        // update their icons to reflect policy.
                        fFireRefreshAll = TRUE;
                        m_fSameNetwork  = FALSE;
                    }

                    TraceTag(ttidGPNLA, "Network does not Match");
                }
                ReconfigureHomeNet();
            }
            else
            {
                // No Networks so don't do anything.
            }
        }
        else // Member of a workgroup
        {
            m_fSameNetwork = FALSE;
            ReconfigureHomeNet();
        }

        if (HRESULT_FROM_WIN32(WSA_E_NO_MORE) == hr)
        {
            hr = S_OK;
        }

        DWORD cbOutBuffer;

        if (!m_fShutdown)
        {
            Reference();

            // Wait for Network Change
            WSANSPIoctl(m_hQuery, SIO_NSP_NOTIFY_CHANGE,
                        NULL, 0, NULL, 0, &cbOutBuffer,
                        &m_wsaCompletion);

            if (fFireRefreshAll)
            {
                LanEventNotify (REFRESH_ALL, NULL, NULL, NULL);
            }
        }

        TraceTag(ttidGPNLA, "Exiting EnumChanges");

        return hr;
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::EventHandler
    //
    //  Purpose:    Called when NLA changes occur.
    //
    //  Arguments:
    //      LPVOID  pContext    - generally the "this" pointer.
    //      BOOLEAN fTimerFired - if this happened because of a timer or the event
    //                            getting set.  Since we specify INFINITE, this is
    //                            not going to get fired by the timer.
    //  Returns:    nothing
    //
    //  Author:     sjkhan   20 Feb 2001
    //
    //  Notes:
    //
    VOID NTAPI CGroupPolicyNetworkLocationAwareness::EventHandler(IN LPVOID pContext, IN BOOLEAN fTimerFired)
    {
        CGroupPolicyNetworkLocationAwareness* pGPNLA = reinterpret_cast<CGroupPolicyNetworkLocationAwareness*>(pContext);

        DWORD dwBytes;

        BOOL bSucceeded = GetOverlappedResult(pGPNLA->m_hQuery, &pGPNLA->m_wsaOverlapped, &dwBytes, FALSE);

        if (!bSucceeded)
        {
            TraceError("GetOverlappedResult failed", HrFromLastWin32Error());
        }

        if (FALSE == fTimerFired && !pGPNLA->m_fShutdown && bSucceeded)
        {
            pGPNLA->EnumChanges();
        }

        pGPNLA->Unreference();

        if (!bSucceeded)
        {
            pGPNLA->m_fErrorShutdown = TRUE;

            QueueUserWorkItem(ShutdownNlaHandler, pContext, WT_EXECUTEINLONGTHREAD);
        }
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::GroupPolicyChange
    //
    //  Purpose:    Called when Machine Group Policy changes occur.
    //
    //  Arguments:
    //      LPVOID  pContext    - generally the "this" pointer.
    //      BOOLEAN fTimerFired - if this happened because of a timer or the event
    //                            getting set.  Since we specify INFINITE, this is
    //                            not going to get fired by the timer.
    //  Returns:    nothing
    //
    //  Author:     sjkhan   05 Feb 2002
    //
    //  Notes:
    //
    VOID NTAPI CGroupPolicyNetworkLocationAwareness::GroupPolicyChange(IN LPVOID pContext, IN BOOLEAN fTimerFired)
    {
        TraceTag(ttidGPNLA, "GroupPolicyChange called");
        ReconfigureHomeNet(TRUE);
        LanEventNotify(REFRESH_ALL, NULL, NULL, NULL);
    }

    //+---------------------------------------------------------------------------
    //
    //  Member:     CGroupPolicyNetworkLocationAwareness::ShutdownNlaHandler
    //
    //  Purpose:    Shutdown Nla handler, because Nla service is toast.
    //
    //  Arguments:
    //
    //
    //  Returns:    nothing
    //
    //  Author:     sjkhan   05 Feb 2002
    //
    //  Notes:
    //
    DWORD WINAPI CGroupPolicyNetworkLocationAwareness::ShutdownNlaHandler(PVOID pThis)
    {
        TraceFileFunc(ttidGPNLA);

        CGroupPolicyNetworkLocationAwareness* pGPNLA =
            reinterpret_cast<CGroupPolicyNetworkLocationAwareness*>(InterlockedExchangePointer( (PVOID volatile *) &g_pGPNLA, NULL));

        if (pGPNLA)
        {
            Assert(pGPNLA == pThis); // Making the assumption that the context is always g_pGPNLA, since I'm clearing g_pGPNLA.

            pGPNLA->Uninitialize();
            delete pGPNLA;
        }

        return 0;
    }

    //+---------------------------------------------------------------------------
    //
    //  Function:   ReconfigureHomeNet
    //
    //  Purpose:    Change Homenet Configuration
    //
    //  Arguments:
    //              fWaitUntilRunningOrStopped - if the caller should wait for the
    //                                          service to start, or to fail starting
    //                                          and stop.
    //
    //  Returns:    HRESULT indicating success of failure
    //
    //  Author:     sjkhan   09 Dec 2000
    //
    //  Notes:
    //
    //
    //
    //
    HRESULT CGroupPolicyNetworkLocationAwareness::ReconfigureHomeNet(BOOL fWaitUntilRunningOrStopped)
    {
        SC_HANDLE hscManager;
        SC_HANDLE hService;
        SERVICE_STATUS ServiceStatus;

        if (0 != InterlockedExchange(&m_lBusyWithReconfigure, 1L))
        {
            return S_FALSE;
        }

        TraceTag(ttidGPNLA, "Entering ReconfigureHomeNet");
        hscManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);

        if (hscManager)
        {
            TraceTag(ttidGPNLA, "Attempting to open service");
            hService = OpenService(hscManager, L"SharedAccess", SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL);
            if (hService)
            {
                DWORD dwCount = 0;
                SERVICE_STATUS SvcStatus = {0};
                BOOL fRet;

                if (fWaitUntilRunningOrStopped)
                {
                    do
                    {
                        if (!QueryServiceStatus(hService, &SvcStatus))
                        {
                            break;
                        }
                        if (SERVICE_START_PENDING == SvcStatus.dwCurrentState)
                        {
                            TraceTag(ttidGPNLA, "Service is still starting.  Waiting 5 seconds.");
                            Sleep(5000);  // Sleep 5 seconds;
                        }
                    } while ((SERVICE_START_PENDING == SvcStatus.dwCurrentState) && ++dwCount <= 6);
                }
                if (!fWaitUntilRunningOrStopped || (SERVICE_RUNNING == SvcStatus.dwCurrentState))
                {
                    fRet = ControlService(hService, IPNATHLP_CONTROL_UPDATE_CONNECTION, &ServiceStatus);
                    if (!fRet)
                    {
                        DWORD dwErr = GetLastError();
                        TraceError("Control Service returned: 0x%x", HRESULT_FROM_WIN32(dwErr));
                    }
                    else
                    {
                        TraceTag(ttidGPNLA, "Requested Reconfiguration check from ICF/ICS");
                    }
                }
                CloseServiceHandle(hService);
            }
            else
            {
                TraceTag(ttidGPNLA, "Could not open service");
            }

            CloseServiceHandle(hscManager);
        }

        TraceTag(ttidGPNLA, "Leaving ReconfigureHomeNet");

        InterlockedExchange(&m_lBusyWithReconfigure, 0L);

        return S_OK;
    }