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
15#include <cstdlib>
16#include <thread> // for sleep_for
17#include <utility>
18
19#include <QDateTime>
20#include <QFile>
21#include <QStringList>
22#include <QNetworkInterface>
23
28#include "libmythbase/mythversion.h"
29
31#include "libmythupnp/upnp.h"
32
33static QPair<QHostAddress, int> kLinkLocal6 =
34 QHostAddress::parseSubnet("fe80::/10");
35
38//
39// UPnpSearchTask Implementation
40//
43
45//
47
49 const QHostAddress& peerAddress,
50 int nPeerPort,
51 QString sST,
52 QString sUDN ) :
53 Task("UPnpSearchTask"),
54 m_nServicePort(nServicePort),
55 m_peerAddress(peerAddress),
56 m_nPeerPort(nPeerPort),
57 m_sST(std::move(sST)),
58 m_sUDN(std::move(sUDN))
59{
60 m_nMaxAge = XmlConfiguration().GetDuration<std::chrono::seconds>("UPnP/SSDP/MaxAge" , 1h);
61}
62
64//
66
67void UPnpSearchTask::SendMsg( MSocketDevice *pSocket,
68 const QString& sST,
69 const QString& sUDN )
70{
71 QString sUSN;
72
73 if (( sUDN.length() > 0) && ( sUDN != sST ))
74 sUSN = sUDN + "::" + sST;
75 else
76 sUSN = sST;
77
78 QString sDate = MythDate::current().toString( "d MMM yyyy hh:mm:ss" );
79
80 QString sData = QString ( "CACHE-CONTROL: max-age=%1\r\n"
81 "DATE: %2\r\n"
82 "EXT:\r\n"
83 "Server: %3\r\n"
84 "ST: %4\r\n"
85 "USN: %5\r\n"
86 "Content-Length: 0\r\n\r\n" )
87 .arg( m_nMaxAge.count() )
88 .arg( sDate,
90 sST,
91 sUSN );
92
93#if 0
94 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpSearchTask::SendMsg : %1 : %2 ")
95 .arg(sST) .arg(sUSN));
96
97 LOG(VB_UPNP, LOG_DEBUG,
98 QString("UPnpSearchTask::SendMsg m_peerAddress = %1 Port=%2")
99 .arg(m_peerAddress.toString()) .arg(m_nPeerPort));
100#endif
101
102 // TODO: When we add dynamic handling of interfaces the information
103 // for each address on the system should be available from the
104 // "central" location on request.
105 //
106 // loop through all available interfaces
107 QList<QNetworkInterface> IFs = QNetworkInterface::allInterfaces();
108 for (const auto & qni : std::as_const(IFs))
109 {
110 QList<QNetworkAddressEntry> netAddressList = qni.addressEntries();
111 for (const auto & netAddr : std::as_const(netAddressList))
112 {
113 QString ip_subnet = QString("%1/%2").arg(netAddr.ip().toString()).arg(netAddr.prefixLength());
114 QPair<QHostAddress, int> subnet = QHostAddress::parseSubnet(ip_subnet);
115 if (m_peerAddress.isInSubnet(subnet)) {
116 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpSearchTask::SendMsg : IP: [%1], Found network [%2], relevant to peer [%3]")
117 .arg(netAddr.ip().toString(), subnet.first.toString(), m_peerAddress.toString()));
118
119 QString ipaddress;
120 QHostAddress ip = netAddr.ip();
121 // Descope the Link Local address. The scope is only valid
122 // on the server sending the announcement, not the clients
123 // that receive it
124 ip.setScopeId(QString());
125
126 // If this looks like an IPv6 address, then enclose it in []'s
127 if (ip.protocol() == QAbstractSocket::IPv6Protocol)
128 ipaddress = "[" + ip.toString() + "]";
129 else
130 ipaddress = ip.toString();
131
132 QString sHeader = QString ( "HTTP/1.1 200 OK\r\n"
133 "LOCATION: http://%1:%2/getDeviceDesc\r\n" )
134 .arg( ipaddress )
135 .arg( m_nServicePort);
136
137
138 QString sPacket = sHeader + sData;
139 QByteArray scPacket = sPacket.toUtf8();
140
141 // ------------------------------------------------------------------
142 // Send Packet to UDP Socket (Send same packet twice)
143 // ------------------------------------------------------------------
144
145 pSocket->writeBlock( scPacket, scPacket.length(), m_peerAddress,
146 m_nPeerPort );
147
148 std::this_thread::sleep_for(std::chrono::milliseconds(MythRandom(0, 250)));
149
150 pSocket->writeBlock( scPacket, scPacket.length(), m_peerAddress,
151 m_nPeerPort );
152 }
153 }
154 }
155}
156
158//
160
162{
163 auto *pSocket = new MSocketDevice( MSocketDevice::Datagram );
164
165 // ----------------------------------------------------------------------
166 // Refresh IP Address List in case of changes
167 // ----------------------------------------------------------------------
168
170
171 // ----------------------------------------------------------------------
172 // Check to see if this is a rootdevice or all request.
173 // ----------------------------------------------------------------------
174
176
177 if ((m_sST == "upnp:rootdevice") || (m_sST == "ssdp:all" ))
178 {
179 SendMsg( pSocket, "upnp:rootdevice", device.GetUDN() );
180
181 if (m_sST == "ssdp:all")
182 ProcessDevice( pSocket, &device );
183 }
184 else
185 {
186 // ------------------------------------------------------------------
187 // Send Device/Service specific response.
188 // ------------------------------------------------------------------
189
190 SendMsg( pSocket, m_sST, m_sUDN );
191 }
192
193 delete pSocket;
194 pSocket = nullptr;
195}
196
198//
200
202 MSocketDevice *pSocket, UPnpDevice *pDevice)
203{
204 // ----------------------------------------------------------------------
205 // Loop for each device and send the 2 required messages
206 //
207 // -=>TODO: We need to add support to only notify
208 // Version 1 of a service.
209 // ----------------------------------------------------------------------
210
211 SendMsg( pSocket, pDevice->GetUDN(), "" );
212 SendMsg( pSocket, pDevice->m_sDeviceType, pDevice->GetUDN() );
213
214 // ------------------------------------------------------------------
215 // Loop for each service in this device and send the 1 required message
216 // ------------------------------------------------------------------
217
218 for (auto sit = pDevice->m_listServices.cbegin();
219 sit != pDevice->m_listServices.cend(); ++sit)
220 SendMsg(pSocket, (*sit)->m_sServiceType, pDevice->GetUDN());
221
222 // ----------------------------------------------------------------------
223 // Process any Embedded Devices
224 // ----------------------------------------------------------------------
225
226 for (auto *device : std::as_const(pDevice->m_listDevices))
227 ProcessDevice( pSocket, device);
228}
229
static QString GetServerVersion(void)
Definition: httpserver.cpp:276
Definition: taskqueue.h:48
UPnpDevice m_rootDevice
Definition: upnpdevice.h:158
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)
void SendMsg(MSocketDevice *pSocket, const QString &sST, const QString &sUDN)
std::chrono::seconds m_nMaxAge
QList< QHostAddress > m_addressList
void Execute(TaskQueue *pQueue) override
QHostAddress m_peerAddress
void ProcessDevice(MSocketDevice *pSocket, UPnpDevice *pDevice)
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:108
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:107
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDuration(const QString &setting, T defaultValue=T::zero())
#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.
static QPair< QHostAddress, int > kLinkLocal6