diff --git a/smb/wsdiscoverer.cpp b/smb/wsdiscoverer.cpp --- a/smb/wsdiscoverer.cpp +++ b/smb/wsdiscoverer.cpp @@ -115,8 +115,6 @@ + QUuid::createUuid().toString(QUuid::WithoutBraces)); message.setMessageAddressingProperties(addressing); - QString computer; - KDSoapMessage response = client.call(QString(), message); if (response.isFault()) { qCDebug(KIO_SMB_LOG) << "Failed to obtain PBSD response" @@ -132,50 +130,111 @@ // For simplicity's sake we'll manually pop the value (or empty) out, if we get a name it's grand // otherwise we'll attempt reverse resolution from the IP (which ideally would yield results // over systemd-resolved's llmnr). + // Domain is the netbios domain name, not a FQDN. Pretty worthless. + // e.g. an AD domain may be 'foo.example.com' but the netbios domain is usually 'foo'. const auto childValues = response.childValues(); for (const auto §ion : qAsConst(childValues)) { - computer = section + m_prettyName = section .childValues().child("Relationship") .childValues().child("Host") .childValues().child("Computer").value().toString(); - if (!computer.isEmpty()) { + if (!m_prettyName.isEmpty()) { break; } } - computer = nameFromComputerInfo(computer); + m_prettyName = nameFromComputerInfo(m_prettyName); } - if (computer.isEmpty()) { + m_hostname = m_prettyName; + + // Continue onwards with a mountain of async host resolution... + + if (m_prettyName.isEmpty()) { // Chances are if we get here the remote doesn't implement PBSD. // Shouldn't really happen. Seeing as we got a return message // for our PBSD request however we'll assume the implementation is // just misbehaving and will assume it supports SMB all the same. // As a shot in the dark try to resolver via QHostInfo (which ideally // does a LMNR lookup via libc/systemd) - auto hostInfo = QHostInfo::fromName(m_endpointUrl.host()); - if (hostInfo.error() == QHostInfo::NoError) { - computer = hostInfo.hostName(); - } else { - qCWarning(KIO_SMB_LOG) << "failed to resolve host for endpoint url:" << m_endpointUrl; - } + QHostInfo::lookupHost(m_endpointUrl.host(), this, &PBSDResolver::resolvedIPAddress); + } else { + // Try to resolve the pretty computer name to an IP Address (llmnr/dnssd) + // if we cannot then we need to use the IP address as host name. + // Kind of a slow crutch to aid against badly configured resolution tech. + + // Try .local first. Desktop linuxes tend to have llmnr disabled by contrast win10 + // has dnssd enabled, so chances are we'll be able to find a host.local more reliably. + // This hack is technically not correct though. Avahi has a configurable domain (defaults + // to local) it also has N browse-domains which are configured globally but also + // per user, making it fairly expensive to figure out which domain a host is part of. + // Not to mention that host 'foo' may be in multiple domains but a different physical + // host. If we cannot find the host in .local we'll give up on the dnssd approach + // and do a lookup of the verbatim name (which would likely be resolved via either + // the system default DNS domain or llmnr). If that doesn't work either we'll give up + // and just use the IP. + QHostInfo::lookupHost(m_hostname + ".local", this, &PBSDResolver::resolvedDNSSDHostInfo); } + } - if (computer.isEmpty()) { - computer = xi18nc("host entry when no pretty name is available. %1 likely is an IP address", + // Called when done trying to resolve a hostname. + void resolvedHostName() + { + Q_ASSERT(!m_hostname.isEmpty()); + + if (m_prettyName.isEmpty()) { + m_prettyName = xi18nc("host entry when no pretty name is available. %1 likely is an IP address", "Unknown Device @ %1", m_endpointUrl.host()); } - m_discovery.reset(new WSDiscovery(computer, m_endpointUrl.host())); + m_discovery.reset(new WSDiscovery(m_prettyName, m_hostname)); emit resolved(m_discovery); } + void resolvedIPAddress(const QHostInfo &info) + { + if (info.error() == QHostInfo::NoError) { + m_prettyName = info.hostName(); + m_hostname = m_prettyName; + } else { + qCWarning(KIO_SMB_LOG) << "failed to reverse-resolve host for endpoint url:" << m_endpointUrl; + m_hostname = m_endpointUrl.host(); // fall back to ip address on error + // prettyName fallback is applied inside resolvedHostName + } + resolvedHostName(); + } + + void resolvedDNSSDHostInfo(const QHostInfo &info) + { + if (info.error() == QHostInfo::NoError) { + m_hostname = info.hostName(); + resolvedHostName(); + return; + } + QHostInfo::lookupHost(m_hostname, this, &PBSDResolver::resolvedLLMNRHostInfo); + } + + void resolvedLLMNRHostInfo(const QHostInfo &info) + { + if (info.error() == QHostInfo::NoError) { + m_hostname = info.hostName(); + } else { + qCWarning(KIO_SMB_LOG) << "failed to resolve host for endpoint url:" << m_endpointUrl; + m_hostname = m_endpointUrl.host(); // fall back to ip address on error + } + resolvedHostName(); + } + private: const QUrl m_endpointUrl; const QString m_destination; + // Pretty host name (ideally). Used for UDS_NAME. May be fallback string if unknown. + QString m_prettyName; + // The host name for URLs. Ideally prettyish but may be IP address. + QString m_hostname; Discovery::Ptr m_discovery; };