MythTV master
upnptasknotify.cpp
Go to the documentation of this file.
1
2// Program Name: upnptasknotify.cpp
3// Created : Oct. 24, 2005
4//
5// Purpose : UPnp Task to send Notification messages
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 "upnptasknotify.h"
13
14#include <chrono> // for milliseconds
15#include <thread> // for sleep_for
16
17// Qt headers
18#include <QByteArray>
19#include <QHostAddress>
20#include <QList>
21#include <QString>
22
23// MythTV headers
27
28#include "httpserver.h"
29#include "ssdp.h"
30#include "upnp.h"
31
33 Task("UPnpNotifyTask"),
34 m_nServicePort(nServicePort),
35 m_nMaxAge(XmlConfiguration().GetDuration<std::chrono::seconds>("UPnP/SSDP/MaxAge", 1h))
36{
37}
38
39void UPnpNotifyTask::SendNotifyMsg(QUdpSocket& socket, const QString& sNT, const QString& sUDN)
40{
41 QString uniqueServiceName = sNT;
42 if (sUDN.length() > 0)
43 uniqueServiceName = sUDN + "::" + uniqueServiceName;
44
45 QByteArray data = QString("Server: %1\r\n"
46 "NTS: %3\r\n"
47 "NT: %4\r\n"
48 "USN: %5\r\n"
49 "CACHE-CONTROL: max-age=%6\r\n"
50 "Content-Length: 0\r\n\r\n")
53 sNT,
54 uniqueServiceName,
55 QString::number(m_nMaxAge.count())
56 ).toUtf8();
57
58 LOG(VB_UPNP, LOG_INFO,
59 QString("UPnpNotifyTask::SendNotifyMsg : %1:%2 : %3 : %4")
60 .arg(SSDP_GROUP, QString::number(SSDP_PORT),
61 sNT, uniqueServiceName
62 )
63 );
64
65 QList<QHostAddress> addressList = UPnp::g_IPAddrList;
66 for (const auto & addr : std::as_const(addressList))
67 {
68 if (addr.toString().isEmpty())
69 {
70 LOG(VB_GENERAL, LOG_ERR,
71 "UPnpNotifyTask::SendNotifyMsg - NULL in address list");
72 continue;
73 }
74 if (addr.isLoopback())
75 {
76 continue; // LOCATION will be invalid for any peer not on this host
77 }
78
79 QHostAddress ip = addr;
80 // Descope the Link Local address. The scope is only valid
81 // on the server sending the announcement, not the clients
82 // that receive it
83 ip.setScopeId(QString());
84
85 QString ipaddress = ip.toString();
86
87 // If this looks like an IPv6 address, then enclose it in []'s
88 if (ipaddress.contains(":"))
89 ipaddress = "[" + ipaddress + "]";
90
91 QByteArray datagram =
92 QString("NOTIFY * HTTP/1.1\r\n"
93 "HOST: %1:%2\r\n"
94 "LOCATION: http://%3:%4/getDeviceDesc\r\n")
95 .arg(SSDP_GROUP, QString::number(SSDP_PORT),
96 ipaddress, QString::number(m_nServicePort)
97 ).toUtf8()
98 + data;
99
100 LOG(VB_UPNP, LOG_DEBUG, QString("Sending SSDP notify datagram\n%1")
101 .arg(QString::fromUtf8(datagram))
102 );
103
104 // Send Packet to Socket (Send same packet twice)
105 socket.writeDatagram(datagram, QHostAddress(QString(SSDP_GROUP)), SSDP_PORT);
106 if (m_eNTS != NTS_byebye)
107 {
108 std::this_thread::sleep_for(std::chrono::milliseconds(MythRandom(0, 250)));
109
110 socket.writeDatagram(datagram, QHostAddress(QString(SSDP_GROUP)), SSDP_PORT);
111 }
112 }
113}
114
116{
117 QUdpSocket socket;
118 socket.bind(QHostAddress(QHostAddress::AnyIPv4), 0); // required for setSocketOption()
119 socket.setSocketOption(QAbstractSocket::MulticastTtlOption, 4);
120
121 // Must send rootdevice Notification for first device.
122 SendNotifyMsg(socket, "upnp:rootdevice", UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
123 // Process rest of notifications
124 ProcessDevice(socket, UPnp::g_UPnpDeviceDesc.m_rootDevice);
125
126 // reshedule task if needed (timeout = m_nMaxAge / 2).
127 m_mutex.lock();
128 if (m_eNTS == NTS_alive)
129 pQueue->AddTask( (m_nMaxAge / 2), (Task *)this );
130
131 m_mutex.unlock();
132}
133
134void UPnpNotifyTask::ProcessDevice(QUdpSocket& socket, const UPnpDevice& device)
135{
136 // Loop for each device and send the 2 required messages
137 // -=>TODO: Need to add support to only notify
138 // Version 1 of a service.
139 SendNotifyMsg(socket, device.GetUDN(), "");
140 SendNotifyMsg(socket, device.m_sDeviceType, device.GetUDN());
141 // Loop for each service in this device and send the 1 required message
142 for (const auto* service : std::as_const(device.m_listServices))
143 {
144 SendNotifyMsg(socket, service->m_sServiceType, device.GetUDN());
145 }
146
147 // Process any Embedded Devices
148 for (const auto* embedded_device : std::as_const(device.m_listDevices))
149 {
150 ProcessDevice(socket, *embedded_device);
151 }
152}
static QString GetServerVersion(void)
Definition: httpserver.cpp:280
void AddTask(std::chrono::milliseconds msec, Task *pTask)
Add a task to run in the future.
Definition: taskqueue.cpp:170
Definition: taskqueue.h:48
QString GetUDN(void) const
Definition: upnpdevice.cpp:784
QString m_sDeviceType
Definition: upnpdevice.h:109
UPnpServiceList m_listServices
Definition: upnpdevice.h:129
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:130
QString GetNTSString()
std::chrono::seconds m_nMaxAge
UPnpNotifyTask(int nServicePort)
void ProcessDevice(QUdpSocket &socket, const UPnpDevice &device)
void SendNotifyMsg(QUdpSocket &socket, const QString &sNT, const QString &sUDN)
void Execute(TaskQueue *pQueue) override
UPnpNotifyNTS m_eNTS
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:49
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:48
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
static constexpr uint16_t SSDP_PORT
Definition: ssdp.h:29
static constexpr const char * SSDP_GROUP
Definition: ssdp.h:28
@ NTS_alive
@ NTS_byebye