Mercurial > projects > hoofbaby
view deps/Platinum/Source/Core/PltSsdp.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line source
/***************************************************************** | | Platinum - SSDP | | Copyright (c) 2004-2008, 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. | | 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") /*---------------------------------------------------------------------- | constants +---------------------------------------------------------------------*/ const int NPT_SSDP_MAX_LINE_SIZE = 2048; const int NPT_SSDP_MAX_DGRAM_SIZE = 4096; /*---------------------------------------------------------------------- | 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_LOG_FINE_2("Sending SSDP %s for %s", (const char*)request.GetMethod(), usn); PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request); // use a memory stream to write all the data NPT_MemoryStream stream; NPT_Result res = request.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) return NPT_ERROR_OUT_OF_RANGE; NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size); return socket.Send(packet, addr); } /*---------------------------------------------------------------------- | 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_LOG_FINE("Sending SSDP:"); PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &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) return NPT_ERROR_OUT_OF_RANGE; NPT_DataBuffer packet(stream.GetData(), (NPT_Size)size); return socket.Send(packet, addr); } /*---------------------------------------------------------------------- | 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_HttpHelper::SetContentLength(message, 0); return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_SsdpDeviceSearchResponseInterfaceIterator class +---------------------------------------------------------------------*/ NPT_Result PLT_SsdpDeviceSearchResponseInterfaceIterator::operator()(NPT_NetworkInterface*& net_if) const { NPT_Result res; const NPT_SocketAddress* remote_addr = &m_RemoteAddr; NPT_List<NPT_NetworkInterfaceAddress>::Iterator netaddr = net_if->GetAddresses().GetFirstItem(); if (!netaddr) { return NPT_SUCCESS; } // don't respond on loopback // windows media player on vista sends it's M-SEARCH to loopback interface if it's a local clients sometimes //if (net_if->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK) { // return NPT_SUCCESS; //} NPT_SocketAddress local_addr((*netaddr).GetPrimaryAddress(), 0); // 1900? NPT_UdpSocket socket; //if (NPT_FAILED(res = socket.Bind(local_addr))) { // return res; //} // get the output socket stream NPT_OutputStreamReference stream; if (NPT_FAILED(res = socket.GetOutputStream(stream))) { return res; } NPT_HttpResponse response(200, "OK", NPT_HTTP_PROTOCOL_1_1); // get location URL based on ip address of interface // by connecting, the kernel chooses which interface to use to route to the remote // this is the IP we should use in our Location if (NPT_FAILED(res = socket.Connect(m_RemoteAddr, 5000))) { return res; } 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 // FIXME: Should we fail instead and stop sending a response once we get NPT_SUCCESS? if (local_addr.GetIpAddress().AsLong() != info.local_address.GetIpAddress().AsLong()) { return NPT_SUCCESS; } // already connected, so we don't need to specify where to go remote_addr = NULL; } PLT_UPnPMessageHelper::SetLocation(response, m_Device->GetDescriptionUrl(local_addr.GetIpAddress().ToString())); PLT_UPnPMessageHelper::SetLeaseTime(response, (NPT_Timeout)((float)m_Device->GetLeaseTime())); PLT_UPnPMessageHelper::SetServer(response, NPT_HttpServer::m_ServerHeader, false); response.GetHeaders().SetHeader("EXT", ""); // process search response twice to be NMPR compliant NPT_CHECK_SEVERE(m_Device->SendSsdpSearchResponse(response, socket, m_ST, remote_addr)); 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), 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; NPT_HttpUrl url; NPT_UdpMulticastSocket multicast_socket; NPT_UdpSocket broadcast_socket; NPT_UdpSocket* socket; if (m_Broadcast) { //url = NPT_HttpUrl("255.255.255.255", 1900, "*"); url = NPT_HttpUrl((*niaddr).GetBroadcastAddress().ToString(), 1900, "*"); socket = &broadcast_socket; } else { url = NPT_HttpUrl("239.255.255.250", 1900, "*"); socket = &multicast_socket; NPT_CHECK_SEVERE(((NPT_UdpMulticastSocket*)socket)->SetInterface(addr)); } NPT_HttpRequest req(url, "NOTIFY", NPT_HTTP_PROTOCOL_1_1); PLT_HttpHelper::SetHost(req, "239.255.255.250:1900"); // put a location only if alive message if (m_IsByeBye == false) { PLT_UPnPMessageHelper::SetLocation(req, m_Device->GetDescriptionUrl(addr.ToString())); } NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_IsByeBye)); NPT_CHECK_SEVERE(m_Device->Announce(req, *socket, m_IsByeBye)); return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_SsdpDeviceAnnounceUnicastTask::DoRun +---------------------------------------------------------------------*/ void PLT_SsdpDeviceAnnounceTask::DoRun() { NPT_Result res = NPT_SUCCESS; NPT_List<NPT_NetworkInterface*> if_list; while (1) { NPT_CHECK_LABEL_FATAL(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list), cleanup); // if we're announcing our arrival, sends a byebye first (NMPR compliance) if (m_IsByeByeFirst == true) { m_IsByeByeFirst = false; res = if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, true, m_IsBroadcast)); if (NPT_FAILED(res)) goto cleanup; // schedule to announce alive in 300 ms if (NPT_FAILED(res) || IsAborting((NPT_Timeout)300)) break; } res = if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(m_Device, false, m_IsBroadcast)); cleanup: if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>()); if_list.Clear(); if (NPT_FAILED(res) || IsAborting(m_Repeat.m_Seconds*1000)) break; }; } /*---------------------------------------------------------------------- | PLT_SsdpListenTask::DoInit +---------------------------------------------------------------------*/ void PLT_SsdpListenTask::DoInit() { if (m_IsMulticast) { NPT_List<NPT_NetworkInterface*> if_list; NPT_CHECK_LABEL_FATAL(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list), done); /* Join multicast group for every interface we found */ if_list.ApplyUntil( PLT_SsdpInitMulticastIterator((NPT_UdpMulticastSocket*)m_Socket), NPT_UntilResultNotEquals(NPT_SUCCESS)); if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>()); } done: return; } /*---------------------------------------------------------------------- | 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 write to the socket directly // we need to write into a datagramstream (buffer) that redirects to the real stream when flushed m_Datagram = new PLT_InputDatagramStream((NPT_UdpSocket*)m_Socket); stream = m_Datagram; return NPT_SUCCESS; } } /*---------------------------------------------------------------------- | 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::ProcessRequest +---------------------------------------------------------------------*/ NPT_Result PLT_SsdpListenTask::ProcessRequest(NPT_HttpRequest& request, const NPT_HttpRequestContext& context, NPT_HttpResponse*& response, bool& headers_only) { NPT_COMPILER_UNUSED(headers_only); NPT_AutoLock lock(m_Mutex); m_Listeners.Apply(PLT_SsdpPacketListenerIterator(request, context)); // set response to NULL since we don't have anything to respond // as we use a separate task to respond with ssdp response = NULL; return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_SsdpSearchTask::PLT_SsdpSearchTask +---------------------------------------------------------------------*/ PLT_SsdpSearchTask::PLT_SsdpSearchTask(NPT_UdpSocket* socket, PLT_SsdpSearchResponseListener* listener, NPT_HttpRequest* request, NPT_Timeout timeout, bool repeat /* = true */) : m_Listener(listener), m_Request(request), m_Timeout(timeout), m_Repeat(repeat), m_Socket(socket) { m_Socket->SetReadTimeout(timeout); m_Socket->SetWriteTimeout(10000); } /*---------------------------------------------------------------------- | PLT_SsdpSearchTask::~PLT_SsdpSearchTask +---------------------------------------------------------------------*/ PLT_SsdpSearchTask::~PLT_SsdpSearchTask() { delete m_Socket; delete m_Request; } /*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::DoAbort +---------------------------------------------------------------------*/ void PLT_SsdpSearchTask::DoAbort() { m_Socket->Disconnect(); } /*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::DoRun +---------------------------------------------------------------------*/ void PLT_SsdpSearchTask::DoRun() { NPT_HttpResponse* response = NULL; PLT_HttpClient client; NPT_Timeout timeout = 30; 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(client.SendRequest( output_stream, *m_Request), done); NPT_CHECK_LABEL_SEVERE(client.SendRequest( output_stream, *m_Request), 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 = client.WaitForResponse(stream, *m_Request, context, 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); } input_stream = NULL; // check if it's time to resend request NPT_TimeStamp now; NPT_System::GetCurrentTimeStamp(now); if (now >= last_send + (long)m_Timeout/1000) break; } } while (!IsAborting(0) && m_Repeat); done: return; } /*---------------------------------------------------------------------- | PLT_CtrlPointGetDescriptionTask::ProcessResponse +---------------------------------------------------------------------*/ NPT_Result PLT_SsdpSearchTask::ProcessResponse(NPT_Result res, NPT_HttpRequest* request, const NPT_HttpRequestContext& context, NPT_HttpResponse* response) { NPT_COMPILER_UNUSED(request); return m_Listener->ProcessSsdpSearchResponse(res, context, response); }