Changeset View
Changeset View
Standalone View
Standalone View
files/patch-mdns
Property | Old Value | New Value |
---|---|---|
fbsd:nokeywords | null | yes \ No newline at end of property |
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
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, |