< Service Registration Program Example | RnR Main | Querying DNS >


 

 

Registration and Name Resolution 8 Part 4

 

 

What do we have in this chapter 8 part 4?

  1. WSALookupServiceBegin() and WSALookupServiceNext() Example

  2. Querying a Service

  3. Forming a Query

WSALookupServiceBegin() and WSALookupServiceNext() Example

 

Create a new empty Win32 console mode application and add the project/solution name.

 

Winsock 2 - Registration and Name Resolution (RNR): The WSALookupServiceBegin and WSALookupServiceNext (Bug)

 

Add the following code.

 

// Standard I/O

#include <stdio.h>

// Link to ws2_32.lib

#include <winsock2.h>

// For guids

#include <svcguid.h>

 

int main(int argc, char * argv[])

{

            WSADATA wsaData;

            DWORD dwResult;

            HANDLE hLookup = 0;

            WSAQUERYSET lpRestrictions;

            GUID guid = SVCID_HOSTNAME;

 

            dwResult = WSAStartup(MAKEWORD(2,2), &wsaData);

 

            if (dwResult != 0)

            {

                        printf("Cannot startup Winsock, error code %d\n", dwResult);

                        exit(1);

            }

            else

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

 

            ZeroMemory(&lpRestrictions, sizeof(WSAQUERYSET));

            lpRestrictions.dwSize = sizeof(WSAQUERYSET);

            lpRestrictions.lpServiceClassId = &guid;

 

            dwResult = WSALookupServiceBegin(&lpRestrictions, LUP_RETURN_NAME, &hLookup);

            if (dwResult != SOCKET_ERROR)

            {

                        DWORD dwLength = 0;

                        WSAQUERYSET * pqs = NULL;

 

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

 

                        // Picking an arbitrary value works fine

                        // COMMENT/UNCOMMENT below for success on Windows 2000, XP

                        pqs = (WSAQUERYSET *) malloc(sizeof(WSAQUERYSET) + 100);

                        dwLength = sizeof(WSAQUERYSET) + 100;

 

                        do

                        {

                                    if (WSALookupServiceNext(hLookup, 0, &dwLength, pqs) != 0)

                                    {

                                                dwResult = WSAGetLastError();

                                                if((dwResult == WSA_E_NO_MORE) || (dwResult == WSAENOMORE))

                                                {

                                                            printf("No more record found!\n");

                                                            break;

                                                }

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

                                    }

                                    else

                                    {

                                                dwResult = 0;

                                                printf("WSALookupServiceNext() looks fine!\n");

                                    }

 

                                    if (dwResult == WSAEFAULT)

                                    {

                                                if (pqs)

                                                {

                                                            printf("Freeing pqs...\n");

                                                            free(pqs);

                                                }

 

                                                // Reallocate

                                                pqs = (WSAQUERYSET *) malloc(dwLength);

                                                if (!pqs)

                                                {

                                                            printf("Could not allocate memory: %d\n", GetLastError());

                                                            exit(2);

                                                }

                                                else

                                                {

                                                            printf("Memory allocated for pqs successfully!\n");

                                                            continue;

                                                }

                                    }

 

                                    // Output it since we have it now

                                    if ((dwResult == 0) && (pqs))

                                    {

                                                printf("  Service instance name: %S\n", pqs->lpszServiceInstanceName);

                                                printf("  Name space num: %d\n", pqs->dwNameSpace);

                                                printf("  Num of protocols: %d\n", pqs->dwNumberOfProtocols);

                                                printf("  Version: %d\n", pqs->lpVersion);

                                    }

                        } while ((dwResult != WSA_E_NO_MORE) && (dwResult != WSAENOMORE));

 

                        // clean-up

                        free(pqs);

                        if(WSALookupServiceEnd(hLookup) == 0)

                                    printf("hLookup handle was released!\n");

                        else

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

            }

            else

            {

                        printf("Error on WSALookupServiceBegin: %d\n", WSAGetLastError());

                        exit(3);

            }

 

            if(WSACleanup() == 0)

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

            else

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

            return 0;

}

 

Build and run the project.

 

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

Winsock 2 - Registration and Name Resolution (RNR): The WSALookupServiceBegin and WSALookupServiceNext (Bug), sample output

 

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.

 

 

 


< Service Registration Program Example | RnR Main | Querying DNS >