1 /*****************************************************************************
2 * upnp-wrapper.hpp : UPnP Instance Wrapper class header
3 *****************************************************************************
4 * Copyright © 2004-2018 VLC authors and VideoLAN
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>
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.
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.
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 *****************************************************************************/
36 #include <upnp/upnp.h>
37 #include <upnp/upnptools.h>
39 #include <vlc_common.h>
40 #include <vlc_charset.h>
42 #if UPNP_VERSION < 10800
43 typedef void* UpnpEventPtr;
45 typedef const void* UpnpEventPtr;
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.
56 class UpnpInstanceWrapper
62 virtual ~Listener() {}
63 virtual int onEvent( Upnp_EventType event_type,
65 void* p_user_data ) = 0;
69 static UpnpInstanceWrapper* s_instance;
70 static vlc_mutex_t s_lock;
71 UpnpClient_Handle m_handle;
73 typedef std::shared_ptr<Listener> ListenerPtr;
74 typedef std::vector<ListenerPtr> Listeners;
75 static Listeners s_listeners;
78 // This increases the refcount before returning the instance
79 static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
81 UpnpClient_Handle handle() const;
82 void addListener(ListenerPtr listener);
83 void removeListener(ListenerPtr listener);
86 static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
88 UpnpInstanceWrapper();
89 ~UpnpInstanceWrapper();
92 // **************************
94 // **************************
96 #if UPNP_VERSION < 10623
98 * Compat functions and typedefs for libupnp prior to 1.8
101 typedef Upnp_Discovery UpnpDiscovery;
102 typedef Upnp_Action_Complete UpnpActionComplete;
104 inline const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
106 return p_discovery->Location;
109 inline const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
111 return p_discovery->DeviceId;
114 inline static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
116 return p_result->ActionResult;
121 * Returns the value of a child element, or NULL on error
123 inline const char* xml_getChildElementValue( IXML_Element* p_parent,
124 const char* psz_tag_name )
127 assert( psz_tag_name );
129 IXML_NodeList* p_node_list;
130 p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
131 if ( !p_node_list ) return NULL;
133 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
134 ixmlNodeList_free( p_node_list );
135 if ( !p_element ) return NULL;
137 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
138 if ( !p_text_node ) return NULL;
140 return ixmlNode_getNodeValue( p_text_node );
145 inline IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
147 const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
149 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
150 while (p_multicast != NULL)
152 if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
154 p_multicast = p_multicast->Next;
159 inline bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
161 if ( p_adapter->OperStatus != IfOperStatusUp )
163 if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
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.
168 return p_adapter_xp->Ipv6IfIndex != 0;
169 return p_adapter_xp->IfIndex != 0;
171 IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
172 if (p_adapter_lh->FirstGatewayAddress == NULL)
175 return p_adapter_lh->Ipv6Enabled;
176 return p_adapter_lh->Ipv4Enabled;
179 inline IP_ADAPTER_ADDRESSES* ListAdapters()
182 const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
183 IP_ADAPTER_ADDRESSES* addresses = NULL;
187 * https://msdn.microsoft.com/en-us/library/aa365915.aspx
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.
195 addrSize = 15 * 1024;
199 addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
200 if (addresses == NULL)
202 hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
203 } while (hr == ERROR_BUFFER_OVERFLOW);
204 if (hr != NO_ERROR) {
211 #ifdef UPNP_ENABLE_IPV6
213 inline char* getPreferedAdapter()
215 IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
217 addresses = ListAdapters();
218 if (addresses == NULL)
221 /* find one with multicast capabilities */
222 p_adapter = addresses;
223 while (p_adapter != NULL)
225 if (isAdapterSuitable( p_adapter, true ))
227 /* make sure it supports 239.255.255.250 */
228 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
229 if (p_multicast != NULL)
231 char* res = FromWide( p_adapter->FriendlyName );
236 p_adapter = p_adapter->Next;
244 inline char *getIpv4ForMulticast()
246 IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
249 IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
251 addresses = ListAdapters();
252 if (addresses == NULL)
255 /* find one with multicast capabilities */
256 p_adapter = addresses;
257 while (p_adapter != NULL)
259 if (isAdapterSuitable( p_adapter, false ))
261 /* make sure it supports 239.255.255.250 */
262 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
263 if (p_multicast != NULL)
265 /* get an IPv4 address */
266 IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
267 while (p_unicast != NULL)
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 )
274 if ( p_best_ip == NULL ||
275 p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
277 p_best_ip = p_unicast;
280 p_unicast = p_unicast->Next;
284 p_adapter = p_adapter->Next;
287 if ( p_best_ip != NULL )
290 /* find any with IPv4 */
291 p_adapter = addresses;
292 while (p_adapter != NULL)
294 if (isAdapterSuitable(p_adapter, false))
296 /* get an IPv4 address */
297 IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
298 while (p_unicast != NULL)
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 )
305 if ( p_best_ip == NULL ||
306 p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
308 p_best_ip = p_unicast;
311 p_unicast = p_unicast->Next;
314 p_adapter = p_adapter->Next;
318 if (p_best_ip != NULL)
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 );
325 return FromWide( psz_uri );
330 #endif /* UPNP_ENABLE_IPV6 */
333 #ifdef UPNP_ENABLE_IPV6
335 inline char *getPreferedAdapter()
342 inline char *getIpv4ForMulticast()