fix 1678
[oweals/gnunet.git] / src / nat / upnp-discover.c
index ba9594462ed3969c5e5ff32df64cecc5839971ce..2e609d79034038d533274161e547b987ee3a52e3 100644 (file)
@@ -22,7 +22,7 @@
  * Code in this file is originally based on the miniupnp library.
  * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
  *
- * Original licence:
+ * Original license:
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -70,7 +70,7 @@
 #define DESCRIPTION_BUFSIZE 2048
 #define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0)
 #define PRINT_SOCKET_ERROR(a) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s'\n"), a, __FILE__, __LINE__, strerror (errno));
-
+#define PRINT_SOCKET_ERROR_STR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s' on `%s'\n"), a, __FILE__, __LINE__, strerror (errno), b);
 
 /**
  * Callback function called when download is finished.
@@ -85,11 +85,6 @@ typedef void (*download_cb) (char *data, void *cls);
  */
 struct download_cls
 {
-  /**
-   * Scheduler used for the download task.
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
-
   /**
    * curl_easy handle.
    */
@@ -187,7 +182,7 @@ callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
 }
 
 static void
-task_download (struct download_cls *cls,
+task_download (void *cls,
                const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 /**
@@ -243,13 +238,12 @@ download_prepare (struct download_cls *cls)
   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
 
-  GNUNET_SCHEDULER_add_select (cls->sched,
-                               GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+  GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                GNUNET_SCHEDULER_NO_TASK,
                                rtime,
                                grs,
                                gws,
-                               (GNUNET_SCHEDULER_Task) & task_download, cls);
+                               & task_download, cls);
   GNUNET_NETWORK_fdset_destroy (gws);
   GNUNET_NETWORK_fdset_destroy (grs);
 }
@@ -261,10 +255,10 @@ download_prepare (struct download_cls *cls)
  * @param tc task context
  */
 static void
-task_download (struct download_cls *cls,
+task_download (void *cls,
                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-
+  struct download_cls *dc = cls;
   int running;
   struct CURLMsg *msg;
   CURLMcode mret;
@@ -274,33 +268,33 @@ task_download (struct download_cls *cls,
 #if DEBUG_UPNP
       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
                        "Shutdown requested while trying to download device description from `%s'\n",
-                       cls->url);
+                       dc->url);
 #endif
-      cls->caller_cb (NULL, cls->caller_cls);
-      download_clean_up (cls);
+      dc->caller_cb (NULL, dc->caller_cls);
+      download_clean_up (dc);
       return;
     }
-  if (GNUNET_TIME_absolute_get_remaining (cls->end_time).rel_value == 0)
+  if (GNUNET_TIME_absolute_get_remaining (dc->end_time).rel_value == 0)
     {
       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
                        _
                        ("Timeout trying to download UPnP device description from '%s'\n"),
-                       cls->url);
-      cls->caller_cb (NULL, cls->caller_cls);
-      download_clean_up (cls);
+                       dc->url);
+      dc->caller_cb (NULL, dc->caller_cls);
+      download_clean_up (dc);
       return;
     }
 
   do
     {
       running = 0;
-      mret = curl_multi_perform (cls->multi, &running);
+      mret = curl_multi_perform (dc->multi, &running);
 
       if (running == 0)
         {
           do
             {
-              msg = curl_multi_info_read (cls->multi, &running);
+              msg = curl_multi_info_read (dc->multi, &running);
               GNUNET_break (msg != NULL);
               if (msg == NULL)
                 break;
@@ -311,23 +305,23 @@ task_download (struct download_cls *cls,
                   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                               _("%s failed for `%s' at %s:%d: `%s'\n"),
                               "curl_multi_perform",
-                              cls->url,
+                              dc->url,
                               __FILE__,
                               __LINE__,
                               curl_easy_strerror (msg->data.result));
-                  cls->caller_cb (NULL, cls->caller_cls);
+                  dc->caller_cb (NULL, dc->caller_cls);
                 }
               else
                 {
                   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
                                    _
                                    ("Download of device description `%s' completed.\n"),
-                                   cls->url);
-                  cls->caller_cb (GNUNET_strdup (cls->download_buffer),
-                                  cls->caller_cls);
+                                   dc->url);
+                  dc->caller_cb (GNUNET_strdup (dc->download_buffer),
+                                  dc->caller_cls);
                 }
 
-              download_clean_up (cls);
+              download_clean_up (dc);
               return;
             }
           while ((running > 0));
@@ -341,25 +335,23 @@ task_download (struct download_cls *cls,
                        _("%s failed at %s:%d: `%s'\n"),
                        "curl_multi_perform", __FILE__, __LINE__,
                        curl_multi_strerror (mret));
-      download_clean_up (cls);
-      cls->caller_cb (NULL, cls->caller_cls);
+      download_clean_up (dc);
+      dc->caller_cb (NULL, dc->caller_cls);
     }
 
-  download_prepare (cls);
+  download_prepare (dc);
 }
 
 
 /**
  * Download description from devices.
  *
- * @param sched the scheduler to use for the download task
  * @param url URL of the file to download
  * @param caller_cb user function to call when done
- * @caller_cls closure to pass to caller_cb
+ * @param caller_cls closure to pass to caller_cb
  */
 void
-download_device_description (struct GNUNET_SCHEDULER_Handle *sched,
-                             char *url, download_cb caller_cb,
+download_device_description (char *url, download_cb caller_cb,
                              void *caller_cls)
 {
   CURL *curl;
@@ -425,7 +417,6 @@ download_device_description (struct GNUNET_SCHEDULER_Handle *sched,
                    url);
 #endif
 
-  cls->sched = sched;
   cls->curl = curl;
   cls->multi = multi;
   cls->url = url;
@@ -587,11 +578,6 @@ struct UPNP_Dev_
  */
 struct UPNP_discover_cls
 {
-  /**
-   * Scheduler to use for networking tasks.
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
-
   /**
    * Remote address used for multicast emission and reception.
    */
@@ -696,18 +682,20 @@ get_absolute_url (const char *ref_url, int is_desc_file, const char *raw_url)
 
 
 /**
- * Construct control URL for device from its description URL and
- * UPNP_IGD_Data_ information. This involves resolving relative paths
+ * Construct control URL and service type for device from its description URL
+ * and UPNP_IGD_Data_ information. This involves resolving relative paths
  * and choosing between Common Interface Config and interface-specific
  * paths.
  *
  * @param desc_url URL to the description file of the device
  * @param data IGD information obtained from the description file
- * @returns a URL to control the IGD device, or the empty string
- *   in case of failure
+ * @param control_url place to store a URL to control the IGD device (will be
+ *   the empty string in case of failure)
+ * @param service_type place to store the service type corresponding to control_url
+ *   (will be the empty string in case of failure)
  */
-static char *
-format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data)
+static void
+format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data, char **control_url, char **service_type)
 {
   const char *ref_url;
   int is_desc_file;
@@ -724,11 +712,22 @@ format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data)
     }
 
   if (data->control_url[0] != '\0')
-    return get_absolute_url (ref_url, is_desc_file, data->control_url);
+    {
+      *control_url = get_absolute_url (ref_url, is_desc_file, data->control_url);
+      *service_type = GNUNET_strdup (data->service_type);
+    }
   else if (data->control_url_CIF[0] != '\0')
-    return get_absolute_url (ref_url, is_desc_file, data->control_url_CIF);
+    {
+      *control_url = get_absolute_url (ref_url, is_desc_file, data->control_url_CIF);
+      *service_type = GNUNET_strdup (data->service_type_CIF);
+    }
   else
-    return GNUNET_strdup ("");
+    {
+      /* If no suitable URL-service type pair was found, set both to empty
+       * to avoid pretending things will work */
+      *control_url = GNUNET_strdup ("");
+      *service_type = GNUNET_strdup ("");
+    }
 }
 
 static void get_valid_igd (struct UPNP_discover_cls *cls);
@@ -778,7 +777,7 @@ get_valid_igd_connected_cb (char *response, size_t received, void *data)
  * Then, schedule UPnP command to check whether device is connected.
  *
  * @param desc UPnP IGD description (in XML)
- * @data closure from UPNP_discover()
+ * @param data closure from UPNP_discover()
  */
 static void
 get_valid_igd_receive (char *desc, void *data)
@@ -803,23 +802,15 @@ get_valid_igd_receive (char *desc, void *data)
   memset (igd_data, 0, sizeof (struct UPNP_IGD_Data_));
   UPNP_IGD_parse_desc_ (desc, strlen (desc), igd_data);
 
-  cls->current_dev->control_url =
-    format_control_urls (cls->current_dev->desc_url, igd_data);
-
-  if (igd_data->service_type != '\0')
-    cls->current_dev->service_type = GNUNET_strdup (igd_data->service_type);
-  else if (igd_data->service_type_CIF != '\0')
-    cls->current_dev->service_type =
-      GNUNET_strdup (igd_data->service_type_CIF);
-  else
-    cls->current_dev->service_type = GNUNET_strdup ("");
+  format_control_urls (cls->current_dev->desc_url, igd_data,
+                       &cls->current_dev->control_url,
+                       &cls->current_dev->service_type);
 
   cls->current_dev->data = igd_data;
 
   /* Check whether device is connected */
   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
-  UPNP_command_ (cls->sched,
-                 cls->current_dev->control_url,
+  UPNP_command_ (cls->current_dev->control_url,
                  cls->current_dev->data->service_type,
                  "GetStatusInfo", NULL, buffer, UPNP_COMMAND_BUFSIZE,
                  get_valid_igd_connected_cb, cls);
@@ -900,7 +891,7 @@ get_valid_igd (struct UPNP_discover_cls *cls)
     }
 
   /* There are still devices to ask, go on */
-  download_device_description (cls->sched, cls->current_dev->desc_url,
+  download_device_description (cls->current_dev->desc_url,
                                get_valid_igd_receive, cls);
 }
 
@@ -908,7 +899,6 @@ static const char *const discover_type_list[] = {
   "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
   "urn:schemas-upnp-org:service:WANIPConnection:1",
   "urn:schemas-upnp-org:service:WANPPPConnection:1",
-  "upnp:rootdevice",
   NULL
 };
 
@@ -920,11 +910,7 @@ discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc);
  * and get their descriptions.
  *
  * @param data closure from UPNP_discover()
- * @buf content of the reply
- * @available number of bytes stored in buf
- * @addr address of the sender
- * @addrlen size of addr
- * @param errCode value of errno
+ * @param tc task context
  */
 static void
 discover_recv (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
@@ -999,16 +985,14 @@ discover_recv (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
       GNUNET_NETWORK_fdset_zero (cls->fdset);
       GNUNET_NETWORK_fdset_set (cls->fdset, cls->sudp);
 
-      task_w = GNUNET_SCHEDULER_add_select (cls->sched,
-                                            GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+      task_w = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                             GNUNET_SCHEDULER_NO_TASK,
                                             GNUNET_TIME_relative_multiply
                                             (GNUNET_TIME_UNIT_SECONDS, 15),
                                             NULL, cls->fdset, &discover_send,
                                             cls);
 
-      GNUNET_SCHEDULER_add_select (cls->sched,
-                                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                    task_w,
                                    GNUNET_TIME_relative_multiply
                                    (GNUNET_TIME_UNIT_SECONDS, 5), cls->fdset,
@@ -1079,15 +1063,13 @@ discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * If several devices are found, a device that is connected to the WAN
  * is returned first (if any).
  *
- * @param sched scheduler to use for network tasks
  * @param multicastif network interface to send discovery messages, or NULL
  * @param addr address used to send messages on multicastif, or NULL
  * @param caller_cb user function to call when done
  * @param caller_cls closure to pass to caller_cb
  */
 void
-UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
-                const char *multicastif,
+UPNP_discover_ (const char *multicastif,
                 const struct sockaddr *addr,
                 UPNP_discover_cb_ caller_cb, void *caller_cls)
 {
@@ -1129,7 +1111,6 @@ UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
 
 
   cls = GNUNET_malloc (sizeof (struct UPNP_discover_cls));
-  cls->sched = sched;
   cls->sudp = sudp;
   cls->type_index = 0;
   cls->dev_list = NULL;
@@ -1216,9 +1197,14 @@ UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
         {
           if (multicastif)
             {
+#ifndef MINGW
               if_index = if_nametoindex (multicastif);
+#else
+              // FIXME
+              if_index = 0;
+#endif
               if (!if_index)
-                PRINT_SOCKET_ERROR ("if_nametoindex");
+                PRINT_SOCKET_ERROR_STR ("if_nametoindex", multicastif);
 
               if (GNUNET_NETWORK_socket_setsockopt
                   (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
@@ -1270,15 +1256,13 @@ UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
   GNUNET_NETWORK_fdset_zero (cls->fdset);
   GNUNET_NETWORK_fdset_set (cls->fdset, sudp);
 
-  task_w = GNUNET_SCHEDULER_add_select (sched,
-                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+  task_w = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                         GNUNET_SCHEDULER_NO_TASK,
                                         GNUNET_TIME_relative_multiply
                                         (GNUNET_TIME_UNIT_SECONDS, 15), NULL,
                                         cls->fdset, &discover_send, cls);
 
-  GNUNET_SCHEDULER_add_select (sched,
-                               GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+  GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                task_w,
                                GNUNET_TIME_relative_multiply
                                (GNUNET_TIME_UNIT_SECONDS, 15), cls->fdset,