Querying a Service

Querying a Service

Now that you know how to register a service within a name space, let's look at how a client can query the name space for a given service so it can get information about the service for communication purposes. Name resolution is quite a bit simpler than service registration, even though name resolution uses three functions for querying: WSALookupServiceBegin, WSALookupServiceNext, and WSALookupServiceEnd.

The first step when performing a query is to call WSALookupServiceBegin, which initiates the query by setting up the constraints within which the query will act. The function prototype is as follows:

INT WSALookupServiceBegin (     LPWSAQUERYSET lpqsRestrictions,      DWORD dwControlFlags,      LPHANDLE lphLookup );

The first parameter is a WSAQUERYSET structure that places constraints on the query, such as limiting the name spaces to query. The second parameter, dwControlFlags, determines the depth of the search. Table 8-7 contains the various possible flags and their meanings. These flags affect how the query behaves as well as which data is returned from the query. The last parameter is of type HANDLE and is initialized upon function return. The return value is 0 on success; otherwise, SOCKET_ERROR is returned. If one or more parameters are invalid, WSAGetLastError returns WSAEINVAL. If the name is found in the name space but no data matches the given restrictions, the error is WSANO_DATA. If the given service does not exist, WSASERVICE_NOT_FOUND is the error.

Table 8-7 Control Flags

Flag

Meaning

LUP_DEEP

In hierarchical name spaces, query deep as opposed to the first level.

LUP_CONTAINERS

Retrieve container objects only. This flag pertains to hierarchical name spaces only.

LUP_NOCONTAINERS

Do not return any containers. This flag pertains to hierarchical name spaces only.

LUP_FLUSHCACHE

Ignore cached information, and query the name space directly. Note that not all name providers cache queries.

LUP_FLUSHPREVIOUS

Instruct the name provider to discard the information set previously returned. This flag is typically used after WSALookupServiceNext returns WSA_NOT_ENOUGH_MEMORY. The information that was too big for the supplied buffer is discarded, and the next information set is to be retrieved.

LUP_NEAREST

Retrieve the results in order of distance. Note that it is up to the name provider to calculate this distance metric, as there is no specific provision for this information when a service is registered. Name providers are not required to support this concept.

LUP_RES_SERVICE

Specifies that the local address be returned in the CSADDR_INFO structure

LUP_RETURN_ADDR

Retrieve the addresses as lpcsaBuffer.

LUP_RETURN_ALIASES

Retrieve only alias information. Each alias is returned in successive calls to WSALookupServiceNext and will have the RESULT_IS_ALIAS flag set.

LUP_RETURN_ALL

Retrieve all available information.

LUP_RETURN_BLOB

Retrieve the private data as lpBlob.

LUP_RETURN_COMMENT

Retrieve the comment as lpszComment.

LUP_RETURN_NAME

Retrieve the name as lpszServiceInstanceName.

LUP_RETURN_TYPE

Retrieve the type as lpServiceClassId.

LUP_RETURN_VERSION

Retrieve the version as lpVersion.

When you make a call to WSALookupServiceBegin, a handle for the query is returned that you pass to WSALookupServiceNext, which returns the data to you. The function is defined as

INT WSALookupServiceNext (     HANDLE hLookup,      DWORD dwControlFlags,      LPDWORD lpdwBufferLength,      LPWSAQUERYSET lpqsResults );

The handle hLookup is returned from WSALookupServiceBegin. The dwControlFlags parameter has the same meaning as in WSALookupServiceBegin except that only LUP_FLUSHPREVIOUS is supported. The parameter lpdwBufferLength is the length of the buffer passed as lpqsResults. Because the WSAQUERYSET structure could contain binary large object (BLOB) data, it is often required that you pass a buffer larger than the structure itself. If the buffer size is insufficient for the data to be returned, the function call fails with WSA_NOT_ENOUGH_MEMORY.

Once you have initiated the query with WSALookupServiceBegin, call WSALookupServiceNext until the error WSA_E_NO_MORE (10110) is generated. Caution: in earlier implementations of Winsock, the error code for no more data is WSAENOMORE (10102), so robust code should check for both error codes. Once all the data has been returned or you have finished querying, call WSALookupServiceEnd with the HANDLE variable used in the queries. The function is

INT WSALookupServiceEnd ( HANDLE hLookup );

Forming a Query

Let's look at how you can query the service you registered in the previous section. The first thing to do is set up a WSAQUERYSET structure that defines the query. Look at the following code.

WSAQUERYSET   qs; GUID          guid = SVCID_NETWARE(200); AFPROTOCOLS   afp[2] = {{AF_IPX,  NSPROTO_IPX}, {AF_INET, IPPROTO_UDP}}; HANDLE        hLookup; int           ret; memset(&qs, 0, sizeof(qs)); qs.dwSize = sizeof (WSAQUERYSET); qs.lpszServiceInstanceName = "Widget Server"; qs.lpServiceClassId = &guid; qs.dwNameSpace = NS_ALL; qs.dwNumberOfProtocols = 2;  qs.lpafpProtocols = afp; ret = WSALookupServiceBegin(&qs, LUP_RETURN_ADDR   LUP_RETURN_NAME,          &hLookup); if (ret == SOCKET_ERROR)     // Error

Remember that all service lookups are based on the service class GUID that the service you are searching for is based on. The variable guid is set to the service class ID of our server. You first initialize qs to 0 and set the dwSize field to the size of the structure. The next step is to give the name of the service you are searching for. The service name can be the exact name of the server, or you can specify a wildcard (*) that will return all services of the given service class GUID. Next, tell the query to search all name spaces by using the NS_ALL constant. Last, set up the protocols that the client is capable of connecting with, which are IPX and UDP/IP. This is done by using an array of two AFPROTOCOLS structures.

To begin the query, you must call WSALookupServiceBegin. The first parameter is our WSAQUERYSET structure, and the next parameters are flags defining which data should be returned if a matching service is found. Here you specify that you want addressing information and the service name by logically ORing the two flags LUP_RETURN_ADDR and LUP_RETURN_NAME. The flag LUP_RETURN_NAME is necessary only if you're specifying the wildcard (*) for the service name; otherwise, you already know the service name. The last parameter is a HANDLE variable that identifies this particular query. It will be initialized upon successful return.

Once the query is successfully opened, you call WSALookupServiceNext until WSA_E_NO_MORE is returned. Each successful call returns information about a service that matches our criteria. Here is what the code looks like for this step:

char          buff[sizeof(WSAQUERYSET) + 2000]; DWORD         dwLength, dwErr; WSAQUERYSET  *pqs = NULL; SOCKADDR     *addr; int           i; pqs = (WSAQUERYSET *)buff; dwLength = sizeof(WSAQUERYSET) + 2000; while (1) {     ret = WSALookupServiceNext(hLookup, 0, &dwLength, pqs);     if (ret == SOCKET_ERROR)     {         if ((dwErr = WSAGetLastError()) == WSAEFAULT)         {             printf("Buffer too small; required size is: %d\n", dwLength);             break;         }        else if ((dwErr == WSA_E_NO_MORE)   (dwErr = WSAENOMORE))             break;         else         {             printf("Failed with error: %d\n", dwErr);             break;         }     }     for (i = 0; i < pqs->dwNumberOfCsAddrs; i++)     {         addr = (SOCKADDR *)pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr;         if (addr->sa_family == AF_INET)         {             SOCKADDR_IN *ipaddr = (SOCKADDR_IN *)addr;             printf("IP address:port = %s:%d\n",             inet_ntoa(ipaddr->sin_addr),             ipaddr->sin_port);         }         else if (addr->sa_family == AF_IPX)         {             CHAR  IPXString[64];             DWORD IPXStringSize = sizeof(IPXString);             if (WSAAddressToString((LPSOCKADDR) addr,                  sizeof(SOCKADDR_IPX), NULL, IPXString,                  &IPXStringSize) == SOCKET_ERROR)                 printf("WSAAddressToString returned error %d\n",                     WSAGetLastError());             else                    printf("IPX address: %s\n", IPXString);            }        }    }    WSALookupServiceEnd(hLookup);

The example code is straightforward, although a bit simplified. Calling WSALookupServiceNext requires only a valid handle to a query, the length of the return buffer, and the return buffer. You don't need to specify any control flags because the only valid flag for this function is LUP_FLUSHPREVIOUS. If our supplied buffer is too small and this flag is set, the results from this call are discarded. However, in this example we don't use LUP_FLUSHPREVIOUS, and if our buffer is too small, the WSAEFAULT error is generated. If this occurs, lpdwBufferLength is set to the required size. Our example uses a fixed-size buffer equal to the size of the WSAQUERYSET structure plus 2000 bytes. Because all you are asking for are service names and addresses, this should be a sufficient size. Of course, in production code, your applications should be prepared to handle the WSAEFAULT error.

Once you successfully call WSALookupServiceNext, the buffer is filled with a WSAQUERYSET structure containing the results. In our query, you asked for names and addresses; the fields of WSAQUERYSET that are of most interest are lpszServiceInstanceName and lpcsaBuffer. The former contains the name of the service, the latter is an array of CSADDR_INFO structures that contains addressing information for the service. The parameter dwNumberOfCsAddrs tells us exactly how many addresses have been returned. In the example code, all we do is simply print out the addresses. Check for only IPX and IP addresses to print because those are the only address families you requested when you opened the query.

If our query used a wildcard (*) for the service name, each call to WSALookupServiceNext would return a particular instance of that service running somewhere on the network—provided, of course, that multiple instances are actually registered and running. Once all instances of the service have been returned, the error WSA_E_NO_MORE is generated, and you break out of the loop. The last thing you do is call WSALookupServiceEnd on the query handle. This releases any resources allocated for the query.

Querying DNS

Previously we mentioned that the DNS name space is static, which means you cannot dynamically register your service; however, you can still use the Winsock name resolution functions to perform a DNS query. Performing a DNS query is actually a bit more complicated than performing a normal query for a service that you have registered because the DNS name space provider returns the query information as a BLOB. Why does it do this? Remember from the Chapter 3 discussion of gethostbyname that a name lookup returns a HOSTENT structure that contains not only IP addresses but also aliases. That information doesn't quite fit into the fields of the WSAQUERYSET structure.

The tricky aspect about BLOB data is that its data format is not well documented, which makes directly querying DNS challenging. First, let's take a look at how to open the query. The file DNSQUERY.CPP on the companion CD contains the entire example code for querying DNS directly; however, we'll take a look at it piece by piece. The following code illustrates initializing the DNS query:

WSAQUERYSET qs; AFPROTOCOLS afp [2] = {{AF_INET, IPPROTO_UDP},{AF_INET, IPPROTO_TCP}}; GUID        hostnameguid = SVCID_INET_HOSTADDRBYNAME; DWORD       dwLength = sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048; char        buff[dwLength]; HANDLE       hQuery; int          ret; qs = (WSAQUERYSET *)buff; memset(&qs, 0, sizeof(qs)); qs.dwSize = sizeof(WSAQUERYSET); qs.lpszServiceInstanceName = argv[1]; qs.lpServiceClassId = &hostnameguid; qs.dwNameSpace = NS_DNS; qs.dwNumberOfProtocols = 2; qs.lpafpProtocols = afp; ret = WSALookupServiceBegin(&qs, LUP_RETURN_NAME   LUP_RETURN_BLOB,     &hQuery); if (ret == SOCKET_ERROR)     // Error

Setting up the query is quite similar to our previous example. The most noticeable change is that we use the predefined GUID SVCID_INET_ HOSTADDRBYNAME. This is the GUID that identifies host name queries. The lpszServiceInstanceName is the host name that we want to resolve. Because we are resolving host names through DNS, we need to specify only NS_DNS for dwNameSpace. Finally, lpafProtocols is set to an array of two AFPROTOCOLS structures, which defines the TCP/IP and UDP/IP protocols as those that our query is interested in.

Once you establish the query, you can call WSALookupServiceNext to return data.

char         buff[sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048]; DWORD        dwLength = sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048; WSAQUERYSET *pqs; HOSTENT     *hostent; pqs = (WSAQUERYSET *)buff; pqs->dwSize = sizeof(WSAQUERYSET); ret = WSALookupServiceNext(hQuery, 0, &dwLength, pqs); if (ret == SOCKET_ERROR)     // Error WSALookupServiceEnd(hQuery); hostent = (HOSTENT *) pqs->lpBlob->pBlobData;

Because a DNS name space provider returns the host information as a BLOB, you need to supply a sufficiently large buffer. Use a buffer equal in size to a WSAQUERYSET structure, plus a HOSTENT structure, plus 2048 bytes for good measure. Again, if this were insufficient, the call would fail with WSAEFAULT. In a DNS query, the host information is returned within the HOSTENT structure, even if a host name is associated with multiple IP addresses. That is why you don't need to call WSALookupServiceNext multiple times.

Now comes the tricky part—decoding the BLOB structure that the query returns. From Chapter 3, you know the HOSTENT structure is defined as

typedef struct hostent {    char FAR * h_name;     char FAR * FAR * h_aliases;     short h_addrtype;     short h_length;     char FAR * FAR * h_addr_list; } HOSTENT;

When the HOSTENT structure is returned as the BLOB data, the pointers within the structure are actually offsets into memory where the data lies. The offsets are from the start of the BLOB data, requiring you to fix up the pointers to reference the absolute memory location before you can access the data. Figure 8-1 shows the HOSTENT structure and memory layout returned. The DNS query is performed on the host name riven, which has a single IP address and no aliases. Each field in the structure has the offset value. To correct this so the fields reference the right location, you need to add the offset value to the address of the head of the HOSTENT structure. This needs to be performed on the h_name, h_aliases, and h_addr_list fields. In addition, the h_aliases and h_addr_list fields are an array of pointers. Once you obtain the correct pointer to the array of pointers, each 32-bit field in the references location is made up of offsets. If you take a look at the h_addr_list field in Figure 8-1, you'll see that the initial offset is 16 bytes, which references the byte after the end of the HOSTENT structure. This is the array of pointers to the 4-byte IP address. However, the first pointer in the array is an offset of 28 bytes. To reference the correct location, take the address of the HOSTENT structure and add 28 bytes, which points to a 4-byte location with the data 0x9D36B9BA, which is the IP address 157.54.185.186. You then take the 4 bytes after the entry with the offset of 28 bytes, which is 0. This signifies the end of the array of pointers. If multiple IP addresses were associated with this host name, another offset would be present and you would fix the pointer exactly as in the first case. The same procedure is done to fix the h_aliases pointer and the array of pointers it references. In this example, there are no aliases for our host. The first entry in the array is 0, which indicates that you don't have to do any further work for that field. The last field is the h_name field, which is easy to correct; simply add the offset to the address of the HOSTENT structure and it points to the start of a null-terminated string.

Figure 8-1 HOSTENT BLOB format

The code needed to fix these offsets into real addresses is simple, although quite a bit of pointer arithmetic is involved. To fix the h_name field, a simple offset adjustment such as the following will do:

hostent->h_name = (PCHAR)((DWORD_PTR)hostent->h_name +  (PCHAR)hostent);

To fix the array of pointers, as in the h_aliases and h_addr_list fields, requires a bit more code, but only to traverse the array and fix the references until a null entry is hit. The code looks like this:

PCHAR  *addr; if (hostent->h_aliases) {     addr = hostent->h_aliases =      (PCHAR)((DWORD_PTR)hostent->h_aliases + (PCHAR)hostent);    while (addr)    {         addr = (PCHAR)((DWORD_PTR)addr + (PCHAR *)hostent);         addr++;     } }

The code simply steps through each array entry and adds the starting address of the HOSTENT structure to the given offset, which becomes the value for that entry. Of course, once you hit an array entry whose value is 0, you stop. The same process needs to be applied to the h_addr_list field as well. Once the offsets are fixed, you can use the HOSTENT structure as you normally would.

Querying 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 as described in the Overlapped I/O model of Chapter 5. 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

note

On the companion CD, you will find a sample named NLAQUERY.CPP that demonstrates how to query for NLA names from our discussion so far.

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.



Network Programming for Microsoft Windows
Network Programming for Microsoft Windows (Microsoft Professional Series)
ISBN: 0735605602
EAN: 2147483647
Year: 2001
Pages: 172
Authors: Anthony Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net