Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 55eb68ff authored by Harald Sitter's avatar Harald Sitter 🌼

fallback to dnssd service discovery if smb listDir failed on root

Summary:
This elevates a problem with newer SMB protocol versions and smbclient
not supporting discovery/browsing. when not using older (as in:
ancient) protocol versions discovery doesn't work and smb:/ gives no
results.

By falling back to DNSSD based discovery we can ensure discovery of DNSSD
remotes (namely linux and osx) is always working. Windows unfortunately
does not support DNSSD and as such will not benefit from this mode of
discovery and continue to be unlisted when using a protocol version
without browsing support.

CHANGELOG: When SMB Discovery is not working (protocol > SMB1) host discovery will now fall back to DNS-SD/Avahi/Bonjour.

CCBUG: 392447
CCBUG: 390551

Test Plan:
smb.conf:

```
[global]
client min protocol = SMB2
```

Lists devices

Reviewers: #frameworks, #dolphin, broulik

Reviewed By: broulik

Subscribers: acrouthamel, alexde, bcooksley, ngraham, kde-frameworks-devel, kfm-devel

Tags: #dolphin, #frameworks

Differential Revision: https://phabricator.kde.org/D16299
parent 39da278f
......@@ -181,6 +181,12 @@ if(NOT WIN32)
endif()
endif()
# KDNSSD before 5.54 suffers from a race condition in avahi's dbus API and
# ideally should not be used in ways that can deadlock a slave.
if(${KF5DNSSD_FOUND} AND ${KF5DNSSD_VERSION} VERSION_GREATER "5.54")
set(HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION TRUE)
endif()
if(SAMBA_FOUND)
add_subdirectory(smb)
endif()
......
......@@ -14,3 +14,6 @@
* Using the definition from this project so as to be independent of Plasma.
*/
#define KDE_VERSION_STRING "@PROJECT_VERSION@"
/* kdnssd with signal race protection */
#cmakedefine HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION 1
add_feature_info("SMB DNS-SD Discovery" HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION
"Discover SMB hosts via DNS-SD/Avahi/Bonjour. KF5DNSSD >= 5.54 is required to support this.")
add_definitions(-DTRANSLATION_DOMAIN=\"kio5_smb\")
include(CheckIncludeFile)
......@@ -22,15 +25,24 @@ include_directories(${SAMBA_INCLUDE_DIR})
add_library(kio_smb MODULE ${kio_smb_PART_SRCS})
target_link_libraries(kio_smb KF5::KIOCore KF5::I18n ${SAMBA_LIBRARIES} Qt5::Network)
target_link_libraries(kio_smb
KF5::KIOCore
KF5::I18n
${SAMBA_LIBRARIES}
Qt5::Network
KF5::DNSSD)
else()
set(kio_smb_PART_SRCS
kio_smb_win.cpp)
add_library(kio_smb MODULE ${kio_smb_PART_SRCS})
target_link_libraries(kio_smb KF5::KIOCore KF5::I18n mpr Qt5::Network)
target_link_libraries(kio_smb
KF5::KIOCore
KF5::I18n
mpr
Qt5::Network
KF5::DNSSD)
endif()
set_target_properties(kio_smb PROPERTIES OUTPUT_NAME "smb")
......@@ -43,3 +55,4 @@ install(TARGETS kio_smb DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kio)
install( FILES smb-network.desktop DESTINATION ${DATA_INSTALL_DIR}/konqueror/dirtree/remote )
install( FILES smb.protocol DESTINATION ${SERVICES_INSTALL_DIR} )
install( FILES smb-network.desktop DESTINATION ${DATA_INSTALL_DIR}/remoteview )
......@@ -279,6 +279,7 @@ private:
void smbCopyGet(const QUrl& src, const QUrl& dest, int permissions, KIO::JobFlags flags);
void smbCopyPut(const QUrl& src, const QUrl& dest, int permissions, KIO::JobFlags flags);
bool workaroundEEXIST(const int errNum) const;
void listDNSSD(UDSEntry &udsentry, const QUrl &url, const uint direntCount);
void fileSystemFreeSpace(const QUrl &url);
......
......@@ -13,6 +13,7 @@
//---------------------------------------------------------------------------
//
// Copyright (c) 2000 Caldera Systems, Inc.
// Copyright (c) 2018 Harald Sitter <sitter@kde.org>
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
......@@ -30,14 +31,21 @@
//
/////////////////////////////////////////////////////////////////////////////
#include <pwd.h>
#include <grp.h>
#include "kio_smb.h"
#include "kio_smb_internal.h"
#include <DNSSD/ServiceBrowser>
#include <DNSSD/RemoteService>
#include <KLocalizedString>
#include <KIO/Job>
#include <QEventLoop>
#include <pwd.h>
#include <grp.h>
#include <config-runtime.h>
using namespace KIO;
int SMBSlave::cache_stat(const SMBUrl &url, struct stat* st )
......@@ -330,12 +338,15 @@ void SMBSlave::listDir( const QUrl& kurl )
qCDebug(KIO_SMB) << "open " << m_current_url.toSmbcUrl() << " " << m_current_url.getType() << " " << dirfd;
if(dirfd >= 0)
{
uint direntCount = 0;
do {
qCDebug(KIO_SMB) << "smbc_readdir ";
dirp = smbc_readdir(dirfd);
if(dirp == nullptr)
break;
++direntCount;
// Set name
QString udsName;
const QString dirpName = QString::fromUtf8( dirp->name );
......@@ -447,6 +458,8 @@ void SMBSlave::listDir( const QUrl& kurl )
udsentry.clear();
} while (dirp); // checked already in the head
listDNSSD(udsentry, url, direntCount);
if (dir_is_root) {
udsentry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.insert(KIO::UDSEntry::UDS_NAME, ".");
......@@ -490,6 +503,116 @@ void SMBSlave::listDir( const QUrl& kurl )
finished();
}
void SMBSlave::listDNSSD(UDSEntry &udsentry, const QUrl &url, const uint direntCount)
{
// Certain versions of KDNSSD suffer from signal races which can easily
// deadlock the slave.
#ifndef HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION
return;
#endif // HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION
// This entire method act as fallback logic iff SMB discovery is not working
// (for example when using a protocol version that doesn't have discovery).
// As such we can return if entries were discovered or the URL is not '/'
auto normalizedUrl = url.adjusted(QUrl::NormalizePathSegments);
if (direntCount > 0 || !normalizedUrl.path().isEmpty()) {
return;
}
// Slaves have no event loop, start one for the poll.
// KDNSSD has an internal timeout which may trigger if this takes too long
// so in theory this should not ever be able to get stuck.
// The eventloop runs until the discovery is finished. The finished slot
// will quit it.
QList<KDNSSD::RemoteService::Ptr> services;
QEventLoop e;
KDNSSD::ServiceBrowser browser(QStringLiteral("_smb._tcp"));
connect(&browser, &KDNSSD::ServiceBrowser::serviceAdded,
this, [&services](KDNSSD::RemoteService::Ptr service){
qCDebug(KIO_SMB) << "DNSSD added:"
<< service->serviceName()
<< service->type()
<< service->domain()
<< service->hostName()
<< service->port();
// Manual contains check. We need to use the == of the underlying
// objects, not the pointers. The same service may have >1
// RemoteService* instances representing it, so the == impl of
// RemoteService::Ptr is useless here.
for (const auto &it : services) {
if (*service == *it) {
return;
}
}
// Schedule resolution of hostname. We'll later call resolve
// which will block until the resolution is done. This basically
// gives us a head start.
service->resolveAsync();
services.append(service);
});
connect(&browser, &KDNSSD::ServiceBrowser::serviceRemoved,
this, [&services](KDNSSD::RemoteService::Ptr service){
qCDebug(KIO_SMB) << "DNSSD removed:"
<< service->serviceName()
<< service->type()
<< service->domain()
<< service->hostName()
<< service->port();
services.removeAll(service);
});
connect(&browser, &KDNSSD::ServiceBrowser::finished,
this, [&]() {
browser.disconnect(); // Stop sending anything, we'll exit here.
// Resolution still requires an event loop. So, let the resolutions
// finish and then quit the loop. Services that fail resolution
// get dropped since we won't be able to access them properly.
for (auto it = services.begin(); it != services.end(); ++it) {
auto service = *it;
if (!service->resolve()) {
qCWarning(KIO_SMB) << "Failed to resolve DNSSD service"
<< service->serviceName();
it = services.erase(it);
}
}
e.quit();
});
browser.startBrowse();
e.exec();
for (const auto &service : services) {
udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, service->serviceName());
udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
// TODO: it may be better to resolve the host to an ip address. dnssd
// being able to find a service doesn't mean name resolution is
// properly set up for its domain. So, we may not be able to resolve
// this without help from avahi. OTOH KDNSSD doesn't have API for this
// and from a platform POV we should probably assume that if avahi
// is functional it is also set up as resolution provider.
// Given the plugin design on glibc's libnss however I am not sure
// that assumption will be true all the time. ~sitter, 2018
QUrl u(QStringLiteral("smb://"));
u.setHost(service->hostName());
if (service->port() > 0 && service->port() != 445 /* default smb */) {
u.setPort(service->port());
}
udsentry.fastInsert(KIO::UDSEntry::UDS_URL, u.url());
udsentry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE,
QStringLiteral("application/x-smb-server"));
listEntry(udsentry);
udsentry.clear();
}
// NOTE: workgroups cannot be properly resolved because libsmbclient
// seems to lack the appropriate API to pull this data out of netbios.
// Netbios is also not working on IPv6 and its replacement (LLMNR)
// doesn't support the concept of workgroups.
}
void SMBSlave::fileSystemFreeSpace(const QUrl& url)
{
qCDebug(KIO_SMB) << url;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment