MythTV master
upnptasksearch.cpp
Go to the documentation of this file.
1
2// Program Name: upnptasksearch.cpp
3// Created : Oct. 24, 2005
4//
5// Purpose : UPnp Task to handle Discovery Responses
6//
7// Copyright (c) 2005 David Blain <dblain@mythtv.org>
8//
9// Licensed under the GPL v2 or later, see LICENSE for details
10//
12#include "upnptasksearch.h"
13
14#include <chrono> // for milliseconds
15using namespace std::chrono_literals;
16#include <thread> // for sleep_for
17#include <utility>
18
19#include <QByteArray>
20#include <QNetworkAddressEntry>
21#include <QNetworkInterface>
22#include <QPair>
23#include <QString>
24#include <QUdpSocket>
25
30
32#include "libmythupnp/upnp.h"
33
35 const QHostAddress& peerAddress,
36 int nPeerPort,
37 QString sST,
38 QString sUDN ) :
39 Task("UPnpSearchTask"),
40 m_nServicePort(nServicePort),
41 m_nMaxAge(XmlConfiguration().GetDuration<std::chrono::seconds>("UPnP/SSDP/MaxAge" , 1h)),
42 m_peerAddress(peerAddress),
43 m_nPeerPort(nPeerPort),
44 m_sST(std::move(sST)),
45 m_sUDN(std::move(sUDN))
46{
47}
48
49void UPnpSearchTask::SendMsg(QUdpSocket& socket, const QString& sST, const QString& sUDN)
50{
51 QString uniqueServiceName = sST;
52 if (( sUDN.length() > 0) && ( sUDN != sST ))
53 uniqueServiceName = sUDN + "::" + uniqueServiceName;
54
55 QByteArray data = QString("CACHE-CONTROL: max-age=%1\r\n"
56 "DATE: %2\r\n"
57 "EXT:\r\n"
58 "Server: %3\r\n"
59 "ST: %4\r\n"
60 "USN: %5\r\n"
61 "Content-Length: 0\r\n\r\n")
62 .arg(QString::number(m_nMaxAge.count()),
63 MythDate::current().toString("d MMM yyyy hh:mm:ss"),
65 sST,
66 uniqueServiceName
67 ).toUtf8();
68
69 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpSearchTask::SendMsg ST: %1 USN: %2; m_peerAddress = %3 Port=%4")
70 .arg(sST, uniqueServiceName, m_peerAddress.toString(), QString::number(m_nPeerPort))
71 );
72
73 // TODO: When we add dynamic handling of interfaces the information
74 // for each address on the system should be available from the
75 // "central" location on request.
76 //
77 // loop through all available interfaces
78 QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
79 for (const auto & qni : std::as_const(IFs))
80 {
81 QList<QNetworkAddressEntry> netAddressList = qni.addressEntries();
82 for (const auto & netAddr : std::as_const(netAddressList))
83 {
84 QString ip_subnet = QString("%1/%2").arg(netAddr.ip().toString()).arg(netAddr.prefixLength());
85 QPair<QHostAddress, int> subnet = QHostAddress::parseSubnet(ip_subnet);
86 if (m_peerAddress.isInSubnet(subnet)) {
87 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpSearchTask::SendMsg : IP: [%1], Found network [%2], relevant to peer [%3]")
88 .arg(netAddr.ip().toString(), subnet.first.toString(), m_peerAddress.toString()));
89
90 QString ipaddress;
91 QHostAddress ip = netAddr.ip();
92 // Descope the Link Local address. The scope is only valid
93 // on the server sending the announcement, not the clients
94 // that receive it
95 ip.setScopeId(QString());
96
97 // If this looks like an IPv6 address, then enclose it in []'s
98 if (ip.protocol() == QAbstractSocket::IPv6Protocol)
99 ipaddress = "[" + ip.toString() + "]";
100 else
101 ipaddress = ip.toString();
102
103 QByteArray datagram =
104 QString("HTTP/1.1 200 OK\r\n"
105 "LOCATION: http://%1:%2/getDeviceDesc\r\n")
106 .arg(ipaddress, QString::number(m_nServicePort)).toUtf8()
107 + data;
108
109 LOG(VB_UPNP, LOG_DEBUG,
110 QString("Sending SSDP search reply datagram to %1:%2\n%3")
111 .arg(m_peerAddress.toString(), QString::number(m_nPeerPort),
112 QString::fromUtf8(datagram))
113 );
114 // Send Packet to UDP Socket (Send same packet twice)
115 socket.writeDatagram(datagram, m_peerAddress, m_nPeerPort);
116
117 std::this_thread::sleep_for(std::chrono::milliseconds(MythRandom(0, 250)));
118
119 socket.writeDatagram(datagram, m_peerAddress, m_nPeerPort);
120 }
121 }
122 }
123}
124
126{
127 QUdpSocket socket;
128
129 // Check to see if this is a rootdevice or all request.
130 if ((m_sST == "upnp:rootdevice") || (m_sST == "ssdp:all" ))
131 {
132 SendMsg(socket, "upnp:rootdevice", UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
133
134 if (m_sST == "ssdp:all")
135 ProcessDevice(socket, UPnp::g_UPnpDeviceDesc.m_rootDevice);
136 }
137 else
138 {
139 // Send Device/Service specific response.
140 SendMsg(socket, m_sST, m_sUDN);
141 }
142}
143
144void UPnpSearchTask::ProcessDevice(QUdpSocket& socket, const UPnpDevice& device)
145{
146 // Loop for each device and send the 2 required messages
147 // -=>TODO: We need to add support to only notify
148 // Version 1 of a service.
149 SendMsg(socket, device.GetUDN(), "");
150 SendMsg(socket, device.m_sDeviceType, device.GetUDN());
151 // Loop for each service in this device and send the 1 required message
152 for (const auto* service : std::as_const(device.m_listServices))
153 {
154 SendMsg(socket, service->m_sServiceType, device.GetUDN());
155 }
156
157 // Process any Embedded Devices
158 for (const auto* embedded_device : std::as_const(device.m_listDevices))
159 {
160 ProcessDevice(socket, *embedded_device);
161 }
162}
static QString GetServerVersion(void)
Definition: httpserver.cpp:280
Definition: taskqueue.h:48
QString GetUDN(void) const
Definition: upnpdevice.cpp:780
QString m_sDeviceType
Definition: upnpdevice.h:109
UPnpServiceList m_listServices
Definition: upnpdevice.h:129
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:130
UPnpSearchTask(int nServicePort, const QHostAddress &peerAddress, int nPeerPort, QString sST, QString sUDN)
std::chrono::seconds m_nMaxAge
void Execute(TaskQueue *pQueue) override
void SendMsg(QUdpSocket &socket, const QString &sST, const QString &sUDN)
QHostAddress m_peerAddress
void ProcessDevice(QUdpSocket &socket, const UPnpDevice &device)
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:107
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
STL namespace.