summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp')
-rw-r--r--lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp b/lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp
new file mode 100644
index 0000000..a0643a9
--- /dev/null
+++ b/lib/libUPnP/Platinum/Source/Tools/SsdpProxy/SsdpProxy.cpp
@@ -0,0 +1,405 @@
+/*****************************************************************
+|
+| Platinum - Ssdp Proxy
+|
+| 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "SsdpProxy.h"
+#include "PltUPnPHelper.h"
+
+#if defined (WIN32)
+#include <windows.h>
+#include <conio.h>
+#else
+#include <unistd.h>
+#endif
+
+NPT_SET_LOCAL_LOGGER("platinum.tools.ssdpproxy")
+
+/*----------------------------------------------------------------------
+| globals
++---------------------------------------------------------------------*/
+static struct {
+ NPT_UInt32 port;
+} Options;
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxy::PLT_SsdpProxy
++---------------------------------------------------------------------*/
+PLT_SsdpProxy::PLT_SsdpProxy() :
+ m_UnicastListener(NULL)
+{
+
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxy::~PLT_SsdpProxy
++---------------------------------------------------------------------*/
+PLT_SsdpProxy::~PLT_SsdpProxy()
+{
+ delete m_UnicastListener;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxy::Start
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpProxy::Start(NPT_UInt32 port)
+{
+ /* create a SSDP multicast listener task */
+ NPT_Socket* multicast_socket = new NPT_UdpMulticastSocket();
+ NPT_CHECK_SEVERE(multicast_socket->Bind(NPT_SocketAddress(NPT_IpAddress::Any, 1900)));
+ PLT_SsdpListenTask* m_SsdpMulticastListenTask = new PLT_SsdpListenTask(multicast_socket, true);
+ NPT_CHECK_SEVERE(m_SsdpMulticastListenTask->AddListener(this));
+ NPT_CHECK_SEVERE(StartTask(m_SsdpMulticastListenTask));
+
+ /* create a SSDP unicast listener task */
+ /* any broadcast message sent to that, we will receive */
+ NPT_Socket* unicast_socket = new NPT_UdpSocket();
+ NPT_CHECK_SEVERE(unicast_socket->Bind(NPT_SocketAddress(NPT_IpAddress::Any, port)));
+ m_UnicastListener = new PLT_SsdpUnicastListener(this);
+ PLT_SsdpListenTask* m_SsdpUnicastListenTask = new PLT_SsdpListenTask(unicast_socket, false);
+ NPT_CHECK_SEVERE(m_SsdpUnicastListenTask->AddListener(m_UnicastListener));
+ NPT_CHECK_SEVERE(StartTask(m_SsdpUnicastListenTask));
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| CopyRequest
++---------------------------------------------------------------------*/
+static NPT_HttpRequest*
+CopyRequest(const NPT_HttpUrl& url, NPT_HttpRequest* request)
+{
+ NPT_HttpRequest* new_request = new NPT_HttpRequest(
+ url,
+ request->GetMethod(),
+ request->GetProtocol());
+ NPT_List<NPT_HttpHeader*>::Iterator headers = request->GetHeaders().GetHeaders().GetFirstItem();
+ while (headers) {
+ new_request->GetHeaders().AddHeader((*headers)->GetName(), (*headers)->GetValue());
+ ++headers;
+ }
+
+ return new_request;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxy::OnSsdpPacket
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpProxy::OnSsdpPacket(NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context)
+{
+ NPT_COMPILER_UNUSED(context);
+
+ // is it a forward message ?
+ if (request.GetHeaders().GetHeader("X_SsdpProxy"))
+ return NPT_SUCCESS;
+
+ request.GetHeaders().AddHeader("X_SsdpProxy", "forwarded");
+
+ // override MX to force a fast response
+ NPT_UInt32 MX;
+ if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetMX(request, MX))) {
+ PLT_UPnPMessageHelper::SetMX(request, 1);
+ }
+
+ // for each interface, send this request on the broadcast address
+ NPT_List<NPT_NetworkInterface*> if_list;
+ NPT_List<NPT_NetworkInterface*>::Iterator net_if;
+ NPT_List<NPT_NetworkInterfaceAddress>::Iterator net_if_addr;
+ NPT_CHECK(NPT_NetworkInterface::GetNetworkInterfaces(if_list));
+
+ for (net_if = if_list.GetFirstItem(); net_if; net_if++) {
+ if (!((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_BROADCAST) ||
+ ((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK))
+ continue;
+
+ for (net_if_addr = (*net_if)->GetAddresses().GetFirstItem(); net_if_addr; net_if_addr++) {
+ // copy request
+ NPT_HttpRequest* new_request = CopyRequest(
+ NPT_HttpUrl((*net_if_addr).GetBroadcastAddress().ToString(), 1900, request.GetUrl().GetPath()),
+ &request);
+
+ // create search task to send request in broadcast
+ // and forward unicast responses back to remote in unicast
+ PLT_SsdpProxyForwardTask* task = new PLT_SsdpProxyForwardTask(
+ new NPT_UdpSocket(),
+ new_request,
+ 10000,
+ context.GetRemoteAddress());
+ StartTask(task);
+ }
+ }
+
+ if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
+
+ // send special broadcast message for xbox
+ NPT_HttpRequest* new_request = CopyRequest(
+ NPT_HttpUrl("255.255.255.255", 1900, request.GetUrl().GetPath()),
+ &request);
+
+ // create search task to send request in broadcast
+ // and forward unicast responses back to remote in unicast
+ PLT_SsdpProxyForwardTask* task = new PLT_SsdpProxyForwardTask(
+ new NPT_UdpSocket(),
+ new_request,
+ 10000,
+ context.GetRemoteAddress());
+ StartTask(task);
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxy::OnUnicastSsdpPacket
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpProxy::OnUnicastSsdpPacket(NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context)
+{
+ // is it a forward message ?
+ if (request.GetHeaders().GetHeader("X_SsdpProxy"))
+ return NPT_SUCCESS;
+
+ request.GetHeaders().AddHeader("X_SsdpProxy", "forwarded");
+
+ // look on which interface we received it and send the ssdp search request on this only
+ NPT_List<NPT_NetworkInterface*> if_list;
+ NPT_List<NPT_NetworkInterface*>::Iterator net_if;
+ NPT_List<NPT_NetworkInterfaceAddress>::Iterator net_if_addr;
+
+ NPT_CHECK_SEVERE(NPT_NetworkInterface::GetNetworkInterfaces(if_list));
+
+ for (net_if = if_list.GetFirstItem(); net_if; net_if++) {
+ //if (!((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_MULTICAST) ||
+ // (*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK) {
+ if ((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_LOOPBACK) {
+ continue;
+ }
+
+ for (net_if_addr = (*net_if)->GetAddresses().GetFirstItem(); net_if_addr; net_if_addr++) {
+ // by using the netmask on each interface, we can figure out if the remote IP address
+ // we received the request from matches the interface we want to multicast to.
+ // it's important to do that as to avoid sending a NOTIFY or M-SEARCH packet to a host
+ // that would not be reachable from the remote which sends this packet in the first place
+ int i=0;
+ while (i<4) {
+ if ((context.GetRemoteAddress().GetIpAddress().AsBytes()[i] & (*net_if_addr).GetNetMask().AsBytes()[i]) !=
+ ((*net_if_addr).GetPrimaryAddress().AsBytes()[i] & (*net_if_addr).GetNetMask().AsBytes()[i])) {
+ break;
+ }
+ i++;
+ }
+
+ /* check that we have a match */
+ if (i != 4) {
+ continue;
+ }
+
+ // override MX to force a fast response
+ NPT_UInt32 MX;
+ if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetMX(request, MX))) {
+ PLT_UPnPMessageHelper::SetMX(request, 1);
+ }
+
+ // unicast
+ // WMC doesn't respond to multicast searches issued from the same machine
+ // a work around is to send a unicast request!
+ // copy request and change host header
+ NPT_HttpRequest* unicast_request = CopyRequest(
+ NPT_HttpUrl((*net_if_addr).GetPrimaryAddress().ToString(), 1900, request.GetUrl().GetPath()),
+ &request);
+
+ // create unicast socket
+ NPT_UdpSocket* unicast_socket = new NPT_UdpSocket();
+ // create search task to send request in unicast
+ // and forward responses back to remote
+ PLT_SsdpProxyForwardTask* uncast_task = new PLT_SsdpProxyForwardTask(
+ unicast_socket,
+ unicast_request,
+ 10000,
+ context.GetRemoteAddress());
+ StartTask(uncast_task);
+
+ // multicast
+ // simply redirect request to 239.255.255.250
+ NPT_HttpRequest* new_request = CopyRequest(
+ NPT_HttpUrl("239.255.255.250", 1900, request.GetUrl().GetPath()),
+ &request);
+
+ // create multicast socket
+ NPT_UdpMulticastSocket* socket = new NPT_UdpMulticastSocket();
+ socket->SetInterface((*net_if_addr).GetPrimaryAddress());
+ socket->SetTimeToLive(4);
+
+ // create search task to send request in multicast
+ // and forward responses back to remote
+ PLT_SsdpProxyForwardTask* task = new PLT_SsdpProxyForwardTask(
+ socket,
+ new_request,
+ 10000,
+ context.GetRemoteAddress());
+ StartTask(task);
+ }
+ }
+
+ if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpUnicastListener::OnSsdpPacket
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpUnicastListener::OnSsdpPacket(NPT_HttpRequest& request,
+ const NPT_HttpRequestContext& context)
+{
+ return m_Proxy->OnUnicastSsdpPacket(request, context);
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxyForwardTask::PLT_SsdpProxyForwardTask
++---------------------------------------------------------------------*/
+PLT_SsdpProxyForwardTask::PLT_SsdpProxyForwardTask(NPT_UdpSocket* socket,
+ NPT_HttpRequest* request,
+ NPT_Timeout timeout,
+ const NPT_SocketAddress& forward_address) :
+ PLT_SsdpSearchTask(socket, NULL, request, timeout, false),
+ m_ForwardAddress(forward_address)
+{
+}
+
+/*----------------------------------------------------------------------
+| PLT_SsdpProxyForwardTask::ProcessResponse
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_SsdpProxyForwardTask::ProcessResponse(NPT_Result res,
+ NPT_HttpRequest* request,
+ const NPT_HttpRequestContext& context,
+ NPT_HttpResponse* response)
+{
+ NPT_COMPILER_UNUSED(context);
+ NPT_COMPILER_UNUSED(request);
+
+ if (NPT_FAILED(res) || response == NULL) return NPT_FAILURE;
+
+ // use a memory stream to write the response
+ NPT_MemoryStream stream;
+ NPT_CHECK_SEVERE(response->Emit(stream));
+
+ // copy stream into a data packet and forward 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);
+ NPT_UdpSocket socket;
+ return socket.Send(packet, &m_ForwardAddress);
+}
+
+/*----------------------------------------------------------------------
+| PrintUsageAndExit
++---------------------------------------------------------------------*/
+static void
+PrintUsageAndExit(char** args)
+{
+ fprintf(stderr, "usage: %s [-p <port>] \n", args[0]);
+ fprintf(stderr, "-p : optional upnp unicast ssdp port (default: 1901)\n");
+ exit(1);
+}
+
+/*----------------------------------------------------------------------
+| ParseCommandLine
++---------------------------------------------------------------------*/
+static void
+ParseCommandLine(char** args)
+{
+ const char* arg;
+ char** tmp = args+1;
+
+ /* default values */
+ Options.port = 1900;
+
+ while ((arg = *tmp++)) {
+ if (!strcmp(arg, "-p")) {
+ if (NPT_FAILED(NPT_ParseInteger32(*tmp++, Options.port, false))) {
+ fprintf(stderr, "ERROR: invalid argument\n");
+ PrintUsageAndExit(args);
+ }
+ } else {
+ fprintf(stderr, "ERROR: invalid arguments\n");
+ PrintUsageAndExit(args);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
+| main
++---------------------------------------------------------------------*/
+int
+main(int argc, char** argv)
+{
+ NPT_COMPILER_UNUSED(argc);
+
+ //PLT_SetLogLevel(4);
+ PLT_SsdpProxy proxy;
+
+ /* parse command line */
+ ParseCommandLine(argv);
+
+ NPT_Result res = proxy.Start(Options.port);
+ if (res == NPT_ERROR_BIND_FAILED) {
+ fprintf(stderr, "ERROR: couldn't bind to port %d\n", Options.port);
+ return -1;
+ } else if (NPT_FAILED(res)) {
+ fprintf(stderr, "ERROR: unknown (%d)\n", res);
+ return -1;
+ }
+
+ fprintf(stdout, "Listening for SSDP unicast packets on port %d\n", Options.port);
+ fprintf(stdout, "Enter q to quit\n");
+
+ char buf[256];
+ while (gets(buf)) {
+ if (*buf == 'q')
+ break;
+ }
+
+ return 0;
+}