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