[vlc-commits] upnp: lookup the best interface to use libupnp with

Hugo Beauzée-Luyssen git at videolan.org
Fri Mar 17 18:44:14 CET 2017


vlc | branch: master | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Fri Mar 17 18:37:32 2017 +0100| [e4d90da06b95172910406eafc94ac68722f66cff] | committer: Hugo Beauzée-Luyssen

upnp: lookup the best interface to use libupnp with

libupnp cannot handle more than one interface/IP at a time, so we need to make
sure we use an appropriate one.
Original patch by: Steve Lhomme <robux4 at videolabs.io>

Fix #14526

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e4d90da06b95172910406eafc94ac68722f66cff
---

 modules/services_discovery/upnp.cpp | 218 +++++++++++++++++++++++++++++++++++-
 1 file changed, 216 insertions(+), 2 deletions(-)

diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index 98afcf8..f35dfa1 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -30,6 +30,7 @@
 #include <vlc_plugin.h>
 #include <vlc_interrupt.h>
 #include <vlc_services_discovery.h>
+#include <vlc_charset.h>
 
 #include <assert.h>
 #include <limits.h>
@@ -1316,6 +1317,215 @@ UpnpInstanceWrapper::~UpnpInstanceWrapper()
     UpnpFinish();
 }
 
+#ifdef _WIN32
+
+static IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
+{
+    const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
+
+    IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
+    while (p_multicast != NULL)
+    {
+        if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
+            return p_multicast;
+        p_multicast = p_multicast->Next;
+    }
+    return NULL;
+}
+
+static bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
+{
+    if ( p_adapter->OperStatus != IfOperStatusUp )
+        return false;
+    if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
+    {
+        IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
+        // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
+        if (ipv6)
+            return p_adapter_xp->Ipv6IfIndex != 0;
+        return p_adapter_xp->IfIndex != 0;
+    }
+    IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
+    if (p_adapter_lh->FirstGatewayAddress == NULL)
+        return false;
+    if (ipv6)
+        return p_adapter_lh->Ipv6Enabled;
+    return p_adapter_lh->Ipv4Enabled;
+}
+
+static IP_ADAPTER_ADDRESSES* ListAdapters()
+{
+    ULONG addrSize;
+    const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
+    IP_ADAPTER_ADDRESSES* addresses = NULL;
+    HRESULT hr;
+
+    /**
+     * https://msdn.microsoft.com/en-us/library/aa365915.aspx
+     *
+     * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
+     * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
+     * this dramatically reduces the chances that the GetAdaptersAddresses function returns
+     * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
+     * times. The example code illustrates this method of use.
+     */
+    addrSize = 15 * 1024;
+    do
+    {
+        free(addresses);
+        addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
+        if (addresses == NULL)
+            return NULL;
+        hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
+    } while (hr == ERROR_BUFFER_OVERFLOW);
+    if (hr != NO_ERROR) {
+        free(addresses);
+        return NULL;
+    }
+    return addresses;
+}
+
+#ifdef UPNP_ENABLE_IPV6
+
+static char* getPreferedAdapter()
+{
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        printf("Adapter %S\n", p_adapter->FriendlyName);
+        if (isAdapterSuitable( p_adapter, true ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                char* res = FromWide( p_adapter->FriendlyName );
+                free( addresses );
+                return res;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+    free(addresses);
+    return NULL;
+}
+
+#else
+
+static char *getIpv4ForMulticast()
+{
+    IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
+    wchar_t psz_uri[32];
+    DWORD strSize;
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable( p_adapter, false ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                /* get an IPv4 address */
+                IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+                while (p_unicast != NULL)
+                {
+                    strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                    if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                            p_unicast->Address.iSockaddrLength,
+                                            NULL, psz_uri, &strSize ) == 0 )
+                    {
+                        if ( p_best_ip == NULL ||
+                             p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                        {
+                            p_best_ip = p_unicast;
+                        }
+                    }
+                    p_unicast = p_unicast->Next;
+                }
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+    if ( p_best_ip != NULL )
+        goto done;
+
+    /* find any with IPv4 */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable(p_adapter, false))
+        {
+            /* get an IPv4 address */
+            IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+            while (p_unicast != NULL)
+            {
+                strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                        p_unicast->Address.iSockaddrLength,
+                                        NULL, psz_uri, &strSize ) == 0 )
+                {
+                    if ( p_best_ip == NULL ||
+                         p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                    {
+                        p_best_ip = p_unicast;
+                    }
+                }
+                p_unicast = p_unicast->Next;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+done:
+    if (p_best_ip != NULL)
+    {
+        strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+        WSAAddressToString( p_best_ip->Address.lpSockaddr,
+                            p_best_ip->Address.iSockaddrLength,
+                            NULL, psz_uri, &strSize );
+        free(addresses);
+        return FromWide( psz_uri );
+    }
+    free(addresses);
+    return NULL;
+}
+#endif /* UPNP_ENABLE_IPV6 */
+#else /* _WIN32 */
+
+#ifdef UPNP_ENABLE_IPV6
+
+static char *getPreferedAdapter()
+{
+    return NULL;
+}
+
+#else
+
+static char *getIpv4ForMulticast()
+{
+    return NULL;
+}
+
+#endif
+
+#endif /* _WIN32 */
+
 UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_discovery_t *p_sd)
 {
     SD::MediaServerList *p_server_list = NULL;
@@ -1341,13 +1551,17 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_disc
 
     #ifdef UPNP_ENABLE_IPV6
         char* psz_miface = var_InheritString( p_obj, "miface" );
+        if (psz_miface == NULL)
+            psz_miface = getPreferedAdapter();
         msg_Info( p_obj, "Initializing libupnp on '%s' interface", psz_miface ? psz_miface : "default" );
         int i_res = UpnpInit2( psz_miface, 0 );
         free( psz_miface );
     #else
         /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
-        int i_res = UpnpInit( 0, 0 );
-    #endif
+        char *psz_hostip = getIpv4ForMulticast();
+        int i_res = UpnpInit( psz_hostip, 0 );
+        free(psz_hostip);
+    #endif /* UPNP_ENABLE_IPV6 */
         if( i_res != UPNP_E_SUCCESS )
         {
             msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );



More information about the vlc-commits mailing list