ebae8fc96fa63bb485ecdd7e055e9f1bc9fdc9ae
[vlc.git] / modules / services_discovery / upnp-wrapper.hpp
1 /*****************************************************************************
2  * upnp-wrapper.hpp :  UPnP Instance Wrapper class header
3  *****************************************************************************
4  * Copyright © 2004-2018 VLC authors and VideoLAN
5  *
6  * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
7  *          Christian Henz <henz # c-lab.de>
8  *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
9  *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
10  *          Shaleen Jain <shaleen@jain.sh>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #include <vlc_charset.h>
28
29 #include <memory>
30 #include <vector>
31 #include <algorithm>
32 #include <assert.h>
33
34 #include <upnp/upnp.h>
35 #include <upnp/upnptools.h>
36
37 #if UPNP_VERSION < 10800
38 typedef void* UpnpEventPtr;
39 #else
40 typedef const void* UpnpEventPtr;
41 #endif
42
43 /**
44  * libUpnp allows only one instance per process, so we create a wrapper
45  * class around it that acts and behaves as a singleton. Letting us get
46  * multiple references to it but only ever having a single instance in memory.
47  * At the same time we let any module wishing to get a callback from the library
48  * to register a UpnpInstanceWrapper::Listener to get the Listener#onEvent()
49  * callback without having any hard dependencies.
50  */
51 class UpnpInstanceWrapper
52 {
53 public:
54     class Listener
55     {
56         public:
57             virtual ~Listener() {}
58             virtual int onEvent( Upnp_EventType event_type,
59                                  UpnpEventPtr p_event,
60                                  void* p_user_data ) = 0;
61     };
62
63 private:
64     static UpnpInstanceWrapper* s_instance;
65     static vlc_mutex_t s_lock;
66     UpnpClient_Handle m_handle;
67     int m_refcount;
68     typedef std::shared_ptr<Listener> ListenerPtr;
69     typedef std::vector<ListenerPtr> Listeners;
70     static Listeners s_listeners;
71
72 public:
73     // This increases the refcount before returning the instance
74     static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
75     void release();
76     UpnpClient_Handle handle() const;
77     void addListener(ListenerPtr listener);
78     void removeListener(ListenerPtr listener);
79
80 private:
81     static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
82
83     UpnpInstanceWrapper();
84     ~UpnpInstanceWrapper();
85 };
86
87 // **************************
88 // Helper functions
89 // **************************
90
91 #if UPNP_VERSION < 10623
92 /*
93  * Compat functions and typedefs for libupnp prior to 1.8
94  */
95
96 typedef Upnp_Discovery UpnpDiscovery;
97 typedef Upnp_Action_Complete UpnpActionComplete;
98
99 inline const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
100 {
101   return p_discovery->Location;
102 }
103
104 inline const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
105 {
106   return p_discovery->DeviceId;
107 }
108
109 inline static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
110 {
111   return p_result->ActionResult;
112 }
113 #endif
114
115 /*
116  * Returns the value of a child element, or NULL on error
117  */
118 inline const char* xml_getChildElementValue( IXML_Element* p_parent,
119                                       const char*   psz_tag_name )
120 {
121     assert( p_parent );
122     assert( psz_tag_name );
123
124     IXML_NodeList* p_node_list;
125     p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
126     if ( !p_node_list ) return NULL;
127
128     IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
129     ixmlNodeList_free( p_node_list );
130     if ( !p_element )   return NULL;
131
132     IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
133     if ( !p_text_node ) return NULL;
134
135     return ixmlNode_getNodeValue( p_text_node );
136 }
137
138 #ifdef _WIN32
139
140 inline IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
141 {
142     const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
143
144     IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
145     while (p_multicast != NULL)
146     {
147         if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
148             return p_multicast;
149         p_multicast = p_multicast->Next;
150     }
151     return NULL;
152 }
153
154 inline bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
155 {
156     if ( p_adapter->OperStatus != IfOperStatusUp )
157         return false;
158     if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
159     {
160         IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
161         // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
162         if (ipv6)
163             return p_adapter_xp->Ipv6IfIndex != 0;
164         return p_adapter_xp->IfIndex != 0;
165     }
166     IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
167     if (p_adapter_lh->FirstGatewayAddress == NULL)
168         return false;
169     if (ipv6)
170         return p_adapter_lh->Ipv6Enabled;
171     return p_adapter_lh->Ipv4Enabled;
172 }
173
174 inline IP_ADAPTER_ADDRESSES* ListAdapters()
175 {
176     ULONG addrSize;
177     const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
178     IP_ADAPTER_ADDRESSES* addresses = NULL;
179     HRESULT hr;
180
181     /**
182      * https://msdn.microsoft.com/en-us/library/aa365915.aspx
183      *
184      * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
185      * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
186      * this dramatically reduces the chances that the GetAdaptersAddresses function returns
187      * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
188      * times. The example code illustrates this method of use.
189      */
190     addrSize = 15 * 1024;
191     do
192     {
193         free(addresses);
194         addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
195         if (addresses == NULL)
196             return NULL;
197         hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
198     } while (hr == ERROR_BUFFER_OVERFLOW);
199     if (hr != NO_ERROR) {
200         free(addresses);
201         return NULL;
202     }
203     return addresses;
204 }
205
206 #ifdef UPNP_ENABLE_IPV6
207
208 inline char* getPreferedAdapter()
209 {
210     IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
211
212     addresses = ListAdapters();
213     if (addresses == NULL)
214         return NULL;
215
216     /* find one with multicast capabilities */
217     p_adapter = addresses;
218     while (p_adapter != NULL)
219     {
220         if (isAdapterSuitable( p_adapter, true ))
221         {
222             /* make sure it supports 239.255.255.250 */
223             IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
224             if (p_multicast != NULL)
225             {
226                 char* res = FromWide( p_adapter->FriendlyName );
227                 free( addresses );
228                 return res;
229             }
230         }
231         p_adapter = p_adapter->Next;
232     }
233     free(addresses);
234     return NULL;
235 }
236
237 #else
238
239 inline char *getIpv4ForMulticast()
240 {
241     IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
242     wchar_t psz_uri[32];
243     DWORD strSize;
244     IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
245
246     addresses = ListAdapters();
247     if (addresses == NULL)
248         return NULL;
249
250     /* find one with multicast capabilities */
251     p_adapter = addresses;
252     while (p_adapter != NULL)
253     {
254         if (isAdapterSuitable( p_adapter, false ))
255         {
256             /* make sure it supports 239.255.255.250 */
257             IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
258             if (p_multicast != NULL)
259             {
260                 /* get an IPv4 address */
261                 IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
262                 while (p_unicast != NULL)
263                 {
264                     strSize = sizeof( psz_uri ) / sizeof( wchar_t );
265                     if( WSAAddressToString( p_unicast->Address.lpSockaddr,
266                                             p_unicast->Address.iSockaddrLength,
267                                             NULL, psz_uri, &strSize ) == 0 )
268                     {
269                         if ( p_best_ip == NULL ||
270                              p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
271                         {
272                             p_best_ip = p_unicast;
273                         }
274                     }
275                     p_unicast = p_unicast->Next;
276                 }
277             }
278         }
279         p_adapter = p_adapter->Next;
280     }
281
282     if ( p_best_ip != NULL )
283         goto done;
284
285     /* find any with IPv4 */
286     p_adapter = addresses;
287     while (p_adapter != NULL)
288     {
289         if (isAdapterSuitable(p_adapter, false))
290         {
291             /* get an IPv4 address */
292             IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
293             while (p_unicast != NULL)
294             {
295                 strSize = sizeof( psz_uri ) / sizeof( wchar_t );
296                 if( WSAAddressToString( p_unicast->Address.lpSockaddr,
297                                         p_unicast->Address.iSockaddrLength,
298                                         NULL, psz_uri, &strSize ) == 0 )
299                 {
300                     if ( p_best_ip == NULL ||
301                          p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
302                     {
303                         p_best_ip = p_unicast;
304                     }
305                 }
306                 p_unicast = p_unicast->Next;
307             }
308         }
309         p_adapter = p_adapter->Next;
310     }
311
312 done:
313     if (p_best_ip != NULL)
314     {
315         strSize = sizeof( psz_uri ) / sizeof( wchar_t );
316         WSAAddressToString( p_best_ip->Address.lpSockaddr,
317                             p_best_ip->Address.iSockaddrLength,
318                             NULL, psz_uri, &strSize );
319         free(addresses);
320         return FromWide( psz_uri );
321     }
322     free(addresses);
323     return NULL;
324 }
325 #endif /* UPNP_ENABLE_IPV6 */
326 #else /* _WIN32 */
327
328 #ifdef UPNP_ENABLE_IPV6
329
330 inline char *getPreferedAdapter()
331 {
332     return NULL;
333 }
334
335 #else
336
337 inline char *getIpv4ForMulticast()
338 {
339     return NULL;
340 }
341
342 #endif
343
344 #endif /* _WIN32 */