File indexing completed on 2024-12-01 04:19:16
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 }