Writing a UPnP Client


This chapter explains how to write an application which fetches the external IP address from an UPnP-compliant modem. To do this a Control Point is created, which searches for services of the type urn:schemas-upnp-org:service:WANIPConnection:1 (part of the Internet Gateway Device specification). As services are discovered Service Proxy objects are created by GUPnP to allow interaction with the service, on which we can invoke the action GetExternalIPAddress to fetch the external IP address.

Finding Services

First, we initialize GUPnP and create a control point targeting the service type. Then we connect a signal handler so that we are notified when services we are interested in are found.

#include <libgupnp/gupnp-control-point.h>

static GMainLoop *main_loop;

main (int argc, char **argv)
  GUPnPContext *context;
  GUPnPControlPoint *cp;
  /* Required initialisation */
  g_thread_init (NULL);
  g_type_init ();

  /* Create a new GUPnP Context.  By here we are using the default GLib main
     context, and connecting to the current machine's default IP on an
     automatically generated port. */
  context = gupnp_context_new (NULL, NULL, 0, NULL);

  /* Create a Control Point targeting WAN IP Connection services */
  cp = gupnp_control_point_new
    (context, "urn:schemas-upnp-org:service:WANIPConnection:1");

  /* The service-proxy-available signal is emitted when any services which match
     our target are found, so connect to it */
  g_signal_connect (cp,
		    G_CALLBACK (service_proxy_available_cb),

  /* Tell the Control Point to start searching */
  gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
  /* Enter the main loop. This will start the search and result in callbacks to
     service_proxy_available_cb. */
  main_loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (main_loop);

  /* Clean up */
  g_main_loop_unref (main_loop);
  g_object_unref (cp);
  g_object_unref (context);
  return 0;

static void
service_proxy_available_cb (GUPnPControlPoint *cp,
                            GUPnPServiceProxy *proxy)
  /* … */

Invoking Actions

Now we have an application which searches for the service we specified and calls service_proxy_available_cb for each one it found. To get the external IP address we need to invoke the GetExternalIPAddress action. This action takes no in arguments, and has a single out argument called "NewExternalIPAddress". GUPnP has a set of methods to invoke actions (which will be very familiar to anyone who has used dbus-glib) where you pass a NULL-terminated varargs list of (name, GType, value) tuples for the in arguments, then a NULL-terminated varargs list of (name, GType, return location) tuples for the out arguments.

static void
service_proxy_available_cb (GUPnPControlPoint *cp,
                            GUPnPServiceProxy *proxy)
  GError *error = NULL;
  char *ip = NULL;
  gupnp_service_proxy_send_action (proxy,
				   /* Action name and error location */
				   "GetExternalIPAddress", &error,
				   /* IN args */
				   /* OUT args */
				   G_TYPE_STRING, &ip,
  if (error == NULL) {
    g_print ("External IP address is %s\n", ip);
    g_free (ip);
  } else {
    g_printerr ("Error: %s\n", error->message);
    g_error_free (error);
  g_main_loop_quit (main_loop);

Note that gupnp_service_proxy_send_action() blocks until the service has replied. If you need to make non-blocking calls then use gupnp_service_proxy_begin_action(), which takes a callback that will be called from the mainloop when the reply is received.

Generating Wrappers

After making several method calls using gupnp_service_proxy_send_action() can become tedious, because of the requirement to specify the types. An alternative is to use gupnp-binding-tool(1), which generates wrappers which hide the boilerplate code from you. Using the generated wrapper would replace gupnp_service_proxy_send_action() with this code:

GetExternalIPAddress (proxy, &ip, &error);