Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C++

Captive Portal Detection with Windows APIs

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
27 Mar 2016CPOL2 min read 17.1K   1   4
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)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionRetrieve redirected url Pin
agrawalamit20055-Feb-18 2:07
agrawalamit20055-Feb-18 2:07 
Questionmemory usage Pin
Member 821693126-Nov-16 15:50
Member 821693126-Nov-16 15:50 
Questionif (SUCCEEDED(hrQueryInterface 1) && pNetworkPropertyBag != nullptr) compiler error Pin
Member 1086352124-Jun-16 3:08
Member 1086352124-Jun-16 3:08 
Questionhave you consider to post this as a tip? Pin
Nelek27-Mar-16 6:08
protectorNelek27-Mar-16 6:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.