Name Space Service Providers

In Chapter 10, we showed you how an application can register and resolve services within a name space, which is an especially powerful feature for services that might be dynamically created on the network. Unfortunately, the existing name spaces available are limited in their usefulness. The Winsock 2 specification, however, provides a method for creating your own name spaces in which you can handle name registration and resolution in whatever manner you prefer.

This is accomplished by creating a DLL that implements the nine name space functions. These functions all begin with the NSP prefix and are companions to the RNR functions from Chapter 10. For example, the name space function equivalent to WSASetService is NSPSetService. After the DLL is created, it is then installed into the system catalog with a GUID that identifies the name space. Once this is done, applications can register and query services in your name space.

In this section, we'll first present how to install a name space provider, and then we'll describe the functions a name space provider must implement. Finally, we'll present a sample name space provider as well as a sample application that registers and resolves services.

Installing a Name Space Provider

A name space provider is simply a DLL that implements the name space provider functions. Before applications can use a name space, you must make the system aware of it via the WSCInstallNameSpace function. Conversely, once a provider is installed, you can either disable the provider or remove the provider altogether from the system catalog using the functions WSAEnableNSProvider and WSAUnInstallNameSpace, respectively. We will describe each of these functions next.

WSCInstallNameSpace

This function is used to install a provider into the system catalog and is declared as

 int WSCInstallNameSpace ( LPWSTR lpszIdentifier, LPWSTR lpszPathName, DWORD dwNameSpace, DWORD dwVersion, LPGUID lpProviderId ); 

The first thing that you will notice is that all string parameters are wide character strings. Actually, all name space providers are implemented using wide character strings. We'll talk more about this later. The lpszIdentifier parameter is the name of the name space provider. This is the name that is enumerated when you call WSAEnumNameSpaceProviders, which we saw in Chapter 10. The lpszPathName parameter is the location of the DLL. The string can include environment variables, such as %SystemRoot%. The dwNameSpace parameter is a numeric identifier for the name space. For example, the header file Nspapi.h defines constants for other well-known name spaces, such as NS_SAP for IPX SAP. The dwVersion parameter sets the version number for the name space. Finally, lpProviderId is a GUID that identifies the name space provider.

Upon success, WSCInstallNameSpace returns 0; upon failure, the function returns SOCKET_ERROR. The most common failures are WSAEINVAL, which indicates that a name space with that GUID already exists; and WSAEACCESS, which indicates that the calling process does not have sufficient privilege. Only users of the Administrators group can install a name space.

WSCEnableNSProvider

This function is used to modify the state of a name space provider. It can be used to enable or disable the provider. The function is declared as

 int WSCEnableNSProvider ( LPGUID lpProviderId, BOOL fEnable ); 

The lpProviderId parameter is the GUID identifier for the name space that you want to modify. The fEnable parameter is a Boolean value indicating you should either enable or disable the provider. A disabled provider is unable to process queries or registrations.

Upon success, WSCEnableNSProvider returns 0; upon failure, the function returns SOCKET_ERROR. If the provider GUID is invalid, WSAEINVAL is returned.

WSCUninstallNameSpace

This function removes a name space provider from the catalog. The function is defined as

 int WSCUnInstallNameSpace ( LPGUID lpProviderId ); 

The lpProviderId parameter is the GUID for the name space to remove. If the GUID is invalid, the function fails with WSAEINVAL.

Implementing a Name Space

A name space must implement all nine name space functions that map to the RNR functions covered in Chapter 10. In addition to implementing these functions, you must also develop a method for persisting the data. That is, you must maintain the data beyond the instance of the DLL. Each process that loads the DLL receives its own data segment, which means that data stored within the DLL cannot be shared between instances. (Actually, it is possible to share information between multiple applications that have loaded the DLL, but this practice is discouraged.) See Programming Applications for Microsoft Windows, 4th ed., by Jeffrey Richter (Microsoft Press, 1999) for more information on DLLs. Remember from Chapter 10 that there are three types of name spaces: dynamic, persistent, and static. Obviously, implementing a static name space might not be a good idea since it disallows programmatic registration of services. Later in this chapter, we'll present some ideas on how to maintain the data that the name space needs to persist.

You must also understand the importance of using wide character strings in all name space provider functions. This not only includes string parameters to functions but also strings within the RNR structures, such as WSAQUERYSET and WSASERVICECLASSINFO. You might be wondering how this is possible since when an application registers or resolves a name it can use either the normal (ASCII) or the wide character (UNICODE) version of the RNR functions and structure. The answer is that either version works because all ASCII calls go through an intermediate layer that converts all strings to wide character strings. This is true upon function call and return. That is, if WSAQUERYSET is returned to the calling application—as with WSALookupServiceNext—any data returned by the name space provider is originally UNICODE and is converted to ASCII before returning from the function call. That said, you can see that if your application uses RNR functions, calling the wide character versions will be faster since no conversions are required.

Of the nine functions that a name space provider must implement, only seven of the functions map to Winsock 2 RNR functions, as shown in Table 14-3. The remaining two functions are for initialization and cleanup. Once the name space is installed into the system, applications can utilize the name space by specifying either the GUID under which the name space was installed or the name space identifier that is also specified during installation. An application then makes calls to the standard Winsock 2 RNR function, as described in Chapter 10. When one of these functions is called, the equivalent name space provider function is invoked. For example, when an application calls WSAInstallServiceClass, which references the GUID for a custom name space, the function NSPInstallServiceClass for that provider is invoked. In the next section, we'll cover each of the name space functions.

Table 14-3. Mapping Winsock 2 registration and name resolution functions to name space provider functions

Winsock FunctionEquivalent Name Space Provider Function
WSAInstallServiceClassNSPInstallServiceClass
WSARemoveServiceClassNSPRemoveServiceClass
WSAGetServiceClassInfoNSPGetServiceClassInfo
WSASetServiceNSPSetService
WSALookupServiceBeginNSPLookupServiceBegin
WSALookupServiceNextNSPLookupServiceNext
WSALookupServiceEndNSPLookupServiceEnd

NSPStartup

The NSPStartup function is called whenever the name space provider DLL is loaded. Your name space implementation must include this function, and it must be exported from the DLL. Any per-DLL data structures required for the provider to operate can be allocated when this function is called. NSPStartup is prototyped as

 int NSPStartup ( LPGUID lpProviderId, LPNSP_ROUTINE lpnspRoutines ); 

The first parameter, lpProviderId, is the GUID for this name space provider. The lpnspRoutines parameter is an NSP_ROUTINE structure that your implementation of this function must fill out. This structure provides function pointers to the other eight name space functions that belong to your provider. The NSP_ROUTINE object is defined as

 typedef struct _NSP_ROUTINE { DWORD cbSize; DWORD dwMajorVersion; DWORD dwMinorVersion; LPNSPCLEANUP NSPCleanup; LPNSPLOOKUPSERVICEBEGIN NSPLookupServiceBegin; LPNSPLOOKUPSERVICENEXT NSPLookupServiceNext; LPNSPLOOKUPSERVICEEND NSPLookupServiceEnd; LPNSPSETSERVICE NSPSetService; LPNSPINSTALLSERVICECLASS NSPInstallServiceClass; LPNSPREMOVESERVICECLASS NSPRemoveServiceClass; LPNSPGETSERVICECLASSINFO NSPGetServiceClassInfo; } NSP_ROUTINE, FAR * LPNSP_ROUTINE; 

The first field of the structure, cbSize, indicates the size of the NSP_ROUTINE structure. The next two fields, dwMajorVersion and dwMinorVersion, are included for versioning your provider. The versioning is arbitrary and serves no other purpose. The provider sets the rest of the entries to their respective function pointers. For example, the provider assigns its NSPSetService function address (no matter what it is named) to the NSPSetService field. The names of your provider functions can be arbitrary, but their parameters and return types must match the provider definition.

The only action required of an NSPStartup implementation is filling in the NSP_ROUTINE structure. Once the provider completes this and any other initialization routines of its own, it returns NO_ERROR if everything is successful. If an error occurs, the NSPStartup implementation returns SOCKET_ERROR and sets the Winsock error code. For example, if a provider attempts and fails to allocate memory, it calls WSASetLastError with WSA_NOT_ENOUGH_MEMORY as the parameter and then returns SOCKET_ERROR.

This might be a good time to discuss error handling in your provider's DLL. All of the functions you must implement for a provider return NO_ERROR upon success and SOCKET_ERROR upon failure. If you determine that the call fails, set the appropriate Winsock error code before returning. If you fail to do this, any application attempting to register or query services using your name space provider will report the failure of an RNR function but WSAGetLastError will return 0. This will cause tremendous trouble for applications that attempt to handle errors gracefully; 0 is certainly not an expected return value upon error.

NSPCleanup

This routine is called when the provider's DLL is unloaded. Within this function, you can free any memory allocated in the NSPStartup routine. This routine is defined as

 int NSPCleanup ( LPGUID lpProviderId ); 

The only parameter is your name space provider's GUID. Other than cleaning up any dynamically allocated memory, you're not required to do anything in this function.

NSPInstallServiceClass

The NSPInstallServiceClass function maps to WSAInstallServiceClass and is responsible for registering a service class. NSPInstallServiceClass is defined as

 int NSPInstallServiceClass ( LPGUID lpProviderId, LPWSASERVICECLASSINFOW lpServiceClassInfo ); 

The first parameter is the provider's GUID. The lpServiceClassInfo parameter is the WSASERVICECLASSINFOW structure that is being registered. Your provider has to maintain a list of service classes and has to ensure that a service class doesn't already exist using the same GUID within the WSASERVICECLASSINFOW structure. In the event that the GUID is already in use, the provider must return the error WSAEALREADY. Otherwise, the provider should maintain this service class so that other RNR operations can refer to it.

The majority of the remaining name space provider functions refer to an installed service class.

NSPRemoveServiceClass

This function is the complement of the NSPInstallServiceClass function and removes the specified service class. This name space function maps to WSARemoveServiceClass. The function is declared as

 int NSPRemoveServiceClass ( LPGUID lpProviderId, LPGUID lpServiceClassId ); 

As in the previous function, the first parameter is the provider's GUID. The second parameter, lpServiceClassId, is the service class GUID that is to be removed. The provider must remove the given service class from its storage. If the service class specified by lpServiceClassId is not found, the provider must generate the error WSATYPE_NOT_FOUND.

NSPGetServiceClassInfo

The NSPGetServiceClassInfo function maps to the WSAGetServiceClassInfo function. It retrieves the WSANAMESPACE_INFOW structure associated with a GUID. The function is defined as

 int NSPGetServiceClassInfo ( LPGUID lpProviderId, LPDWORD lpdwBufSize, LPWSASERVICECLASSINFOW lpServiceClassInfo ); 

Again, the first parameter is the provider's GUID. The lpdwBufSize parameter indicates the number of bytes contained in the third parameter, lpServiceClassInfo. On input, the third parameter is a WSASERVICECLASSINFOW structure that contains the search criteria specifying which service class to return. This structure can contain either a service class name or the GUID for the service class to return. If the provider finds a match, it must return the WSASERVICECLASSINFOW structure in lpServiceClassInfo and should update lpdwBufSize to indicate the number of bytes being returned.

If, given the criteria, no service classes are found, the call should fail and set WSATYPE_NOT_FOUND as the error. Additionally, if a service class does match but the supplied buffer is too small, the provider should update the lpdwBufSize parameter to indicate the correct number of bytes required and the error WSAEFAULT should be set.

NSPSetService

The NSPSetService function maps to WSASetService and either registers or removes services from the name space. The function is defined as

 int NSPSetService ( LPGUID lpProviderId, LPWSASERVICECLASSINFOW lpServiceClassInfo, LPWSAQUERYSETW lpqsRegInfo, WSAESETSERVICEOP essOperation, DWORD dwControlFlags ); 

The first parameter is the provider's GUID. The lpServiceClassInfo parameter is the WSASERVICECLASSINFOW structure to which this service belongs. The lpqsRegInfo parameter is the service to either register or delete depending on the operation specified in the fourth parameter, essOperation. The last parameter, dwControlFlags, might specify the flag SERVICE_MULTIPLE that can modify the specified operation.

The name space provider first verifies that the supplied service class does exist. Then, depending on which operation is specified, appropriate action is taken. For a full description of valid essOperation values as well as the effect of dwControlFlags on them, see the section on service registration in Chapter 10, which discusses WSASetService. Your provider's NSPSetService function handles these flags accordingly.

If your service provider is updating or deleting a service that cannot be found, the error WSASERVICE_NOT_FOUND is set. If the provider is registering a service and the WSAQUERYSETW structure is invalid or incomplete, the provider generates the WSAEINVAL error.

This function is one of the most complicated name space provider functions to implement (next to NSPLookupServiceNext). The provider must maintain a scheme for persisting the services that can be registered and must allow the NSPSetService function to update this data.

NSPLookupServiceBegin

The NSPLookupServiceBegin function is associated with the functions NSPLookupServiceNext and NSPLookupServiceEnd and is used to initiate a query of your name space. This function maps to WSALookupServiceBegin and establishes the criteria for your search. This function is prototyped as

 int NSPLookupServiceBegin ( LPGUID lpProviderId, LPWSAQUERYSETW lpqsRestrictions, LPWSASERVICECLASSINFOW lpServiceClassInfo, DWORD dwControlFlags, LPHANDLE lphLookup ); 

As with previous functions in this section, the first parameter is the provider's GUID. The lpqsRestrictions parameter is the WSAQUERYSETW structure that defines the parameters for the query. The third parameter, lpServiceClassInfo, is the WSASERVICECLASSINFOW structure containing the schema information for the service class in which the query is to take place. The dwControlFlags parameter takes zero or more flags that affect how the query is performed. Again, for information on WSALookupServiceBegin and the different flags that can be used, refer to Chapter 10. Note that not all of the flags make sense for every provider. For example, if your name space does not support the notion of container objects, you don't have to worry about those flags dealing with containers. (A container is simply a way of conceptually organizing the services—what constitutes a container is open to interpretation.) Finally, lphLookup is an output parameter, which is a handle that defines this particular query. The handle is used in the subsequent calls to WSALookupServiceNext and WSALookupServiceEnd.

When implementing NSPLookupServiceBegin, keep in mind that the operation cannot be canceled, and it should complete as quickly as possible. Therefore, if you need to initiate a network query, a response should not be required in order to return successfully.

The provider itself should save the query parameters and associate a unique handle with the query for later reference. In addition to saving the handle and the query, the provider should maintain state information. We'll explore the significance of this in our discussion of the next function, NSPLookupServiceNext.

NSPLookupServiceNext

Once a query has been initiated with WSALookupServiceBegin, an application calls WSALookupServiceNext, which in turn calls the name space provider function NSPLookupServiceNext. This call is what actually searches for results that match the search criteria registered for this query. The function is defined as

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

The first parameter, hLookup, is the query handle returned from WSALookupServiceBegin. The dwControlFlags parameter can be the flag LUP_FLUSHPREVIOUS, which indicates that the provider should discard the last result set and move to the next one. Typically an application requests that the last result set be discarded when the application cannot supply a large enough buffer for the results. The next parameter, lpdwBufferLength, indicates the size of the buffer passed as the last parameter, lpqsResults.

When NSPLookupServiceNext is triggered, the provider should look up the query parameters identified by the handle hLookup. Once the query parameters are retrieved, a search should be initiated for all registered services within the service class specified by the query that match the supplied criteria. As we mentioned in the section on NSPLookupServiceBegin, the state of the query should be saved. In the event of multiple matching entries, a calling process calls WSALookupServiceNext multiple times, and with each call your provider needs to return a data set. When there are no more matches, the provider returns the error WSA_E_NO_MORE. It is also possible to cancel a query in progress if the application makes a call to WSALookupServiceEnd from another thread while a call to WSALookupServiceNext is in progress. In this event, NSPLookupServiceNext should fail with the error WSA_E_CANCELLED.

NSPLookupServiceEnd

After a query has been completed, NSPLookupServiceEnd is called to end the query and release any underlying resources. This function is defined as

 int NSPLookupServiceEnd ( HANDLE hLookup ); 

The single parameter to the function is hLookup, which is the handle to the query that is to be closed. If the given handle cannot be found (for example, if it's an invalid handle), the call must fail with the error WSA_INVALID_HANDLE.

Name Space Provider Example

We have covered the steps for creating your own name space, and we have touched on some of the important name-space creation issues, such as methods for data persistence. However, developing an entire name space provider can be complicated, and the rest of this section will be devoted to our example name space provider. While the example is not the fastest or most optimized code, it illustrates the topics that require the most attention. Additionally, we kept it simple so that it's easy to follow and understand.

The example provider is located on the companion CD-ROM under the Examples\Chapter14\NSP directory in the files Mynsp.h, Mynsp.cpp, and Mynsp.def. These three files make up the name space DLL. In addition to the DLL, you'll find the name space service that is a Winsock server responsible for handling requests from the DLL. This server, which maintains the service registration data, is found in the file Mynspsvc.cpp. Two additional files, Nspsvc.cpp and Printobj.cpp, are used by both the DLL and the service and contain support routines for marshaling and demarshaling data sent on a socket between the service and the DLL. Marshaling and demarshaling data will be explained later in this section. In addition to these two files, you'll find their header files, Nspsvc.h and Printobj.h, which contain the function prototypes for the support routines. Finally, the file Rnrcs.c is a modified sample from Chapter 10 that registers and looks up services in our custom name space.

In the following sections, we will discuss how our name space is implemented. First we'll give an overview of the method we chose to persist the data. This overview will be followed by an examination of how the actual name space DLL is structured as well as how to install the name space. Then we'll cover the implementation of the name space service. Finally, we'll look at how an application performs service registrations and queries to our custom name space.

Data persistence

For our name space, we chose a separate Winsock service to maintain the name space information. In each of the name space functions implemented in the DLL, a connection is made to this service and data is transacted in order to complete the operation. For the sake of simplicity, this service runs locally (the service listens on the loopback address 127.0.0.1). In an actual implementation, our name space service's IP address would be accessible via the Registry or some other means so that when an application invoked the name space, it could connect to the service wherever it was running. For example, in the case of DNS, the IP address of the DNS server is either set statically or obtained during a DHCP request.

Of course, writing a service to maintain the information is not the only option available. You could maintain a file on the network that keeps the necessary information; however, this is probably not the best option, as performance is then bound by disk operations. One performance limitation of our sample name space is that it establishes TCP connections to the service. A production-quality implementation is more likely to use a connectionless datagram protocol such as UDP to improve performance. Of course, this would involve additional programming requirements—such as ensuring that dropped packets are retransmitted—but the overall performance gains would be considerable.

Name space DLL

Before we look at how the name space service is implemented, let's take a look at the name space DLL. Each name space provider requires a unique GUID, and ours is defined in Mynsp.h. In addition to the unique identifier, we need a simple integer identifier for our name space. This identifier can be used in the dwNameSpace field of the WSAQUERYSET structure, as you saw in Chapter 10. The GUID and name space identifier are

 GUID MY_NAMESPACE_GUID = {0x55a2bd9e,0xbb30,0x11d2, {0x91,0x66,0x00,0xa0,0xc9,0xa7,0x86,0xe8} }; #define NS_MYNSP     66 

These values are important because applications that want to use this name space must specify these values in their Winsock calls. Of course, the developer of an application can specify these values directly or retrieve them with a WSAEnumNameSpaceProviders call. (See Chapter 10 for more information.) Also, be aware that if an application performs an operation specifying the NS_ALL name provider, the operation occurs on all installed name providers. You should bear this in mind because several Windows applications, such as Microsoft Internet Explorer, perform queries on all installed name providers. Be very careful, therefore, when testing a name provider. A poorly written name provider can cause systemwide problems. Additionally, the GUID and name space identifier values are important because they are required to install the name provider.

Now let's take a look at the NSP functions implemented in Mynsp.cpp. For the most part, these functions are quite similar except for the startup and cleanup functions, NSPStartup and NSPCleanup. The startup function simply initializes the NSP_ROUTINE structure with our custom name space functions. The cleanup routine does nothing because no cleanup is necessary.

The rest of the functions require interaction with our service to either query or register data. When communication with the service is necessary, follow these steps:

  1. Connect to the service (via the MyNspConnect function).
  2. Write a 1-byte action code. This indicates to the service which action is about to take place (service registration, service deletion, query, and so on).
  3. Marshal parameters and send them to the service. The type of parameters sent depends on the operation. For example, NSPLookupServiceNext sends the query handle to the service so that the service can resume the query, whereas NSPSetService sends an entire WSAQUERYSET structure.
  4. Read the return code. Once the service has the necessary parameters to perform the requested operation, the return code (success or failure) of the operation is returned. The file Mynsp.h defines two constants—MYNSP_SUCCESS and MYNSP_ERROR—for this purpose.
  5. If the requested operation was a query and the return code was success, read and demarshal the results. For example, NSPLookupServiceNext returns a WSAQUERYSET structure in the event that a matching service is found.

As you can see, implementing the DLL is not overly complicated. The NSP functions must take the parameters and process them, which in our case is to pass this information to the name space service. After this, it is up to the service to perform the requested operation. However, we have glossed over one difficult operation that must be performed: sending data over a socket. Normally there aren't any special requirements for sending data, but in the case of sending entire data structures there are. Most of the name space functions take either a WSAQUERYSET or a WSASERVICECLASSINFO structure as a parameter. This object must be sent or received on the socket connection to the service. This presents some difficulty because these structures aren't contiguous blocks of memory. That is, they contain pointers to strings and other structures that can be located anywhere in memory, as illustrated in Figure 14-7. What you need to do is take all these pieces of memory—wherever they are—and copy them into a single buffer one after another. This is known as marshaling data. On the receiving end, this process has to be reversed. That is, the data read needs to be reassembled into the original structure, and the pointers have to be "fixed" so that they point to valid memory locations on the recipient's machine.

For our name space provider, we provide functions to marshal and demarshal both the WSANAMESPACEINFO and WSAQUERYSET structures. These functions are located in Nspsvc.cpp and are used by both the name space DLL and the name space service (since both sides need the ability to marshal and demarshal these structures). All four functions are self-explanatory—we won't cover them in depth here.

click to view at full size.

Figure 14-7. Marshaling data

Installing the name space

Installing a name space provider is the most trivial step in the entire process. The file Nspinstall.c is a simple installation program. The following code installs our provider:

 ret = WSCInstallNameSpace(L"Custom Name Space Provider", L"%SystemRoot%\\System32\\Mynsp.dll", NS_MYNSP, 1, &MY_NAMESPACE_GUID); if (ret == SOCKET_ERROR) { printf("Failed to install name space provider: %d\n", WSAGetLastError()); } 

The only parameters to the call are the name of the provider, the location of the DLL, the integer identifier, the version, and the GUID. After installation, the only requirement is to make sure the name space DLL is actually located where you say it is. The only error that's a real possibility is trying to install a name provider with a GUID that's already in use by another provider.

Removing a name space provider is even easier. The following code snippet from our installation program removes our provider:

 ret = WSCUnInstallNameSpace(&MY_NAMESPACE_GUID); if (ret == SOCKET_ERROR) { printf("Failed to remove provider: %d\n", WSAGetLastError()); } 

Name space service

The name space service is the real guts of the name provider. This service keeps track of all registered service classes and service instances. When the name space DLL is triggered by a user's application, it connects to the name space service to perform the operation. The service is simple. Within the main function, a listening socket is established. Then, within a loop, connections are accepted from instances of the name space DLL. For the sake of simplicity, only a single connection is handled at a time. This also prevents you from having to synchronize access to the data structures that maintain the name space information. Again, a real provider would not do this because it degrades performance, but it does make the example easier to understand. Once a connection is accepted, the service reads a single byte from the name space DLL that identifies the action to follow.

Within the loop, the action is decoded and parameters are passed from the name space DLL to the service. From there, the requested actions are performed. These actions aren't complicated. The code is easy to follow, and by examining the steps for each possible action you can determine how the service works—we don't need to go into detail here. However, we will examine the structures that maintain the information. There are only two data types that name space providers are really concerned about: the WSASERVICECLASSINFO and WSAQUERYSET structures. As you have seen, the majority of the RNR functions reference one or the other of these two structures in their parameters. As a result, we maintain two global arrays—one for each structure type—along with a counter for each.

When the DLL requests to install a service class, the name service provider's main function first calls LookupServiceClass, a support routine defined in Mynspsvc.cpp. This function iterates through the array of WSASERVICECLASSINFO structures, ServiceClasses. If a service class is found with the same GUID, the service returns an error (which the DLL translates as WSAEALREADY). Otherwise, the new service class is added to the end of the array and the dwNumServiceClasses counter is incremented.

During the deletion of a service class (as when installing a service class), the main function calls LookupServiceClass. In this case however, if the service class is found, the code moves the last service class in the array to the location of the deleted class. The code then decrements the counter. One thing that is not specifically covered in the Winsock 2 specification for name space providers is what happens when a service class is to be deleted but there are still services registered that refer to it. How you choose to handle this case is up to you. Our example name space won't allow the removal of a service class if there are services registered that reference it.

The same principle that's used for maintaining WSASERVICECLASSINFO structures is also used for keeping track of WSAQUERYSET structures. There is an array of these structures named Services and a counter named dwNumServices. The addition and deletion of services is handled in the same manner as for service classes.

The last bits of information that the service must maintain are for queries. When an application initiates a query, the query parameters must be maintained for the life of the query and assigned a unique handle. This is necessary because WSALookupServiceNext refers to the query by the handle only. The other piece of information that must be kept is the state of the query. That is, each call to WSALookupServiceNext can return a single information set. The code must remember the last position within the Services array where data was returned so that subsequent calls to WSALookupServiceNext begin where the previous call left off.

Querying the name space

The last part of our name space sample is the file Rnrcs.c. This is a modified version of the name registration and resolution example presented in Chapter 10. We've made only a few changes to make the example as simple as possible. The first change causes the code to enumerate the installed name space providers but to return only the NS_MYNSP provider. Second, when registering a service, Rnrcs.c enumerates only the local IP interfaces to use as the address of our service. Our service provider supports the registration of any SOCKADDR type. Finally, for service registration this example does not create an instance of the service; it just registers the name. Otherwise, this example behaves like the Chapter 10 example.

Running the example

Once all the examples have been compiled, installing and using the provider is simple. The following command installs the provider:

Nspinstall.exe install

Of course, don't forget to copy Mynsp.dll to %SystemRoot%\System32. Once the name space is installed, an instance of the service must be running in order to query and register services. This is done by the following command:

Mynspsvc.exe

Now you can query and register services using Rnrcs.exe. Table 14-4 shows some commands that you should execute and the order you should follow. This sequence of commands registers two services and then performs a wildcard query and a specific query. Then the command sequence queries for each of the two services and deletes them. Finally, we perform a wildcard query to illustrate that the services have been deleted.

Table 14-4. The sequence of commands that uses the example name space to register and query services

CommandMeaning
Rnrcs.exe -s:ASERVICERegister the service ASERVICE.
Rnrcs.exe -s:BSERVICERegister the service BSERVICE.
Rnrcs.exe -c:*Query for all registered services.
Rnrcs.exe -c:BSERVICEQuery only for services named BSERVICE.
Rnrcs.exe -c:ASERVICE -dQuery only for services named ASERVICE, and delete them if found.
Rnrcs.exe -c:BSERVICE -dQuery only for services named BSERVICE, and delete them if found.
Rnrcs.exe -c:*Query for all registered services.


Network Programming for Microsoft Windows
Linux Server Hacks, Volume Two: Tips & Tools for Connecting, Monitoring, and Troubleshooting
ISBN: 735615799
EAN: 2147483647
Year: 1998
Pages: 159

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