< HOSTENT, gethostbyname & gethostbyaddr Example | RnR Main | Chap 9: Multicasting >


 

 

Registration and Name Resolution 8  Part 7

 

 

What do we have in this chapter 8 part 7?

  1. Querying Network Location Awareness (NLA)

 

Querying Network Location Awareness (NLA)

 

Windows XP offers a new name service called NLA which can help an application identify network names and connection properties of all networks connected to a machine at any given time or when a change has been made to the available network interfaces. For example, you might be developing an application that must connect to the Internet; NLA can determine if or when an Internet connection path becomes available. This is especially useful in the world of mobile computing, where network interfaces come and go depending on the physical location of a computer, such as a laptop used at home and work. Winsock allows you to query the NLA service through the name space provider interface. Retrieving network name information requires calling WSALookupServiceBegin(), WSALookupServiceNext(), and WSALookupServiceEnd().

To prepare for NLA queries, call WSALookupServiceBegin() with the following restrictive WSAQUERYSET parameters and guidelines. The dwSize field must be set to the size of a WSAQUERYSET structure, dwNameSpace must be set to NS_NLA, and lpServiceClassId must be set to the GUID define NLA_SERVICE_CLASS_GUID. The remaining fields, except lpszServiceInstanceName, are ignored. You can optionally set lpszServiceInstanceName to restrict the query to a particular network name. For example, suppose you have two networks available: a home network and an Internet connection network by an ISP. Let's assume your ISP names your Internet connection “MyDSLProvider”. You can limit the NLA query to a single network by specifying the network name like “MyDSLProvider”. The following code fragment demonstrates how to prepare for a query by setting up a restrictive WSAQUERYSET structure from our discussion so far.

 

WSAQUERYSET qs;

GUID NLANameSpaceGUID = NLA_SERVICE_CLASS_GUID;

// Required fields to begin a query

qs.dwSize = sizeof(WSAQUERYSET);

qs.dwNameSpace = NS_NLA;

qs.lpServiceClassId = &NLANameSpaceGUID;

// Optional field to restrict the query

qs.lpszServiceInstanceName = "MyDSLProvider";

 

Once you have the restrictive WSAQUERYSET established, you have to decide how to control the query from the NLA service with the correct control flags for WSALookupServiceBegin(). Table 8-8 describes the control flags that are used specifically in NLA queries. If you use either LUP_RETURN_NAME or LUP_RETURN_COMMENT exclusively you will receive only a unique list of network names. If you also include LUP_RETURN_BLOB, you will receive both network names and network characteristics for each network identified. The LUP_DEEP flag allows you to receive the maximum amount of information available; however, the queries will take longer to complete. The most common way to start the query is to simply OR the LUP_RETURN_ALL and the LUP_DEEP flags as follows:

 

LPHANDLE hNLA;

WSALookupServiceBegin(&qs, LUP_RETURN_ALL │ LUP_DEEP, &hNLA)

 

Once you successfully retrieve a lookup handle lphLookup from WSALookup-ServiceBegin(), you are ready to begin your NLA queries for available network names.

 

Table 8-8 NLA Specific Control Flags

 

Flag

Meaning

LUP_RETURN_ALL

Equates to using LUP_RETURN_NAME, LUP_RETURN_COMMENT, and LUP_RETURN_BLOB

LUP_RETURN_NAME

Retrieves the network name in the WSAQUERYSET field lpszServiceInstanceName.

LUP_RETURN_COMMENT

Retrieves a friendly network name in the WSAQUERYSET field lpszComment.

LUP_RETURN_BLOB

Retrieves network characteristic information, such as connection speed, adapter identification, and Internet connectivity.

LUP_DEEP

Tells NLA to query for all characteristics for each network available.

LUP_FLUSHPREVIOUS

Used only on calls to WSALookupServiceNext() to skip over network information that you can't handle/allocate memory for.

 

Because the NLA name space provider is implemented as a service, it is designed to inform your application any time a change has occurred to the available network names. When you first call WSALookupServiceBegin(), you can immediately query current network information using WSALookupServiceNext() in which each call will return a network name until the function fails with error WSA_E_NO_MORE.

Depending on the control flags established in WSALookupServiceBegin(), if you pass LUP_RETURN_BLOB, you will need to pass a sufficient-sized buffer for the WSAQUERYSET passed to WSALookupServiceNext(); otherwise the function will fail with error WSAEFAULT. LUP_RETURN_BLOB can allow the query to return with an array of NLA_BLOB data structures containing specific network information such as speed and connectivity settings for each network returned from WSALookupServiceNext(). An NLA_BLOB can consist of an array of zero or more NLA_BLOB data structures and is defined as:

 

typedef struct _NLA_BLOB

{

        struct {

        NLA_BLOB_DATA_TYPE type;

        DWORD dwSize;

        DWORD nextOffset;

    } header;

    union {

        // header.type -> NLA_RAW_DATA

        CHAR rawData[1];

        // header.type -> NLA_INTERFACE

        struct {

            DWORD dwType;

            DWORD dwSpeed;

            CHAR adapterName[1];

        } interfaceData;

        // header.type -> NLA_802_1X_LOCATION

        struct {

            CHAR information[1];

        } locationData;

 

        // header.type -> NLA_CONNECTIVITY

        struct {

            NLA_CONNECTIVITY_TYPE type;

            NLA_INTERNET internet;

        } connectivity;

 

        // header.type -> NLA_ICS

        struct {

            struct {

                DWORD speed;

                DWORD type;

                DWORD state;

                WCHAR machineName[256];

                WCHAR sharedAdapterName[256];

            } remote;

        } ICS;

    } data;

 

} NLA_BLOB, *PNLA_BLOB, * FAR LPNLA_BLOB;

 

After successfully calling WSALookupServiceNext(), you should first check if the WSAQUERYSET lpBlob field is not NULL. If it isn't, then you can begin looking at the NLA_BLOB structure returned from the call. As seen in the code above, an NLA_BLOB has two portions: header and data. The header section describes which NLA type the data portion of the BLOB represents and if there are any more NLA_BLOBs (through the nextOffset field) to be processed on the network name. Your application should cycle through all the data BLOBs identified by the nextOffset field in every NLA_BLOB until the field equals zero. Each NLA_BLOB found can have one of the following data types, which are defined in Table 8-9.

 

Table 8-9 NLA Data Types

 

NLA_BLOB Data Types

Corresponding NLA_BLOB Data Section

NLA_802_1X_LOCATION

Wireless network information stored in the locationData structure

NLA_CONNECTIVITY

Basic network connectivity information stored in connectivity structure, there are two fields: type and Internet, which refer to NLA_ CONNECTIVITY_TYPE, NLA_INTERNET enumeration types, respectively, see Tables 8-10 and 8-11 for descriptions of these types.

NLA_ICS

An internal network sharing an Internet connection in the ICS structure.

NLA_INTERFACE

General network information, such as media type, connection speed, and adapter name, stored in the interfaceData structure. Possible media types are defined in Ipifcons.h in the media types section.

NLA_RAW_DATA

Refers to the rawData field and is not used.

 

Table 8-10 NLA Connectivity Types

 

NLA_CONNECTIVITY_TYPE

Meaning

NLA_NETWORK_AD_HOC

Represents a private network not connected to any other network.

NLA_NETWORK_MANAGED

Represents a network that is managed with a domain controller.

NLA_NETWORK_UNKNOWN

Represents the network that is the private (non-Internet) side of an ICS connection.

NLA_NETWORK_UNMANAGED

The service cannot determine the connection characteristics.

 

Table 8-11 NLA Internet Types

 

NLA_INTERNET Data Types

Meaning

NLA_INTERNET_NO

Indicates that a path to the Internet is not available.

NLA_INTERNET_UNKNOWN

Indicates that the service could not determine if there is a path to the Internet.

NLA_INTERNET_YES

Indicates a path to the Internet is available.

 

As you probably can tell, the NLA_INTERFACE and NLA_CONNECTIVITY NLA_BLOB data types offer some of the most useful information about the available networks. For example, if you receive an NLA_CONNECTIVITY data BLOB, you will be able to determine if you have a connection to the Internet. The following code fragment demonstrates how to get each network name by calling WSALookupServiceNext() and cycle through the NLA_BLOB data found in each name.

 

char           buff[16384];

DWORD          BufferSize;

while (1)

{

    memset(qs, 0, sizeof(*qs));

    BufferSize = sizeof(buff);

    if (WSALookupServiceNext(hNLA, LUP_RETURN_ALL, &BufferSize, qs) == SOCKET_ERROR)

    {

        int Err = WSAGetLastError();

 

        if (Err == WSA_E_NO_MORE)

        {

            // There is no more data. Stop asking.

            break;

        }

        printf("WSALookupServiceNext failed with error %d\n", WSAGetLastError());

        WSALookupServiceEnd(hNLA);

        return;

    }

 

    printf("\nNetwork Name: %s\n", qs->lpszServiceInstanceName);

    printf("Network Friendly Name: %s\n", qs->lpszComment);

    if (qs->lpBlob != NULL)

    {

        // Cycle through BLOB data list

        DWORD     Offset = 0;

        PNLA_BLOB pNLA;

 

        do

        {

            pNLA = (PNLA_BLOB) &(qs->lpBlob->pBlobData[Offset]);

            switch (pNLA->header.type)

            {

                case NLA_RAW_DATA:

                    printf("\tNLA Data Type: NLA_RAW_DATA\n");

                    break;

                case NLA_INTERFACE:

                    printf("\tNLA Data Type: NLA_INTERFACE\n");

                    printf("\t\tType: %d\n",

                        pNLA->data.interfaceData.dwType);

                    printf("\t\tSpeed: %d\n",

                        pNLA->data.interfaceData.dwSpeed);

                    printf("\t\tAdapter Name: %s\n",

                        pNLA->data.interfaceData.adapterName);

                    break;

                case NLA_802_1X_LOCATION:

                    printf("\tNLA Data Type: NLA_802_1X_LOCATION\n");

                    printf("\t\tInformation: %s\n",

                        pNLA->data.locationData.information);

                    break;

                case NLA_CONNECTIVITY:

                    printf("\tNLA Data Type: NLA_CONNECTIVITY\n");

                    switch(pNLA->data.connectivity.type)

                    {

                        case NLA_NETWORK_AD_HOC:

                            printf("\t\tNetwork Type: AD HOC\n");

                            break;

                        case NLA_NETWORK_MANAGED:

                            printf("\t\tNetwork Type: Managed\n");

                           break;

                       case NLA_NETWORK_UNMANAGED:

                           printf("\t\tNetwork Type: Unmanaged\n");

                           break;

                       case NLA_NETWORK_UNKNOWN:

                           printf("\t\tNetwork Type: Unknown\n");

                   }

                   switch(pNLA->data.connectivity.internet)

                   {

                       case NLA_INTERNET_NO:

                           printf("\t\tInternet connectivity: No\n");

                           break;

                       case NLA_INTERNET_YES:

                           printf("\t\tInternet connectivity: Yes\n");

                           break;

                       case NLA_INTERNET_UNKNOWN:

                           printf("\t\tInternet connectivity:

                                Unknown\n");

                            break;

                    }

                    break;

                case NLA_ICS:

                    printf("\tNLA Data Type: NLA_ICS\n");

                    printf("\t\tSpeed: %d\n",

                         pNLA->data.ICS.remote.speed);

                    printf("\t\tType: %d\n",

                         pNLA->data.ICS.remote.type);

                    printf("\t\tState: %d\n",

                         pNLA->data.ICS.remote.state);

                    printf("\t\tMachine Name: %S\n",

                        pNLA->data.ICS.remote.machineName);

                    printf("\t\tShared Adapter Name: %S\n",

                      pNLA->data.ICS.remote.sharedAdapterName);

                 break;

              default:

                 printf("\tNLA Data Type: Unknown to this program\n");

                 break;

          }

          Offset = pNLA->header.nextOffset;

      }

      while (Offset != 0);

  }

}

 

Once you have exhausted all the names and NLA_BLOB data returned from WSALookupServiceNext(), the NLA service offers the capability to inform your application of changes made to these interfaces through the WSANSPIoctrl() API. WSANSPIoctrl() takes the lookup handle that WSALookupServiceBegin() returned and associates a completion method for your application to receive notification of changes. WSANSPIoctl() is defined as:

 

int WSAAPI WSANSPIoctl(

  HANDLE hLookup,

  DWORD dwControlCode,

  LPVOID lpvInBuffer,

  DWORD cbInBuffer,

  LPVOID lpvOutBuffer,

  DWORD cbOutBuffer,

  LPDWORD lpcbBytesReturned,

  LPWSACOMPLETION lpCompletion

);

 

The hLookup parameter should be set to the handle that was originally returned from WSALookupServiceBegin(). The dwControlCode should be set to SIO_NSP_NOTIFY_CHANGE. NLA does not use lpvInBuffer and lpvOutBuffer and they should be NULL. The cbInBuffer and cbOutBuffer are also not used and should be set to zero. The lpcbBytesReturned is not useful but you have to supply a buffer. The lpCompletion field allows you to specify an asynchronous wait method. The following code fragment demonstrates how to set up event-based notification for waiting on network changes.

 

WSAOverlap.hEvent = Event;

WSAComplete.Type = NSP_NOTIFY_EVENT;

WSAComplete.Parameters.Event.lpOverlapped = &WSAOverlap;

 

if (WSANSPIoctl(hNLA, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &BytesReturned, &WSAComplete) == SOCKET_ERROR)

{

    int Ret = WSAGetLastError();

 

    if (Ret != WSA_IO_PENDING)

    {

        printf("WSANSPIoctrl failed with error %d\n", Ret);

        return;

    }

}

 

if (WSAWaitForMultipleEvents(1, &Event, TRUE, WSA_INFINITE, FALSE) == WSA_WAIT_FAILED)

{

    printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());

    return;

}

WSAResetEvent(Event);

 

// Query for all current network names available using the same

// lookup handle returned originally through WSALookupServiceBegin

 

The following program example tries to demonstrate the NLA. Create a new empty Win32 console application project and add the project/solution name.

 

--------------------------------------------------------------------------

Winsock 2 - Registration and Name Resolution (RNR): Program example tries to demonstrate the NLA, creating a new project

 

Add the following source code.

 

// Description:

//    This sample demonstrates how to query and monitor the

//    Network Location Awareness service using the

//    WSALookupServiceBegin, WSALookupServiceNext and the

//    WSALookupServiceEnd APIs to obtain a list of network

//    names available to the system. For each network name

//    found, network characteristics such as speed and

//    connectivity properties are printed.

//

// No command line argument

//    nlaqueryexample

//

// Link to ws2_32.lib

#include <winsock2.h>

#include <mswsock.h>

#include <stdio.h>

 

int main(int argc, char **argv)

{

    WSADATA        wsd;

    int                    Ret;

    char                buff[16384];

    int                   Err;

    DWORD        BufferSize;

    WSAQUERYSET   *qs=NULL;

    GUID           NLANameSpaceGUID = NLA_SERVICE_CLASS_GUID;

    HANDLE         hNLA;

    WSAEVENT       Event;

    PNLA_BLOB                     pNLA;

    DWORD BytesReturned;

    WSACOMPLETION WSAComplete;

    WSAOVERLAPPED WSAOverlap;

    DWORD     Offset = 0;

 

    // Load Winsock

    if ((WSAStartup(MAKEWORD(2,2), &wsd)) != 0)

    {

        printf("WSAStartup() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("WSAStartup() should be OK!\n");

 

    // Initialize the WSAQUERYSET structure for the query

    qs = (WSAQUERYSET *) buff;

    memset(qs, 0, sizeof(*qs));

    // Required values

    qs->dwSize = sizeof(WSAQUERYSET);

    qs->dwNameSpace = NS_NLA;

    qs->lpServiceClassId = &NLANameSpaceGUID;

    // Optional values

    qs->dwNumberOfProtocols = 0;

    qs->lpszServiceInstanceName = NULL;

    qs->lpVersion = NULL;

    qs->lpNSProviderId = NULL;

    qs->lpszContext = NULL;

    qs->lpafpProtocols = NULL;

    qs->lpszQueryString = NULL;

    qs->lpBlob = NULL;

 

    if (WSALookupServiceBegin(qs, LUP_RETURN_ALL | LUP_DEEP, &hNLA) == SOCKET_ERROR)

    {

        printf("WSALookupServiceBegin() failed with error %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("WSALookupServiceBegin() should be OK!\n");

 

    if ((Event = WSACreateEvent()) == WSA_INVALID_EVENT)

    {

        printf("WSACreateEvent() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("WSACreateEvent() should be fine!\n");

 

    while(1)

    {

        printf("Querying for Networks...\n");

        while (1)

        {

            memset(qs, 0, sizeof(*qs));

            BufferSize = sizeof(buff);

 

            if (WSALookupServiceNext(hNLA, LUP_RETURN_ALL, &BufferSize, qs) == SOCKET_ERROR)

            {

                Err = WSAGetLastError();

                if (Err == WSA_E_NO_MORE)

                {

                    // There is no more data. Stop asking

                    printf("No more data, exit...\n");

                    break;

                }

                printf("WSALookupServiceNext failed with error %d\n", WSAGetLastError());

                WSALookupServiceEnd(hNLA);

                return -1;

            }

            printf("\nNetwork Name: %s\n", qs->lpszServiceInstanceName);

            printf("Comment (if any): %s\n", qs->lpszComment);

 

            if (qs->lpBlob != NULL)

            {

                // Cycle through BLOB data list

                do

                {

                    pNLA = (PNLA_BLOB) &(qs->lpBlob->pBlobData[Offset]);

                    switch (pNLA->header.type)

                    {

                        case NLA_RAW_DATA:

                            printf("\tNLA Data Type: NLA_RAW_DATA\n");

                            break;

                        case NLA_INTERFACE:

                            printf("\tNLA Data Type: NLA_INTERFACE\n");

                            printf("\t\tType: %d\n", pNLA->data.interfaceData.dwType);

                            printf("\t\tSpeed: %d\n", pNLA->data.interfaceData.dwSpeed);

                            printf("\t\tAdapter Name: %s\n", pNLA->data.interfaceData.adapterName);

                            break;

                        case NLA_802_1X_LOCATION:

                            printf("\tNLA Data Type: NLA_802_1X_LOCATION\n");

                            printf("\t\tInformation: %s\n", pNLA->data.locationData.information);

                            break;

                        case NLA_CONNECTIVITY:

                            printf("\tNLA Data Type: NLA_CONNECTIVITY\n");

                            switch(pNLA->data.connectivity.type)

                            {

                                 case NLA_NETWORK_AD_HOC:

                                     printf("\t\tNetwork Type: AD HOC\n");

                                     break;

                                 case NLA_NETWORK_MANAGED:

                                     printf("\t\tNetwork Type: Managed\n");

                                     break;

                                 case NLA_NETWORK_UNMANAGED:

                                     printf("\t\tNetwork Type: Unmanaged\n");

                                     break;

                                 case NLA_NETWORK_UNKNOWN:

                                     printf("\t\tNetwork Type: Unknown\n");

                            }

                            switch(pNLA->data.connectivity.internet)

                            {

                                 case NLA_INTERNET_NO:

                                     printf("\t\tInternet connectivity: No\n");

                                     break;

                                 case NLA_INTERNET_YES:

                                     printf("\t\tInternet connectivity: Yes\n");

                                     break;

                                 case NLA_INTERNET_UNKNOWN:

                                     printf("\t\tInternet connectivity: Unknown\n");

                                     break;

                            }

                            break;

                        case NLA_ICS:

                            printf("\tNLA Data Type: NLA_ICS\n");

                            printf("\t\tSpeed: %d\n", pNLA->data.ICS.remote.speed);

                            printf("\t\tType: %d\n", pNLA->data.ICS.remote.type);

                            printf("\t\tState: %d\n", pNLA->data.ICS.remote.state);

                            printf("\t\tMachine Name: %S\n", pNLA->data.ICS.remote.machineName);

                            printf("\t\tShared Adapter Name: %S\n", pNLA->data.ICS.remote.sharedAdapterName);

                            break;

                        default:

                            printf("\tNLA Data Type: Unknown to this program\n");

                            break;

                    }

                    Offset = pNLA->header.nextOffset;

                }

                while (Offset != 0);

            }

        }

        printf("\nFinished query, Now wait for change notification...\n");

 

        WSAOverlap.hEvent = Event;

        WSAComplete.Type = NSP_NOTIFY_EVENT;

        WSAComplete.Parameters.Event.lpOverlapped = &WSAOverlap;

 

        if (WSANSPIoctl(hNLA, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0,

            &BytesReturned, &WSAComplete) == SOCKET_ERROR)

        {

            Ret = WSAGetLastError();

            if (Ret != WSA_IO_PENDING)

            {

                printf("WSANSPIoctrl() failed with error code %d\n", Ret);

                return -1;

            }

        }

        else

               printf("WSANSPIoctl() should be OK!\n");

        if (WSAWaitForMultipleEvents(1, &Event, TRUE, WSA_INFINITE, FALSE) == WSA_WAIT_FAILED)

        {

            printf("WSAWaitForMultipleEvents() failed with error code %d\n", WSAGetLastError());

            return -1;

        }

        else

            printf("WSAWaitForMultipleEvents() is OK!\n");

 

        if(WSAResetEvent(Event) == TRUE)

             printf("WSAResetEvent() is OK!\n");

        else

             printf("WSAResetEvent() failed with error code %d\n", WSAGetLastError());

    }

 

    if(WSALookupServiceEnd(hNLA) == 0)

        printf("WSALookupServiceEnd() is OK!\n");

    else

        printf("WSALookupServiceEnd() failed with error code %d\n", WSAGetLastError());

    if(WSACleanup() == 0)

        printf("WSACleanup() is OK!\n");

    else

        printf("WSACleanup() failed with error code %d\n", WSAGetLastError());

}

 

Build and run the project.

 

Winsock 2 - Registration and Name Resolution (RNR): Program example tries to demonstrate the NLA, a sample output

 

Finally, NLA will allow you to associate additional comment information with each network name returned from a query using the WSASetService() API. All you have to do is fill in a WSAQUERYSET structure with a valid name in the lpszServiceInstanceName field and provide a new friendly name in the lpszComment field. Once the fields are set, call WSASetService() with the first parameter (lpqsRegInfo) set to a pointer to your WSAQUERYSET structure, the second parameter (essOperation) set to the flag RNRSERVICE_REGISTER, and the third parameter (dwControlFlags) set to the value NLA_FRIENDLY_NAME.

 

 

 


< HOSTENT, gethostbyname & gethostbyaddr Example | RnR Main | Chap 9: Multicasting >