Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Platinum / Source / Core / PltSsdp.cpp
blobfda6f35892824e75ee110fb47b570a550606eea5
1 /*****************************************************************
3 | Platinum - SSDP
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
33 ****************************************************************/
35 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "PltSsdp.h"
39 #include "PltDatagramStream.h"
40 #include "PltDeviceHost.h"
41 #include "PltUPnP.h"
42 #include "PltHttp.h"
43 #include "PltVersion.h"
45 NPT_SET_LOCAL_LOGGER("platinum.core.ssdp")
47 /*----------------------------------------------------------------------
48 | PLT_SsdpSender::SendSsdp
49 +---------------------------------------------------------------------*/
50 NPT_Result
51 PLT_SsdpSender::SendSsdp(NPT_HttpRequest& request,
52 const char* usn,
53 const char* target,
54 NPT_UdpSocket& socket,
55 bool notify,
56 const NPT_SocketAddress* addr /* = NULL */)
58 NPT_CHECK_SEVERE(FormatPacket(request, usn, target, socket, notify));
60 // logging
61 NPT_String prefix = NPT_String::Format("Sending SSDP %s packet for %s",
62 (const char*)request.GetMethod(),
63 usn);
64 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, prefix, &request);
66 // use a memory stream to write all the data
67 NPT_MemoryStream stream;
68 NPT_Result res = request.Emit(stream);
69 NPT_CHECK(res);
71 // copy stream into a data packet and send it
72 NPT_LargeSize size;
73 stream.GetSize(size);
74 if (size != (NPT_Size)size) NPT_CHECK(NPT_ERROR_OUT_OF_RANGE);
76 NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size);
77 NPT_CHECK_WARNING(socket.Send(packet, addr));
78 return NPT_SUCCESS;
81 /*----------------------------------------------------------------------
82 | PLT_SsdpSender::SendSsdp
83 +---------------------------------------------------------------------*/
84 NPT_Result
85 PLT_SsdpSender::SendSsdp(NPT_HttpResponse& response,
86 const char* usn,
87 const char* target,
88 NPT_UdpSocket& socket,
89 bool notify,
90 const NPT_SocketAddress* addr /* = NULL */)
92 NPT_CHECK_SEVERE(FormatPacket(response, usn, target, socket, notify));
94 // logging
95 NPT_String prefix = NPT_String::Format("Sending SSDP Response:");
96 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, &response);
98 // use a memory stream to write all the data
99 NPT_MemoryStream stream;
100 NPT_Result res = response.Emit(stream);
101 if (NPT_FAILED(res)) return res;
103 // copy stream into a data packet and send it
104 NPT_LargeSize size;
105 stream.GetSize(size);
106 if (size != (NPT_Size)size) NPT_CHECK(NPT_ERROR_OUT_OF_RANGE);
108 NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size);
109 NPT_CHECK_WARNING(socket.Send(packet, addr));
110 return NPT_SUCCESS;
113 /*----------------------------------------------------------------------
114 | PLT_SsdpSender::FormatPacket
115 +---------------------------------------------------------------------*/
116 NPT_Result
117 PLT_SsdpSender::FormatPacket(NPT_HttpMessage& message,
118 const char* usn,
119 const char* target,
120 NPT_UdpSocket& socket,
121 bool notify)
123 NPT_COMPILER_UNUSED(socket);
125 PLT_UPnPMessageHelper::SetUSN(message, usn);
126 if (notify) {
127 PLT_UPnPMessageHelper::SetNT(message, target);
128 } else {
129 PLT_UPnPMessageHelper::SetST(message, target);
130 PLT_UPnPMessageHelper::SetDate(message);
132 //PLT_HttpHelper::SetContentLength(message, 0);
134 return NPT_SUCCESS;
137 /*----------------------------------------------------------------------
138 | PLT_SsdpDeviceSearchResponseInterfaceIterator class
139 +---------------------------------------------------------------------*/
140 NPT_Result
141 PLT_SsdpDeviceSearchResponseInterfaceIterator::operator()(NPT_NetworkInterface*& net_if) const
143 const NPT_SocketAddress* remote_addr = &m_RemoteAddr;
145 NPT_List<NPT_NetworkInterfaceAddress>::Iterator niaddr =
146 net_if->GetAddresses().GetFirstItem();
147 if (!niaddr) return NPT_SUCCESS;
149 // don't try to bind on port 1900 or connect will fail later
150 NPT_UdpSocket socket(NPT_SOCKET_FLAG_CANCELLABLE);
151 //socket.Bind(NPT_SocketAddress(NPT_IpAddress::Any, 1900), true);
153 // by connecting, the kernel chooses which interface to use to route to the remote
154 // this is the IP we should use in our Location URL header
155 NPT_CHECK_WARNING(socket.Connect(m_RemoteAddr, 5000));
156 NPT_SocketInfo info;
157 socket.GetInfo(info);
159 // did we successfully connect and found out which interface is used?
160 if (info.local_address.GetIpAddress().AsLong()) {
161 // check that the interface the kernel chose matches the interface
162 // we wanted to send on
163 if ((*niaddr).GetPrimaryAddress().AsLong() != info.local_address.GetIpAddress().AsLong()) {
164 return NPT_SUCCESS;
167 // socket already connected, so we don't need to specify where to go
168 remote_addr = NULL;
171 NPT_HttpResponse response(200, "OK", NPT_HTTP_PROTOCOL_1_1);
172 PLT_UPnPMessageHelper::SetLocation(response, m_Device->GetDescriptionUrl(info.local_address.GetIpAddress().ToString()));
173 PLT_UPnPMessageHelper::SetLeaseTime(response, m_Device->GetLeaseTime());
174 PLT_UPnPMessageHelper::SetServer(response, PLT_HTTP_DEFAULT_SERVER, false);
175 response.GetHeaders().SetHeader("EXT", "");
177 // process search response twice to be DLNA compliant
178 #if defined(PLATINUM_UPNP_SPECS_STRICT)
180 //NPT_UdpSocket socket;
181 NPT_CHECK_SEVERE(m_Device->SendSsdpSearchResponse(response, socket, m_ST, remote_addr));
183 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY_GROUP));
184 #endif
186 //NPT_UdpSocket socket;
187 NPT_CHECK_SEVERE(m_Device->SendSsdpSearchResponse(response, socket, m_ST, remote_addr));
190 return NPT_SUCCESS;
193 /*----------------------------------------------------------------------
194 | PLT_SsdpDeviceSearchResponseTask::DoRun()
195 +---------------------------------------------------------------------*/
196 void
197 PLT_SsdpDeviceSearchResponseTask::DoRun()
199 NPT_List<NPT_NetworkInterface*> if_list;
200 NPT_CHECK_LABEL_WARNING(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, true),
201 done);
203 if_list.Apply(PLT_SsdpDeviceSearchResponseInterfaceIterator(
204 m_Device,
205 m_RemoteAddr,
206 m_ST));
207 if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
209 done:
210 return;
213 /*----------------------------------------------------------------------
214 | PLT_SsdpAnnounceInterfaceIterator class
215 +---------------------------------------------------------------------*/
216 NPT_Result
217 PLT_SsdpAnnounceInterfaceIterator::operator()(NPT_NetworkInterface*& net_if) const
219 // don't use this interface address if it's not broadcast capable
220 if (m_Broadcast && !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_BROADCAST)) {
221 return NPT_FAILURE;
224 NPT_List<NPT_NetworkInterfaceAddress>::Iterator niaddr =
225 net_if->GetAddresses().GetFirstItem();
226 if (!niaddr) return NPT_FAILURE;
228 // Remove disconnected interfaces
229 NPT_IpAddress addr = (*niaddr).GetPrimaryAddress();
230 if (!addr.ToString().Compare("0.0.0.0")) return NPT_FAILURE;
232 if (!m_Broadcast &&
233 !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_MULTICAST) &&
234 !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK)) {
235 NPT_LOG_INFO_2("Not a valid interface: %s (flags: %d)",
236 (const char*)addr.ToString(), net_if->GetFlags());
237 return NPT_FAILURE;
240 NPT_HttpUrl url;
241 NPT_UdpMulticastSocket multicast_socket(NPT_SOCKET_FLAG_CANCELLABLE);
242 NPT_UdpSocket broadcast_socket(NPT_SOCKET_FLAG_CANCELLABLE);
243 NPT_UdpSocket* socket;
245 if (m_Broadcast) {
246 url = NPT_HttpUrl((*niaddr).GetBroadcastAddress().ToString(), 1900, "*");
247 socket = &broadcast_socket;
248 } else {
249 url = NPT_HttpUrl("239.255.255.250", 1900, "*");
250 NPT_CHECK_SEVERE(multicast_socket.SetInterface(addr));
251 socket = &multicast_socket;
252 multicast_socket.SetTimeToLive(PLT_Constants::GetInstance().GetAnnounceMulticastTimeToLive());
255 NPT_HttpRequest req(url, "NOTIFY", NPT_HTTP_PROTOCOL_1_1);
256 PLT_HttpHelper::SetHost(req, "239.255.255.250:1900");
258 // Location header valid only for ssdp:alive or ssdp:update messages
259 if (m_Type != PLT_ANNOUNCETYPE_BYEBYE) {
260 PLT_UPnPMessageHelper::SetLocation(req, m_Device->GetDescriptionUrl(addr.ToString()));
263 NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_Type));
265 #if defined(PLATINUM_UPNP_SPECS_STRICT)
266 // delay alive only as we don't want to delay when stopping
267 if (m_Type != PLT_ANNOUNCETYPE_BYEBYE) {
268 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY_GROUP));
271 NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_Type));
272 #endif
274 return NPT_SUCCESS;
277 /*----------------------------------------------------------------------
278 | PLT_SsdpDeviceAnnounceUnicastTask::DoRun
279 +---------------------------------------------------------------------*/
280 void
281 PLT_SsdpDeviceAnnounceTask::DoRun()
283 NPT_List<NPT_NetworkInterface*> if_list;
285 while (1) {
286 NPT_CHECK_LABEL_FATAL(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, false),
287 cleanup);
289 // if we're announcing our arrival, sends a byebye first (NMPR compliance)
290 if (m_IsByeByeFirst == true) {
291 m_IsByeByeFirst = false;
293 if (m_ExtraBroadcast) {
294 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_BYEBYE, true));
297 // multicast now
298 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_BYEBYE, false));
300 // schedule to announce alive in 200 ms
301 if (IsAborting(200)) break;
304 if (m_ExtraBroadcast) {
305 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_ALIVE, true));
308 // multicast now
309 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_ALIVE, false));
312 cleanup:
313 if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
314 if_list.Clear();
316 if (IsAborting((NPT_Timeout)m_Repeat.ToMillis())) break;
320 /*----------------------------------------------------------------------
321 | PLT_SsdpListenTask::GetInputStream
322 +---------------------------------------------------------------------*/
323 NPT_Result
324 PLT_SsdpListenTask::GetInputStream(NPT_InputStreamReference& stream)
326 if (!m_Datagram.IsNull()) {
327 stream = m_Datagram;
328 return NPT_SUCCESS;
329 } else {
330 NPT_InputStreamReference input_stream;
331 NPT_Result res = m_Socket->GetInputStream(input_stream);
332 if (NPT_FAILED(res)) {
333 return res;
335 // for datagrams, we can't simply read from the socket directly
336 // we need to read datagram into a buffer
337 m_Datagram = new PLT_InputDatagramStream((NPT_UdpSocket*)m_Socket);
338 stream = m_Datagram;
339 return NPT_SUCCESS;
343 /*----------------------------------------------------------------------
344 | PLT_SsdpListenTask::GetInfo
345 +---------------------------------------------------------------------*/
346 void
347 PLT_SsdpListenTask::DoAbort()
349 PLT_HttpServerSocketTask::DoAbort();
352 /*----------------------------------------------------------------------
353 | PLT_SsdpListenTask::GetInfo
354 +---------------------------------------------------------------------*/
355 NPT_Result
356 PLT_SsdpListenTask::GetInfo(NPT_SocketInfo& info)
358 if (m_Datagram.IsNull()) return NPT_FAILURE;
359 return m_Datagram->GetInfo(info);
362 /*----------------------------------------------------------------------
363 | PLT_SsdpListenTask::SetupResponse
364 +---------------------------------------------------------------------*/
365 NPT_Result
366 PLT_SsdpListenTask::SetupResponse(NPT_HttpRequest& request,
367 const NPT_HttpRequestContext& context,
368 NPT_HttpResponse& response)
370 NPT_COMPILER_UNUSED(response);
372 NPT_AutoLock lock(m_Mutex);
373 m_Listeners.Apply(PLT_SsdpPacketListenerIterator(request, context));
375 // return error since we don't have anything to respond
376 // as we use a separate task to respond with ssdp
377 return NPT_ERROR_TERMINATED;
380 /*----------------------------------------------------------------------
381 | PLT_SsdpSearchTask::PLT_SsdpSearchTask
382 +---------------------------------------------------------------------*/
383 PLT_SsdpSearchTask::PLT_SsdpSearchTask(NPT_UdpSocket* socket,
384 PLT_SsdpSearchResponseListener* listener,
385 NPT_HttpRequest* request,
386 NPT_TimeInterval frequency) :
387 m_Listener(listener),
388 m_Request(request),
389 m_Frequency(frequency?frequency:NPT_TimeInterval(30.)),
390 m_Repeat(frequency.ToSeconds()!=0),
391 m_Socket(socket)
393 m_Socket->SetReadTimeout((NPT_Timeout)m_Frequency.ToMillis());
394 m_Socket->SetWriteTimeout(10000);
397 /*----------------------------------------------------------------------
398 | PLT_SsdpSearchTask::~PLT_SsdpSearchTask
399 +---------------------------------------------------------------------*/
400 PLT_SsdpSearchTask::~PLT_SsdpSearchTask()
402 delete m_Socket;
403 delete m_Request;
406 /*----------------------------------------------------------------------
407 | PLT_SsdpSearchTask::DoAbort
408 +---------------------------------------------------------------------*/
409 void
410 PLT_SsdpSearchTask::DoAbort()
412 m_Socket->Cancel();
415 /*----------------------------------------------------------------------
416 | PLT_SsdpSearchTask::DoRun
417 +---------------------------------------------------------------------*/
418 void
419 PLT_SsdpSearchTask::DoRun()
421 NPT_HttpResponse* response = NULL;
422 NPT_Timeout timeout = 30000;
423 NPT_HttpRequestContext context;
425 do {
426 // get the address of the server
427 NPT_IpAddress server_address;
428 NPT_CHECK_LABEL_SEVERE(server_address.ResolveName(
429 m_Request->GetUrl().GetHost(),
430 timeout),
431 done);
432 NPT_SocketAddress address(server_address,
433 m_Request->GetUrl().GetPort());
435 // send 2 requests in a row
436 NPT_OutputStreamReference output_stream(
437 new PLT_OutputDatagramStream(m_Socket,
438 4096,
439 &address));
440 NPT_CHECK_LABEL_SEVERE(NPT_HttpClient::WriteRequest(
441 *output_stream.AsPointer(),
442 *m_Request,
443 false),
444 done);
445 NPT_CHECK_LABEL_SEVERE(NPT_HttpClient::WriteRequest(
446 *output_stream.AsPointer(),
447 *m_Request,
448 false),
449 done);
450 output_stream = NULL;
452 // keep track of when we sent the request
453 NPT_TimeStamp last_send;
454 NPT_System::GetCurrentTimeStamp(last_send);
456 while (!IsAborting(0)) {
457 // read response
458 PLT_InputDatagramStreamReference input_stream(
459 new PLT_InputDatagramStream(m_Socket));
461 NPT_InputStreamReference stream = input_stream;
462 NPT_Result res = NPT_HttpClient::ReadResponse(
463 stream,
464 false,
465 false,
466 response);
467 // callback to process response
468 if (NPT_SUCCEEDED(res)) {
469 // get source info
470 NPT_SocketInfo info;
471 input_stream->GetInfo(info);
473 context.SetLocalAddress(info.local_address);
474 context.SetRemoteAddress(info.remote_address);
476 // process response
477 ProcessResponse(NPT_SUCCESS, *m_Request, context, response);
478 delete response;
479 response = NULL;
480 } else if (res != NPT_ERROR_TIMEOUT) {
481 NPT_LOG_WARNING_1("PLT_SsdpSearchTask got an error (%d) waiting for response", res);
482 if (IsAborting(0))
483 break;
485 NPT_System::Sleep(NPT_TimeInterval(.15f));
488 input_stream = NULL;
490 // check if it's time to resend request
491 NPT_TimeStamp now;
492 NPT_System::GetCurrentTimeStamp(now);
493 if (now >= last_send + m_Frequency)
494 break;
496 } while (!IsAborting(0) && m_Repeat);
498 done:
499 return;
502 /*----------------------------------------------------------------------
503 | PLT_CtrlPointGetDescriptionTask::ProcessResponse
504 +---------------------------------------------------------------------*/
505 NPT_Result
506 PLT_SsdpSearchTask::ProcessResponse(NPT_Result res,
507 const NPT_HttpRequest& request,
508 const NPT_HttpRequestContext& context,
509 NPT_HttpResponse* response)
511 NPT_COMPILER_UNUSED(request);
512 return m_Listener->ProcessSsdpSearchResponse(res, context, response);