Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Captive Portal Detection with Windows APIs

4.50/5 (2 votes)
27 Mar 2016CPOL2 min read 18.2K  
How to detect if your network is behind a captive portal (Wi-Fi Hot spot) through Windows NLM_INTERNET_CONNECTIVITY

Introduction

Recently, I came across the need of detecting if my connection was behind a Captive Portal (Wi-Fi hot spot) (read this) or not. I looked on the internet and found interesting articles on how Mozilla approaches this issue; as well as Safari and other possible alternatives. But in the end, and that would be the approach explained in this article, I opted for the use of the Windows Network List Manager API Reference and looked at the NLM_INTERNET_CONNECTIVITY flags here.

Background

MSDN exposes a handy API to query for Network Connectivity properties. Through the NLM_INTERNET_CONNECTIVITY, a set of additional properties are exposed for IPV4 and IPV6 network protocols; such as NLM_INTERNET_CONNECTIVITY_WEBHIJACK, which is the property we want to find to identify if our network connection is behind a Captive Portal.

Using the Code

The overall strategy here is to use the Network List Manager to enumerate the connections that are connected. And then, through the INetwork interface query for IPropertyBag looking for the NLM_INTERNET_CONNECTIVITY flag desired. As the MSDN documentation proposed: “These connectivity flags can be retrieved by querying for the NA_InternetConnectivityV4 or NA_InternetConnectivityV6 properties using the IPropertyBag interface for an INetwork or INetworkConnection interface.” I first tried INetworkConnection interface to query for the IPropertyBag but it kept failing when retrieving the flags. So I switched to INetwork interface (rest of the code remains the same) and it just worked. I didn’t mind investigating why INetworkConnection failed. However, I thought I should point this out if you run into something similar.

Finally, we look at network connectivity flags checking for the NLM_INTERNET_CONNECTIVITY_WEBHIJACK flag to be set. If this is set for NLM_CONNECTIVITY_IPV6_INTERNET or NLM_CONNECTIVITY_IPV4_INTERNET, we are under a Captive Portal connection for IPV6 or IPV4 respectively.

Here is the chunk of code that would perform the logic above explained and set boolean variable if a captive portal was detected:

C++
bool fCaptivePortalDetected = false;

    // Initialize COM.
    if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
    {
        // Declare a pointer to INetworkListManager
        INetworkListManager* pNetworkListManager;

        // Create instance of the CLSID_NetworkListManger COM object
        if (SUCCEEDED(CoCreateInstance(CLSID_NetworkListManager, NULL,
            CLSCTX_ALL, IID_INetworkListManager,
            (LPVOID*)&pNetworkListManager)))
        {           
            // Declare pointer to an IEnumNetworkConnections
            IEnumNetworks* pEnum;

            // Call to GetNetworks from INetworkListManager interface
            if (SUCCEEDED(pNetworkListManager->GetNetworks
            (NLM_ENUM_NETWORK_CONNECTED, &pEnum)) && pEnum != NULL)
            {
                INetwork *pINetwork;
                HRESULT hr = pEnum->Next(1, &pINetwork, nullptr);
                while (hr == S_OK)
                {
                    if (pINetwork != NULL)
                    {
                        IPropertyBag *pNetworkPropertyBag;
                        HRESULT hrQueryInterface = pINetwork->QueryInterface
                        (IID_IPropertyBag, (LPVOID*)&pNetworkPropertyBag);
                        if (SUCCEEDED(hrQueryInterface 1) && pNetworkPropertyBag != nullptr)
                        {
                            NLM_CONNECTIVITY networkConnectivity;
                            VARIANT variantConnectivity;

                            if (SUCCEEDED(pINetwork->GetConnectivity(&networkConnectivity)))
                            {
                                if ((networkConnectivity & 
                                NLM_CONNECTIVITY_IPV4_INTERNET) == 
                                         NLM_CONNECTIVITY_IPV4_INTERNET)
                                {
                                    VariantInit(&variantConnectivity);
                                    if (SUCCEEDED(pNetworkPropertyBag->Read
                                    (NA_InternetConnectivityV4, 
                                           &variantConnectivity, nullptr)) 
                                    && (V_UINT(&variantConnectivity) & 
                                    NLM_INTERNET_CONNECTIVITY_WEBHIJACK) == 
						            NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
                                    {
                                        fCaptivePortalDetected = true;
                                    }
                                    auto t = V_UINT(&variantConnectivity);
                                    VariantClear(&variantConnectivity);
                                }
                                if (!fCaptivePortalDetected && (networkConnectivity 
                                & NLM_CONNECTIVITY_IPV6_INTERNET) == 
                                                  NLM_CONNECTIVITY_IPV6_INTERNET)
                                {
                                    VariantInit(&variantConnectivity);
                                    if (SUCCEEDED(pNetworkPropertyBag->Read
                                                     (NA_InternetConnectivityV6, 
                                    &variantConnectivity, nullptr)) && 
                                    (V_UINT(&variantConnectivity) & 
                                    NLM_INTERNET_CONNECTIVITY_WEBHIJACK) == 
						            NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
                                    {
                                        fCaptivePortalDetected = true;
                                    }
                                    VariantClear(&variantConnectivity);
                                }
                            }
                        }

                        pINetwork->Release();
                    }

                    if (fCaptivePortalDetected)
                        break;


                    hr = hr = pEnum->Next(1, &pINetwork, nullptr);
                }
            }
        }
    }

    // Uninitialize COM.
    // (This should be called on application shutdown.)
    CoUninitialize();

References and Important Links

I would like to say that since this article uses Windows functions, MSDN library is your best friend and the first source of information you should check. But also, these are the two main articles that helped me in understanding the APIs:

History

  • 27th March, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)