Commit 4b796d12 authored by Geoffrey Métais's avatar Geoffrey Métais

SMB2/3 patch for vlc

parent febf38b3
From b4d0f49473c29d09060528c64d130ff9543a289a Mon Sep 17 00:00:00 2001
From: Thomas Guillem <thomas@gllm.fr>
Date: Fri, 13 Apr 2018 16:15:16 +0200
Subject: [PATCH] access: add smb2 module
Using libsmb2 from Ronnie Sahlberg https://github.com/sahlberg/libsmb2
This is LGPL 2.1 fully async lib for accessing SMB2 and SMB3 shares.
This module use the async feature of the libsmb2 lib with the vlc interrupt
mechanism, therefore every network requests are cancellable almost immediately.
The 2.0.0 version is required since this version drop OpenSSL dependency and
allow to use Builtin NTLMSSP authentication instead of libkrb5.
---
configure.ac | 14 +-
contrib/src/smb2/SHA512SUMS | 1 +
contrib/src/smb2/rules.mak | 27 ++
modules/MODULES_LIST | 1 +
modules/access/Makefile.am | 11 +
modules/access/smb2.c | 721 ++++++++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
7 files changed, 775 insertions(+), 1 deletion(-)
create mode 100644 contrib/src/smb2/SHA512SUMS
create mode 100644 contrib/src/smb2/rules.mak
create mode 100644 modules/access/smb2.c
diff --git a/configure.ac b/configure.ac
index fbc92f952d..5700403019 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1854,7 +1854,14 @@ AS_IF([test "${SYS}" = "mingw32" -a "${enable_winstore_app}" != "yes"], [ VLC_AD
dnl
dnl liBDSM access module
dnl
-PKG_ENABLE_MODULES_VLC([DSM], [dsm], [libdsm >= 0.2.0], [libdsm SMB/CIFS access/sd module], [auto])
+AM_CONDITIONAL(HAVE_DSM, [test "$AS_TR_SH(with_dsm)" = "yes"])
+PKG_WITH_MODULES([DSM], [libdsm >= 0.2.0], [
+ VLC_ADD_PLUGIN([dsm])
+ VLC_ADD_CFLAGS([dsm], [$DSM_CFLAGS])
+ VLC_ADD_LIBS([dsm], [$DSM_LIBS])
+ have_dsm="yes"
+ ],,[libdsm SMB/CIFS access/sd module], [auto])
+AM_CONDITIONAL([HAVE_DSM], [test "${have_dsm}" = "yes"])
dnl
dnl sftp access support
@@ -1866,6 +1873,11 @@ dnl nfs access support
dnl
PKG_ENABLE_MODULES_VLC([NFS], [nfs], [libnfs >= 1.10.0], (support nfs protocol via libnfs), [auto])
+dnl
+dnl smb2 access support
+dnl
+PKG_ENABLE_MODULES_VLC([SMB2], [smb2], [libsmb2 >= 2.0.0], (support smb2 protocol via libsmb2), [auto])
+
dnl
dnl Video4Linux 2
dnl
diff --git a/contrib/src/smb2/SHA512SUMS b/contrib/src/smb2/SHA512SUMS
new file mode 100644
index 0000000000..eae3dd89c5
--- /dev/null
+++ b/contrib/src/smb2/SHA512SUMS
@@ -0,0 +1 @@
+5e7101e54a4a95eae2ed5b05dfb51a33e9d4ce19275a405fbb1e86f9e9fe197de53b41c301ca992f19a03e5117ccc90d0acb174eb9c000c71674a0dbacf57614 libsmb2-2.0.0.tar.gz
diff --git a/contrib/src/smb2/rules.mak b/contrib/src/smb2/rules.mak
new file mode 100644
index 0000000000..81937b95a7
--- /dev/null
+++ b/contrib/src/smb2/rules.mak
@@ -0,0 +1,27 @@
+# SMB2
+SMB2_VERSION := 2.0.0
+SMB2_URL := https://github.com/sahlberg/libsmb2/archive/v$(SMB2_VERSION).tar.gz
+
+ifdef BUILD_NETWORK
+ifndef HAVE_WIN32
+PKGS += smb2
+endif
+endif
+ifeq ($(call need_pkg,"smb2"),)
+PKGS_FOUND += smb2
+endif
+
+$(TARBALLS)/libsmb2-$(SMB2_VERSION).tar.gz:
+ $(call download_pkg,$(SMB2_URL),smb2)
+
+.sum-smb2: libsmb2-$(SMB2_VERSION).tar.gz
+
+smb2: libsmb2-$(SMB2_VERSION).tar.gz .sum-smb2
+ $(UNPACK)
+ $(MOVE)
+
+.smb2: smb2
+ cd $< && ./bootstrap
+ cd $< && $(HOSTVARS) ./configure --disable-examples --disable-werror --without-libkrb5 $(HOSTCONF)
+ cd $< && $(MAKE) install
+ touch $@
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 051344019c..cbc2a52b69 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -363,6 +363,7 @@ $Id$
* skins2: Skinnable interface, new generation
* skiptags: APE & ID3 tags-skipping stream filter
* smb: SMB shares access module
+ * smb2: SMB2/3 access module
* smf: Standard MIDI file demuxer
* sndio: OpenBSD sndio audio output
* soxr: SoX Resampler library audio filter
diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index 765ceec45f..2a773029e3 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -344,6 +344,17 @@ libdsm_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
access_LTLIBRARIES += $(LTLIBdsm)
EXTRA_LTLIBRARIES += libdsm_plugin.la
+libsmb2_plugin_la_SOURCES = access/smb2.c
+libsmb2_plugin_la_CFLAGS = $(AM_CFLAGS) $(SMB2_CFLAGS)
+libsmb2_plugin_la_LIBADD = $(SMB2_LIBS) $(SOCKET_LIBS)
+libsmb2_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
+if HAVE_DSM
+libsmb2_plugin_la_CFLAGS += $(DSM_CFLAGS) -DHAVE_DSM
+libsmb2_plugin_la_LIBADD += $(DSM_LIBS)
+endif
+access_LTLIBRARIES += $(LTLIBsmb2)
+EXTRA_LTLIBRARIES += libsmb2_plugin.la
+
libtcp_plugin_la_SOURCES = access/tcp.c
libtcp_plugin_la_LIBADD = $(SOCKET_LIBS)
access_LTLIBRARIES += libtcp_plugin.la
diff --git a/modules/access/smb2.c b/modules/access/smb2.c
new file mode 100644
index 0000000000..713e0ab0b9
--- /dev/null
+++ b/modules/access/smb2.c
@@ -0,0 +1,721 @@
+/*****************************************************************************
+ * smb2.c: SMB2 access plug-in
+ *****************************************************************************
+ * Copyright © 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif
+
+#include <vlc_common.h>
+#include <vlc_access.h>
+#include <vlc_dialog.h>
+#include <vlc_input_item.h>
+#include <vlc_plugin.h>
+#include <vlc_url.h>
+#include <vlc_keystore.h>
+#include <vlc_interrupt.h>
+#include <vlc_network.h>
+
+#include <smb2/smb2.h>
+#include <smb2/libsmb2.h>
+#include <smb2/libsmb2-raw.h>
+
+#ifdef HAVE_DSM
+# include <bdsm/netbios_ns.h>
+# include <bdsm/netbios_defs.h>
+
+# ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+# endif
+#endif
+
+#include "smb_common.h"
+
+#define CIFS_PORT 445
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+ set_shortname("smb2")
+ set_description(N_("SMB2 / SMB3 input"))
+ set_help(N_("Samba (Windows network shares) input via libsmb2"))
+ set_capability("access", 21)
+ set_category(CAT_INPUT)
+ set_subcategory(SUBCAT_INPUT_ACCESS)
+ add_string("smb-user", NULL, SMB_USER_TEXT, SMB_USER_LONGTEXT, false)
+ add_password("smb-pwd", NULL, SMB_PASS_TEXT, SMB_PASS_LONGTEXT, false)
+ add_string("smb-domain", NULL, SMB_DOMAIN_TEXT, SMB_DOMAIN_LONGTEXT, false)
+ add_shortcut("smb", "smb2")
+ set_callbacks(Open, Close)
+vlc_module_end()
+
+struct access_sys
+{
+ struct smb2_context * smb2;
+ struct smb2fh * smb2fh;
+ struct smb2dir * smb2dir;
+ struct srvsvc_netshareenumall_rep *share_enum;
+ uint64_t smb2_size;
+ vlc_url_t encoded_url;
+ bool eof;
+ bool smb2_connected;
+ int error_status;
+
+ bool res_done;
+ union {
+ struct
+ {
+ size_t len;
+ } read;
+ } res;
+};
+
+static int
+smb2_check_status(stream_t *access, int status, const char *psz_func)
+{
+ struct access_sys *sys = access->p_sys;
+
+ if (status < 0)
+ {
+ if (status != -EINTR)
+ {
+ const char *psz_error = vlc_strerror_c(-status);
+ msg_Err(access, "%s failed: %d, '%s'", psz_func, status, psz_error);
+ if (sys->error_status == 0)
+ vlc_dialog_display_error(access,
+ _("SMB2 operation failed"), "%s",
+ psz_error);
+ }
+ else
+ msg_Warn(access, "%s interrupted", psz_func);
+ sys->error_status = status;
+ return -1;
+ }
+ else
+ {
+ sys->res_done = true;
+ return 0;
+ }
+}
+
+static void
+smb2_set_generic_error(stream_t *access, const char *psz_func)
+{
+ struct access_sys *sys = access->p_sys;
+
+ msg_Err(access, "%s failed: %s", psz_func, smb2_get_error(sys->smb2));
+ sys->error_status = 1;
+}
+
+#define VLC_SMB2_CHECK_STATUS(access, status) \
+ smb2_check_status(access, status, __func__)
+
+#define VLC_SMB2_SET_GENERIC_ERROR(access, func) \
+ smb2_set_generic_error(access, func)
+
+#define VLC_SMB2_STATUS_DENIED(x) (x == -ECONNREFUSED || x == -EACCES)
+
+static int
+vlc_smb2_mainloop(stream_t *access, bool teardown)
+{
+ struct access_sys *sys = access->p_sys;
+
+ int timeout = -1;
+ int (*poll_func)(struct pollfd *, unsigned, int) = vlc_poll_i11e;
+
+ if (teardown && vlc_killed())
+ {
+ /* The thread is interrupted, so vlc_poll_i11e will return immediatly.
+ * Use poll() with a timeout instead for tear down. */
+ timeout = 500;
+ poll_func = (void *)poll;
+ }
+
+ sys->res_done = false;
+ while (sys->error_status == 0 && !sys->res_done)
+ {
+ struct pollfd p_fds[1];
+ int ret;
+ p_fds[0].fd = smb2_get_fd(sys->smb2);
+ p_fds[0].events = smb2_which_events(sys->smb2);
+
+ if (p_fds[0].fd == -1 || (ret = poll_func(p_fds, 1, timeout)) < 0)
+ {
+ if (errno == EINTR)
+ msg_Warn(access, "vlc_poll_i11e interrupted");
+ else
+ msg_Err(access, "vlc_poll_i11e failed");
+ sys->error_status = -errno;
+ }
+ else if (ret > 0 && p_fds[0].revents
+ && smb2_service(sys->smb2, p_fds[0].revents) < 0)
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_service");
+ }
+ return sys->error_status == 0 ? 0 : -1;
+}
+
+#define VLC_SMB2_GENERIC_CB() \
+ VLC_UNUSED(smb2); \
+ stream_t *access = private_data; \
+ struct access_sys *sys = access->p_sys; \
+ assert(sys->smb2 == smb2); \
+ if (VLC_SMB2_CHECK_STATUS(access, status)) \
+ return
+
+static void
+smb2_generic_cb(struct smb2_context *smb2, int status, void *data,
+ void *private_data)
+{
+ VLC_UNUSED(data);
+ VLC_SMB2_GENERIC_CB();
+}
+
+static void
+smb2_read_cb(struct smb2_context *smb2, int status, void *data,
+ void *private_data)
+{
+ VLC_UNUSED(data);
+ VLC_SMB2_GENERIC_CB();
+
+ if (status == 0)
+ sys->eof = true;
+ else
+ sys->res.read.len = status;
+}
+
+static ssize_t
+FileRead(stream_t *access, void *buf, size_t len)
+{
+ struct access_sys *sys = access->p_sys;
+
+ if (sys->error_status != 0)
+ return -1;
+
+ if (sys->eof)
+ return 0;
+
+ sys->res.read.len = 0;
+ if (smb2_read_async(sys->smb2, sys->smb2fh, buf, len,
+ smb2_read_cb, access) < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_read_async");
+ return -1;
+ }
+
+ if (vlc_smb2_mainloop(access, false) < 0)
+ return -1;
+
+ return sys->res.read.len;
+}
+
+static int
+FileSeek(stream_t *access, uint64_t i_pos)
+{
+ struct access_sys *sys = access->p_sys;
+
+ if (sys->error_status != 0)
+ return VLC_EGENERIC;
+
+ if (smb2_lseek(sys->smb2, sys->smb2fh, i_pos, SEEK_SET, NULL) < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_seek_async");
+ return VLC_EGENERIC;
+ }
+ sys->eof = false;
+
+ return VLC_SUCCESS;
+}
+
+static int
+FileControl(stream_t *access, int i_query, va_list args)
+{
+ struct access_sys *sys = access->p_sys;
+
+ switch (i_query)
+ {
+ case STREAM_CAN_SEEK:
+ *va_arg(args, bool *) = true;
+ break;
+
+ case STREAM_CAN_FASTSEEK:
+ *va_arg(args, bool *) = false;
+ break;
+
+ case STREAM_CAN_PAUSE:
+ case STREAM_CAN_CONTROL_PACE:
+ *va_arg(args, bool *) = true;
+ break;
+
+ case STREAM_GET_SIZE:
+ {
+ *va_arg(args, uint64_t *) = sys->smb2_size;
+ break;
+ }
+
+ case STREAM_GET_PTS_DELAY:
+ *va_arg( args, int64_t * ) = INT64_C(1000)
+ * var_InheritInteger( access, "network-caching" );
+ break;
+
+ case STREAM_SET_PAUSE_STATE:
+ break;
+
+ default:
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+static char *
+vlc_smb2_get_url(vlc_url_t *url, const char *file)
+{
+ /* smb2://<psz_host><psz_path><file>?<psz_option> */
+ char *buf;
+ if (asprintf(&buf, "smb://%s%s%s%s%s%s", url->psz_host,
+ url->psz_path != NULL ? url->psz_path : "",
+ url->psz_path != NULL && url->psz_path[0] != '\0' &&
+ url->psz_path[strlen(url->psz_path) - 1] != '/' ? "/" : "",
+ file,
+ url->psz_option != NULL ? "?" : "",
+ url->psz_option != NULL ? url->psz_option : "") == -1)
+ return NULL;
+ else
+ return buf;
+}
+
+static int AddItem(stream_t *access, struct vlc_readdir_helper *rdh,
+ const char *name, int i_type)
+{
+ struct access_sys *sys = access->p_sys;
+ char *name_encoded = vlc_uri_encode(name);
+ if (name_encoded == NULL)
+ return VLC_ENOMEM;
+
+ char *url = vlc_smb2_get_url(&sys->encoded_url, name_encoded);
+ free(name_encoded);
+ if (url == NULL)
+ return VLC_ENOMEM;
+
+ int ret = vlc_readdir_helper_additem(rdh, url, NULL, name, i_type,
+ ITEM_NET);
+ free(url);
+ return ret;
+}
+
+static int
+DirRead(stream_t *access, input_item_node_t *p_node)
+{
+ struct access_sys *sys = access->p_sys;
+ struct smb2dirent *smb2dirent;
+ int ret = VLC_SUCCESS;
+ assert(sys->smb2dir);
+
+ struct vlc_readdir_helper rdh;
+ vlc_readdir_helper_init(&rdh, access, p_node);
+
+ while (ret == VLC_SUCCESS
+ && (smb2dirent = smb2_readdir(sys->smb2, sys->smb2dir)) != NULL)
+ {
+ int i_type;
+ switch (smb2dirent->st.smb2_type)
+ {
+ case SMB2_TYPE_FILE:
+ i_type = ITEM_TYPE_FILE;
+ break;
+ case SMB2_TYPE_DIRECTORY:
+ i_type = ITEM_TYPE_DIRECTORY;
+ break;
+ default:
+ i_type = ITEM_TYPE_UNKNOWN;
+ break;
+ }
+ ret = AddItem(access, &rdh, smb2dirent->name, i_type);
+ }
+
+ vlc_readdir_helper_finish(&rdh, ret == VLC_SUCCESS);
+
+ return ret;
+}
+
+static int
+ShareEnum(stream_t *access, input_item_node_t *p_node)
+{
+ struct access_sys *sys = access->p_sys;
+ assert(sys->share_enum != NULL);
+
+ int ret = VLC_SUCCESS;
+ struct vlc_readdir_helper rdh;
+ vlc_readdir_helper_init(&rdh, access, p_node);
+
+ struct srvsvc_netsharectr *ctr = sys->share_enum->ctr;
+ for (uint32_t iinfo = 0;
+ iinfo < ctr->ctr1.count && ret == VLC_SUCCESS; ++iinfo)
+ {
+ struct srvsvc_netshareinfo1 *info = &ctr->ctr1.array[iinfo];
+ if (info->type & SHARE_TYPE_HIDDEN)
+ continue;
+ switch (info->type & 0x3)
+ {
+ case SHARE_TYPE_DISKTREE:
+ ret = AddItem(access, &rdh, info->name, ITEM_TYPE_DIRECTORY);
+ break;
+ }
+ }
+
+ vlc_readdir_helper_finish(&rdh, ret == VLC_SUCCESS);
+ return 0;
+}
+
+static int
+vlc_smb2_close_fh(stream_t *access)
+{
+ struct access_sys *sys = access->p_sys;
+
+ assert(sys->smb2fh);
+
+ if (smb2_close_async(sys->smb2, sys->smb2fh, smb2_generic_cb, access) < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_close_async");
+ return -1;
+ }
+
+ sys->smb2fh = NULL;
+
+ return vlc_smb2_mainloop(access, true);
+}
+
+static int
+vlc_smb2_disconnect_share(stream_t *access)
+{
+ struct access_sys *sys = access->p_sys;
+
+ if (!sys->smb2_connected)
+ return 0;
+
+ if (smb2_disconnect_share_async(sys->smb2, smb2_generic_cb, access) < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_connect_share_async");
+ return -1;
+ }
+
+ int ret = vlc_smb2_mainloop(access, true);
+ sys->smb2_connected = false;
+ return ret;
+}
+
+static void
+smb2_opendir_cb(struct smb2_context *smb2, int status, void *data,
+ void *private_data)
+{
+ VLC_SMB2_GENERIC_CB();
+
+ sys->smb2dir = data;
+}
+
+static void
+smb2_open_cb(struct smb2_context *smb2, int status, void *data,
+ void *private_data)
+{
+ VLC_SMB2_GENERIC_CB();
+
+ sys->smb2fh = data;
+}
+
+static void
+smb2_share_enum_cb(struct smb2_context *smb2, int status, void *data,
+ void *private_data)
+{
+ VLC_SMB2_GENERIC_CB();
+
+ sys->share_enum = data;
+}
+
+static int
+vlc_smb2_open_share(stream_t *access, const struct smb2_url *smb2_url,
+ const vlc_credential *credential)
+{
+ struct access_sys *sys = access->p_sys;
+
+ const bool do_enum = smb2_url->share[0] == '\0';
+ const char *username = credential->psz_username;
+ const char *password = credential->psz_password;
+ const char *domain = credential->psz_realm;
+ const char *share = do_enum ? "IPC$" : smb2_url->share;
+
+ if (!username)
+ {
+ username = "Guest";
+ password = "";
+ }
+
+ smb2_set_password(sys->smb2, password);
+ smb2_set_domain(sys->smb2, domain ? domain : "");
+
+ if (smb2_connect_share_async(sys->smb2, smb2_url->server, share,
+ username, smb2_generic_cb, access) < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_connect_share_async");
+ goto error;
+ }
+ if (vlc_smb2_mainloop(access, false) != 0)
+ goto error;
+ sys->smb2_connected = true;
+
+ int ret;
+ if (do_enum)
+ ret = smb2_share_enum_async(sys->smb2, smb2_share_enum_cb, access);
+ else
+ {
+ struct smb2_stat_64 smb2_stat;
+ if (smb2_stat_async(sys->smb2, smb2_url->path, &smb2_stat,
+ smb2_generic_cb, access) < 0)
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_stat_async");
+
+ if (vlc_smb2_mainloop(access, false) != 0)
+ goto error;
+
+ if (smb2_stat.smb2_type == SMB2_TYPE_FILE)
+ {
+ sys->smb2_size = smb2_stat.smb2_size;
+ ret = smb2_open_async(sys->smb2, smb2_url->path, O_RDONLY,
+ smb2_open_cb, access);
+ }
+ else if (smb2_stat.smb2_type == SMB2_TYPE_DIRECTORY)
+ ret = smb2_opendir_async(sys->smb2, smb2_url->path,
+ smb2_opendir_cb, access);
+ else
+ {
+ msg_Err(access, "smb2_stat_cb: file type not handled");
+ sys->error_status = 1;
+ goto error;
+ }
+ }
+
+ if (ret < 0)
+ {
+ VLC_SMB2_SET_GENERIC_ERROR(access, "smb2_open*_async");
+ goto error;
+ }
+
+ if (vlc_smb2_mainloop(access, false) != 0)
+ goto error;
+ return 0;
+
+error:
+ vlc_smb2_disconnect_share(access);
+ return -1;
+}
+
+static char *
+vlc_smb2_resolve(stream_t *access, const char *host, unsigned port)
+{
+ (void) access;
+ if (!host)
+ return NULL;
+
+#ifdef HAVE_DSM
+ /* Test if the host is an IP */
+ struct in_addr addr;
+ if (inet_pton(AF_INET, host, &addr) == 1)
+ return NULL;
+
+ /* Test if the host can be resolved */
+ struct addrinfo *info = NULL;
+ if (vlc_getaddrinfo_i11e(host, port, NULL, &info) == 0)
+ {
+ freeaddrinfo(info);
+ /* Let smb2 resolve it */
+ return NULL;
+ }
+
+ /* Test if the host is a netbios name */
+ char *out_host = NULL;
+ netbios_ns *ns = netbios_ns_new();
+ uint32_t ip4_addr;
+ if (netbios_ns_resolve(ns, host, NETBIOS_FILESERVER, &ip4_addr) == 0)
+ {
+ char ip[] = "xxx.xxx.xxx.xxx";
+ if (inet_ntop(AF_INET, &ip4_addr, ip, sizeof(ip)))
+ out_host = strdup(ip);
+ }
+ netbios_ns_destroy(ns);
+ return out_host;
+#else
+ (void) port;
+ return NULL;
+#endif
+}
+
+static int
+Open(vlc_object_t *p_obj)
+{
+ stream_t *access = (stream_t *)p_obj;
+ struct access_sys *sys = vlc_obj_calloc(p_obj, 1, sizeof (*sys));
+ struct smb2_url *smb2_url = NULL;
+ char *var_domain = NULL;
+
+ if (unlikely(sys == NULL))
+ return VLC_ENOMEM;
+ access->p_sys = sys;
+
+ /* Parse the encoded URL */
+ if (vlc_UrlParseFixup(&sys->encoded_url, access->psz_url) != 0)
+ return VLC_ENOMEM;
+
+ if (sys->encoded_url.i_port != 0 && sys->encoded_url.i_port != CIFS_PORT)
+ goto error;
+ sys->encoded_url.i_port = 0;
+
+ sys->smb2 = smb2_init_context();
+ if (sys->smb2 == NULL)
+ {
+ msg_Err(access, "smb2_init_context failed");
+ goto error;
+ }
+
+ if (sys->encoded_url.psz_path == NULL)
+ sys->encoded_url.psz_path = (char *) "/";
+
+ char *resolved_host = vlc_smb2_resolve(access, sys->encoded_url.psz_host,
+ CIFS_PORT);
+
+ /* smb2_* functions need a decoded url. Re compose the url from the
+ * modified sys->encoded_url (without port and with the resolved host). */
+ char *url;
+ if (resolved_host != NULL)
+ {