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 // Send Packet to UDP Socket (Send same packet twice)
110 socket.writeDatagram(datagram, m_peerAddress, m_nPeerPort);
111
112 std::this_thread::sleep_for(std::chrono::milliseconds(MythRandom(0, 250)));
113
114 socket.writeDatagram(datagram, m_peerAddress, m_nPeerPort);
115 }
116 }
117 }
118}
119
121{
122 QUdpSocket socket;
123
124 // Check to see if this is a rootdevice or all request.
125 if ((m_sST == "upnp:rootdevice") || (m_sST == "ssdp:all" ))
126 {
127 SendMsg(socket, "upnp:rootdevice", UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
128
129 if (m_sST == "ssdp:all")
130 ProcessDevice(socket, UPnp::g_UPnpDeviceDesc.m_rootDevice);
131 }
132 else
133 {
134 // Send Device/Service specific response.
135 SendMsg(socket, m_sST, m_sUDN);
136 }
137}
138
139void UPnpSearchTask::ProcessDevice(QUdpSocket& socket, const UPnpDevice& device)
140{
141 // Loop for each device and send the 2 required messages
142 // -=>TODO: We need to add support to only notify
143 // Version 1 of a service.
144 SendMsg(socket, device.GetUDN(), "");
145 SendMsg(socket, device.m_sDeviceType, device.GetUDN());
146 // Loop for each service in this device and send the 1 required message
147 for (const auto* service : std::as_const(device.m_listServices))
148 {
149 SendMsg(socket, service->m_sServiceType, device.GetUDN());
150 }
151
152 // Process any Embedded Devices
153 for (const auto* embedded_device : std::as_const(device.m_listDevices))
154 {
155 ProcessDevice(socket, *embedded_device);
156 }
157}
static QString GetServerVersion(void)
Definition: httpserver.cpp:276
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.