mirror of
https://git.freebsd.org/ports.git
synced 2025-07-01 17:40:40 -04:00
532 lines
15 KiB
Text
532 lines
15 KiB
Text
From 923bc7a1afeb0b920e60e14846987ae1d2d7dca4 Mon Sep 17 00:00:00 2001
|
|
From: John Hixson <john@ixsystems.com>
|
|
Date: Thu, 7 Dec 2017 09:36:32 -0500
|
|
Subject: [PATCH] Freenas/master mdns fixes (#22)
|
|
|
|
* mDNS fixes for Samba (work in progress).
|
|
|
|
* Fix mDNS - Can advertise on individual interfaces
|
|
|
|
* Fix mDNS browsing in smbclient
|
|
|
|
Signed-off-by: Timur I. Bakeyev <timur@iXsystems.com>
|
|
|
|
--- source3/client/dnsbrowse.c.orig 2019-01-15 10:07:00 UTC
|
|
+++ source3/client/dnsbrowse.c
|
|
@@ -39,6 +39,7 @@ struct mdns_smbsrv_result
|
|
struct mdns_browse_state
|
|
{
|
|
struct mdns_smbsrv_result *listhead; /* Browse result list head */
|
|
+ TALLOC_CTX * ctx;
|
|
int browseDone;
|
|
|
|
};
|
|
@@ -64,7 +65,7 @@ static void do_smb_resolve(struct mdns_s
|
|
struct timeval tv;
|
|
DNSServiceErrorType err;
|
|
|
|
- TALLOC_CTX * ctx = talloc_tos();
|
|
+ TALLOC_CTX * ctx = talloc_new(NULL);
|
|
|
|
err = DNSServiceResolve(&mdns_conn_sdref, 0 /* flags */,
|
|
browsesrv->ifIndex,
|
|
@@ -91,7 +92,7 @@ static void do_smb_resolve(struct mdns_s
|
|
}
|
|
}
|
|
|
|
- TALLOC_FREE(fdset);
|
|
+ TALLOC_FREE(ctx);
|
|
DNSServiceRefDeallocate(mdns_conn_sdref);
|
|
}
|
|
|
|
@@ -124,18 +125,19 @@ do_smb_browse_reply(DNSServiceRef sdRef,
|
|
return;
|
|
}
|
|
|
|
- bresult = talloc_array(talloc_tos(), struct mdns_smbsrv_result, 1);
|
|
+ bresult = talloc_array(bstatep->ctx, struct mdns_smbsrv_result, 1);
|
|
if (bresult == NULL) {
|
|
return;
|
|
}
|
|
|
|
+ bresult->nextResult = NULL;
|
|
if (bstatep->listhead != NULL) {
|
|
bresult->nextResult = bstatep->listhead;
|
|
}
|
|
|
|
- bresult->serviceName = talloc_strdup(talloc_tos(), serviceName);
|
|
- bresult->regType = talloc_strdup(talloc_tos(), regtype);
|
|
- bresult->domain = talloc_strdup(talloc_tos(), replyDomain);
|
|
+ bresult->serviceName = talloc_strdup(bstatep->ctx, serviceName);
|
|
+ bresult->regType = talloc_strdup(bstatep->ctx, regtype);
|
|
+ bresult->domain = talloc_strdup(bstatep->ctx, replyDomain);
|
|
bresult->ifIndex = interfaceIndex;
|
|
bstatep->listhead = bresult;
|
|
}
|
|
@@ -151,10 +153,13 @@ int do_smb_browse(void)
|
|
DNSServiceRef mdns_conn_sdref = NULL;
|
|
DNSServiceErrorType err;
|
|
|
|
- TALLOC_CTX * ctx = talloc_stackframe();
|
|
+ TALLOC_CTX * ctx = talloc_new(NULL);
|
|
|
|
ZERO_STRUCT(bstate);
|
|
|
|
+ bstate.ctx = ctx;
|
|
+ bstate.listhead = NULL;
|
|
+
|
|
err = DNSServiceBrowse(&mdns_conn_sdref, 0, 0, "_smb._tcp", "",
|
|
do_smb_browse_reply, &bstate);
|
|
|
|
--- source3/smbd/dnsregister.c.orig 2019-01-15 10:07:00 UTC
|
|
+++ source3/smbd/dnsregister.c
|
|
@@ -29,6 +29,29 @@
|
|
* browse for advertised SMB services.
|
|
*/
|
|
|
|
+/*
|
|
+ * Time Machine Errata:
|
|
+ * sys=adVF=0x100 -- this is required when ._adisk._tcp is present on device. When it is
|
|
+ * set, the MacOS client will send a NetShareEnumAll IOCTL and shares will be visible.
|
|
+ * Otherwise, Finder will only see the Time Machine share. In the absence of ._adisk._tcp
|
|
+ * MacOS will _always_ send NetShareEnumAll IOCTL.
|
|
+ *
|
|
+ * waMa=0 -- MacOS server uses waMa=0, while embedded devices have it set to their Mac Address.
|
|
+ * Speculation in Samba-Technical indicates that this stands for "Wireless AirDisk Mac Address".
|
|
+ *
|
|
+ * adVU -- AirDisk Volume UUID. Mac OS servers generate a UUID. Time machine over SMB works without one
|
|
+ * set. Netatalk generates a UUID and stores it persistently in afp_voluuid.conf. This can be
|
|
+ * set by adding the share parameter "fruit:volume_uuid = "
|
|
+ *
|
|
+ * dk(n)=adVF=
|
|
+ * 0xa1, 0x81 - AFP support
|
|
+ * 0xa2, 0x82 - SMB support
|
|
+ * 0xa3, 0x83 - AFP and SMB support
|
|
+ *
|
|
+ * adVN -- AirDisk Volume Name. We set this to the share name.
|
|
+ *
|
|
+ */
|
|
+
|
|
#define DNS_REG_RETRY_INTERVAL (5*60) /* in seconds */
|
|
|
|
#ifdef WITH_DNSSD_SUPPORT
|
|
@@ -36,85 +59,177 @@
|
|
#include <dns_sd.h>
|
|
|
|
struct dns_reg_state {
|
|
- struct tevent_context *event_ctx;
|
|
- uint16_t port;
|
|
- DNSServiceRef srv_ref;
|
|
- struct tevent_timer *te;
|
|
- int fd;
|
|
- struct tevent_fd *fde;
|
|
+ int count;
|
|
+ struct reg_state {
|
|
+ DNSServiceRef srv_ref;
|
|
+ TALLOC_CTX *mem_ctx;
|
|
+ struct tevent_context *event_ctx;
|
|
+ struct tevent_timer *te;
|
|
+ struct tevent_fd *fde;
|
|
+ uint16_t port;
|
|
+ int if_index;
|
|
+ int fd;
|
|
+ } *drs;
|
|
};
|
|
|
|
-static int dns_reg_state_destructor(struct dns_reg_state *dns_state)
|
|
+static void dns_register_smbd_retry(struct tevent_context *ctx,
|
|
+ struct tevent_timer *te,
|
|
+ struct timeval now,
|
|
+ void *private_data);
|
|
+static void dns_register_smbd_fde_handler(struct tevent_context *ev,
|
|
+ struct tevent_fd *fde,
|
|
+ uint16_t flags,
|
|
+ void *private_data);
|
|
+
|
|
+
|
|
+static int reg_state_destructor(struct reg_state *state)
|
|
{
|
|
- if (dns_state->srv_ref != NULL) {
|
|
+ if (state == NULL) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (state->srv_ref != NULL) {
|
|
/* Close connection to the mDNS daemon */
|
|
- DNSServiceRefDeallocate(dns_state->srv_ref);
|
|
- dns_state->srv_ref = NULL;
|
|
+ DNSServiceRefDeallocate(state->srv_ref);
|
|
+ state->srv_ref = NULL;
|
|
}
|
|
|
|
/* Clear event handler */
|
|
- TALLOC_FREE(dns_state->te);
|
|
- TALLOC_FREE(dns_state->fde);
|
|
- dns_state->fd = -1;
|
|
+ TALLOC_FREE(state->te);
|
|
+ TALLOC_FREE(state->fde);
|
|
+ state->fd = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static void dns_register_smbd_retry(struct tevent_context *ctx,
|
|
- struct tevent_timer *te,
|
|
- struct timeval now,
|
|
- void *private_data);
|
|
-static void dns_register_smbd_fde_handler(struct tevent_context *ev,
|
|
- struct tevent_fd *fde,
|
|
- uint16_t flags,
|
|
- void *private_data);
|
|
+int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... )
|
|
+{
|
|
+ int ret = 0;
|
|
+ char *str;
|
|
+ va_list ap;
|
|
+ va_start( ap, fmt );
|
|
|
|
-static bool dns_register_smbd_schedule(struct dns_reg_state *dns_state,
|
|
+ if( 0 > vasprintf(&str, fmt, ap ) ) {
|
|
+ va_end(ap);
|
|
+ return -1;
|
|
+ }
|
|
+ va_end(ap);
|
|
+
|
|
+ if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
|
|
+ ret = -1;
|
|
+ }
|
|
+
|
|
+ free(str);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...)
|
|
+{
|
|
+ int ret = 0;
|
|
+ char *key = NULL, *str = NULL;
|
|
+ va_list ap;
|
|
+
|
|
+ if( 0 > asprintf(&key, key_fmt, key_var)) {
|
|
+ DEBUG(1, ("Failed in asprintf\n"));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ va_start( ap, fmt );
|
|
+ if( 0 > vasprintf(&str, fmt, ap )) {
|
|
+ va_end(ap);
|
|
+ DEBUG(1, ("Failed in vasprintf\n"));
|
|
+ ret = -1;
|
|
+ goto exit;
|
|
+ }
|
|
+ va_end(ap);
|
|
+
|
|
+ if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
|
|
+ DEBUG(1, ("Failed in TXTRecordSetValuen"));
|
|
+ ret = -1;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ exit:
|
|
+ if (str)
|
|
+ free(str);
|
|
+ if (key)
|
|
+ free(key);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static bool dns_register_smbd_schedule(struct reg_state *state,
|
|
struct timeval tval)
|
|
{
|
|
- dns_reg_state_destructor(dns_state);
|
|
+ reg_state_destructor(state);
|
|
|
|
- dns_state->te = tevent_add_timer(dns_state->event_ctx,
|
|
- dns_state,
|
|
+ state->te = tevent_add_timer(state->event_ctx,
|
|
+ state->mem_ctx,
|
|
tval,
|
|
dns_register_smbd_retry,
|
|
- dns_state);
|
|
- if (!dns_state->te) {
|
|
+ state);
|
|
+ if (!state->te) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
+static void dns_register_smbd_callback(DNSServiceRef service,
|
|
+ DNSServiceFlags flags,
|
|
+ DNSServiceErrorType errorCode,
|
|
+ const char *name,
|
|
+ const char *type,
|
|
+ const char *domain,
|
|
+ void *context)
|
|
+{
|
|
+ if (errorCode != kDNSServiceErr_NoError) {
|
|
+ DEBUG(6, ("error=%d\n", errorCode));
|
|
+ } else {
|
|
+ DEBUG(6, ("%-15s %s.%s%s\n", "REGISTER", name, type, domain));
|
|
+ }
|
|
+}
|
|
+
|
|
static void dns_register_smbd_retry(struct tevent_context *ctx,
|
|
struct tevent_timer *te,
|
|
struct timeval now,
|
|
void *private_data)
|
|
{
|
|
- struct dns_reg_state *dns_state = talloc_get_type_abort(private_data,
|
|
- struct dns_reg_state);
|
|
+ struct reg_state *state = (struct reg_state *)private_data;
|
|
DNSServiceErrorType err;
|
|
+ int snum;
|
|
+ size_t dk = 0;
|
|
+ bool sys_txt_created = false;
|
|
+ TXTRecordRef txt_adisk;
|
|
+ TXTRecordRef txt_devinfo;
|
|
+ char *servname;
|
|
+ char *v_uuid;
|
|
+ int num_services = lp_numservices();
|
|
|
|
- dns_reg_state_destructor(dns_state);
|
|
+ reg_state_destructor(state);
|
|
|
|
- DEBUG(6, ("registering _smb._tcp service on port %d\n",
|
|
- dns_state->port));
|
|
+ TXTRecordCreate(&txt_adisk, 0, NULL);
|
|
+
|
|
+ DEBUG(6, ("registering _smb._tcp service on port %d index %d\n",
|
|
+ state->port, state->if_index));
|
|
|
|
/* Register service with DNS. Connects with the mDNS
|
|
* daemon running on the local system to perform DNS
|
|
* service registration.
|
|
*/
|
|
- err = DNSServiceRegister(&dns_state->srv_ref, 0 /* flags */,
|
|
- kDNSServiceInterfaceIndexAny,
|
|
- NULL /* service name */,
|
|
- "_smb._tcp" /* service type */,
|
|
- NULL /* domain */,
|
|
- "" /* SRV target host name */,
|
|
- htons(dns_state->port),
|
|
- 0 /* TXT record len */,
|
|
- NULL /* TXT record data */,
|
|
- NULL /* callback func */,
|
|
- NULL /* callback context */);
|
|
+ err = DNSServiceRegister(&state->srv_ref,
|
|
+ 0 /* flags */,
|
|
+ state->if_index /* interface index */,
|
|
+ NULL /* service name */,
|
|
+ "_smb._tcp" /* service type */,
|
|
+ NULL /* domain */,
|
|
+ "" /* SRV target host name */,
|
|
+ htons(state->port) /* port */,
|
|
+ 0 /* TXT record len */,
|
|
+ NULL /* TXT record data */,
|
|
+ dns_register_smbd_callback /* callback func */,
|
|
+ NULL /* callback context */);
|
|
+
|
|
|
|
if (err != kDNSServiceErr_NoError) {
|
|
/* Failed to register service. Schedule a re-try attempt.
|
|
@@ -123,24 +238,96 @@ static void dns_register_smbd_retry(stru
|
|
goto retry;
|
|
}
|
|
|
|
- dns_state->fd = DNSServiceRefSockFD(dns_state->srv_ref);
|
|
- if (dns_state->fd == -1) {
|
|
+ /*
|
|
+ * Check for services that are configured as Time Machine targets
|
|
+ *
|
|
+ */
|
|
+ for (snum = 0; snum < num_services; snum++) {
|
|
+ if (lp_snum_ok(snum) && lp_parm_bool(snum, "fruit", "time machine", false))
|
|
+ {
|
|
+ if (!sys_txt_created) {
|
|
+ if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "adVF=0x100") ) {
|
|
+ DEBUG(1, ("Failed to create Zeroconf TXTRecord for sys") );
|
|
+ goto retry;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sys_txt_created = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ v_uuid = lp_parm_const_string(snum, "fruit", "volume_uuid", NULL);
|
|
+ servname = lp_const_servicename(snum);
|
|
+ DEBUG(1, ("Registering volume %s for TimeMachine\n", servname));
|
|
+ if (v_uuid) {
|
|
+ if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%zu", dk++, "adVN=%s,adVF=0x82,adVU=%s",
|
|
+ servname, v_uuid) ) {
|
|
+ DEBUG(1, ("Could not set Zeroconf TXTRecord for dk%zu \n", dk));
|
|
+ goto retry;
|
|
+ }
|
|
+ DEBUG(1, ("Registering TimeMachine with the following TXT parameters: "
|
|
+ "dk%zu,adVN=%s,adVF=0x82,adVU=%s\n", dk, servname, v_uuid) );
|
|
+ }
|
|
+ else {
|
|
+ if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%zu", dk++, "adVN=%s,adVF=0x82",
|
|
+ servname) ) {
|
|
+ DEBUG(1, ("Could not set Zeroconf TXTRecord for dk%zu \n", dk));
|
|
+ goto retry;
|
|
+ }
|
|
+ DEBUG(1, ("Registering TimeMachine with the following TXT parameters: "
|
|
+ "dk%zu,adVN=%s,adVF=0x82\n", dk, servname) );
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (dk) {
|
|
+ err = DNSServiceRegister(&state->srv_ref,
|
|
+ 0 /* flags */,
|
|
+ state->if_index /* interface index */,
|
|
+ NULL /* service name */,
|
|
+ "_adisk._tcp" /* service type */,
|
|
+ NULL /* domain */,
|
|
+ "" /* SRV target host name */,
|
|
+ /*
|
|
+ * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
|
|
+ * "A value of 0 for a port is passed to register placeholder services.
|
|
+ * Place holder services are not found when browsing, but other
|
|
+ * clients cannot register with the same name as the placeholder service."
|
|
+ * We therefor use port 9 which is used by the adisk service type.
|
|
+ */
|
|
+ htons(9) /* port */,
|
|
+ TXTRecordGetLength(&txt_adisk) /* TXT record len */,
|
|
+ TXTRecordGetBytesPtr(&txt_adisk) /* TXT record data */,
|
|
+ dns_register_smbd_callback /* callback func */,
|
|
+ NULL /* callback context */);
|
|
+
|
|
+
|
|
+ if (err != kDNSServiceErr_NoError) {
|
|
+ /* Failed to register service. Schedule a re-try attempt.
|
|
+ */
|
|
+ DEBUG(1, ("unable to register with mDNS (err %d)\n", err));
|
|
+ goto retry;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ state->fd = DNSServiceRefSockFD(state->srv_ref);
|
|
+ if (state->fd == -1) {
|
|
goto retry;
|
|
}
|
|
|
|
- dns_state->fde = tevent_add_fd(dns_state->event_ctx,
|
|
- dns_state,
|
|
- dns_state->fd,
|
|
- TEVENT_FD_READ,
|
|
- dns_register_smbd_fde_handler,
|
|
- dns_state);
|
|
- if (!dns_state->fde) {
|
|
+ state->fde = tevent_add_fd(state->event_ctx,
|
|
+ state->mem_ctx,
|
|
+ state->fd,
|
|
+ TEVENT_FD_READ,
|
|
+ dns_register_smbd_fde_handler,
|
|
+ state);
|
|
+ if (!state->fde) {
|
|
goto retry;
|
|
}
|
|
|
|
return;
|
|
retry:
|
|
- dns_register_smbd_schedule(dns_state,
|
|
+ dns_register_smbd_schedule(state,
|
|
timeval_current_ofs(DNS_REG_RETRY_INTERVAL, 0));
|
|
}
|
|
|
|
@@ -150,44 +337,77 @@ static void dns_register_smbd_fde_handle
|
|
uint16_t flags,
|
|
void *private_data)
|
|
{
|
|
- struct dns_reg_state *dns_state = talloc_get_type_abort(private_data,
|
|
- struct dns_reg_state);
|
|
+ struct reg_state *state = (struct reg_state *)private_data;
|
|
DNSServiceErrorType err;
|
|
|
|
- err = DNSServiceProcessResult(dns_state->srv_ref);
|
|
+ err = DNSServiceProcessResult(state->srv_ref);
|
|
if (err != kDNSServiceErr_NoError) {
|
|
- DEBUG(3, ("failed to process mDNS result (err %d), re-trying\n",
|
|
- err));
|
|
+ DEBUG(3, ("failed to process mDNS result (err %d), re-trying\n", err));
|
|
goto retry;
|
|
}
|
|
|
|
- talloc_free(dns_state);
|
|
return;
|
|
|
|
retry:
|
|
- dns_register_smbd_schedule(dns_state,
|
|
- timeval_current_ofs(DNS_REG_RETRY_INTERVAL, 0));
|
|
+ dns_register_smbd_schedule(state, timeval_zero());
|
|
+}
|
|
+
|
|
+static int dns_reg_state_destructor(struct dns_reg_state *state)
|
|
+{
|
|
+ if (state != NULL) {
|
|
+ talloc_free(state);
|
|
+ }
|
|
+ return 0;
|
|
}
|
|
|
|
+
|
|
bool smbd_setup_mdns_registration(struct tevent_context *ev,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint16_t port)
|
|
{
|
|
struct dns_reg_state *dns_state;
|
|
+ bool bind_all = true;
|
|
+ int i;
|
|
|
|
dns_state = talloc_zero(mem_ctx, struct dns_reg_state);
|
|
- if (dns_state == NULL) {
|
|
+ if (dns_state == NULL)
|
|
+ return false;
|
|
+
|
|
+ if (lp_interfaces() && lp_bind_interfaces_only())
|
|
+ bind_all = false;
|
|
+
|
|
+ dns_state->count = iface_count();
|
|
+ if (dns_state->count <= 0 || bind_all == true)
|
|
+ dns_state->count = 1;
|
|
+
|
|
+ dns_state->drs = talloc_array(mem_ctx, struct reg_state, dns_state->count);
|
|
+ if (dns_state->drs == NULL) {
|
|
+ talloc_free(dns_state);
|
|
return false;
|
|
}
|
|
- dns_state->event_ctx = ev;
|
|
- dns_state->port = port;
|
|
- dns_state->fd = -1;
|
|
|
|
- talloc_set_destructor(dns_state, dns_reg_state_destructor);
|
|
+ for (i = 0; i < dns_state->count; i++) {
|
|
+ struct interface *iface = get_interface(i);
|
|
+ struct reg_state *state = &dns_state->drs[i];
|
|
|
|
- return dns_register_smbd_schedule(dns_state, timeval_zero());
|
|
+ state->mem_ctx = mem_ctx;
|
|
+ state->srv_ref = NULL;
|
|
+ state->event_ctx = ev;
|
|
+ state->te = NULL;
|
|
+ state->fde = NULL;
|
|
+ state->port = port;
|
|
+ state->fd = -1;
|
|
+
|
|
+ state->if_index = bind_all ? kDNSServiceInterfaceIndexAny : iface->if_index;
|
|
+
|
|
+ dns_register_smbd_schedule(&dns_state->drs[i], timeval_zero());
|
|
+ }
|
|
+
|
|
+ talloc_set_destructor(dns_state, dns_reg_state_destructor);
|
|
+ return true;
|
|
}
|
|
|
|
+
|
|
#else /* WITH_DNSSD_SUPPORT */
|
|
|
|
bool smbd_setup_mdns_registration(struct tevent_context *ev,
|