Bug 4510 - DNS service discovery support in samba
DNS service discovery support in samba
Status: RESOLVED FIXED
Product: Samba 3.0
Classification: Unclassified
Component: smbclient
3.0.25
All Solaris
: P3 enhancement
: 3.0.26
Assigned To: James Peach
Samba QA Contact
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2007-04-14 16:24 UTC by Rishi Srivatsavai
Modified: 2013-02-18 13:31 UTC (History)
1 user (show)

See Also:


Attachments
PATCH for service discovery support in samba via DNS (12.21 KB, patch)
2007-04-17 15:01 UTC, Rishi Srivatsavai
no flags Details
PATCH for service discovery support in samba via DNS (16.46 KB, patch)
2007-08-08 15:57 UTC, Rishi Srivatsavai
no flags Details
service discovery support via DNS (16.97 KB, patch)
2007-09-30 08:53 UTC, Rishi Srivatsavai
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rishi Srivatsavai 2007-04-14 16:24:18 UTC
The following patch adds support for service discovery in samba.
Changes are in smbd and smbclient components. This support
will be turned on for Samba in Solaris. Changes to autoconf
for this feature is not included in the patch below. But I
hope to include it as well.


Index: smbd/server.c
===================================================================
--- smbd/server.c    (revision 21843)
+++ smbd/server.c    (working copy)
@@ -296,6 +296,120 @@
     return num_children < max_processes;
 }
 
+
+#ifdef HAVE_LIBDNS_SD
+
+/* Uses DNS service discovery (libdns_sd) to
+ * register the SMB service. SMB service is registered
+ * on ".local" domain via Multicast DNS & any
+ * other unicast DNS domains available.
+ *
+ * Users use the smbclient -B (Browse) option to
+ * browse for advertised SMB services.
+ */
+
+
+static void dns_register_close(struct dns_reg_state *regstateptr)
+{
+    int mdnsd_conn_fd;
+
+    if (regstateptr == NULL)
+        return;
+
+    if (regstateptr->regsrvref != NULL) {
+        /* Close connection to the mDNS daemon */
+        DNSServiceRefDeallocate(regstateptr->regsrvref);
+        regstateptr->regsrvref = NULL;
+    }
+
+    /* Clear event handler */
+        if (regstateptr->dnsregretryhandler != NULL) {
+                TALLOC_FREE(regstateptr->dnsregretryhandler);
+                regstateptr->dnsregretryhandler = NULL;
+        }
+}
+
+
+static void dns_register_smbd_retry(struct event_context *ctx,
+                                   struct timed_event *te,
+                                   const struct timeval *now,
+                                   void *private_data)
+{
+        struct dns_reg_state *regstateptr = (struct dns_reg_state *)private_data;
+    /* Clear previous registration state to force new
+     * registration attempt. Clears event handler. */
+    dns_register_close(regstateptr);
+}
+
+static void schedule_dns_register_smbd_retry(struct dns_reg_state *regstateptr)
+{
+    regstateptr->regsrvref = NULL;
+    regstateptr->dnsregretryhandler = event_add_timed(
+            smbd_event_context(),
+            NULL, timeval_current_ofs(DNS_REG_RETRY_INTERVAL, 0),
+            "DNS registration handler",
+            dns_register_smbd_retry, regstateptr);
+}
+
+
+
+static void dns_register_smbd(struct dns_reg_state *regstateptr, int *maxfd, fd_set *listen_set)
+{
+    int mdnsd_conn_fd;
+   
+    /* Quit if a re-try attempt has been scheduled.  */
+    if (regstateptr->dnsregretryhandler != NULL)
+        return;
+
+    /* If a registration is active add conn
+     * fd to select listen_set and return */
+    if (regstateptr->regsrvref != NULL) {
+        mdnsd_conn_fd = DNSServiceRefSockFD(regstateptr->regsrvref);
+        FD_SET(mdnsd_conn_fd, listen_set);
+        return;
+    }
+
+    /* Register service with DNS. Connects with the mDNS
+     * daemon running on the local system to perform DNS
+     * service registration.
+     */
+    if (!DNSServiceRegister(&regstateptr->regsrvref, 0,
+        kDNSServiceInterfaceIndexAny, "", "_smb._tcp", "", "",
+        htons(139), 0, NULL, NULL, NULL)) {
+        mdnsd_conn_fd = DNSServiceRefSockFD(regstateptr->regsrvref);
+        FD_SET(mdnsd_conn_fd, listen_set);
+        *maxfd = MAX(*maxfd, mdnsd_conn_fd);
+    } else {
+        /* Failed to register service. Schedule a re-try attempt.
+         */
+        DEBUG(3,("dns_sd: could not register with mDNS daemon.\n"));
+        schedule_dns_register_smbd_retry(regstateptr);
+    }
+}
+
+/* Processes reply from mDNS daemon. Returns True if a reply was received */
+static BOOL dns_register_smbd_reply(struct dns_reg_state *regstateptr, fd_set *lfds)
+{
+    int mdnsd_conn_fd = -1;
+
+    if (regstateptr->regsrvref != NULL) {
+        mdnsd_conn_fd = DNSServiceRefSockFD(regstateptr->regsrvref);
+        /* Process reply from daemon. Handles any errors. */
+        if( (mdnsd_conn_fd != -1) && (FD_ISSET(mdnsd_conn_fd, lfds)) ) {
+            if ( DNSServiceProcessResult(regstateptr->regsrvref) != kDNSServiceErr_NoError) {
+                DEBUG(3,("dns_sd: mdns process result returned error. Keep re-trying.\n"));
+                schedule_dns_register_smbd_retry(regstateptr);
+            }
+            return True;
+        }
+    }
+    return False;
+}
+
+#endif /* HAVE_LIBDNS_SD */
+
+
+
 /****************************************************************************
  Open the socket communication.
 ****************************************************************************/
@@ -311,6 +425,11 @@
     int i;
     char *ports;
 
+#ifdef HAVE_LIBDNS_SD
+    struct dns_reg_state dnsregptr;
+    (void) memset(&dnsregptr, 0, sizeof(struct dns_reg_state));
+#endif
+
     if (!is_daemon) {
         return open_sockets_inetd();
     }
@@ -459,6 +578,7 @@
     while (1) {
         fd_set lfds;
         int num;
+        struct timeval tmo;
        
         /* Free up temporary memory from the main smbd. */
         lp_TALLOC_FREE();
@@ -477,9 +597,16 @@
 
         memcpy((char *)&lfds, (char *)&listen_set,
                sizeof(listen_set));
+
+#ifdef HAVE_LIBDNS_SD
+        dns_register_smbd(&dnsregptr, &maxfd, &lfds);
+#endif
+
+        if (get_timed_events_timeout(smbd_event_context(), &tmo) != NULL)
+            num = sys_select(maxfd+1,&lfds,NULL,NULL,&tmo);
+        else
+            num = sys_select(maxfd+1,&lfds,NULL,NULL,NULL);
        
-        num = sys_select(maxfd+1,&lfds,NULL,NULL,NULL);
-       
         if (num == -1 && errno == EINTR) {
             if (got_sig_term) {
                 exit_server_cleanly(NULL);
@@ -495,7 +622,15 @@
 
             continue;
         }
-       
+   
+#ifdef HAVE_LIBDNS_SD
+        if (dns_register_smbd_reply(&dnsregptr, &lfds) && ((--num) == 0))
+            continue;
+#endif
+
+        /* Process expired events */
+        run_events(smbd_event_context(), 0, NULL, NULL);
+
         /* check if we need to reload services */
         check_reload(time(NULL));
 
@@ -546,6 +681,11 @@
                 /* close the listening socket(s) */
                 for(i = 0; i < num_sockets; i++)
                     close(fd_listenset[i]);
+
+#ifdef HAVE_LIBDNS_SD
+                /* close socket to mDNS daemon */
+                dns_register_close(&dnsregptr);
+#endif
                
                 /* close our standard file
                    descriptors */
Index: include/includes.h
===================================================================
--- include/includes.h    (revision 21843)
+++ include/includes.h    (working copy)
@@ -1234,4 +1234,13 @@
 #include "libnscd.h"
 #endif
 
+#ifdef HAVE_LIBDNS_SD
+#include "dns_sd.h"
+#define DNS_REG_RETRY_INTERVAL 5*60  /* in seconds */
+struct dns_reg_state {
+    DNSServiceRef regsrvref;
+    struct timed_event *dnsregretryhandler;
+};
+#endif /* HAVE_LIBDNS_SD */
+
 #endif /* _INCLUDES_H */
Index: client/client.c
===================================================================
--- client/client.c    (revision 21843)
- Hide quoted text -

+++ client/client.c    (working copy)
@@ -3863,6 +3863,9 @@
         { "send-buffer", 'b', POPT_ARG_INT, &io_bufsize, 'b', "Changes the transmit/send buffer", "BYTES" },
         { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
         { "grepable", 'g', POPT_ARG_NONE, NULL, 'g', "Produce grepable output" },
+#ifdef HAVE_LIBDNS_SD
+                { "browse", 'B', POPT_ARG_NONE, NULL, 'B', "Browse SMB servers using DNS" },
+#endif /*HAVE_LIBDNS_SD */
         POPT_COMMON_SAMBA
         POPT_COMMON_CONNECTION
         POPT_COMMON_CREDENTIALS
@@ -3968,6 +3971,11 @@
         case 'g':
             grepable=True;
             break;
+#ifdef HAVE_LIBDNS_SD
+        case 'B':
+            return(do_smb_browse());
+#endif /* HAVE_LIBDNS_SD */
+
         }
     }
 
Index: client/dnsbrowse.c
===================================================================
--- client/dnsbrowse.c    (revision 0)
+++ client/dnsbrowse.c    (revision 0)
@@ -0,0 +1,214 @@
+/*
+   Unix SMB/CIFS implementation.
+   SMB client
+  
+   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 Free Software Foundation; either version 2 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 General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_LIBDNS_SD
+
+/* Holds service instances found during DNS browse */
+struct mdns_smbsrv_result
+{
+    char *serviceName;
+    char *regType;
+    char *domain;
+    uint32_t ifIndex;
+    struct mdns_smbsrv_result *nextResult;
+};
+
+/* Maintains state during DNS browse */
+struct mdns_browse_state
+{
+    struct mdns_smbsrv_result *listhead; /* Browse result list head */
+    int browseDone;
+
+};
+
+
+static void
+do_smb_resolve_reply (DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+ const char *fullname, const char *hosttarget, uint16_t port,
+ uint16_t txtLen, const unsigned char *txtRecord, void *context)
+{
+    printf("SMB service available on %s\n", hosttarget);
+}
+
+
+static void
+do_smb_resolve(struct mdns_smbsrv_result *browsesrv)
+{
+    DNSServiceRef mdns_conn_sdref = NULL;
+    int mdnsfd;
+    int fdsetsz;
+    int ret;
+    fd_set *fdset = NULL;
+    struct timeval tv;
+
+    if(DNSServiceResolve(&mdns_conn_sdref, NULL, browsesrv->ifIndex,
+        browsesrv->serviceName, browsesrv->regType, browsesrv->domain,
+        do_smb_resolve_reply, NULL) != kDNSServiceErr_NoError)
+            return;
+
+    mdnsfd = DNSServiceRefSockFD(mdns_conn_sdref);
+    for (;;)  {
+        if (fdset != NULL)
+            free(fdset);
+
+        fdsetsz = howmany(mdnsfd+1, NFDBITS) * sizeof(fd_mask);
+        fdset = (fd_set *)malloc(fdsetsz);
+        (void) memset(fdset, 0, fdsetsz);
+        FD_SET(mdnsfd, fdset);
+
+        tv.tv_sec = 1;
+        tv.tv_usec = 0;
+
+        /* Wait until response received from mDNS daemon */
+        ret = select(mdnsfd+1, fdset, NULL, NULL, &tv);
+        if (ret <= 0 && errno != EINTR)
+            break;
+
+        if (FD_ISSET(mdnsfd, fdset)) {
+            /* Invoke callback function */
+            DNSServiceProcessResult(mdns_conn_sdref);
+            break;
+        }
+    }
+   
+    free(fdset);
+    DNSServiceRefDeallocate(mdns_conn_sdref);
+}
+
+
+static void
+do_smb_browse_reply(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+        const char  *serviceName, const char *regtype,
+        const char  *replyDomain, void  *context)
+{
+    struct mdns_browse_state *bstatep = (struct mdns_browse_state *)context;
+    struct mdns_smbsrv_result *bresult;
+
+    if (bstatep == NULL)
+        return;
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        bstatep->browseDone = 1;
+        return;
+    }
+
+    if (flags & kDNSServiceFlagsMoreComing)
+        bstatep->browseDone = 0;
+    else
+        bstatep->browseDone = 1;
+
+    if (!(flags & kDNSServiceFlagsAdd))
+        return;
+
+    bresult = (struct mdns_smbsrv_result *)calloc(1, sizeof(struct mdns_smbsrv_result));
+    if (bresult == NULL)
+        return;
+
+    if (bstatep->listhead != NULL)
+        bresult->nextResult = bstatep->listhead;
+    bresult->serviceName = strdup(serviceName);
+    bresult->regType = strdup(regtype);
+    bresult->domain = strdup(replyDomain);
+    bresult->ifIndex = interfaceIndex;
+    bstatep->listhead = bresult;
+}
+
+
+int do_smb_browse(void)
+{
+    int mdnsfd;
+    int fdsetsz;
+    int ret;
+    fd_set *fdset = NULL;
+    struct mdns_browse_state bstate;
+    struct mdns_smbsrv_result *resptr;
+    struct timeval tv;
+    DNSServiceRef mdns_conn_sdref = NULL;
+
+    (void) memset(&bstate, 0, sizeof(struct mdns_browse_state));
+
+    if(DNSServiceBrowse(&mdns_conn_sdref, 0, 0, "_smb._tcp", "",
+       do_smb_browse_reply, &bstate) != kDNSServiceErr_NoError)
+    {
+        d_printf("Error connecting to the Multicast DNS daemon\n");
+        return 1;
+    }
+
+    mdnsfd = DNSServiceRefSockFD(mdns_conn_sdref);
+    for (;;)  {
+        if (fdset != NULL)
+            free(fdset);
+
+        fdsetsz = howmany(mdnsfd+1, NFDBITS) * sizeof(fd_mask);
+        fdset = (fd_set *)malloc(fdsetsz);
+        (void) memset(fdset, 0, fdsetsz);
+        FD_SET(mdnsfd, fdset);
+
+        tv.tv_sec = 1;
+        tv.tv_usec = 0;
+
+        /* Wait until response received from mDNS daemon */
+        ret = select(mdnsfd+1, fdset, NULL, NULL, &tv);
+        if (ret <= 0 && errno != EINTR)
+            break;
+
+        if (FD_ISSET(mdnsfd, fdset)) {
+            /* Invoke callback function */
+            if (DNSServiceProcessResult(mdns_conn_sdref))
+                break;
+            if (bstate.browseDone)
+                break;
+        }
+    }
+   
+    free(fdset);
+    DNSServiceRefDeallocate(mdns_conn_sdref);
+
+    if (bstate.listhead != NULL) {
+        resptr = bstate.listhead;
+        while (resptr != NULL) {
+            struct mdns_smbsrv_result *oldresptr;
+            oldresptr = resptr;
+
+            /* Resolve smb service instance */
+            do_smb_resolve(resptr);
+
+            /* Free result structure */
+            free(resptr->domain);
+            free(resptr->serviceName);
+            free(resptr->regType);
+            resptr = resptr->nextResult;
+            free(oldresptr);
+        }
+    }
+    return 0;
+}
+
+#endif /* HAVE_LIBDNS_SD */
Comment 1 Gerald (Jerry) Carter 2007-04-14 17:26:22 UTC
Rishi, Please include the patch as an attachment and not inline.  Thanks.
Comment 2 Gerald (Jerry) Carter 2007-04-14 17:27:44 UTC
Reviewing for 3.0.26.
Comment 3 Rishi Srivatsavai 2007-04-17 15:01:24 UTC
Created attachment 2391 [details]
PATCH for service discovery support in samba via DNS

Proposed patch for DNS service discovery support in Samba.
Comment 4 Rishi Srivatsavai 2007-08-08 15:57:08 UTC
Created attachment 2859 [details]
PATCH for service discovery support in samba via DNS

Updated patch.
Added configure option with-dnssd and synced patch to revision 24280
Comment 5 James Peach 2007-09-26 22:22:49 UTC
Jerry, you can assign this to me if you like. I can build and test on Mac.

Rishi, this patch needs copyright attributions for all the files that have substantive changes, as per the Samba copyright policy: http://us1.samba.org/samba/devel/copyright-policy.html

thanks
Comment 6 Gerald (Jerry) Carter 2007-09-27 12:31:17 UTC
Thanks James.
Comment 7 Rishi Srivatsavai 2007-09-30 08:53:06 UTC
Created attachment 2934 [details]
service discovery support via DNS
Comment 8 James Peach 2007-12-13 22:59:11 UTC
Merged service registration as 
    <http://git.samba.org/?p=samba.git;a=commit;h=1e7241517d1f55d60af22570e0c9feb280e3fdb5>
Comment 9 James Peach 2007-12-18 00:10:04 UTC
Merged smbclient support:
    <http://git.samba.org/?p=samba.git;a=commit;h=db74b99d0ef1a60894c838b4c9d0d454db6cf620>
Comment 10 James Peach 2007-12-18 00:10:30 UTC
All merged, thanks a lot for the patch.