summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp')
-rw-r--r--lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp b/lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp
new file mode 100644
index 0000000..fda6f35
--- /dev/null
+++ b/lib/libUPnP/Platinum/Source/Core/PltSsdp.cpp
@@ -0,0 +1,513 @@
+/*****************************************************************
+|
+| Platinum - SSDP
+|
+| Copyright (c) 2004-2010, Plutinosoft, LLC.
+| All rights reserved.
+| http://www.plutinosoft.com
+|
+| This program is free software; you can redistribute it and/or
+| modify it under the terms of the GNU General Public License
+| as published by the Free Software Foundation; either version 2
+| of the License, or (at your option) any later version.
+|
+| OEMs, ISVs, VARs and other distributors that combine and
+| distribute commercially licensed software with Platinum software
+| and do not wish to distribute the source code for the commercially
+| licensed software under version 2, or (at your option) any later
+| version, of the GNU General Public License (the "GPL") must enter
+| into a commercial license agreement with Plutinosoft, LLC.
+| licensing@plutinosoft.com
+|
+| This program is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+| GNU General Public License for more details.
+|
+| You should have received a copy of the GNU General Public License
+| along with this program; see the file LICENSE.txt. If not, write to
+| the Free Software Foundation, Inc.,
+| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+| http://www.gnu.org/licenses/gpl-2.0.html
+|
+****************************************************************/
+
+/*----------------------------------------------------------------------
+| includes
++---------------------------------------------------------------------*/
+#include "PltSsdp.h"
+#include "PltDatagramStream.h"
+#include "PltDeviceHost.h"
+#include "PltUPnP.h"
+#include "PltHttp.h"
+#include "PltVersion.h"
+
+NPT_SET_LOCAL_LOGGER("platinum.core.ssdp")
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSender::SendSsdp
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpSender::SendSsdp(NPT_HttpRequest& request,
+ const char* usn,
+ const char* target,
+ NPT_UdpSocket& socket,
+ bool notify,
+ const NPT_SocketAddress* addr /* = NULL */)
+{
+ NPT_CHECK_SEVERE(FormatPacket(request, usn, target, socket, notify));
+
+ // logging
+ NPT_String prefix = NPT_String::Format("Sending SSDP %s packet for %s",
+ (const char*)request.GetMethod(),
+ usn);
+ PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, prefix, &request);
+
+ // use a memory stream to write all the data
+ NPT_MemoryStream stream;
+ NPT_Result res = request.Emit(stream);
+ NPT_CHECK(res);
+
+ // copy stream into a data packet and send it
+ NPT_LargeSize size;
+ stream.GetSize(size);
+ if (size != (NPT_Size)size) NPT_CHECK(NPT_ERROR_OUT_OF_RANGE);
+
+ NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size);
+ NPT_CHECK_WARNING(socket.Send(packet, addr));
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSender::SendSsdp
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpSender::SendSsdp(NPT_HttpResponse& response,
+ const char* usn,
+ const char* target,
+ NPT_UdpSocket& socket,
+ bool notify,
+ const NPT_SocketAddress* addr /* = NULL */)
+{
+ NPT_CHECK_SEVERE(FormatPacket(response, usn, target, socket, notify));
+
+ // logging
+ NPT_String prefix = NPT_String::Format("Sending SSDP Response:");
+ PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, &response);
+
+ // use a memory stream to write all the data
+ NPT_MemoryStream stream;
+ NPT_Result res = response.Emit(stream);
+ if (NPT_FAILED(res)) return res;
+
+ // copy stream into a data packet and send it
+ NPT_LargeSize size;
+ stream.GetSize(size);
+ if (size != (NPT_Size)size) NPT_CHECK(NPT_ERROR_OUT_OF_RANGE);
+
+ NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size);
+ NPT_CHECK_WARNING(socket.Send(packet, addr));
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSender::FormatPacket
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpSender::FormatPacket(NPT_HttpMessage& message,
+ const char* usn,
+ const char* target,
+ NPT_UdpSocket& socket,
+ bool notify)
+{
+ NPT_COMPILER_UNUSED(socket);
+
+ PLT_UPnPMessageHelper::SetUSN(message, usn);
+ if (notify) {
+ PLT_UPnPMessageHelper::SetNT(message, target);
+ } else {
+ PLT_UPnPMessageHelper::SetST(message, target);
+ PLT_UPnPMessageHelper::SetDate(message);
+ }
+ //PLT_HttpHelper::SetContentLength(message, 0);
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpDeviceSearchResponseInterfaceIterator class
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpDeviceSearchResponseInterfaceIterator::operator()(NPT_NetworkInterface*& net_if) const
+{
+ const NPT_SocketAddress* remote_addr = &m_RemoteAddr;
+
+ NPT_List<NPT_NetworkInterfaceAddress>::Iterator niaddr =
+ net_if->GetAddresses().GetFirstItem();
+ if (!niaddr) return NPT_SUCCESS;
+
+ // don't try to bind on port 1900 or connect will fail later
+ NPT_UdpSocket socket(NPT_SOCKET_FLAG_CANCELLABLE);
+ //socket.Bind(NPT_SocketAddress(NPT_IpAddress::Any, 1900), true);
+
+ // by connecting, the kernel chooses which interface to use to route to the remote
+ // this is the IP we should use in our Location URL header
+ NPT_CHECK_WARNING(socket.Connect(m_RemoteAddr, 5000));
+ NPT_SocketInfo info;
+ socket.GetInfo(info);
+
+ // did we successfully connect and found out which interface is used?
+ if (info.local_address.GetIpAddress().AsLong()) {
+ // check that the interface the kernel chose matches the interface
+ // we wanted to send on
+ if ((*niaddr).GetPrimaryAddress().AsLong() != info.local_address.GetIpAddress().AsLong()) {
+ return NPT_SUCCESS;
+ }
+
+ // socket already connected, so we don't need to specify where to go
+ remote_addr = NULL;
+ }
+
+ NPT_HttpResponse response(200, "OK", NPT_HTTP_PROTOCOL_1_1);
+ PLT_UPnPMessageHelper::SetLocation(response, m_Device->GetDescriptionUrl(info.local_address.GetIpAddress().ToString()));
+ PLT_UPnPMessageHelper::SetLeaseTime(response, m_Device->GetLeaseTime());
+ PLT_UPnPMessageHelper::SetServer(response, PLT_HTTP_DEFAULT_SERVER, false);
+ response.GetHeaders().SetHeader("EXT", "");
+
+ // process search response twice to be DLNA compliant
+#if defined(PLATINUM_UPNP_SPECS_STRICT)
+ {
+ //NPT_UdpSocket socket;
+ NPT_CHECK_SEVERE(m_Device->SendSsdpSearchResponse(response, socket, m_ST, remote_addr));
+ }
+ NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY_GROUP));
+#endif
+ {
+ //NPT_UdpSocket socket;
+ NPT_CHECK_SEVERE(m_Device->SendSsdpSearchResponse(response, socket, m_ST, remote_addr));
+ }
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpDeviceSearchResponseTask::DoRun()
++---------------------------------------------------------------------*/
+void
+PLT_SsdpDeviceSearchResponseTask::DoRun()
+{
+ NPT_List<NPT_NetworkInterface*> if_list;
+ NPT_CHECK_LABEL_WARNING(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, true),
+ done);
+
+ if_list.Apply(PLT_SsdpDeviceSearchResponseInterfaceIterator(
+ m_Device,
+ m_RemoteAddr,
+ m_ST));
+ if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
+
+done:
+ return;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpAnnounceInterfaceIterator class
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpAnnounceInterfaceIterator::operator()(NPT_NetworkInterface*& net_if) const
+{
+ // don't use this interface address if it's not broadcast capable
+ if (m_Broadcast && !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_BROADCAST)) {
+ return NPT_FAILURE;
+ }
+
+ NPT_List<NPT_NetworkInterfaceAddress>::Iterator niaddr =
+ net_if->GetAddresses().GetFirstItem();
+ if (!niaddr) return NPT_FAILURE;
+
+ // Remove disconnected interfaces
+ NPT_IpAddress addr = (*niaddr).GetPrimaryAddress();
+ if (!addr.ToString().Compare("0.0.0.0")) return NPT_FAILURE;
+
+ if (!m_Broadcast &&
+ !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_MULTICAST) &&
+ !(net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK)) {
+ NPT_LOG_INFO_2("Not a valid interface: %s (flags: %d)",
+ (const char*)addr.ToString(), net_if->GetFlags());
+ return NPT_FAILURE;
+ }
+
+ NPT_HttpUrl url;
+ NPT_UdpMulticastSocket multicast_socket(NPT_SOCKET_FLAG_CANCELLABLE);
+ NPT_UdpSocket broadcast_socket(NPT_SOCKET_FLAG_CANCELLABLE);
+ NPT_UdpSocket* socket;
+
+ if (m_Broadcast) {
+ url = NPT_HttpUrl((*niaddr).GetBroadcastAddress().ToString(), 1900, "*");
+ socket = &broadcast_socket;
+ } else {
+ url = NPT_HttpUrl("239.255.255.250", 1900, "*");
+ NPT_CHECK_SEVERE(multicast_socket.SetInterface(addr));
+ socket = &multicast_socket;
+ multicast_socket.SetTimeToLive(PLT_Constants::GetInstance().GetAnnounceMulticastTimeToLive());
+ }
+
+ NPT_HttpRequest req(url, "NOTIFY", NPT_HTTP_PROTOCOL_1_1);
+ PLT_HttpHelper::SetHost(req, "239.255.255.250:1900");
+
+ // Location header valid only for ssdp:alive or ssdp:update messages
+ if (m_Type != PLT_ANNOUNCETYPE_BYEBYE) {
+ PLT_UPnPMessageHelper::SetLocation(req, m_Device->GetDescriptionUrl(addr.ToString()));
+ }
+
+ NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_Type));
+
+#if defined(PLATINUM_UPNP_SPECS_STRICT)
+ // delay alive only as we don't want to delay when stopping
+ if (m_Type != PLT_ANNOUNCETYPE_BYEBYE) {
+ NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY_GROUP));
+ }
+
+ NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_Type));
+#endif
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpDeviceAnnounceUnicastTask::DoRun
++---------------------------------------------------------------------*/
+void
+PLT_SsdpDeviceAnnounceTask::DoRun()
+{
+ NPT_List<NPT_NetworkInterface*> if_list;
+
+ while (1) {
+ NPT_CHECK_LABEL_FATAL(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, false),
+ cleanup);
+
+ // if we're announcing our arrival, sends a byebye first (NMPR compliance)
+ if (m_IsByeByeFirst == true) {
+ m_IsByeByeFirst = false;
+
+ if (m_ExtraBroadcast) {
+ if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_BYEBYE, true));
+ }
+
+ // multicast now
+ if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_BYEBYE, false));
+
+ // schedule to announce alive in 200 ms
+ if (IsAborting(200)) break;
+ }
+
+ if (m_ExtraBroadcast) {
+ if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_ALIVE, true));
+ }
+
+ // multicast now
+ if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, PLT_ANNOUNCETYPE_ALIVE, false));
+
+
+cleanup:
+ if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
+ if_list.Clear();
+
+ if (IsAborting((NPT_Timeout)m_Repeat.ToMillis())) break;
+ };
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpListenTask::GetInputStream
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpListenTask::GetInputStream(NPT_InputStreamReference& stream)
+{
+ if (!m_Datagram.IsNull()) {
+ stream = m_Datagram;
+ return NPT_SUCCESS;
+ } else {
+ NPT_InputStreamReference input_stream;
+ NPT_Result res = m_Socket->GetInputStream(input_stream);
+ if (NPT_FAILED(res)) {
+ return res;
+ }
+ // for datagrams, we can't simply read from the socket directly
+ // we need to read datagram into a buffer
+ m_Datagram = new PLT_InputDatagramStream((NPT_UdpSocket*)m_Socket);
+ stream = m_Datagram;
+ return NPT_SUCCESS;
+ }
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpListenTask::GetInfo
++---------------------------------------------------------------------*/
+void
+PLT_SsdpListenTask::DoAbort()
+{
+ PLT_HttpServerSocketTask::DoAbort();
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpListenTask::GetInfo
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpListenTask::GetInfo(NPT_SocketInfo& info)
+{
+ if (m_Datagram.IsNull()) return NPT_FAILURE;
+ return m_Datagram->GetInfo(info);
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpListenTask::SetupResponse
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpListenTask::SetupResponse(NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context,
+ NPT_HttpResponse& response)
+{
+ NPT_COMPILER_UNUSED(response);
+
+ NPT_AutoLock lock(m_Mutex);
+ m_Listeners.Apply(PLT_SsdpPacketListenerIterator(request, context));
+
+ // return error since we don't have anything to respond
+ // as we use a separate task to respond with ssdp
+ return NPT_ERROR_TERMINATED;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSearchTask::PLT_SsdpSearchTask
++---------------------------------------------------------------------*/
+PLT_SsdpSearchTask::PLT_SsdpSearchTask(NPT_UdpSocket* socket,
+ PLT_SsdpSearchResponseListener* listener,
+ NPT_HttpRequest* request,
+ NPT_TimeInterval frequency) :
+ m_Listener(listener),
+ m_Request(request),
+ m_Frequency(frequency?frequency:NPT_TimeInterval(30.)),
+ m_Repeat(frequency.ToSeconds()!=0),
+ m_Socket(socket)
+{
+ m_Socket->SetReadTimeout((NPT_Timeout)m_Frequency.ToMillis());
+ m_Socket->SetWriteTimeout(10000);
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSearchTask::~PLT_SsdpSearchTask
++---------------------------------------------------------------------*/
+PLT_SsdpSearchTask::~PLT_SsdpSearchTask()
+{
+ delete m_Socket;
+ delete m_Request;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSearchTask::DoAbort
++---------------------------------------------------------------------*/
+void
+PLT_SsdpSearchTask::DoAbort()
+{
+ m_Socket->Cancel();
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpSearchTask::DoRun
++---------------------------------------------------------------------*/
+void
+PLT_SsdpSearchTask::DoRun()
+{
+ NPT_HttpResponse* response = NULL;
+ NPT_Timeout timeout = 30000;
+ NPT_HttpRequestContext context;
+
+ do {
+ // get the address of the server
+ NPT_IpAddress server_address;
+ NPT_CHECK_LABEL_SEVERE(server_address.ResolveName(
+ m_Request->GetUrl().GetHost(),
+ timeout),
+ done);
+ NPT_SocketAddress address(server_address,
+ m_Request->GetUrl().GetPort());
+
+ // send 2 requests in a row
+ NPT_OutputStreamReference output_stream(
+ new PLT_OutputDatagramStream(m_Socket,
+ 4096,
+ &address));
+ NPT_CHECK_LABEL_SEVERE(NPT_HttpClient::WriteRequest(
+ *output_stream.AsPointer(),
+ *m_Request,
+ false),
+ done);
+ NPT_CHECK_LABEL_SEVERE(NPT_HttpClient::WriteRequest(
+ *output_stream.AsPointer(),
+ *m_Request,
+ false),
+ done);
+ output_stream = NULL;
+
+ // keep track of when we sent the request
+ NPT_TimeStamp last_send;
+ NPT_System::GetCurrentTimeStamp(last_send);
+
+ while (!IsAborting(0)) {
+ // read response
+ PLT_InputDatagramStreamReference input_stream(
+ new PLT_InputDatagramStream(m_Socket));
+
+ NPT_InputStreamReference stream = input_stream;
+ NPT_Result res = NPT_HttpClient::ReadResponse(
+ stream,
+ false,
+ false,
+ response);
+ // callback to process response
+ if (NPT_SUCCEEDED(res)) {
+ // get source info
+ NPT_SocketInfo info;
+ input_stream->GetInfo(info);
+
+ context.SetLocalAddress(info.local_address);
+ context.SetRemoteAddress(info.remote_address);
+
+ // process response
+ ProcessResponse(NPT_SUCCESS, *m_Request, context, response);
+ delete response;
+ response = NULL;
+ } else if (res != NPT_ERROR_TIMEOUT) {
+ NPT_LOG_WARNING_1("PLT_SsdpSearchTask got an error (%d) waiting for response", res);
+ if (IsAborting(0))
+ break;
+
+ NPT_System::Sleep(NPT_TimeInterval(.15f));
+ }
+
+ input_stream = NULL;
+
+ // check if it's time to resend request
+ NPT_TimeStamp now;
+ NPT_System::GetCurrentTimeStamp(now);
+ if (now >= last_send + m_Frequency)
+ break;
+ }
+ } while (!IsAborting(0) && m_Repeat);
+
+done:
+ return;
+}
+
+/*----------------------------------------------------------------------
+| PLT_CtrlPointGetDescriptionTask::ProcessResponse
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpSearchTask::ProcessResponse(NPT_Result res,
+ const NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context,
+ NPT_HttpResponse* response)
+{
+ NPT_COMPILER_UNUSED(request);
+ return m_Listener->ProcessSsdpSearchResponse(res, context, response);
+}