smb service discovery support

Rishi Srivatsavai rishisv at gmail.com
Thu Mar 15 18:10:46 GMT 2007


Hello All,

I have included svn diff output below that adds support
for discovering smb service. The new diff below uses
lib/events to perform the registration re-try operation.
Please provide feedback on the proposed changes.

Thanks,
Rishi


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)
+++ 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