File indexing completed on 2024-12-01 07:25:01

0001 // SPDX-License-Identifier: GPL-3.0-or-later
0002 // SPDX-FileCopyrightText: 2019 Casper Meijn <casper@meijn.net>
0003 // SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004 
0005 #include "wsdiscoveryclient.h"
0006 
0007 #include "loggingcategory.h"
0008 #include "wsdiscoverytargetservice.h"
0009 
0010 #include <KDSoapClient/KDSoapMessage>
0011 #include <KDSoapClient/KDSoapMessageAddressingProperties>
0012 #include <KDSoapClient/KDSoapUdpClient>
0013 #include <QHostAddress>
0014 #include <QTimer>
0015 #include <QUuid>
0016 
0017 #include "wsdl_ws-discovery.h"
0018 #include "wsdl_wsdd-discovery-1.h"
0019 
0020 static const int DISCOVERY_PORT = 3702;
0021 #define DISCOVERY_ADDRESS_IPV4 (QHostAddress(QStringLiteral("239.255.255.250")))
0022 #define DISCOVERY_ADDRESS_IPV6 (QHostAddress(QStringLiteral("FF02::C")))
0023 
0024 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (KDSOAP_VERSION <= KDSOAP_VERSION_CHECK(2, 1, 1))
0025 QDebug operator<<(QDebug dbg, const KDQName &qn)
0026 {
0027     if (!qn.nameSpace().isEmpty())
0028         dbg << "(" << qn.nameSpace() << "," << qn.localName() << ")";
0029     else
0030         dbg << qn.qname();
0031     return dbg;
0032 }
0033 #endif
0034 
0035 WSDiscoveryClient::WSDiscoveryClient(QObject *parent)
0036     : QObject(parent)
0037 {
0038     m_soapUdpClient = new KDSoapUdpClient(this);
0039     connect(m_soapUdpClient, &KDSoapUdpClient::receivedMessage, this, &WSDiscoveryClient::receivedMessage);
0040 }
0041 
0042 WSDiscoveryClient::~WSDiscoveryClient() = default;
0043 
0044 void WSDiscoveryClient::start(quint16 port)
0045 {
0046     bool rc = m_soapUdpClient->bind(port, QAbstractSocket::DefaultForPlatform);
0047     Q_ASSERT(rc);
0048 }
0049 
0050 void WSDiscoveryClient::sendProbe(const QList<KDQName> &typeList, const QList<QUrl> &scopeList)
0051 {
0052     WSDiscovery200504::TNS__ProbeType probe;
0053     if (!typeList.isEmpty()) {
0054         WSDiscovery200504::TNS__QNameListType types;
0055         types.setEntries(typeList);
0056         probe.setTypes(types);
0057     }
0058     if (!scopeList.isEmpty()) {
0059         WSDiscovery200504::TNS__UriListType scopeValues;
0060         scopeValues.setEntries(QUrl::toStringList(scopeList));
0061         WSDiscovery200504::TNS__ScopesType scopes;
0062         scopes.setValue(scopeValues);
0063         probe.setScopes(scopes);
0064     }
0065 
0066     KDSoapMessage message;
0067     message = probe.serialize(QStringLiteral("Probe"));
0068     message.setUse(KDSoapMessage::LiteralUse);
0069     message.setNamespaceUri(QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery"));
0070 
0071     KDSoapMessageAddressingProperties addressing;
0072     addressing.setAddressingNamespace(KDSoapMessageAddressingProperties::Addressing200408);
0073     addressing.setAction(QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe"));
0074 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
0075     addressing.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
0076 #else
0077     addressing.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString().remove("{").remove("}"));
0078 #endif
0079     addressing.setDestination(QStringLiteral("urn:schemas-xmlsoap-org:ws:2005:04:discovery"));
0080     addressing.setReplyEndpointAddress(KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessageAddressingProperties::Anonymous));
0081     message.setMessageAddressingProperties(addressing);
0082 
0083     auto rc = m_soapUdpClient->sendMessage(message, KDSoapHeaders(), DISCOVERY_ADDRESS_IPV4, DISCOVERY_PORT);
0084     Q_ASSERT(rc);
0085     rc = m_soapUdpClient->sendMessage(message, KDSoapHeaders(), DISCOVERY_ADDRESS_IPV6, DISCOVERY_PORT);
0086     Q_ASSERT(rc);
0087 }
0088 
0089 void WSDiscoveryClient::sendResolve(const QString &endpointReferenceString)
0090 {
0091     WSDiscovery200504::TNS__ResolveType resolve;
0092 
0093     WSDiscovery200504::WSA__AttributedURI endpointReferenceAddress;
0094     endpointReferenceAddress.setValue(endpointReferenceString);
0095     WSDiscovery200504::WSA__EndpointReferenceType endpointReference;
0096     endpointReference.setAddress(endpointReferenceAddress);
0097     resolve.setEndpointReference(endpointReference);
0098 
0099     KDSoapMessage message;
0100     message = resolve.serialize(QStringLiteral("Resolve"));
0101     message.setUse(KDSoapMessage::LiteralUse);
0102     message.setNamespaceUri(QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery"));
0103 
0104     KDSoapMessageAddressingProperties addressing;
0105     addressing.setAddressingNamespace(KDSoapMessageAddressingProperties::Addressing200408);
0106     addressing.setAction(QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/Resolve"));
0107 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
0108     addressing.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
0109 #else
0110     addressing.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString().remove("{").remove("}"));
0111 #endif
0112     addressing.setDestination(QStringLiteral("urn:schemas-xmlsoap-org:ws:2005:04:discovery"));
0113     addressing.setReplyEndpointAddress(KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessageAddressingProperties::Anonymous));
0114     message.setMessageAddressingProperties(addressing);
0115 
0116     auto rc = m_soapUdpClient->sendMessage(message, KDSoapHeaders(), DISCOVERY_ADDRESS_IPV4, DISCOVERY_PORT);
0117     Q_ASSERT(rc);
0118     rc = m_soapUdpClient->sendMessage(message, KDSoapHeaders(), DISCOVERY_ADDRESS_IPV6, DISCOVERY_PORT);
0119     Q_ASSERT(rc);
0120 }
0121 
0122 void WSDiscoveryClient::receivedMessage(const KDSoapMessage &replyMessage,
0123                                         const KDSoapHeaders &replyHeaders,
0124                                         const QHostAddress &senderAddress,
0125                                         quint16 senderPort)
0126 {
0127     Q_UNUSED(replyHeaders);
0128     Q_UNUSED(senderAddress);
0129     Q_UNUSED(senderPort);
0130     if (replyMessage.messageAddressingProperties().action() == QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe")) {
0131         // NO-OP
0132     } else if (replyMessage.messageAddressingProperties().action() == QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/Resolve")) {
0133         // NO-OP
0134     } else if (replyMessage.messageAddressingProperties().action() == QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/ResolveMatches")) {
0135         WSDiscovery200504::TNS__ResolveMatchesType resolveMatches;
0136         resolveMatches.deserialize(replyMessage);
0137 
0138         auto resolveMatch = resolveMatches.resolveMatch();
0139         const QString &endpointReference = resolveMatch.endpointReference().address();
0140         auto service = WSDiscoveryTargetService(endpointReference);
0141         service.setTypeList(resolveMatch.types().entries());
0142         service.setScopeList(QUrl::fromStringList(resolveMatch.scopes().value().entries()));
0143         service.setXAddrList(QUrl::fromStringList(resolveMatch.xAddrs().entries()));
0144         service.updateLastSeen();
0145         emit resolveMatchReceived(service);
0146     } else if (replyMessage.messageAddressingProperties().action() == QStringLiteral("http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches")) {
0147         WSDiscovery200504::TNS__ProbeMatchesType probeMatches;
0148         probeMatches.deserialize(replyMessage);
0149 
0150         const QList<WSDiscovery200504::TNS__ProbeMatchType> &probeMatchList = probeMatches.probeMatch();
0151         for (const WSDiscovery200504::TNS__ProbeMatchType &probeMatch : probeMatchList) {
0152             const QString &endpointReference = probeMatch.endpointReference().address();
0153             auto service = WSDiscoveryTargetService(endpointReference);
0154             service.setTypeList(probeMatch.types().entries());
0155             service.setScopeList(QUrl::fromStringList(probeMatch.scopes().value().entries()));
0156             service.setXAddrList(QUrl::fromStringList(probeMatch.xAddrs().entries()));
0157             service.updateLastSeen();
0158             emit probeMatchReceived(service);
0159         }
0160     } else {
0161         qCDebug(KDSoapWSDiscoveryClient) << "Received message with unknown action:" << replyMessage.messageAddressingProperties().action();
0162     }
0163 }