MythTV  master
upnptasksearch.cpp
Go to the documentation of this file.
1 // 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 
13 #include <chrono> // for milliseconds
14 #include <cstdlib>
15 #include <thread> // for sleep_for
16 
17 #include <QDateTime>
18 #include <QFile>
19 #include <QStringList>
20 #include <QNetworkInterface>
21 #include <utility>
22 
23 #include "libmythbase/compat.h"
25 #include "libmythbase/mythdate.h"
27 #include "libmythbase/mythrandom.h"
28 #include "libmythbase/mythversion.h"
29 
30 #include "upnp.h"
31 #include "upnptasksearch.h"
32 
33 static QPair<QHostAddress, int> kLinkLocal6 =
34  QHostAddress::parseSubnet("fe80::/10");
35 
38 //
39 // UPnpSearchTask Implementation
40 //
43 
45 //
47 
48 UPnpSearchTask::UPnpSearchTask( int nServicePort,
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 
67 void 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 
161 void UPnpSearchTask::Execute( TaskQueue * /*pQueue*/ )
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 
kLinkLocal6
static QPair< QHostAddress, int > kLinkLocal6
Definition: upnptasksearch.cpp:33
UPnpSearchTask::m_nServicePort
int m_nServicePort
Definition: upnptasksearch.h:46
HttpServer::GetServerVersion
static QString GetServerVersion(void)
Definition: httpserver.cpp:288
mythrandom.h
UPnpDevice::m_listServices
UPnpServiceList m_listServices
Definition: upnpdevice.h:126
UPnpSearchTask::SendMsg
void SendMsg(MSocketDevice *pSocket, const QString &sST, const QString &sUDN)
Definition: upnptasksearch.cpp:67
UPnpSearchTask::m_peerAddress
QHostAddress m_peerAddress
Definition: upnptasksearch.h:49
UPnpSearchTask::m_sST
QString m_sST
Definition: upnptasksearch.h:51
upnptasksearch.h
UPnpDevice::m_sDeviceType
QString m_sDeviceType
Definition: upnpdevice.h:106
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
XmlConfiguration::GetDuration
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDuration(const QString &setting, T defaultValue=T::zero())
Definition: configuration.h:114
UPnpSearchTask::Execute
void Execute(TaskQueue *pQueue) override
Definition: upnptasksearch.cpp:161
UPnpSearchTask::UPnpSearchTask
UPnpSearchTask(int nServicePort, const QHostAddress &peerAddress, int nPeerPort, QString sST, QString sUDN)
Definition: upnptasksearch.cpp:48
TaskQueue
Definition: taskqueue.h:82
UPnpSearchTask::m_nMaxAge
std::chrono::seconds m_nMaxAge
Definition: upnptasksearch.h:47
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
XmlConfiguration
Definition: configuration.h:38
UPnpSearchTask::m_nPeerPort
int m_nPeerPort
Definition: upnptasksearch.h:50
UPnpDeviceDesc::m_rootDevice
UPnpDevice m_rootDevice
Definition: upnpdevice.h:155
mythdate.h
upnp.h
mythlogging.h
compat.h
Task
Definition: taskqueue.h:54
UPnpDevice::m_listDevices
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:127
UPnpSearchTask::ProcessDevice
void ProcessDevice(MSocketDevice *pSocket, UPnpDevice *pDevice)
Definition: upnptasksearch.cpp:201
UPnpDevice
Definition: upnpdevice.h:102
UPnpSearchTask::m_addressList
QList< QHostAddress > m_addressList
Definition: upnptasksearch.h:45
configuration.h
UPnpSearchTask::m_sUDN
QString m_sUDN
Definition: upnptasksearch.h:52
UPnpDevice::GetUDN
QString GetUDN(void) const
Definition: upnpdevice.cpp:783
UPnp::g_UPnpDeviceDesc
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:109
UPnp::g_IPAddrList
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:110
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20