Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / wps / wps_upnp_ssdp.c
index c1b2193..968fc03 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright (c) 2000-2003 Intel Corporation
  * Copyright (c) 2006-2007 Sony Corporation
  * Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
  *
  * See wps_upnp.c for more details on licensing and code history.
  */
@@ -13,6 +13,9 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <net/route.h>
+#ifdef __linux__
+#include <net/if.h>
+#endif /* __linux__ */
 
 #include "common.h"
 #include "uuid.h"
@@ -97,16 +100,6 @@ static int line_length(const char *l)
 }
 
 
-/* No. of chars excluding trailing whitespace */
-static int line_length_stripped(const char *l)
-{
-       const char *lp = l + line_length(l);
-       while (lp > l && !isgraph(lp[-1]))
-               lp--;
-       return lp - l;
-}
-
-
 static int str_starts(const char *str, const char *start)
 {
        return os_strncmp(str, start, os_strlen(start)) == 0;
@@ -136,12 +129,17 @@ next_advertisement(struct upnp_wps_device_sm *sm,
        struct wpabuf *msg;
        char *NTString = "";
        char uuid_string[80];
+       struct upnp_wps_device_interface *iface;
 
        *islast = 0;
-       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
+       if (!iface)
+               return NULL;
+       uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
        msg = wpabuf_alloc(800); /* more than big enough */
        if (msg == NULL)
-               goto fail;
+               return NULL;
        switch (a->type) {
        case ADVERTISE_UP:
        case ADVERTISE_DOWN:
@@ -215,10 +213,6 @@ next_advertisement(struct upnp_wps_device_sm *sm,
                *islast = 1;
 
        return msg;
-
-fail:
-       wpabuf_free(msg);
-       return NULL;
 }
 
 
@@ -319,7 +313,8 @@ static void advertisement_state_machine_handler(void *eloop_data,
                         * (see notes above)
                         */
                        next_timeout_msec = 0;
-                       os_get_random((void *) &r, sizeof(r));
+                       if (os_get_random((void *) &r, sizeof(r)) < 0)
+                               r = 32768;
                        next_timeout_sec = UPNP_CACHE_SEC / 4 +
                                (((UPNP_CACHE_SEC / 4) * r) >> 16);
                        sm->advertise_count++;
@@ -527,7 +522,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
 #ifndef CONFIG_NO_STDOUT_DEBUG
        const char *start = data;
 #endif /* CONFIG_NO_STDOUT_DEBUG */
-       const char *end;
        int got_host = 0;
        int got_st = 0, st_match = 0;
        int got_man = 0;
@@ -542,7 +536,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
 
        /* Parse remaining lines */
        for (; *data != '\0'; data += line_length(data)) {
-               end = data + line_length_stripped(data);
                if (token_eq(data, "host")) {
                        /* The host line indicates who the packet
                         * is addressed to... but do we really care?
@@ -588,8 +581,15 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
                        }
                        if (str_starts(data, "uuid:")) {
                                char uuid_string[80];
+                               struct upnp_wps_device_interface *iface;
+                               iface = dl_list_first(
+                                       &sm->interfaces,
+                                       struct upnp_wps_device_interface,
+                                       list);
+                               if (!iface)
+                                       continue;
                                data += os_strlen("uuid:");
-                               uuid_bin2str(sm->wps->uuid, uuid_string,
+                               uuid_bin2str(iface->wps->uuid, uuid_string,
                                             sizeof(uuid_string));
                                if (str_starts(data, uuid_string))
                                        st_match = 1;
@@ -740,11 +740,9 @@ int ssdp_listener_open(void)
        int sd;
 
        sd = socket(AF_INET, SOCK_DGRAM, 0);
-       if (sd < 0)
-               goto fail;
-       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
-               goto fail;
-       if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+       if (sd < 0 ||
+           fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
+           setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
                goto fail;
        os_memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
@@ -756,9 +754,8 @@ int ssdp_listener_open(void)
        mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
        mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
        if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                      (char *) &mcast_addr, sizeof(mcast_addr)))
-               goto fail;
-       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+                      (char *) &mcast_addr, sizeof(mcast_addr)) ||
+           setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
                       &ttl, sizeof(ttl)))
                goto fail;
 
@@ -858,7 +855,7 @@ fail:
 }
 
 
-int ssdp_open_multicast_sock(u32 ip_addr)
+int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname)
 {
        int sd;
         /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
@@ -869,21 +866,41 @@ int ssdp_open_multicast_sock(u32 ip_addr)
        if (sd < 0)
                return -1;
 
+       if (forced_ifname) {
+#ifdef __linux__
+               struct ifreq req;
+               os_memset(&req, 0, sizeof(req));
+               os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name));
+               if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req,
+                              sizeof(req)) < 0) {
+                       wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind "
+                                  "multicast socket to ifname %s: %s",
+                                  forced_ifname, strerror(errno));
+                       close(sd);
+                       return -1;
+               }
+#endif /* __linux__ */
+       }
+
 #if 0   /* maybe ok if we sometimes block on writes */
-       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+               close(sd);
                return -1;
+       }
 #endif
 
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
                       &ip_addr, sizeof(ip_addr))) {
                wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
                           "%d (%s)", ip_addr, errno, strerror(errno));
+               close(sd);
                return -1;
        }
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
                       &ttl, sizeof(ttl))) {
                wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
                           "%d (%s)", errno, strerror(errno));
+               close(sd);
                return -1;
        }
 
@@ -902,6 +919,7 @@ int ssdp_open_multicast_sock(u32 ip_addr)
                                   "WPS UPnP: setsockopt "
                                   "IP_ADD_MEMBERSHIP errno %d (%s)",
                                   errno, strerror(errno));
+                       close(sd);
                        return -1;
                }
        }
@@ -923,7 +941,7 @@ int ssdp_open_multicast_sock(u32 ip_addr)
  */
 int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
 {
-       sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
+       sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL);
        if (sm->multicast_sd < 0)
                return -1;
        return 0;