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 *****************************************************************************/
27 #include <vlc_charset.h>
34 #include <upnp/upnp.h>
35 #include <upnp/upnptools.h>
37 #if UPNP_VERSION < 10800
38 typedef void* UpnpEventPtr;
40 typedef const void* UpnpEventPtr;
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.
51 class UpnpInstanceWrapper
57 virtual ~Listener() {}
58 virtual int onEvent( Upnp_EventType event_type,
60 void* p_user_data ) = 0;
64 static UpnpInstanceWrapper* s_instance;
65 static vlc_mutex_t s_lock;
66 UpnpClient_Handle m_handle;
68 typedef std::shared_ptr<Listener> ListenerPtr;
69 typedef std::vector<ListenerPtr> Listeners;
70 static Listeners s_listeners;
73 // This increases the refcount before returning the instance
74 static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
76 UpnpClient_Handle handle() const;
77 void addListener(ListenerPtr listener);
78 void removeListener(ListenerPtr listener);
81 static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
83 UpnpInstanceWrapper();
84 ~UpnpInstanceWrapper();
87 // **************************
89 // **************************
91 #if UPNP_VERSION < 10623
93 * Compat functions and typedefs for libupnp prior to 1.8
96 typedef Upnp_Discovery UpnpDiscovery;
97 typedef Upnp_Action_Complete UpnpActionComplete;
99 inline const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
101 return p_discovery->Location;
104 inline const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
106 return p_discovery->DeviceId;
109 inline static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
111 return p_result->ActionResult;
116 * Returns the value of a child element, or NULL on error
118 inline const char* xml_getChildElementValue( IXML_Element* p_parent,
119 const char* psz_tag_name )
122 assert( psz_tag_name );
124 IXML_NodeList* p_node_list;
125 p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
126 if ( !p_node_list ) return NULL;
128 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
129 ixmlNodeList_free( p_node_list );
130 if ( !p_element ) return NULL;
132 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
133 if ( !p_text_node ) return NULL;
135 return ixmlNode_getNodeValue( p_text_node );
140 inline IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
142 const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
144 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
145 while (p_multicast != NULL)
147 if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
149 p_multicast = p_multicast->Next;
154 inline bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
156 if ( p_adapter->OperStatus != IfOperStatusUp )
158 if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
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.
163 return p_adapter_xp->Ipv6IfIndex != 0;
164 return p_adapter_xp->IfIndex != 0;
166 IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
167 if (p_adapter_lh->FirstGatewayAddress == NULL)
170 return p_adapter_lh->Ipv6Enabled;
171 return p_adapter_lh->Ipv4Enabled;
174 inline IP_ADAPTER_ADDRESSES* ListAdapters()
177 const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
178 IP_ADAPTER_ADDRESSES* addresses = NULL;
182 * https://msdn.microsoft.com/en-us/library/aa365915.aspx
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.
190 addrSize = 15 * 1024;
194 addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
195 if (addresses == NULL)
197 hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
198 } while (hr == ERROR_BUFFER_OVERFLOW);
199 if (hr != NO_ERROR) {
206 #ifdef UPNP_ENABLE_IPV6
208 inline char* getPreferedAdapter()
210 IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
212 addresses = ListAdapters();
213 if (addresses == NULL)
216 /* find one with multicast capabilities */
217 p_adapter = addresses;
218 while (p_adapter != NULL)
220 if (isAdapterSuitable( p_adapter, true ))
222 /* make sure it supports 239.255.255.250 */
223 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
224 if (p_multicast != NULL)
226 char* res = FromWide( p_adapter->FriendlyName );
231 p_adapter = p_adapter->Next;
239 inline char *getIpv4ForMulticast()
241 IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
244 IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
246 addresses = ListAdapters();
247 if (addresses == NULL)
250 /* find one with multicast capabilities */
251 p_adapter = addresses;
252 while (p_adapter != NULL)
254 if (isAdapterSuitable( p_adapter, false ))
256 /* make sure it supports 239.255.255.250 */
257 IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
258 if (p_multicast != NULL)
260 /* get an IPv4 address */
261 IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
262 while (p_unicast != NULL)
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 )
269 if ( p_best_ip == NULL ||
270 p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
272 p_best_ip = p_unicast;
275 p_unicast = p_unicast->Next;
279 p_adapter = p_adapter->Next;
282 if ( p_best_ip != NULL )
285 /* find any with IPv4 */
286 p_adapter = addresses;
287 while (p_adapter != NULL)
289 if (isAdapterSuitable(p_adapter, false))
291 /* get an IPv4 address */
292 IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
293 while (p_unicast != NULL)
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 )
300 if ( p_best_ip == NULL ||
301 p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
303 p_best_ip = p_unicast;
306 p_unicast = p_unicast->Next;
309 p_adapter = p_adapter->Next;
313 if (p_best_ip != NULL)
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 );
320 return FromWide( psz_uri );
325 #endif /* UPNP_ENABLE_IPV6 */
328 #ifdef UPNP_ENABLE_IPV6
330 inline char *getPreferedAdapter()
337 inline char *getIpv4ForMulticast()