From 12ac4c3119b3b7712e670d95d61413d97ecafaef Mon Sep 17 00:00:00 2001 From: Rishi Srivatsavai Date: Thu, 13 Dec 2007 20:56:29 -0800 Subject: Register the smb service with mDNS if mSDN is supported. If mDNS is supported, attempt to register the first port we are listening on for the _smb._tcp service. This provides more reliable service discovery than NetBIOS browsing. (This used to be commit 1e7241517d1f55d60af22570e0c9feb280e3fdb5) --- source3/Makefile.in | 1 + source3/include/includes.h | 14 +++ source3/smbd/dnsregister.c | 212 +++++++++++++++++++++++++++++++++++++++++++++ source3/smbd/server.c | 50 +++++++++-- 4 files changed, 268 insertions(+), 9 deletions(-) create mode 100644 source3/smbd/dnsregister.c (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index d26f688f80..5a8d7e19a8 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -547,6 +547,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \ $(AFS_SETTOKEN_OBJ) smbd/aio.o smbd/statvfs.o \ smbd/dmapi.o \ smbd/file_access.o \ + smbd/dnsregister.o \ $(MANGLE_OBJ) @VFS_STATIC@ SMBD_OBJ_BASE = $(PARAM_WITHOUT_REG_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \ diff --git a/source3/include/includes.h b/source3/include/includes.h index 4e420881ae..22451741a1 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -752,6 +752,20 @@ struct printjob; #include "smb_ldap.h" +struct dns_reg_state; + +void dns_register_smbd(struct dns_reg_state ** dns_state_ptr, + unsigned port, + int *maxfd, + fd_set *listen_set, + struct timeval *timeout); + +void dns_register_close(struct dns_reg_state ** dns_state_ptr); + + +bool dns_register_smbd_reply(struct dns_reg_state *dns_state, + fd_set *lfds, struct timeval *timeout); + /* * Reasons for cache flush. */ diff --git a/source3/smbd/dnsregister.c b/source3/smbd/dnsregister.c new file mode 100644 index 0000000000..fcd97b5dab --- /dev/null +++ b/source3/smbd/dnsregister.c @@ -0,0 +1,212 @@ +/* + Unix SMB/CIFS implementation. + DNS-SD registration + 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 3 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, see . +*/ + +#include +#include + +/* 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. + */ + +#define DNS_REG_RETRY_INTERVAL (5*60) /* in seconds */ + +struct dns_reg_state { + DNSServiceRef srv_ref; + struct timed_event *retry_handler; +}; + +#ifdef WITH_DNSSD_SUPPORT + +void dns_register_close(struct dns_reg_state **dns_state_ptr) +{ + int mdnsd_conn_fd; + struct dns_reg_state *dns_state = *dns_state_ptr; + + if (dns_state == NULL) { + return; + } + + if (dns_state->srv_ref != NULL) { + /* Close connection to the mDNS daemon */ + DNSServiceRefDeallocate(dns_state->srv_ref); + dns_state->srv_ref = NULL; + } + + /* Clear event handler */ + if (dns_state->retry_handler != NULL) { + TALLOC_FREE(dns_state->retry_handler); + dns_state->retry_handler = NULL; + } + + talloc_free(dns_state); + *dns_state_ptr = 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 *dns_state = (struct dns_reg_state *)private_data; + + /* Clear previous registration state to force new + * registration attempt. Clears event handler. + */ + dns_register_close(dns_state); +} + +static void schedule_dns_register_smbd_retry(struct dns_reg_state *dns_state, + struct timeval *timeout) +{ + struct timed_event * event; + + dns_state->srv_ref = NULL; + event= event_add_timed(smbd_event_context(), + NULL, + timeval_current_ofs(DNS_REG_RETRY_INTERVAL, 0), + "DNS registration handler", + dns_register_smbd_retry, + dns_state); + + dns_state->retry_handler = event; + get_timed_events_timeout(smbd_event_context(), timeout); +} + +/* Kick off a mDNS request to register the "_smb._tcp" on the specified port. + * We really ought to register on all the ports we are listening on. This will + * have to be an exercise for some-one who knows the DNS registration API a bit + * better. + */ +void dns_register_smbd(struct dns_reg_state ** dns_state_ptr, + unsigned port, + int *maxfd, + fd_set *listen_set, + struct timeval *timeout) +{ + int mdnsd_conn_fd; + DNSServiceErrorType err; + struct dns_reg_state *dns_state = *dns_state_ptr; + + if (dns_state == NULL) { + *dns_state_ptr = dns_state = talloc(NULL, struct dns_reg_state); + if (dns_state == NULL) { + return; + } + } + + /* Quit if a re-try attempt has been scheduled. */ + if (dns_state->retry_handler != NULL) { + return; + } + + /* If a registration is active add conn + * fd to select listen_set and return + */ + if (dns_state->srv_ref != NULL) { + mdnsd_conn_fd = DNSServiceRefSockFD(dns_state->srv_ref); + FD_SET(mdnsd_conn_fd, listen_set); + return; + } + + DEBUG(6, ("registering _smb._tcp service on port %d\n", port)); + + /* Register service with DNS. Connects with the mDNS + * daemon running on the local system to perform DNS + * service registration. + */ + err = DNSServiceRegister(&dns_state->srv_ref, 0 /* flags */, + kDNSServiceInterfaceIndexAny, + NULL /* service name */, + "_smb._tcp" /* service type */, + NULL /* domain */, + "" /* SRV target host name */, + htons(port), + 0 /* TXT record len */, + NULL /* TXT record data */, + NULL /* callback func */, + NULL /* callback context */); + + if (err != kDNSServiceErr_NoError) { + /* Failed to register service. Schedule a re-try attempt. + */ + DEBUG(3, ("unable to register with mDNS (err %d)\n", err)); + schedule_dns_register_smbd_retry(dns_state, timeout); + return; + } + + mdnsd_conn_fd = DNSServiceRefSockFD(dns_state->srv_ref); + FD_SET(mdnsd_conn_fd, listen_set); + *maxfd = MAX(*maxfd, mdnsd_conn_fd); + *timeout = timeval_zero(); + +} + +/* Processes reply from mDNS daemon. Returns true if a reply was received */ +bool dns_register_smbd_reply(struct dns_reg_state *dns_state, + fd_set *lfds, struct timeval *timeout) +{ + int mdnsd_conn_fd = -1; + + if (dns_state->srv_ref == NULL) { + return false; + } + + mdnsd_conn_fd = DNSServiceRefSockFD(dns_state->srv_ref); + + /* Process reply from daemon. Handles any errors. */ + if ((mdnsd_conn_fd != -1) && (FD_ISSET(mdnsd_conn_fd,lfds)) ) { + DNSServiceErrorType err; + + err = DNSServiceProcessResult(dns_state->srv_ref); + if (err != kDNSServiceErr_NoError) { + DEBUG(3, ("failed to process mDNS result (err %d), re-trying\n", + err)); + schedule_dns_register_smbd_retry(dns_state, timeout); + } + + return true; + } + + return false; +} + +#else /* WITH_DNSSD_SUPPORT */ + + void dns_register_smbd(struct dns_reg_state ** dns_state_ptr, + unsigned port, + int *maxfd, + fd_set *listen_set, + struct timeval *timeout) +{} + + void dns_register_close(struct dns_reg_state ** dns_state_ptr) +{} + + bool dns_register_smbd_reply(struct dns_reg_state *dns_state, + fd_set *lfds, struct timeval *timeout) +{ + return false; +} + +#endif /* WITH_DNSSD_SUPPORT */ diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 0aa8dac18d..41d036a8b9 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -311,6 +311,8 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ int maxfd = 0; int i; char *ports; + struct dns_reg_state * dns_reg = NULL; + unsigned dns_port = 0; if (!is_daemon) { return open_sockets_inetd(); @@ -372,6 +374,14 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ if (port == 0 || port > 0xffff) { continue; } + + /* Keep the first port for mDNS service + * registration. + */ + if (dns_port == 0) { + dns_port = port; + } + s = fd_listenset[num_sockets] = open_socket_in(SOCK_STREAM, port, 0, ifss, True); @@ -436,6 +446,14 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ if (port == 0 || port > 0xffff) { continue; } + + /* Keep the first port for mDNS service + * registration. + */ + if (dns_port == 0) { + dns_port = port; + } + /* open an incoming socket */ if (!interpret_string_addr(&ss, sock_tok, AI_NUMERICHOST|AI_PASSIVE)) { @@ -543,6 +561,12 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ FD_ZERO(&w_fds); GetTimeOfDay(&now); + /* Kick off our mDNS registration. */ + if (dns_port != 0) { + dns_register_smbd(&dns_reg, dns_port, &maxfd, + &r_fds, &idle_timeout); + } + event_add_to_select_args(smbd_event_context(), &now, &r_fds, &w_fds, &idle_timeout, &maxfd); @@ -567,6 +591,11 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ continue; } + /* process pending nDNS responses */ + if (dns_register_smbd_reply(dns_reg, &r_fds, &idle_timeout)) { + --num; + } + if (run_events(smbd_event_context(), num, &r_fds, &w_fds)) { continue; } @@ -624,6 +653,9 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ for(i = 0; i < num_sockets; i++) close(fd_listenset[i]); + /* close our mDNS daemon handle */ + dns_register_close(&dns_reg); + /* close our standard file descriptors */ close_low_fds(False); @@ -964,8 +996,6 @@ extern void build_options(bool screen); }; TALLOC_CTX *frame = talloc_stackframe(); /* Setup tos. */ - load_case_tables(); - TimeInit(); #ifdef HAVE_SET_AUTH_PARAMETERS @@ -1002,11 +1032,20 @@ extern void build_options(bool screen); } poptFreeContext(pc); + if (interactive) { + Fork = False; + log_stdout = True; + } + + setup_logging(argv[0],log_stdout); + if (print_build_options) { build_options(True); /* Display output to screen as well as debug */ exit(0); } + load_case_tables(); + #ifdef HAVE_SETLUID /* needed for SecureWare on SCO */ setluid(0); @@ -1016,11 +1055,6 @@ extern void build_options(bool screen); set_remote_machine_name("smbd", False); - if (interactive) { - Fork = False; - log_stdout = True; - } - if (interactive && (DEBUGLEVEL >= 9)) { talloc_enable_leak_report(); } @@ -1030,8 +1064,6 @@ extern void build_options(bool screen); exit(1); } - setup_logging(argv[0],log_stdout); - /* we want to re-seed early to prevent time delays causing client problems at a later date. (tridge) */ generate_random_buffer(NULL, 0); -- cgit