smb service discovery support

Rishi Srivatsavai rishisv at gmail.com
Tue Mar 13 21:18:45 GMT 2007


Hello All,

I have included the svn diff output below to add support for DNS service
discovery in smbd and smbclient. Please provide feedback on the proposed
changes. Changes are needed in configure.in for this feature as well.
My original post on adding this support is here:
    http://lists.samba.org/archive/samba/2007-January/128967.html

The changes in smbd only advertise the availability of the smb service
and the advertisement does not include the browseable shares. But
users can use smbclient for the lookup once they discover the smb
service.

Thanks,
Rishi

Index: smbd/server.c
===================================================================
--- smbd/server.c    (revision 21831)
+++ smbd/server.c    (working copy)
@@ -296,6 +296,78 @@
     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 int mdnsd_reg_retry = 1;
+static DNSServiceRef srvregclient = NULL;
+
+static void dns_register_smbd(int *maxfd, fd_set *listen_set)
+{
+    int mdnsd_conn_fd = -1;
+    if (srvregclient != NULL)
+        mdnsd_conn_fd = DNSServiceRefSockFD(srvregclient);
+
+    /* Clear previous state */
+    if (mdnsd_conn_fd != -1) {
+        if (srvregclient != NULL) {
+            DNSServiceRefDeallocate(srvregclient);
+            srvregclient = NULL;
+        }
+        FD_CLR(mdnsd_conn_fd, listen_set);
+        mdnsd_conn_fd = -1;
+    }
+
+    /* Register service with DNS. Connects with the mDNS
+     * daemon running on the local system to perform DNS
+     * service registration.
+     */
+    if (!DNSServiceRegister(&srvregclient, 0, kDNSServiceInterfaceIndexAny,
+        "", "_smb._tcp", "", "", htons(139), 0, NULL, NULL, NULL)) {
+        mdnsd_conn_fd = DNSServiceRefSockFD(srvregclient);
+        FD_SET(mdnsd_conn_fd, listen_set);
+        *maxfd = MAX(*maxfd, mdnsd_conn_fd);
+        mdnsd_reg_retry = 0;
+    } else {
+        /* Failed to register service. Keep re-trying.
+         * We re-try every time the SMB server process
+         * wakes up on receiving a request.
+         */
+        DEBUG(0,("dns_sd: could not register with mDNS daemon.\n"));
+    }
+}
+
+static BOOL dns_register_smbd_reply(fd_set *lfds)
+{
+    int mdnsd_conn_fd = -1;
+    if (srvregclient != NULL)
+        mdnsd_conn_fd = DNSServiceRefSockFD(srvregclient);
+
+    /* Process reply from daemon. Handles any errors. */
+    if( (mdnsd_conn_fd != -1) && (FD_ISSET(mdnsd_conn_fd, lfds)) ) {
+        FD_CLR(mdnsd_conn_fd,lfds);
+        if ( DNSServiceProcessResult(srvregclient) !=
kDNSServiceErr_NoError) {
+            DEBUG(0,("dns_sd: mdns process result returned error. Keep
re-trying.\n"));
+            mdnsd_reg_retry = 1;
+        }
+        return True;
+    }
+    return False;
+}
+
+#endif /* HAVE_LIBDNS_SD */
+
+
+
 /****************************************************************************
  Open the socket communication.
 ****************************************************************************/
@@ -475,6 +547,11 @@
             }
         }

+#ifdef HAVE_LIBDNS_SD
+        if (mdnsd_reg_retry == 1)
+            dns_register_smbd(&maxfd, &listen_set);
+#endif
+
         memcpy((char *)&lfds, (char *)&listen_set,
                sizeof(listen_set));

@@ -495,7 +572,12 @@

             continue;
         }
-
+
+#ifdef HAVE_LIBDNS_SD
+        if (dns_register_smbd_reply(&lfds) && ((--num) == 0))
+            continue;
+#endif
+
         /* check if we need to reload services */
         check_reload(time(NULL));

@@ -546,6 +628,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 */
+                DNSServiceRefDeallocate(srvregclient);
+#endif

                 /* close our standard file
                    descriptors */
Index: include/includes.h
===================================================================
--- include/includes.h    (revision 21831)
+++ include/includes.h    (working copy)
@@ -1234,4 +1234,8 @@
 #include "libnscd.h"
 #endif

+#ifdef HAVE_LIBDNS_SD
+#include "dns_sd.h"
+#endif
+
 #endif /* _INCLUDES_H */
Index: client/client.c
===================================================================
--- client/client.c    (revision 21831)
+++ 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
+   Copyright (C) Andrew Tridgell        1994-1998
+   Copyright (C) Simo Sorce            2001-2002
+   Copyright (C) Jelmer Vernooij        2003
+   Copyright (C) Gerald (Jerry) Carter        2004
+   Copyright (C) Kalim Moghul            2005
+
+   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 */


More information about the samba-technical mailing list