Universal Plug and Play (UPnP) Client Support
By Tom Fout Program Manager – Windows Networking and Communications
On This Page
UPnP Client Support Overview
UPnP Client Support Architecture
UPnP Client Support Initialization and Operation
Troubleshooting UPnP Client Support
UPnP Client Support Overview
Over time, installing, configuring and troubleshooting networks have arguably become easier. This is a necessary outcome required to support networking in the absence of a dedicated information technology group, as is more often the case as smaller networks are being deployed in small businesses and home environments.
Also over the last several years, there has been an increase in the number of devices supporting digital information exchange such as the cell phone, PDA and Tablet devices, and even the television. Devices that are not traditionally computing devices are adding computer technology for various reasons, including to process the digital data formats becoming more popular. Non-traditional devices that now include computing technology vary widely, from the thermostat to the stereo system. A decrease in the cost of implementing computing and networking technology has perpetuated itself and allowed the addition of intelligence, and in some cases reliable networking, into many devices in use today.
The next step in this evolution (which has already begun) is to transparently connect these devices together. This enables new capabilities such as remote control and monitoring, sharing of digital data, and converging data from multiple devices and locations. Doing this requires a common set of standards and protocols for communicating. This is part of what UPnP offers.
While Windows might not be required, a system running Windows with UPnP support can serve several purposes in this new network of devices, including that of central controller and gateway.
The protocols and requirements for UPnP are discussed in detail in documents and specifications found on the UPnP Forum Website. In this article we will discuss the implementation of UPnP on the Windows platform.
UPnP is built on existing protocols and technologies. For example, UPnP uses TCP/IP, UDP/IP, and HTTP protocols as a base. In addition to these base protocols, several other protocols build on top of these to implement the various "steps" or phases of UPnP networking.
A UPnP network defines several component types and their responsibilities as shown in figure 1. These components include control points, devices and services. Control points are considered the clients in a UPnP network and control devices and services. On the Windows platform, these control points are usually applications written to control specific types of devices or services.
Figure 1: Components of a UPnP network and their relation.
Devices are logical containers for a service or set of services, and possibly for other devices. Devices and services are identified partially by their type, which defines their minimum capabilities.
Windows can participate in the UPnP network and make UPnP enabled devices and services that are connected to the network available to the user of the Windows system. Windows can also serve as a platform for building and running control point applications to control UPnP connected devices and services. The Internet Connection Sharing (ICS) service uses UPnP to announce the availability of the service to other computers on the network, which are running UPnP clients.
Windows might be a host for multiple control point applications concurrently. Many different individuals and companies might write these applications. The Windows support architecture for these applications should minimize a developer's need to write code that would be common to all UPnP applications. Including much of the common code in Windows will reduce the effort required to implement as well as improve the stability of the system.
The Steps in UPnP Networking
To implement architecture for UPnP support in Windows, support for the steps in UPnP networking must be provided.
Addressing – Addressing, sometimes called step '0' in UPnP networking, is a common requirement for any device participating in a network; obtaining a network layer address for the device. The TCP/IP (and UDP) and DHCP components of the Windows networking stack provide the addressing support. Addressing can be accomplished through DHCP assigned addresses, or lacking DHCP support in the network, through automatically assigned IP addresses or AutoIP.
Discovery – UPnP discovery enables control points to locate interesting devices on a network. Devices periodically announce their presence and respond when requested with announcements for their UPnP defined device and service types. Control points may solicit device and service announcements. These capabilities are defined in UPnP as using unicast and multicast variants of the HTTP protocol. Discovery also leverages methods and headers defined in two other protocols, Simple Service Discovery Protocol (SSDP) and Generic Event Notification Architecture (GENA). Only a portion of GENA is used and it may not be used in a future version of UPnP.
For Windows to support UPnP control points from a discovery point of view, Windows must provide interfaces for a control point to instigate searches for devices or services based on device and service types. Devices can be added to the network at any time (announce themselves) and refresh announcements periodically. They can also leave the network, with or without notification. The Windows discovery interfaces allow notification of changes in a device's presence and for arrival notification of new devices.
In short, to support discovery, Windows must provide interfaces for multiple dynamic control points and devices, manage these instances, and implement the GENA and SSDP protocols.
Description – Through discovery, enough information is provided about the devices on the network to determine whether the sought after device is available and choose which device to use amongst several similar devices. Once this decision is made, more information is necessary to access and control the devices and their services. A detailed description of the device is needed. In UPnP, this device description is an Extensible Markup Language (XML) document whose format is defined by the UPnP Forum. The description contains a wealth of information about a device and its service(s) (service descriptions are referenced in device descriptions), including information needed to exercise the device as described in the subsequent steps in UPnP networking.
Windows must then support description for the control point (client) application. A control point application must be able to request, receive and parse XML description documents through the architecture implemented. All of this document management can be built on standard HTTP methods.
Control – Once a description is retrieved for a device and its services, a control point can request actions be performed on those services by calling exposed methods called actions. The service description contains information on what actions can be requested along with the parameters associated with those actions. The result of calling one of these actions is a kind of remote procedure call interface to UPnP services. Action requests and responses are formatted using the Simple Object Access Protocol (SOAP). Similar to action, UPnP Control also defines how control points can query state variables exposed by a service, but this capability is being depreciated in favor of generic service actions to retrieve state variables. SOAP is also used for this purpose.
A Windows implementation of UPnP must support control of network devices for control point applications. The control point application is the action requestor. The implementation will need to translate these method requests from the application into SOAP requests to the device over the network. The return parameters and results must be parsed and presented to the application.
Eventing – UPnP Services may contain variables that reflect the state of the service while it is running. The service can publish updates to these variables when they change, and control points can request notification of these changes by subscribing to the service. Notifications are sent using GENA methods.
An interface must be provided for the control point to request subscription to a particular service on the network and register a callback. The implementation would then convert that request into a protocol request to the service for subscription. State variable changes sent to the implementation as GENA notifications would then need to be passed to the registered callback.
Presentation – This last step in UPnP networking can be used to provide an HTML based user interface for a device. This interface can then be used to control or view device status. A presentation URL is provided as part of the device description for this purpose.
For control point support, the elements of the description document would be provided as described under description above. Standard HTTP interfaces and methods can then be used to retrieve a device presentation page.
See UPnP in Action
UPnP is designed to make networking simple, and this includes easily finding new devices when they are added to your network. If you are running a UPnP enabled operating system, such as Windows ME or Windows XP, new UPnP devices added to the network can be made to automatically appear in My Network Places.
Note: In Windows XP (and Windows ME) this capability is not enabled by default since this will cause unnecessary network traffic in networks that cannot benefit from UPnP. Additionally, enhancements might be made to how and where discovered devices are displayed to make locating them simpler.
In Windows XP the current UI component that enables display of UPnP devices in My Network Places can be installed as an optional networking component (all of UPnP is an optional component in Windows ME). To install the UI component:
Go to Start->Control Panel->Network Connections
In Network Connections, select Advanced->Optional Networking Components
Select Networking Services and click Details
Check the box for Universal Plug and Play
Click Ok, and then Next in the Windows Components dialog to finish installing the UPnP UI.
You can now see if any UPnP enabled devices exist on your network by opening My Network Places on the desktop. If there are UPnP devices on your local network, they will appear here with a generic icon based on the device type. You can invoke the device from here through its presentation page.
In order to see this behavior you will need to have a UPnP device on the network. There are a couple of options here. The first option is to use the sample device provided in The UPnP Device Development Kit. This sample device can run on Windows 2000 with IIS or on Windows XP if you stop the SSDP Service. Stopping the service is required because the sample device and SSDP use the same ports, creating a conflict. The sample device requires a second machine running one of these platforms. The UPnP DDK can be downloaded from:
Another option is to purchase a UPnP enabled device. These devices will become available in the near future. Internet gateway devices like Windows XP's Internet Connection Sharing (ICS) may support NAT Traversal and Firewall configuration using UPnP, but these devices are not designed to appear in My Network Places.
UPnP Client Support Architecture
Much of what we need to implement UPnP on Windows is available through the standard protocol stacks and components like TCP/IP and HTTP. On top of these components, support for the protocols used by UPnP, such as SSDP and GENA, must be added. Interfaces into these protocols must be defined for use by control point applications. The architecture must be designed so that it supports and manages multiple control point applications running at once.
The Windows UPnP implementation can be described by two complimentary architectures. First there is a component architecture describing the software components making up the UPnP implementation and how these components interface with the rest of the operating system. There is also COM Architecture for these components, which will help in understanding the interaction of these components and applications built on them. First we will discuss the component architecture and then the COM architecture.
Figure 2 is a diagram of the components that implement the client side (control point) support for UPnP in Windows XP. Included are the other networking components these components interact with. The UPnP specific components and their responsibilities are described below.
Figure 2: Universal Plug and Play Client components in Windows XP. UPnP specific components colored white.
The components shown in figure 2 serve the following purposes.
upnp.dll – This DLL exposes the Windows UPnP API and COM object interfaces for control point applications as described in the UPnP Development Kit and platform SDK. Requests requiring discovery invoke the SSDP protocol and so are passed along to ssdpapi.dll. Control requests and responses, are SOAP messages and take a different path, through the XML Document Object Model (XMLDOM) interfaces provided by msxml.dll and the WinInet interfaces. The XML DOM interfaces are also used elsewhere as required to manage XML documents. XML DOM is implemented such that it uses the WinInet API when network access is required. Upnp.dll also uses a few utility interfaces provided by urlmon.dll such as CoInternetCombineUrl. Upnp.dll is loaded into every UPnP application.
ssdpapi.dll – This DLL exposes SSDP APIs and is used by upnp.dll to process discovery requests for the client. Applications use the Device Finder COM object to cause search messages to be sent on the network. Device finder calls into upnp.dll result in calls into ssdpapi.dll and eventually to Windows Sockets to send the search message(s) on the network. Ssdpapi.dll also implements a search timer and is loaded into every UPnP application. Ssdpapi.dll interfaces with ssdpsrv.dll for the device cache, notification of device announcements, event notification, and so on.
ssdpsrv.dll – This DLL (called ssdpsrv.exe in Windows ME) implements the SSDP Discovery Service as a Windows service. The discovery cache is maintained here and accessed (both for query and update) through RPC. Ssdpsrv manages receipt of device presence announcements; updating its cache and passing these notifications along to clients with outstanding search requests. This component also takes care of event subscriptions for clients that open services. The SSDP Discovery service accepts registration of event callbacks from clients, turns these into subscription requests and monitors for the event notifications; passing them along to the registered callbacks. Ssdpsrv.dll interfaces with the network through both Windows Sockets and the WinInet interface.
upnpui.dll (not shown) – This DLL is responsible for the shell interface for UPnP. Upnpui.dll is not installed by default and must be installed as an optional networking component. Upnpui.dll is a shell extension that is loaded by explorer on logon (if it is installed). Since it is also a UPnP application, it loads upnp.dll and ssdpapi.dll. It is responsible for the display of UPnP devices in My Network Places. To accomplish this, upnpui.dll will register with and be called by Windows Explorer. This DLL also manages the taskbar notification area.
UPnP COM Architecture
The COM object architecture for the support of control point applications is shown in Figure 3.
Figure 3: COM Interfaces to support UPnP Clients
Not shown in figure 3 are the callback interfaces a developer must implement if asynchronous searches or description document loads are used, or if event notifications are to be received from a service. The interfaces shown in the diagram are those implemented in Windows.
To leverage UPnP, a control point application must first create an instance of the Device Finder. The Device Finder object can then be used to perform synchronous or asynchronous searches for devices. Depending upon the search method used, the result will be communicated as a device collection or a single device object. A device collection can be parsed for individual device objects. Device objects can also be retrieved through methods exposed by the Description Document interface.
Once a device object is obtained, the control point can use this object's methods to obtain properties of a particular device. This includes the ability to retrieve a service collection, and from there retrieve individual service objects. Once a service object is obtained, its methods can be used to request event notification and invoke control actions.
Methods called on these objects may result in requests to the various other components described in the component architecture earlier in this chapter. Note from the diagram that the COM architecture is implemented in upnp.dll.
UPnP Client Support Initialization and Operation
This section covers the operation of the components discussed above. We will discuss how and when these components are involved in various operations performed by UPnP control point applications or hosted devices. We will first discuss how and when each component is initialized and then discuss the involvement of these components in several UPnP operations.
UPnP Client Support Initialization
This section covers the initialization of the UPnP components.
SSDP Discovery Service (SSDPSRV) Initialization
The SSDP Discovery Service runs as a demand start service in Windows XP. This means that the service will be automatically started whenever the SSDPAPI initialization function is called. The responsibilities of this component include:
Maintains a cache of devices available on the network by listening for announcements and through updates based on client searches.
Manages event subscriptions for clients by sending subscribe and re-subscribe requests periodically and dispatching incoming event notifications.
The other UPnP components can then leverage SSDPSRV for these services. The SSDP client API, for example, checks the device cache (using RPC) before sending a search request and will update the cache with the results of each search.
The default installation of Windows XP will install UPnP and set the SSDP Discovery Service (SSDPSRV) to start on-demand. The first thing SSDPSRV will do when started is register a service control handler with the service control manager – a normal thing for services to do – to handle service control requests such as requests to stop or start the service.
The SSDP Discovery service then proceeds to initialize, taking the following steps.
The service creates a timer queue (using CreateTimerQueue()) for use by the service. The service will create timers on this queue for various purposes including:
Sending device announcements for hosted devices
Re-sending device removal notifications (SSDP byebye messages)
Expiring cache contents based on SSDP Cache Control
Event Subscription renewal for clients
Sending client search responses
The service then initializes a list of network interfaces available on the system. This list is consulted as a list of available interfaces to be used by Internet Connection Firewall (ICF) and Internet Connection Sharing (ICS) when they utilize UPnP to provide their services such as port mapping and NAT traversal. For security reasons ICS does not use UPnP on the external (Internet) interface.
An event is created that will be signaled when there is a change in the interfaces. A wait thread from the thread pool is directed to wait on this event with a callback that will rebuild the IP addresses in the interface list. The event is used in a call to the Internet Protocol Helper (IPHelper) API NotifyAddrChange, and will be signaled when an interface to IP address mapping changes on the system, triggering the callback to update the IP address list. Another IPHelper API, GetAdaptersInfo, is used to populate the list, both initially and upon change.
The service then initializes two linked lists, one for valid distinct networks on this computer and one for open connections to peer systems. These lists will be populated and used later.
The service will then initialize its Windows Sockets interface. To do this, a call is made to WSAStartup, requesting version 2.0; requiring version 1.1. If the version negotiation succeeds, the service will initialize a socket address structure containing the standard destination address for SSDP discovery requests – 188.8.131.52:1900. This will be used as the remote address when discovery messages need to be sent by the service. A socket is opened for receipt of event notifications, bound to the wildcard local address and event port (5000). This socket will be listened on and used as a template to accept TCP connections for eventing purposes.
The SSDP Discovery service exposes interfaces for clients of the service through RPC, and therefore has to initialize its RPC server side. This portion of the service initialization is done next.
A manual reset event is created for use in synchronizing shutdown of the service. The main thread will wait on this event, which will be signaled when the service is shutdown.
The RPC server side then registers with RPC, specifying local procedure calls as its protocol sequence, by calling RpcServerUseProtSeqEp. This means local applications and services can access the service, but it will be unavailable to remote systems. The RPC server then registers its interface with RpcServerRegisterIf. The service makes methods available through RPC for device publication, event and device notification, device cache management, and specific support for ICS as a hosted device.
The service then populates the linked list of networks created earlier. This is done using a Windows Sockets Ioctl call, SIO_ADDRESS_LIST_QUERY, to retrieve the valid list of IP addresses on the machine. The list is parsed to remove invalid addresses such as NULL, 0 or non-IP addresses. The addresses remaining are placed in protocol specific socket address structures and added to an SSDP Network structure that also contains the network state and an open socket for that network. The socket that is stored in this structure is opened next, and set to join the SSDP multicast group for sending and receiving. If no valid networks are on the list, at minimum the loopback address is added. These "networks" are then stored on the valid network linked list for this machine. This list is used later on to determine which networks to listen or send on.
At this point the RPC server side will listen for incoming RPC calls by calling RpcServerListen. The server is now ready for a client to connect.
The service then creates an object to handle the processing of data received from the network. This object includes two events, one to signal new work and one to signal service shutdown, and a thread. The thread is created and set to run a routine to process incoming data. The thread waits on both events, and if the new work event is signaled, processes the received data. If the shutdown event is signaled, the thread will complete.
The service will create a hidden window from which to receive and dispatch window messages. This is primarily used to process incoming Windows Sockets messages. This window handle is then used in a call to WSAAsyncSelect specifying FD_READ as the event of interest for each network in the list of networks populated earlier. The message specified to be posted to the service's window indicates a SSDP message was received. The effect is the service is set up to listen on all networks for incoming SSDP messages.
Next, The socket created for event notifications is initialized further. A listen is posted on this socket followed by a WSAAsyncSelect with a message indicating that TCP data (or a connection) has been received. The registered events of interest for select on this socket are FD_ACCEPT, FD_CONNECT, FD_READ, and FD_CLOSE.
The service needs to be aware of any changes to the available interfaces in the interface list or the associated IP addresses. If this occurs, among other things the network list will need to be recalculated, the sockets recreated, and the select calls reposted. To accomplish this the service will create an additional event, have a thread pool thread wait on this event with a callback routine, and call NotifyAddrChange (IPHelper API) with the event. When and if the event is signaled, the callback will rebuild the network list, recreate the sockets and repost the listens and selects.
Finally, the service control manager's status for the service is updated to 'running' status through a call to SetServiceStatus and the main thread is sent to execute the process's message loop for the service.
Now that the service is running, several threads have been created or are performing various tasks. The Main thread is running a message loop, two threads are waiting on address change notification, a thread is waiting in the Receive Data Manager for the Work event to be signaled so incoming data can be processed, and an RPC thread is waiting for incoming client API calls.
SSDP Client (UPNP.DLL, SSDPAPI.DLL) Initialization
When an application uses the UPnP client APIs, UPNP.DLL and SSDPAPI.DLL will be loaded into that applications address space. These are the UPnP client (control point) DLLs. As discussed earlier, UPNP.DLL implements the COM interfaces and leverages the SSDP Client API exposed by SSDPAPI.DLL for SSDP network activity. UPNP.DLL also uses APIs and Interfaces exposed by URLMON.DLL and MSXML.DLL as shown in the architecture diagram above. How these are used will be discussed further in the appropriate sections below.
Initialization of UPNP.DLL takes place when the COM objects implemented by UPNP.DLL are instantiated by an application, the first usually being a Device Finder object. Since this is done in the process of a UPnP client operation, the initialization that takes place will be discussed in the appropriate section below (along with the action that causes this initialization).
SSDPAPI.DLL (the SSDP Client) will prepare for calls to its API from any client of the API. One such client is UPNP.DLL. Initialization of the SSDP Client API is started through a call to the exposed interface SsdpStartup (which must be matched by a call to SsdpCleanup). SsdpStartup initializes the global state for the SSDP Client and must be done before any APIs are called. Some of the initialization may only be useful for specific clients. When startup is called, the following steps are taken.
An internal routine to initialize the RPC Client (that uses the RPC Server interfaces in SSDPSRV) is called. In this routine, the RPC protocol sequence is configured to LRPC and the endpoint configured to the SSDPSRV endpoint. Before actually binding to the server, several other steps are taken.
A linked list is created for storage of client notification registrations, along with a mutex to protect access to this list. This list can later be populated and actual notifications matched to a requesting client. Next a timer queue is created using CreateTimerQueue for timers used to timeout SSDP Search requests.
The SSDP Client API will use the network directly through Windows Sockets when sending search requests and receiving responses. To prepare for this, WSAStartup is called and an address structure allocated containing the destination address and port (184.108.40.206/1900) for SSDP requests.
The SSDP Client API creates a couple of synchronization objects for internal thread synchronization. First it creates an Event that will be signaled when a notification loop thread has been created for this client. Other notification threads wait on this event to ensure no calls are made to receive notifications until after the thread that will look for those notifications is started. This will be discussed further when registration for notifications are discussed. The second synchronization object can be acquired for shared or exclusive access and is used to ensure that the callback registered for a notification is not called until the registration completes. To accomplish this, the registration process acquires the object shared and the callback will attempt to get exclusive access – and must wait until the shared access is released.
Finally the SSDP Client calls RpcStringBindingCompose and RpcBindingFromStringBinding to obtain a binding for RPC calls to SSDPSRV.
At this point, the SSDP Client API has:
Created a RPC binding handle to the SSDP Discovery Service
Initialized a list for outstanding notification callbacks
Created a queue for timers to time out search requests
Initialized two thread synchronization objects.
UPnP Client Support Operation
To get a clearer idea of how the various UPnP components interact, we will take a high level view of what happens during various UPnP operations. A UPnP Client implemented on Windows XP (or Windows ME) has all of the COM objects and interfaces necessary find and control UPnP devices. Applications can be scripts embedded in HTML, Visual Basic programs and C++ programs.
In this section we will talk about how UPnP handles client requests to find a device, retrieve the device description, subscribe to service events and control a service. Examples of performing each of these operations using both Visual Basic and C++ are included in the Windows XP SDK, so that is not described here. Rather we will discuss the effects on the UPnP implementation.
Finding a Device
The first thing a client application must do when locating available devices is to create a Device Finder COM object. The Device Finder can then be used to instigate synchronous or asynchronous searches. When the Device Finder is allocated, several steps are taken to initialize for the searches expected to come.
A linked list is created to store the finder and associated information for later location. The list will actually be used when processing a find request. The finder object is implemented in upnp.dll, but needs the services of the SSDP components. The newly allocated finder will call into the SSDPAPI client DLL at SsdpStartup to initialize the DLL as described in the initialization section.
Now that the client has a device finder interface to use, the search can be conducted either synchronously or asynchronously. For an asynchronous search a Device Finder Callback object must be provided and the call will return immediately to the client. This object must implement either the IUPnPDeviceFinderCallback interface or the IDispatch interface. The callback object's methods will be used to indicate any results to the client, finally indicating when the search is complete. After the initial search completes, the callback remains in place and is called again for new announcements of device comings and goings. This continues until the asynchronous search is cancelled.
With the synchronous search request no callback is required from the client, but the UPnP implementation will allocate a callback object of its own and turn this into an asynchronous search. When the final search complete is indicated to UPnP for this synchronous request, the asynchronous search is canceled and the results returned. Because both searches result in an asynchronous search, we discuss how UPnP implements the asynchronous search.
When CreateAsyncFind is called, a private device finder callback object is allocated for the use of UPnP and placed on the device finder list. This object is initialized to contain the search specifics including the resource identifier being searched for, a timer queue for timers pertaining to this search request, and a pointer to the interface to be called with search results (the client provided callback object).
The private callback object then registers its private callback helpers in the global interface table. The search state is set to 'initialized' and a cookie to identify this search is returned to the client.
To instigate the search the client must use the StartAsyncSearch method. The cookie returned from CreateAsyncFind is passed as a parameter to this call and is used to locate the appropriate callback object on the device finder list. A check is then made to insure we are initialized and that the search has not already started.
At this point the search state is set to 'started' and a call is made into SSDPAPI to start the search. This call includes parameters identifying the type of search, a Boolean value indicating a search should be forced for this request (hit the network), a callback routine (global to UPnP.DLL), and a pointer to the private callback object as a context. A handle is returned from this call and stored with the private callback object.
SSDPAPI will allocate an object to represent the search request, a separate object to manage possibly multiple search requests from this client, and a list to store the currently active search requests (on which this request is stored). An event is created to signal the completion of the request. The SSDP search request is composed based upon the information passed. Windows Sockets and IPHelper are then used to create a list of the available interfaces on the system, open a socket on each of these interfaces for sending search requests and then store this socket along with other information about the interface on a list. The loopback interface is included on this list.
The search state is set to 'discovering' and the search is begun. The first thing the SSDP client API does is make a RPC call to check the cache of announcements maintained by the SSDP Discovery service. This cache check is passed to the SSDP Discovery service using RPC. The service maintains a list of devices for which it has received SSDP announcements through listening for SSDP alive or byebye notifications or through updates resulting from clients search requests. The list is adjusted when network interfaces change.
After checking the local cache for any cached entries matching the search request, the devices returned are added to a list of the found devices. The network is then checked by accessing Windows Sockets directly (using sendto on the sockets created during initialization) to send search requests out of all available interfaces. A timer is set for 3 seconds (default) to time out the search and try again (for 3 retries).
A sockets select call is then used to monitor each socket for FD_READ capability. If select returns because of a read capable socket, an ioctl is used to check the amount of data to read, and recvfrom is called on that socket with the appropriate size buffer to retrieve the SSDP response. The response is parsed and compared to the responses we already have on the list of found devices. If this response is unique, it is placed on the list. The response is also forwarded to the SSDP Discovery service in a request to update the service's cache.
Since a response has been received, several other things happen. The device finder receives a call at its private callback (in UPNP.DLL) indicating that a new device has been found. MSXML (XML DOM) and WinInet are used to asynchronously load a description document for this device based on the location specified in the response received. This load is performed asynchronously and when the document has been completely loaded the original client's DeviceAdded callback method is invoked with the new Device Object representing the found device.
As part of the loading process, a description document object is created to manage the device object(s) that might be created to represent the found device. The description document will contain a pointer to the root device object, which will contain pointers to any child device objects or service objects. The description document is parsed to retrieve the XML data and store this information in the appropriate nested device and service objects. This data includes the properties stored in an array for easy access later when a method is called on the device object by the client.
After all of the retries complete and the responses received are compiled onto the response list, the private internal callback is invoked again with an indication that the search is done. The callback has a couple of responsibilities for the search is done case.
The original request was an asynchronous request (remember, even if we called one of the synchronous methods) and the active search phase is over. We still want to be notified of device comings and goings until we cancel the request with a CancelAsyncFind call. To arrange callback when devices come or go on the network based on announcements, the device finder registers to receive notifications. Notifications are covered later in this chapter when we discuss what occurs when a callback is registered to subscribe to service events. Note that any synchronous search requests that are turned into asynchronous requests by the device finder internally do not persist beyond the return of the synchronous call. Rather they are canceled when the search completes internally.
Finally the internal callback calls the SearchComplete method for the asynchronous callback object. Asynchronous searches are no longer in active search mode, but the callbacks are now registered to receive notifications of device comings and goings through SSDP notifications.
Retrieving and Parsing Device Descriptions
After the search completes, the client has access to individual device objects representing devices that match the search criteria. For a search by UDN request, this is a single device object. For a search by type, this is a device collection which must be further parsed to retrieve an individual object. The Item method for the device collection can be used to do this given a specific UDN. To accomplish this, the implementation simply walks through the device object collection, retrieving the UDN from the stored properties (the same way the client application would, with the UniqueDeviceName method) and comparing it to the specified UDN. If we have a match, the device object is returned.
With a device object, the client application has access, through its methods, to all of the properties of the device description. These were loaded into an easily accessible array in the object as discussed previously. If the client application requests child devices, a device collection can be retrieved from the parent device object and it becomes an iterative process.
Retrieving a Service Object
When the device tree was parsed and stored by the UPnP client implementation, the services were also enumerated and added to a list attached to the device. The client application can use the Services method on the device object to retrieve this list of services. This service collection can be parsed to locate a particular service and retrieve a service object.
When UPnP receives the request to retrieve the service object, it will search the list of services for the device to find a matching service ID. If a match is found additional steps are taken. First the URLs for this service (for the description document, control and eventing) are checked for validity. The service description is loaded synchronously using WinInet and the XML DOM interfaces. The next step is to create a service object to represent this service.
In creating the service object, the description for the service is parsed for state variables and a table is created to contain information on these variables. This table includes the variable name, the data type, and default value and an indication whether the variable must be remotely queried (based on whether events are sent for this variable). An action table is also created that contains an array of structures representing the actions supported by the service, including the input and output arguments as well as the return value. These tables and the other information (control URL, Eventing URL, etc.) are copied into the service object returned.
One last step is taken before the service object is returned to the caller. The service is initialized for eventing by the UPnP implementation. A private callback interface is created by UPnP that references this service object. This private interface is stored on the global interface table for the service object. If there are evented variables for the service, the eventing URL is used register for notifications of property changes for the service. This notification allows UPnP to keep its service state table up to date and execute any callbacks registered by clients. Regardless how many callbacks are registered through this service object, only one is registered by the UPnP client with the remote service. The registration proceeds to the SSDP Discovery service just like the registration for device comings and goings previously discussed. We will discuss these registrations next.
Registering a Callback
Once a service object has been retrieved, the client application can use the methods exposed by that object for various things. This includes querying state variables or invoking actions, which will be discussed later, as well as registering a callback to be notified of changes in the state of the service or service state variables.
This callback registration is accomplished through the service object's AddCallback method. A client uses this method to register a service callback to receive notification of service state changes. Prior to this, when the service object was retrieved, the UPnP client DLL registered for notifications from this service. The client's callback will be added to the global interface table for the object along with any other state change callbacks registered. When the callback registered by the UPnP client DLL is executed, the callback interfaces stored in the global interface table are walked and called in turn to receive notification of this change.
Whether the registration for notification is instigated by the device finder (for device notifications) or through the service object (for events), the result is a call into the SSDP Client API, and eventually into the SSDP Discovery service. The request includes the type of notification - either "Alive" (for device notifications) or "Property Change" (for service state changes). Also included are the device type or UDN from an asynchronous device search, the event URL for event notifications, the callback for either the device finder or service, and a context to indicate the original source of the request (the search request or the service object).
The SSDP client API forwards this request to the SSDP Discovery service using RPC. Before this happens, several things occur. A structure is allocated that is populated with all of the information about the registration. This includes place to store a handle that will be returned by the service as a way for it to recognize the registration for later deregistration. A handle to this structure is eventually returned to the caller of the SSDP API (the UPnP Client DLL) as its handle to the registration.
Next a check is made to determine if this is the first time a call to register a notification has been made. If this is the case, a notification message loop thread needs to be created. To start this message loop, first a semaphore is created. A thread is then created that calls into the SSDP Discovery (using RPC) service to retrieve notifications. This thread will wait on the semaphore to be signaled. The semaphore will be signaled once for each new notification that is received, allowing the thread to wake and process the message.
Once this notification loop thread has been created, the notification registration can continue. A call is made into the service for this, and depending upon the notification type (alive or property change), the service will process the request differently. For either type request, the semaphore is passed to the service and stored with the request object created. The semaphore can then be located when a notification is received matching the request and released to allow the notification loop thread to run and process the request.
If the request is for device notifications (alive) the information about the notification (including the callback interface) is stored on a list of active alive callbacks. A pointer to this structure on the list of device notifications is returned as the server's handle to the client API. This is stored along with the other information in a structure representing the registration and a handle to this structure returned to the caller of the SSDP API (The UPnP DLL). The UPnP DLL will use the handle later when calling to deregister the notification if the asynchronous find is canceled or the application shutdown.
The path a registration for property change notification takes is slightly different. In this case we aren't just waiting for new things to appear and announce themselves, but we actually have to subscribe to the service on the network.
To do this, the SSDP Discovery service uses the WinInet API to create a connection to the service's event URL and send the subscription request. The return form the remote service will contain a subscription ID and timeout, which are stored in a structure with the rest of the information about this registration. A timer is set on the service's timer queue to timeout this notification request (based on 65% of the timeout time for the subscription). When this timer fires, a re-subscription request will be sent to the service using the returned subscription ID. The response to the re-subscription request will contain the same subscription ID and a timeout time which may or may not be the same as the time received in the subscription response. The timer is restarted to be 65% of this returned timeout time.
As with the device notification registration, the event notification registration is stored on a list in the service and a handle returned to the client for removing the subscription later. The socket created for event notifications during initialization is used to receive TCP connections for devices submitting notifications.
When Event Notifications Arrive
During initialization the SSDP Discovery Service posted a WSAAsyncSelect call on the socket it created for receiving notifications. A handle to the service's hidden window and a message indicating receipt of TCP data were specified in this call. When such a message arrives, the service's Window process will determine the reason for the message – what caused the select to fire, a connection ready to be accepted, closed, or receive data? From this the next step is determined.
If a close message is received, closesocket is called and the connection is removed from a list of open connection the service maintains. If a new connection request has arrived, accept is called and a new connection allocated to manage the new connection. The event server portion of the SSDP Discovery service maintains a linked list of its open connections, storing information such as the peer socket handle and a pre-prepared SSDP message for that connection.
If the message indicates data is available to be read on the socket, a receive buffer is allocated based on the amount of data available (determined via ioctlsocket) and recvfrom called to populate the buffer. All of the available information about the received data, including the socket it was received on; the remote socket address; and the data itself, is copied into a structure and passed as a context in a work item to a worker thread.
When this work item is processed, a check is made to validate the open connection exists on the list of maintained open connections, and the received data is processed. The message is parsed to verify that it is indeed in the expected format and the headers are verified to be complete and valid. The information in the request is copied into a structure used for processing the data further and passed along for further processing (still in the service).
This processing includes checking whether there is a client application interested in this particular event by checking for a match based on the subscription ID and other header values. If there is a client interested in this event the notification request object is retrieved and a pending notification list allocated and added to the object. The semaphore associated with this notification request is then released. If this is actually an incoming discovery notification, and there are clients subscribed to that service, the discovery cache is updated.
Releasing the semaphore causes the notification loop thread to be released (discussed earlier). This loop uses the semaphore as a context to locate the client request and return from the RPC call with the received message.
Preparation is made for the callback into the original client for each event pending, by copying the callback information into a callback structure and incrementing the number of callbacks to be made. Then for each callback, the client registered callback is invoked – this is the callback registered by the UPnP client DLL.
The UPnP client DLL callback will validate the sequence numbers on the event notification and if invalid, re-subscribe to the service. Otherwise, the message is converted to an XML DOM document and parsed to update the state table being kept for the service and call the actual client application registered callback. The client callbacks are located through the service objects global interface table as described during the registration section.
Action (Control) Requests
A client application will use the service object's InvokeAction method to execute a control request against the service. Control requests, like state variable queries, use SOAP. Because of this they are very much alike in how they are implemented and share code.
This method first does a thorough check of the passed in values – are there the correct number of input variables for this request? Are the output parameters for the output variables and return value correct and writable? To perform these checks, and to execute the action request, the action table maintained for the service object must be parsed to retrieve the structure associated with this action request.
A SOAP request is created, built and executed for this request. WinInet is used for the network interaction required to process this request. The request is sent asynchronously using the control URL from the service object. When the request completes, the results are parsed from the response. If the request was successful, the output parameters and return value are returned to the client application.
Troubleshooting UPnP Client Support
The workings of UPnP are mostly invisible to the user – either the network connected devices and services are available to the control point applications or they are not. If network connectivity is available, such as file and printer sharing, the UPnP devices will likely also be there. Where issues outside of network connectivity and normal network troubleshooting come in is during design and testing of a UPnP device or control point application.
So there are two different types of issues:
Device or Control Point development issues
Device and service discovery and usage issues
Device and Control Point development issues are generally solved before the product ships, whether that product is a UPnP device or a control point application. During the development, many of the same issues may arise as are seen by a consumer in a live deployment. Here are some things to try when encountering device discovery issues. These steps are for Windows XP - the process might vary when using a Windows ME computer as the device client.
Verify connectivity first.
As with any networking issue, physical connectivity issues are the most common. Connectivity can be verified several ways. First, physical connectivity to the network should be examined. In a home network environment, the device might be connected to the same or different network media than the client.
If the two components are connected to different media, for example one is connected to Ethernet and the other to Wireless, check to see that the two networks are connected together. This could be accomplished using the Network Bridge in Windows XP, or through some other bridging device. How this is tested is dependent on the network configuration and media involved. One possible way of checking this level of connectivity is whether the client can see other devices on the same medium as the target device.
If the two components are on the same physical media, connectivity does not require a network bridge. Some mediums have a visual indication of connectivity that can be checked. For example, Ethernet on twisted pair cabling has green link integrity light to indicate the cable is connected and dependable. Once again, access to other devices on the same media is an indication of connectivity.
If physical connectivity appears to be okay, network protocol connectivity should be checked. This can also be done several ways, but since UPnP requires TCP/IP, the "ping" utility is a simple mechanism. Ping can be run from the Windows XP client and targeted at the device, using the IP address of the device. Determining the IP address of the device is device dependent (some devices may have a way to display this information).
In networks that use UPnP, the address may also be configured using Dynamic Host Configuration Protocol (DHCP). For example, if ICS is being used to share an internet connection, the DHCP server in ICS will assign addresses in the reserved private IP address range of 192.168.0.x. The addresses in this range could be tried to see if any elicit a response [***This is lame. Is there an easier way?]
Because SSDP Discovery uses a multicast frame with TTL of 4, you should also pay attention to the TTL returned by the ping (tracert can also be used for this purpose) command. If the ping succeeds, and the TTL is 255 (indicating the destination is found on this subnet) you can proceed with the assumption network connectivity exists. If you find that more than one hop exists between the device and client, put them on the same hub and test again.
Verify the SSDP Discovery Service is running on the Windows XP client and that there are no events in the System log related to it failing.
The SSDP Discovery Service is required as described above in order to detect or search for devices. A simple way to find out if the service is running is to type:
net start <enter>
at the command prompt and look for "SSDP Discovery Service." If the service is not running, type:
net start "SSDP Discovery Service"<enter>
This should start the service.
The System event log can be checked in event viewer through Control Panel | Administrative Tools | Event Viewer.
If you know what port is being used by the device service, you can use telnet to connect to that port and verify HTTP functionality.
If you get the following then the device is not providing access to the XML description (or is doing it on a different port):
Connecting To <computername or IP>...Could not open connection to the host, on port XXX.
No connection could be made because the target machine actively refused it.
Note: If you are trying to troubleshoot the connectivity to a UPnP enabled Internet Gateway device like ICS, this mechanism may not work.
If you can't get a connection using telnet, there may be a problem with the device, it may be incorrectly configured, or using a port other than the port you are trying. Stop and check these possibilities by locally checking the device configuration.
Another way to detect that the device is working is to check for an icon in My Network Place (remember that the UI component must be installed). If the icon is present, discovery and description are working. If there is no icon, it could indicate a problem but may be because the device does not provide a presentation page (optional).
At this point more advanced troubleshooting may be required.
This might involve using Network Monitor or the netcap.exe utility to capture network traffic generated by a UPnP request. The resulting capture could then be examined to verify the SSDP frames are constructed properly, and that the appropriate UPnP traffic was exchanged.
The netcap.exe utility is available on the Windows XP product CD in the \support\tools directory. To read the capture created you will need Network Monitor. Network Monitor is available with Windows Server products or with Microsoft Systems Management Server. A temporary version can also be requested from Microsoft Product Support Services who can provide assistance in analyzing network traces.
In this case particular attention should be paid to the SSDP Response frame to check the Description URL (found in the Location field of this frame). Also check the Description Request (the frame is an HTTP GET for the XML description document) and the Description Response (HTTP Response that passes the XML to the client). If the description request is not sent, or is sent improperly there may be a problem with the SSDP Discovery Service on the client machine, or with the format of the SSDP exchange.
All of the above has verified that the discovery and description traffic is getting to the client machine. The next step would be to figure out what's happening on the local machine once the data arrives. Several things could be going on, including:
A corrupt XML document is causing a problem for the client machine
The SSDP Discovery service is malfunctioning during enumeration
The UUID for the device conflicts with another, previously discovered device
Microsoft Product Support Services (PSS) could be contacted to assist in this effort.
The Universal Plug and Play support provided in Windows XP helps make networking a variety of devices together a simple task. As this standard develops and more devices are created supporting the standard protocols used, controlling or monitoring any device from anywhere else in the network will be taken for granted. As the Microsoft Windows platform evolves, support for the devices, services and applications will be expanded and enhanced.
For more information on the UPnP Forum and UPnP specifications and standards, visit the UPnP Forum website at http://www.upnp.org.
Information on XML DOM, URLMON, WinInet, and the IP Helper APIs can be found in the Platform SDK, part of the MSDN Library. For more information on the MSDN Library, see http://msdn.microsoft.com
This is a chapter of the SOHO Networking section, part of the Inside Windows Networking series.