Index: smbd/server.c =================================================================== --- smbd/server.c (revision 22320) +++ 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(®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); + } 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 22320) +++ include/includes.h (working copy) @@ -1250,4 +1250,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 22320) +++ client/client.c (working copy) @@ -3909,6 +3909,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 @@ -4040,6 +4043,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 */ + +