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