Index: source/Makefile.in =================================================================== --- source/Makefile.in (revision 25440) +++ source/Makefile.in (working copy) @@ -63,6 +63,7 @@ LDAP_LIBS=@LDAP_LIBS@ NSCD_LIBS=@NSCD_LIBS@ UUID_LIBS=@UUID_LIBS@ +DNSSD_LIBS=@DNSSD_LIBS@ INSTALLCMD=@INSTALL@ INSTALLLIBCMD_SH=@INSTALLLIBCMD_SH@ @@ -651,7 +652,7 @@ $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(LIBMSRPC_GEN_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \ $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) $(SMBLDAP_OBJ) $(LDB_OBJ) -CLIENT_OBJ1 = client/client.o client/clitar.o rpc_client/cli_pipe.o \ +CLIENT_OBJ1 = client/client.o client/clitar.o rpc_client/cli_pipe.o client/dnsbrowse.o \ $(RPC_CLIENT_OBJ1) \ $(RPC_PARSE_OBJ2) @@ -1148,7 +1149,8 @@ @echo Linking $@ @$(CC) $(FLAGS) -o $@ $(SMBD_OBJ) $(LDFLAGS) $(LDAP_LIBS) \ $(KRB5LIBS) $(DYNEXP) $(PRINT_LIBS) $(AUTH_LIBS) \ - $(ACL_LIBS) $(PASSDB_LIBS) $(LIBS) @POPTLIBS@ @SMBD_LIBS@ + $(ACL_LIBS) $(PASSDB_LIBS) $(LIBS) @POPTLIBS@ \ + $(DNSSD_LIBS) @SMBD_LIBS@ bin/nmbd@EXEEXT@: $(BINARY_PREREQS) $(NMBD_OBJ) @BUILD_POPT@ @echo Linking $@ @@ -1167,7 +1169,7 @@ bin/smbclient@EXEEXT@: $(BINARY_PREREQS) $(CLIENT_OBJ) @BUILD_POPT@ @echo Linking $@ - @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(NSCD_LIBS) + @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(NSCD_LIBS) $(DNSSD_LIBS) bin/smbctool@EXEEXT@: $(BINARY_PREREQS) $(TOOL_OBJ) @BUILD_POPT@ @echo Linking $@ Index: source/smbd/server.c =================================================================== --- source/smbd/server.c (revision 25440) +++ source/smbd/server.c (working copy) @@ -5,6 +5,7 @@ Copyright (C) Martin Pool 2002 Copyright (C) Jelmer Vernooij 2002-2003 Copyright (C) James Peach 2007 + Copyright (C) Rishi Srivatsavai 2007 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 @@ -315,16 +316,139 @@ 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, + struct timeval *timeout) +{ + 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); + get_timed_events_timeout(smbd_event_context(), timeout); +} + + + +static void dns_register_smbd(struct dns_reg_state *regstateptr, int *maxfd, + fd_set *listen_set, struct timeval *timeout) +{ + 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(®stateptr->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); + *timeout = timeval_zero(); + } 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, timeout); + } +} + +/* 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, struct timeval *timeout) +{ + 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, timeout); + } + return True; + } + } + return False; +} + +#endif /* HAVE_LIBDNS_SD */ + + /**************************************************************************** Are we idle enough that we could safely exit? ****************************************************************************/ static BOOL smbd_is_idle(void) { + if (events_pending(smbd_event_context())) + return False; + /* Currently we define "idle" as having no client connections. */ return count_all_current_connections() == 0; } + + + /**************************************************************************** Open the socket communication. ****************************************************************************/ @@ -339,6 +463,11 @@ int i; struct timeval idle_timeout = timeval_zero(); +#ifdef HAVE_LIBDNS_SD + struct dns_reg_state dnsregptr; + (void) memset(&dnsregptr, 0, sizeof(struct dns_reg_state)); +#endif + if (server_mode == SERVER_MODE_INETD) { return open_sockets_inetd(); } @@ -420,6 +549,10 @@ FD_ZERO(&w_fds); GetTimeOfDay(&now); +#ifdef HAVE_LIBDNS_SD + dns_register_smbd(&dnsregptr, &maxfd, &r_fds, &idle_timeout); +#endif + event_add_to_select_args(smbd_event_context(), &now, &r_fds, &w_fds, &idle_timeout, &maxfd); @@ -456,6 +589,11 @@ continue; } +#ifdef HAVE_LIBDNS_SD + if (dns_register_smbd_reply(&dnsregptr, &r_fds, &idle_timeout) && ((--num) == 0)) + continue; +#endif + if (run_events(smbd_event_context(), num, &r_fds, &w_fds)) { continue; } @@ -515,6 +653,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: source/include/includes.h =================================================================== --- source/include/includes.h (revision 25440) +++ source/include/includes.h (working copy) @@ -1265,4 +1265,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: source/configure.in =================================================================== --- source/configure.in (revision 25440) +++ source/configure.in (working copy) @@ -316,6 +316,7 @@ AC_SUBST(IDMAP_LIBS) AC_SUBST(KRB5_LIBS) AC_SUBST(UUID_LIBS) +AC_SUBST(DNSSD_LIBS) AC_SUBST(LDAP_LIBS) AC_SUBST(PAM_MODULES) AC_SUBST(INSTALL_PAM_MODULES) @@ -6187,9 +6188,65 @@ AC_SUBST(INIPARSERLIBS) AC_SUBST(FLAGS1) +################################################# +# Check if user wants DNS service discovery support +with_dnssd_support=no +AC_MSG_CHECKING([whether to enable DNS service discovery support]) +AC_ARG_WITH(dnssd, +[ --with-dnssd Enable DNS service discovery support (default no)], +[ case "$withval" in + yes|no) + with_dnssd_support=$withval + ;; + esac ]) +AC_MSG_RESULT($with_dnssd_support) + +if test x"$with_dnssd_support" != x"no"; then + + ################################################################## + # then test for dns_sd.h + AC_CHECK_HEADERS(dns_sd.h) + + if test x"$ac_cv_header_dns_sd_h" != x"yes"; then + if test x"$with_dnssd_support" = x"yes"; then + AC_MSG_ERROR(dns_sd.h is needed to enable DNS service discovery support) + else + AC_MSG_WARN(dns_sd.h is needed to enable DNS service discovery support) + fi + with_dnssd_support=no + fi +fi + +if test x"$with_dnssd_support" != x"no"; then + + DNSSD_LIBS="" + AC_LIBTESTFUNC(dns_sd, DNSServiceRegister, + [ + case " $LIBS " in + *\ -ldns_sd\ *) + DNSSD_LIBS="-ldns_sd" + SMB_REMOVE_LIB(dns_sd) + ;; + esac + + with_dnssd_support=yes + AC_DEFINE(HAVE_LIBDNS_SD,1,[Whether to enable DNS service discovery support]) + ], + [ + if test x"$with_dnssd_support" = x"yes"; then + AC_MSG_ERROR(libdns_sd is needed to enable DNS service discovery support) + else + AC_MSG_WARN(libdns_sd is needed to enable DNS service discovery support) + fi + with_dnssd_support=no + ]) +fi ################################################# + + +################################################# # Check if the user wants Python # At the moment, you can use this to set which Python binary to link @@ -6455,6 +6512,9 @@ if test x"$with_dnsupdate_support" != x"no"; then AC_MSG_RESULT([ UUID_LIBS = $UUID_LIBS]) fi +if test x"$with_dnssd_support" != x"no"; then + AC_MSG_RESULT([ DNSSD_LIBS = $DNSSD_LIBS]) +fi AC_MSG_RESULT([ AUTH_LIBS = $AUTH_LIBS]) ################################################# Index: source/client/client.c =================================================================== --- source/client/client.c (revision 25440) +++ source/client/client.c (working copy) @@ -3920,6 +3920,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 @@ -4043,6 +4046,11 @@ case 'g': grepable=True; break; +#ifdef HAVE_LIBDNS_SD + case 'B': + return(do_smb_browse()); +#endif /* HAVE_LIBDNS_SD */ + } } Index: source/client/dnsbrowse.c =================================================================== --- source/client/dnsbrowse.c (revision 0) +++ source/client/dnsbrowse.c (revision 0) @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + SMB client DNS service discovery code + Copyright (C) Rishi Srivatsavai 2007 + + 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 */ + +