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