summaryrefslogtreecommitdiff
path: root/source3/nmbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>1997-12-13 14:16:07 +0000
committerJeremy Allison <jra@samba.org>1997-12-13 14:16:07 +0000
commit64f0348a3f994334abe64a4d4896109c3c8c9039 (patch)
tree50867cc9ccbc060ceb46ea1e8f8bd8ec2a8ca05e /source3/nmbd
parent164c9db4de87ea851a631f1c9d431e0a4525802e (diff)
downloadsamba-64f0348a3f994334abe64a4d4896109c3c8c9039.tar.gz
samba-64f0348a3f994334abe64a4d4896109c3c8c9039.tar.bz2
samba-64f0348a3f994334abe64a4d4896109c3c8c9039.zip
This is it ! The mega-merge of the JRA_NMBD_REWRITE branch
back into the main tree. For the cvs logs of all the files starting nmbd_*.c, look in the JRA_NMBD_REWRITE branch. That branch has now been discontinued. Jeremy. (This used to be commit d80b0cb645f81d16734929a0b27a91c6650499bb)
Diffstat (limited to 'source3/nmbd')
-rw-r--r--source3/nmbd/asyncdns.c64
-rw-r--r--source3/nmbd/nmbd.c811
-rw-r--r--source3/nmbd/nmbd_become_dmb.c471
-rw-r--r--source3/nmbd/nmbd_become_lmb.c534
-rw-r--r--source3/nmbd/nmbd_browserdb.c184
-rw-r--r--source3/nmbd/nmbd_browsesync.c458
-rw-r--r--source3/nmbd/nmbd_elections.c348
-rw-r--r--source3/nmbd/nmbd_incomingdgrams.c625
-rw-r--r--source3/nmbd/nmbd_incomingrequests.c556
-rw-r--r--source3/nmbd/nmbd_lmhosts.c168
-rw-r--r--source3/nmbd/nmbd_logonnames.c166
-rw-r--r--source3/nmbd/nmbd_mynames.c165
-rw-r--r--source3/nmbd/nmbd_namelistdb.c586
-rw-r--r--source3/nmbd/nmbd_namequery.c234
-rw-r--r--source3/nmbd/nmbd_nameregister.c390
-rw-r--r--source3/nmbd/nmbd_namerelease.c238
-rw-r--r--source3/nmbd/nmbd_nodestatus.c99
-rw-r--r--source3/nmbd/nmbd_packets.c1775
-rw-r--r--source3/nmbd/nmbd_processlogon.c250
-rw-r--r--source3/nmbd/nmbd_responserecordsdb.c238
-rw-r--r--source3/nmbd/nmbd_sendannounce.c477
-rw-r--r--source3/nmbd/nmbd_serverlistdb.c454
-rw-r--r--source3/nmbd/nmbd_subnetdb.c291
-rw-r--r--source3/nmbd/nmbd_winsproxy.c195
-rw-r--r--source3/nmbd/nmbd_winsserver.c1565
-rw-r--r--source3/nmbd/nmbd_workgroupdb.c356
26 files changed, 11318 insertions, 380 deletions
diff --git a/source3/nmbd/asyncdns.c b/source3/nmbd/asyncdns.c
index 57d0eda9b3..1ee9dab065 100644
--- a/source3/nmbd/asyncdns.c
+++ b/source3/nmbd/asyncdns.c
@@ -22,28 +22,29 @@
extern int DEBUGLEVEL;
-
/***************************************************************************
- add a DNS result to the name cache
- ****************************************************************************/
+ Add a DNS result to the name cache.
+****************************************************************************/
+
static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
{
int name_type = question->name_type;
char *qname = question->name;
-
+
+
if (!addr.s_addr) {
/* add the fail to WINS cache of names. give it 1 hour in the cache */
- DEBUG(3,("Negative DNS answer for %s\n", qname));
- add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE,60*60,
- DNSFAIL,addr,True);
+ DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
+ add_name_to_subnet(wins_server_subnet,qname,name_type,
+ NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr);
return NULL;
}
/* add it to our WINS cache of names. give it 2 hours in the cache */
- DEBUG(3,("DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
+ DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
- return add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE,
- 2*60*60,DNS,addr, True);
+ return add_name_to_subnet(wins_server_subnet,qname,name_type,
+ NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr);
}
@@ -101,6 +102,23 @@ static void asyncdns_process(void)
_exit(0);
}
+/**************************************************************************** **
+ catch a sigterm
+ We need a separate term handler here so we don't release any
+ names that our parent is going to release, or overwrite a
+ WINS db that our parent is going to write.
+ **************************************************************************** */
+
+static int sig_term()
+{
+ BlockSignals(True,SIGTERM);
+
+ DEBUG(0,("async dns child. Got SIGTERM: going down...\n"));
+
+ exit(0);
+ /* Keep compiler happy.. */
+ return 0;
+}
/***************************************************************************
create a child process to handle DNS lookups
@@ -131,6 +149,7 @@ void start_async_dns(void)
signal(SIGUSR2, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
signal(SIGHUP, SIG_IGN);
+ signal( SIGTERM, SIGNAL_CAST sig_term );
asyncdns_process();
}
@@ -142,7 +161,7 @@ check if a particular name is already being queried
static BOOL query_current(struct query_record *r)
{
return dns_current &&
- name_equal(&r->name,
+ nmb_name_equal(&r->name,
&dns_current->packet.nmb.question.question_name);
}
@@ -166,6 +185,7 @@ void run_dns_queue(void)
{
struct query_record r;
struct packet_struct *p, *p2;
+ struct name_record *namerec;
int size;
if (fd_in == -1)
@@ -184,13 +204,16 @@ void run_dns_queue(void)
return;
}
- add_dns_result(&r.name, r.result);
+ namerec = add_dns_result(&r.name, r.result);
if (dns_current) {
if (query_current(&r)) {
- DEBUG(3,("DNS calling reply_name_query\n"));
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
in_dns = 1;
- reply_name_query(dns_current);
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, dns_current, NULL);
+ else
+ send_wins_name_query_response(0,dns_current,namerec);
in_dns = 0;
}
@@ -205,10 +228,13 @@ void run_dns_queue(void)
struct nmb_packet *nmb = &p->packet.nmb;
struct nmb_name *question = &nmb->question.question_name;
- if (name_equal(question, &r.name)) {
- DEBUG(3,("DNS calling reply_name_query\n"));
+ if (nmb_name_equal(question, &r.name)) {
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
in_dns = 1;
- reply_name_query(p);
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0,p,namerec);
in_dns = 0;
p->locked = False;
@@ -286,6 +312,10 @@ BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
dns_ip.s_addr = interpret_addr(qname);
*n = add_dns_result(question, dns_ip);
+ if(*n == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0, p, *n);
return False;
}
#endif
diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c
index a34e2caf42..11cd50cd76 100644
--- a/source3/nmbd/nmbd.c
+++ b/source3/nmbd/nmbd.c
@@ -34,8 +34,8 @@ pstring servicesf = CONFIGFILE;
extern pstring scope;
-int ClientNMB = -1;
-int ClientDGRAM = -1;
+int ClientNMB = -1;
+int ClientDGRAM = -1;
int global_nmb_port = -1;
extern pstring myhostname;
@@ -49,47 +49,47 @@ static BOOL is_daemon = False;
/* what server type are we currently */
-time_t StartupTime =0;
+time_t StartupTime = 0;
extern struct in_addr ipzero;
- /****************************************************************************
+/**************************************************************************** **
catch a sigterm
- ****************************************************************************/
+ **************************************************************************** */
static int sig_term()
{
BlockSignals(True,SIGTERM);
DEBUG(0,("Got SIGTERM: going down...\n"));
- /* write out wins.dat file if samba is a WINS server */
- dump_names();
+ /* Write out wins.dat file if samba is a WINS server */
+ wins_write_database();
- /* remove all samba names, with wins server if necessary. */
- remove_my_names();
+ /* Remove all SELF registered names. */
+ release_my_names();
- /* announce all server entries as 0 time-to-live, 0 type */
- /* XXXX don't care if we never receive a response back... yet */
+ /* Announce all server entries as 0 time-to-live, 0 type. */
announce_my_servers_removed();
- /* XXXX other things: if we are a master browser, force an election? */
-
exit(0);
/* Keep compiler happy.. */
return 0;
-}
+} /* sig_term */
-/****************************************************************************
-catch a sighup
-****************************************************************************/
+/**************************************************************************** **
+ catch a sighup
+ **************************************************************************** */
static int sig_hup(void)
{
- BlockSignals(True,SIGHUP);
+ BlockSignals( True, SIGHUP );
+
+ DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
+
+ write_browse_list( 0, True );
- DEBUG(0,("Got SIGHUP (reload not implemented)\n"));
- dump_names();
- reload_services(True);
+ dump_all_namelists();
+ reload_services( True );
set_samba_nb_type();
@@ -98,239 +98,294 @@ static int sig_hup(void)
signal(SIGHUP,SIGNAL_CAST sig_hup);
#endif
return(0);
-}
+} /* sig_hup */
-/****************************************************************************
-catch a sigpipe
-****************************************************************************/
+/**************************************************************************** **
+ catch a sigpipe
+ **************************************************************************** */
static int sig_pipe(void)
{
- BlockSignals(True,SIGPIPE);
+ BlockSignals( True, SIGPIPE );
- DEBUG(0,("Got SIGPIPE\n"));
- if (!is_daemon)
+ DEBUG( 0, ("Got SIGPIPE\n") );
+ if ( !is_daemon )
exit(1);
- BlockSignals(False,SIGPIPE);
+ BlockSignals( False, SIGPIPE );
return(0);
-}
+} /* sig_pipe */
#if DUMP_CORE
-/*******************************************************************
-prepare to dump a core file - carefully!
-********************************************************************/
+/**************************************************************************** **
+ prepare to dump a core file - carefully!
+ **************************************************************************** */
static BOOL dump_core(void)
{
char *p;
pstring dname;
- pstrcpy(dname,debugf);
- if ((p=strrchr(dname,'/'))) *p=0;
- strcat(dname,"/corefiles");
- mkdir(dname,0700);
- sys_chown(dname,getuid(),getgid());
- chmod(dname,0700);
- if (chdir(dname)) return(False);
- umask(~(0700));
+ pstrcpy( dname, debugf );
+ if ((p=strrchr(dname,'/')))
+ *p=0;
+ strcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
#ifndef NO_GETRLIMIT
#ifdef RLIMIT_CORE
{
struct rlimit rlp;
- getrlimit(RLIMIT_CORE, &rlp);
- rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
- setrlimit(RLIMIT_CORE, &rlp);
- getrlimit(RLIMIT_CORE, &rlp);
- DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max));
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", rlp.rlim_cur, rlp.rlim_max ) );
}
#endif
#endif
- DEBUG(0,("Dumping core in %s\n",dname));
- return(True);
-}
+ DEBUG( 0, ( "Dumping core in %s\n",dname ) );
+ return( True );
+} /* dump_core */
#endif
-/****************************************************************************
-possibly continue after a fault
-****************************************************************************/
+/**************************************************************************** **
+ possibly continue after a fault
+ **************************************************************************** */
static void fault_continue(void)
{
#if DUMP_CORE
dump_core();
#endif
-}
+} /* fault_continue */
-/*******************************************************************
- expire old names from the namelist and server list
- ******************************************************************/
+/**************************************************************************** **
+ expire old names from the namelist and server list
+ **************************************************************************** */
static void expire_names_and_servers(time_t t)
{
static time_t lastrun = 0;
- if (!lastrun) lastrun = t;
- if (t < lastrun + 5) return;
+ if ( !lastrun )
+ lastrun = t;
+ if ( t < (lastrun + 5) )
+ return;
lastrun = t;
-
+
+ /*
+ * Expire any timed out names on all the broadcast
+ * subnets and those registered with the WINS server.
+ * (nmbd_namelistdb.c)
+ */
expire_names(t);
- expire_servers(t);
-}
-/*****************************************************************************
+ /*
+ * Go through all the broadcast subnets and for each
+ * workgroup known on that subnet remove any expired
+ * server names. If a workgroup has an empty serverlist
+ * and has itself timed out then remove the workgroup.
+ * (nmbd_workgroupdb.c)
+ */
+ expire_workgroups_and_servers(t);
+} /* expire_names_and_servers */
+
+/**************************************************************************** **
reload the services file
- **************************************************************************/
+ **************************************************************************** */
BOOL reload_services(BOOL test)
{
BOOL ret;
extern fstring remote_machine;
- strcpy(remote_machine,"nmbd");
+ strcpy( remote_machine, "nmbd" );
- if (lp_loaded())
+ if ( lp_loaded() )
+ {
+ pstring fname;
+ pstrcpy( fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
{
- pstring fname;
- pstrcpy(fname,lp_configfile());
- if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
- {
- pstrcpy(servicesf,fname);
- test = False;
- }
+ pstrcpy(servicesf,fname);
+ test = False;
}
+ }
- if (test && !lp_file_list_changed())
+ if ( test && !lp_file_list_changed() )
return(True);
- ret = lp_load(servicesf,True);
+ ret = lp_load( servicesf, True );
/* perhaps the config filename is now set */
- if (!test) {
- DEBUG(3,("services not loaded\n"));
- reload_services(True);
+ if ( !test )
+ {
+ DEBUG( 3, ( "services not loaded\n" ) );
+ reload_services( True );
}
/* Do a sanity check for a misconfigured nmbd */
- if(lp_wins_support() && *lp_wins_server()) {
+ if( lp_wins_support() && *lp_wins_server() )
+ {
DEBUG(0,("ERROR: both 'wins support = true' and 'wins server = <server>' \
cannot be set in the smb.conf file. nmbd aborting.\n"));
exit(10);
}
return(ret);
-}
-
-
-
-/****************************************************************************
-load a netbios hosts file
-****************************************************************************/
-static void load_hosts_file(char *fname)
-{
- FILE *f = fopen(fname,"r");
- pstring line;
- if (!f) {
- DEBUG(2,("Can't open lmhosts file %s\n",fname));
- return;
- }
-
- while (!feof(f))
- {
- pstring ip,name,flags,extra;
- struct subnet_record *d;
- char *ptr;
- int count = 0;
- struct in_addr ipaddr;
- enum name_source source = LMHOSTS;
-
- if (!fgets_slash(line,sizeof(pstring),f)) continue;
-
- if (*line == '#') continue;
-
- strcpy(ip,"");
- strcpy(name,"");
- strcpy(flags,"");
-
- ptr = line;
-
- if (next_token(&ptr,ip ,NULL)) ++count;
- if (next_token(&ptr,name ,NULL)) ++count;
- if (next_token(&ptr,flags,NULL)) ++count;
- if (next_token(&ptr,extra,NULL)) ++count;
-
- if (count <= 0) continue;
-
- if (count > 0 && count < 2) {
- DEBUG(0,("Ill formed hosts line [%s]\n",line));
- continue;
- }
-
- if (count >= 4) {
- DEBUG(0,("too many columns in %s (obsolete syntax)\n",fname));
- continue;
- }
-
- DEBUG(4, ("lmhost entry: %s %s %s\n", ip, name, flags));
-
- if (strchr(flags,'G') || strchr(flags,'S')) {
- DEBUG(0,("group flag in %s ignored (obsolete)\n",fname));
- continue;
- }
-
- if (strchr(flags,'M')) {
- source = SELF;
- pstrcpy(myname,name);
- }
-
- ipaddr = *interpret_addr2(ip);
- d = find_subnet_all(ipaddr);
- if (d) {
- add_netbios_entry(d,name,0x00,NB_ACTIVE,0,source,ipaddr,True);
- add_netbios_entry(d,name,0x20,NB_ACTIVE,0,source,ipaddr,True);
- }
- }
-
- fclose(f);
-}
+} /* reload_services */
-
-/****************************************************************************
- The main select loop.
- ***************************************************************************/
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
static void process(void)
{
BOOL run_election;
- while (True)
- {
- time_t t = time(NULL);
- run_election = check_elections();
- if(listen_for_packets(run_election))
- return;
-
- run_packet_queue();
- run_elections(t);
-
- announce_host(t);
- announce_master(t);
- announce_remote(t);
- browse_sync_remote(t);
-
- query_refresh_names(t);
-
- expire_names_and_servers(t);
- expire_netbios_response_entries(t);
- refresh_my_names(t);
-
- write_browse_list(t);
- do_browser_lists(t);
- check_master_browser(t);
- add_domain_names(t);
- }
-}
+ while( True )
+ {
+ time_t t = time(NULL);
+
+ /*
+ * Check all broadcast subnets to see if
+ * we need to run an election on any of them.
+ * (nmbd_elections.c)
+ */
+ run_election = check_elections();
+
+ /*
+ * Read incoming UDP packets.
+ * (nmbd_packets.c)
+ */
+ if(listen_for_packets(run_election))
+ return;
+
+ /*
+ * Process all incoming packets
+ * read above. This calls the success and
+ * failure functions registered when response
+ * packets arrrive, and also deals with request
+ * packets from other sources.
+ * (nmbd_packets.c)
+ */
+ run_packet_queue();
+
+ /*
+ * Run any elections - initiate becoming
+ * a local master browser if we have won.
+ * (nmbd_elections.c)
+ */
+ run_elections(t);
+
+ /*
+ * Send out any broadcast announcements
+ * of our server names. This also announces
+ * the workgroup name if we are a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+ announce_my_server_names(t);
+
+ /*
+ * If we are a local master browser, periodically
+ * announce ourselves to the domain master browser.
+ * This also deals with syncronising the domain master
+ * browser server lists with ourselves as a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+ announce_myself_to_domain_master_browser(t);
+
+ /*
+ * Fullfill any remote announce requests.
+ * (nmbd_sendannounce.c)
+ */
+ announce_remote(t);
+
+ /*
+ * Fullfill any remote browse sync announce requests.
+ * (nmbd_sendannounce.c)
+ */
+ browse_sync_remote(t);
+
+ /*
+ * Scan the broadcast subnets, and WINS client
+ * namelists and refresh any that need refreshing.
+ * (nmbd_mynames.c)
+ */
+ refresh_my_names(t);
+
+ /*
+ * Scan the subnet namelists and server lists and
+ * expire thos that have timed out.
+ * (nmbd.c)
+ */
+ expire_names_and_servers(t);
+
+ /*
+ * Write out a snapshot of our current browse list into
+ * the browse.dat file. This is used by smbd to service
+ * incoming NetServerEnum calls - used to synchronise
+ * browse lists over subnets.
+ * (nmbd_serverlistdb.c)
+ */
+ write_browse_list(t, False);
+
+ /*
+ * If we are a domain master browser, we have a list of
+ * local master browsers we should synchronise browse
+ * lists with (these are added by an incoming local
+ * master browser announcement packet). Expire any of
+ * these that are no longer current, and pull the server
+ * lists from each of these known local master browsers.
+ * (nmbd_browsesync.c)
+ */
+ dmb_expire_and_sync_browser_lists(t);
+
+ /*
+ * Check that there is a local master browser for our
+ * workgroup for all our broadcast subnets. If one
+ * is not found, start an election (which we ourselves
+ * may or may not participate in, depending on the
+ * setting of the 'local master' parameter.
+ * (nmbd_elections.c)
+ */
+ check_master_browser_exists(t);
+
+ /*
+ * If we are configured as a logon server, attempt to
+ * register the special NetBIOS names to become such
+ * (WORKGROUP<1c> name) on all broadcast subnets and
+ * with the WINS server (if used). If we are configured
+ * to become a domain master browser, attempt to register
+ * the special NetBIOS name (WORKGROUP<1b> name) to
+ * become such.
+ * (nmbd_become_dmb.c)
+ */
+ add_domain_names(t);
+
+ /*
+ * If we are a WINS server, do any timer dependent
+ * processing required.
+ * (nmbd_winsserver.c)
+ */
+ initiate_wins_processing(t);
+
+ /*
+ * Go through the repsonse record queue and time out or re-transmit
+ * and expired entries.
+ * (nmbd_packets.c)
+ */
+ retransmit_or_expire_response_records(t);
+ }
+} /* process */
-/****************************************************************************
- open the socket communication
-****************************************************************************/
+/**************************************************************************** **
+ open the socket communication
+ **************************************************************************** */
static BOOL open_sockets(BOOL isdaemon, int port)
{
/* The sockets opened here will be used to receive broadcast
@@ -340,29 +395,29 @@ static BOOL open_sockets(BOOL isdaemon, int port)
now deprecated.
*/
- if (isdaemon)
+ if ( isdaemon )
ClientNMB = open_socket_in(SOCK_DGRAM, port,0,0);
else
ClientNMB = 0;
ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0);
- if (ClientNMB == -1)
- return(False);
+ if ( ClientNMB == -1 )
+ return( False );
- signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+ signal( SIGPIPE, SIGNAL_CAST sig_pipe );
- set_socket_options(ClientNMB,"SO_BROADCAST");
- set_socket_options(ClientDGRAM,"SO_BROADCAST");
+ set_socket_options( ClientNMB, "SO_BROADCAST" );
+ set_socket_options( ClientDGRAM, "SO_BROADCAST" );
- DEBUG(3,("open_sockets: Broadcast sockets opened.\n"));
- return True;
-}
+ DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
+ return( True );
+} /* open_sockets */
-/****************************************************************************
- initialise connect, service and file structs
-****************************************************************************/
+/**************************************************************************** **
+ initialise connect, service and file structs
+ **************************************************************************** */
static BOOL init_structs()
{
extern fstring local_machine;
@@ -372,102 +427,108 @@ static BOOL init_structs()
int nodup;
pstring nbname;
- if (! *myname) {
- fstrcpy(myname,myhostname);
- p = strchr(myname,'.');
- if (p) *p = 0;
+ if (! *myname)
+ {
+ fstrcpy( myname, myhostname );
+ p = strchr( myname, '.' );
+ if (p)
+ *p = 0;
}
- strupper(myname);
+ strupper( myname );
/* Add any NETBIOS name aliases. Ensure that the first entry
- is equal to myname. */
+ is equal to myname.
+ */
/* Work out the max number of netbios aliases that we have */
- ptr=lp_netbios_aliases();
- for (namecount=0; next_token(&ptr,nbname,NULL); namecount++)
+ ptr = lp_netbios_aliases();
+ for( namecount=0; next_token(&ptr,nbname,NULL); namecount++ )
;
- if (*myname)
- namecount++;
+ if ( *myname )
+ namecount++;
/* Allocate space for the netbios aliases */
- if((my_netbios_names=(char **)malloc(sizeof(char *)*(namecount+1))) == NULL)
+ my_netbios_names = (char **)malloc( sizeof(char *) * (namecount+1) );
+ if( NULL == my_netbios_names )
{
- DEBUG(0,("init_structs: malloc fail.\n"));
- return False;
+ DEBUG( 0, ( "init_structs: malloc fail.\n" ) );
+ return( False );
}
/* Use the myname string first */
namecount=0;
- if (*myname)
+ if ( *myname )
my_netbios_names[namecount++] = myname;
- ptr=lp_netbios_aliases();
- while (next_token(&ptr,nbname,NULL)) {
- strupper(nbname);
+ ptr = lp_netbios_aliases();
+ while ( next_token( &ptr, nbname, NULL ) )
+ {
+ strupper( nbname );
/* Look for duplicates */
nodup=1;
- for(n=0; n<namecount; n++) {
- if (strcmp(nbname, my_netbios_names[n])==0)
+ for( n=0; n<namecount; n++ )
+ {
+ if( 0 == strcmp( nbname, my_netbios_names[n] ) )
nodup=0;
}
if (nodup)
- my_netbios_names[namecount++]=strdup(nbname);
+ my_netbios_names[namecount++] = strdup( nbname );
}
/* Check the strdups succeeded. */
- for(n = 0; n < namecount; n++)
- if(my_netbios_names[n]==NULL)
+ for( n = 0; n < namecount; n++ )
+ if( NULL == my_netbios_names[n] )
{
DEBUG(0,("init_structs: malloc fail when allocating names.\n"));
return False;
}
/* Terminate name list */
- my_netbios_names[namecount++]=NULL;
+ my_netbios_names[namecount++] = NULL;
- fstrcpy(local_machine,myname);
- trim_string(local_machine," "," ");
- p = strchr(local_machine,' ');
- if (p)
+ fstrcpy( local_machine, myname );
+ trim_string( local_machine, " ", " " );
+ p = strchr( local_machine, ' ' );
+ if (p)
*p = 0;
- strlower(local_machine);
+ strlower( local_machine );
- DEBUG(5, ("Netbios name list:-\n"));
- for (n=0; my_netbios_names[n]; n++)
- DEBUG(5, ("my_netbios_names[%d]=\"%s\"\n", n, my_netbios_names[n]));
+ DEBUG( 5, ("Netbios name list:-\n") );
+ for( n=0; my_netbios_names[n]; n++ )
+ DEBUG( 5, ( "my_netbios_names[%d]=\"%s\"\n", n, my_netbios_names[n] ) );
- return True;
-}
+ return( True );
+} /* init_structs */
-/****************************************************************************
-usage on the program
-****************************************************************************/
+/**************************************************************************** **
+ usage on the program
+ **************************************************************************** */
static void usage(char *pname)
{
DEBUG(0,("Incorrect program usage - is the command line correct?\n"));
- printf("Usage: %s [-n name] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
- printf("Version %s\n",VERSION);
- printf("\t-D become a daemon\n");
- printf("\t-p port listen on the specified port\n");
- printf("\t-d debuglevel set the debuglevel\n");
- printf("\t-l log basename. Basename for log/debug files\n");
- printf("\t-n netbiosname. the netbios name to advertise for this host\n");
- printf("\t-H hosts file load a netbios hosts file\n");
- printf("\n");
-}
-
-
-/****************************************************************************
- main program
- **************************************************************************/
- int main(int argc,char *argv[])
+ printf( "Usage: %s [-n name] [-D] [-p port] [-d debuglevel] ", pname );
+ printf( "[-l log basename]\n" );
+ printf( "Version %s\n", VERSION );
+ printf( "\t-D become a daemon\n" );
+ printf( "\t-p port listen on the specified port\n" );
+ printf( "\t-d debuglevel set the debuglevel\n" );
+ printf( "\t-l log basename. Basename for log/debug files\n" );
+ printf( "\t-n netbiosname. " );
+ printf( "the netbios name to advertise for this host\n");
+ printf( "\t-H hosts file load a netbios hosts file\n" );
+ printf( "\n");
+} /* usage */
+
+
+/**************************************************************************** **
+ main program
+ **************************************************************************** */
+int main(int argc,char *argv[])
{
int opt;
extern FILE *dbf;
extern char *optarg;
- char pidFile[100];
-
- *pidFile = '\0';
+ char pidFile[100] = { 0 };
global_nmb_port = NMB_PORT;
*host_file = 0;
@@ -476,186 +537,208 @@ static void usage(char *pname)
TimeInit();
- strcpy(debugf,NMBLOGFILE);
+ strcpy( debugf, NMBLOGFILE );
- setup_logging(argv[0],False);
+ setup_logging( argv[0], False );
charset_initialise();
#ifdef LMHOSTSFILE
- strcpy(host_file,LMHOSTSFILE);
+ strcpy( host_file, LMHOSTSFILE );
#endif
/* this is for people who can't start the program correctly */
- while (argc > 1 && (*argv[1] != '-')) {
+ while (argc > 1 && (*argv[1] != '-'))
+ {
argv++;
argc--;
}
- fault_setup(fault_continue);
+ fault_setup( fault_continue );
- signal(SIGHUP ,SIGNAL_CAST sig_hup);
- signal(SIGTERM,SIGNAL_CAST sig_term);
+ signal( SIGHUP, SIGNAL_CAST sig_hup );
+ signal( SIGTERM, SIGNAL_CAST sig_term );
- while ((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF)
+ while((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF)
{
switch (opt)
- {
+ {
case 'f':
strncpy(pidFile, optarg, sizeof(pidFile));
break;
- case 's':
- pstrcpy(servicesf,optarg);
- break;
- case 'N':
- case 'B':
- case 'I':
- case 'C':
- case 'G':
- DEBUG(0,("Obsolete option '%c' used\n",opt));
- break;
- case 'H':
- pstrcpy(host_file,optarg);
- break;
- case 'n':
- pstrcpy(myname,optarg);
- strupper(myname);
- break;
- case 'l':
- sprintf(debugf,"%s.nmb",optarg);
- break;
- case 'i':
- pstrcpy(scope,optarg);
- strupper(scope);
- break;
- case 'a':
- {
- extern BOOL append_log;
- append_log = !append_log;
- }
- break;
- case 'D':
- is_daemon = True;
- break;
- case 'd':
- DEBUGLEVEL = atoi(optarg);
- break;
- case 'p':
- global_nmb_port = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(0);
- break;
- default:
- if (!is_a_socket(0)) {
- usage(argv[0]);
- }
- break;
- }
+ case 's':
+ pstrcpy(servicesf,optarg);
+ break;
+ case 'N':
+ case 'B':
+ case 'I':
+ case 'C':
+ case 'G':
+ DEBUG(0,("Obsolete option '%c' used\n",opt));
+ break;
+ case 'H':
+ pstrcpy(host_file,optarg);
+ break;
+ case 'n':
+ pstrcpy(myname,optarg);
+ strupper(myname);
+ break;
+ case 'l':
+ sprintf(debugf,"%s.nmb",optarg);
+ break;
+ case 'i':
+ pstrcpy(scope,optarg);
+ strupper(scope);
+ break;
+ case 'a':
+ {
+ extern BOOL append_log;
+ append_log = !append_log;
+ }
+ break;
+ case 'D':
+ is_daemon = True;
+ break;
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'p':
+ global_nmb_port = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ default:
+ if (!is_a_socket(0))
+ {
+ usage(argv[0]);
+ }
+ break;
+ }
}
DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION));
DEBUG(1,("Copyright Andrew Tridgell 1994-1997\n"));
- if(!get_myname(myhostname,NULL))
+ if( !get_myname( myhostname, NULL) )
{
DEBUG(0,("Unable to get my hostname - exiting.\n"));
return -1;
}
-#ifndef SYNC_DNS
- start_async_dns();
-#endif
-
- if (!reload_services(False))
- return(-1);
+ if ( !reload_services(False) )
+ return(-1);
codepage_initialise(lp_client_code_page());
if(!init_structs())
return -1;
- reload_services(True);
+ reload_services( True );
- pstrcpy(myworkgroup, lp_workgroup());
+ fstrcpy( myworkgroup, lp_workgroup() );
- if (strequal(myworkgroup,"*")) {
+ if (strequal(myworkgroup,"*"))
+ {
DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n"));
exit(1);
}
set_samba_nb_type();
- if (!is_daemon && !is_a_socket(0)) {
+ if (!is_daemon && !is_a_socket(0))
+ {
DEBUG(0,("standard input is not a socket, assuming -D option\n"));
is_daemon = True;
}
- if (is_daemon) {
+ if (is_daemon)
+ {
DEBUG(2,("%s becoming a daemon\n",timestring()));
become_daemon();
}
- if (!directory_exist(lp_lockdir(), NULL)) {
- mkdir(lp_lockdir(), 0755);
+ if (!directory_exist(lp_lockdir(), NULL))
+ {
+ mkdir(lp_lockdir(), 0755);
}
if (*pidFile)
- {
- int fd;
- char buf[20];
+ {
+ int fd;
+ char buf[20];
- if ((fd = open(pidFile,
#ifdef O_NONBLOCK
- O_NONBLOCK |
+ fd = open( pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC, 0644 );
+#else
+ fd = open( pidFile, O_CREAT | O_WRONLY | O_TRUNC, 0644 );
#endif
- O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
- {
- DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno)));
- exit(1);
- }
- if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False)
- {
- DEBUG(0,("ERROR: nmbd is already running\n"));
- exit(1);
- }
- sprintf(buf, "%u\n", (unsigned int) getpid());
- if (write(fd, buf, strlen(buf)) < 0)
- {
- DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno)));
- exit(1);
- }
- /* Leave pid file open & locked for the duration... */
+ if ( fd < 0 )
+ {
+ DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno)));
+ exit(1);
}
+ if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False)
+ {
+ DEBUG(0,("ERROR: nmbd is already running\n"));
+ exit(1);
+ }
+ sprintf(buf, "%u\n", (unsigned int) getpid());
+ if (write(fd, buf, strlen(buf)) < 0)
+ {
+ DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno)));
+ exit(1);
+ }
+ /* Leave pid file open & locked for the duration... */
+ }
- DEBUG(3,("Opening sockets %d\n", global_nmb_port));
+ DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
- if (!open_sockets(is_daemon,global_nmb_port)) return 1;
+ if ( !open_sockets( is_daemon, global_nmb_port ) )
+ return 1;
+ /* Determine all the IP addresses we have. */
load_interfaces();
- add_my_subnets(myworkgroup);
-
- add_my_names();
- DEBUG(3,("Checked names\n"));
-
- load_netbios_names();
-
- DEBUG(3,("Loaded names\n"));
+ /* Create an nmbd subnet record for each of the above. */
+ if( False == create_subnets() )
+ {
+ DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n"));
+ exit(1);
+ }
- if (*host_file) {
- load_hosts_file(host_file);
+ /* Load in any static local names. */
+ if ( *host_file )
+ {
+ load_lmhosts_file(host_file);
DEBUG(3,("Loaded hosts file\n"));
}
- write_browse_list(time(NULL));
+ /* If we are acting as a WINS server, initialise data structures. */
+ if( !initialise_wins() )
+ {
+ DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) );
+ exit(1);
+ }
- DEBUG(3,("Dumped names\n"));
+ /*
+ * Register nmbd primary workgroup and nmbd names on all
+ * the broadcast subnets, and on the WINS server (if specified).
+ * Also initiate the startup of our primary workgroup (start
+ * elections if we are setup as being able to be a local
+ * master browser.
+ */
+
+ if( False == register_my_workgroup_and_names() )
+ {
+ DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n"));
+ exit(1);
+ }
/* We can only take sigterm signals in the select. */
- BlockSignals(True,SIGTERM);
+ BlockSignals( True, SIGTERM );
process();
close_sockets();
@@ -663,4 +746,6 @@ static void usage(char *pname)
if (dbf)
fclose(dbf);
return(0);
-}
+} /* main */
+
+/* ========================================================================== */
diff --git a/source3/nmbd/nmbd_become_dmb.c b/source3/nmbd/nmbd_become_dmb.c
new file mode 100644
index 0000000000..37fceb9bf1
--- /dev/null
+++ b/source3/nmbd/nmbd_become_dmb.c
@@ -0,0 +1,471 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+extern struct in_addr ipzero;
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+static void become_domain_master_browser_bcast(char *);
+
+/*******************************************************************
+ Unbecome a domain master browser - name release success function.
+ ******************************************************************/
+
+static void unbecome_dmb_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet(subrec, released_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("unbecome_dmb_success: Cannot find workgroup %s on subnet %s\n",
+ released_name->name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("unbecome_dmb_success: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, released_name->name, subrec->subnet_name));
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->dom_state = DOMAIN_NONE;
+
+ /* Update our server status. */
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Remove any list of local master browsers we are syncing with. */
+ remove_workgroup_lmb_browsers(released_name->name);
+
+ /* Delete the known domain master browser name from the workgroup
+ struct. */
+ bzero((char *)&work->dmb_name, sizeof(work->dmb_name));
+ putip((char *)&work->dmb_addr, &ipzero);
+
+ DEBUG(0,("\n ***** Samba server %s has stopped being a domain master browser \
+for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name));
+
+}
+
+/*******************************************************************
+ Unbecome a domain master browser - name release fail function.
+ ******************************************************************/
+
+static void unbecome_dmb_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *released_name)
+{
+ DEBUG(0,("unbecome_dmb_fail: Failed to unbecome domain master browser for \
+workgroup %s on subnet %s.\n", released_name->name, subrec->subnet_name));
+}
+
+/*******************************************************************
+ Unbecome a domain master browser.
+ ******************************************************************/
+
+void unbecome_domain_master(char *workgroup_name)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+ if(work && (work->dom_state == DOMAIN_MST))
+ {
+ struct name_record *namerec;
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,workgroup_name,0x1b,scope);
+
+ /* We can only do this if we are a domain master already. */
+ DEBUG(2,("unbecome_domain_master: attempting to stop being a domain \
+master browser for workgroup %s on subnet %s\n",
+ work->work_group, subrec->subnet_name));
+
+ /* Find the WORKGROUP<1b> name on the subnet namelist. */
+ if((namerec = find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME))==NULL)
+ {
+ DEBUG(0,("unbecome_domain_master: Cannot find name %s on subnet %s.\n",
+ namestr(&nmbname), subrec->subnet_name));
+ continue;
+ }
+ release_name(subrec, namerec,
+ unbecome_dmb_success,
+ unbecome_dmb_fail,
+ NULL);
+ }
+ }
+}
+
+/****************************************************************************
+ Fail to become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+ return;
+ }
+
+ /* Set the state back to DOMAIN_NONE. */
+ work->dom_state = DOMAIN_NONE;
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ /* Update our server status. */
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, namestr(fail_name)));
+}
+
+/****************************************************************************
+ Become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, registered_name->name, subrec->subnet_name));
+ work->dom_state = DOMAIN_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->dom_state = DOMAIN_MST; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ DEBUG(0,("\n ***** Samba server %s is now a domain master browser \
+for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name));
+
+ if(subrec == unicast_subnet)
+ {
+ struct nmb_name nmbname;
+ struct in_addr my_first_ip;
+
+ /* Put our name and first IP address into the
+ workgroup struct as domain master browser. This
+ will stop us syncing with ourself if we are also
+ a local master browser. */
+
+ make_nmb_name(&nmbname, myname, 0x20, scope);
+
+ work->dmb_name = nmbname;
+ /* Pick the first interface ip address as the domain master browser ip. */
+ my_first_ip = *iface_n_ip(0);
+
+ putip((char *)&work->dmb_addr, &my_first_ip);
+
+ /* We successfully registered by unicast with the
+ WINS server. We now expect to become the domain
+ master on the local subnets. If this fails, it's
+ probably a 1.9.16p2 to 1.9.16p11 server's fault.
+
+ This is a configuration issue that should be addressed
+ by the network administrator - you shouldn't have
+ several machines configured as a domain master browser
+ for the same WINS scope (except if they are 1.9.17 or
+ greater, and you know what you're doing.
+
+ see docs/DOMAIN.txt.
+
+ */
+ become_domain_master_browser_bcast(work->work_group);
+ }
+}
+
+/****************************************************************************
+ Start the name registration process when becoming a Domain Master Browser
+ on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_stage1(struct subnet_record *subrec, char *wg_name)
+{
+ struct work_record *work;
+
+ DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \
+workgroup %s on subnet %s\n", wg_name, subrec->subnet_name));
+
+ /* First, find the workgroup on the subnet. */
+ if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL)
+ {
+ DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n",
+ wg_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n"));
+ work->dom_state = DOMAIN_WAIT;
+
+ /* WORKGROUP<1b> is the domain master browser name. */
+ register_name(subrec, work->work_group,0x1b,samba_nb_type,
+ become_domain_master_stage2,
+ become_domain_master_fail, NULL);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+ This is normally a fail condition as it means there is already
+ a domain master browser for a workgroup and we were trying to
+ become one.
+****************************************************************************/
+
+static void become_domain_master_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip,
+ struct res_rec *rrec)
+{
+ /* If the given ip is not ours, then we can't become a domain
+ controler as the name is already registered.
+ */
+
+ /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast
+ address or zero ip for this query. Pretend this is ok. */
+
+ if(ismyip(ip) || ip_equal(allones_ip, ip) || ip_equal(ipzero, ip))
+ {
+ DEBUG(3,("become_domain_master_query_success: Our address (%s) returned \
+in query for name %s (domain master browser name) on subnet %s. \
+Continuing with domain master code.\n",
+ inet_ntoa(ip), namestr(nmbname), subrec->subnet_name));
+
+ become_domain_master_stage1(subrec, nmbname->name);
+ }
+ else
+ {
+ DEBUG(0,("become_domain_master_query_success: There is already a domain \
+master browser at IP %s for workgroup %s registered on subnet %s.\n",
+ inet_ntoa(ip), nmbname->name, subrec->subnet_name));
+ }
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ This is normally a success condition as it then allows us to register
+ our own Domain Master Browser name.
+ ****************************************************************************/
+
+static void become_domain_master_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ /* If the query was unicast, and the error is not NAM_ERR (name didn't exist),
+ then this is a failure. Otherwise, not finding the name is what we want. */
+ if((subrec == unicast_subnet) && (fail_code != NAM_ERR))
+ {
+ DEBUG(0,("become_domain_master_query_fail: Error %d returned when \
+querying WINS server for name %s.\n",
+ fail_code, namestr(question_name)));
+ return;
+ }
+
+ /* Otherwise - not having the name allows us to register it. */
+ become_domain_master_stage1(subrec, question_name->name);
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser on all broadcast subnets.
+ ****************************************************************************/
+
+static void become_domain_master_browser_bcast(char *workgroup_name)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE))
+ {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,workgroup_name,0x1b,scope);
+
+ /*
+ * Check for our name on the given broadcast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL)
+ {
+ DEBUG(0,("become_domain_master_browser_bcast: At time %s attempting to become domain \
+master browser on workgroup %s on subnet %s\n", timestring(),
+ workgroup_name, subrec->subnet_name));
+
+ /* Send out a query to establish whether there's a
+ domain controller on the local subnet. If not,
+ we can become a domain controller.
+ */
+
+ DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \
+for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name));
+
+ query_name(subrec, nmbname.name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser by registering with WINS.
+ ****************************************************************************/
+
+static void become_domain_master_browser_wins(char *workgroup_name)
+{
+ struct work_record *work;
+
+ work = find_workgroup_on_subnet(unicast_subnet, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE))
+ {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname,workgroup_name,0x1b,scope);
+
+ /*
+ * Check for our name on the unicast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL)
+ {
+ DEBUG(0,("%s become_domain_master_browser_wins: attempting to become domain \
+master browser on workgroup %s, subnet %s.\n",
+ timestring(), workgroup_name, unicast_subnet->subnet_name));
+
+ /* Send out a query to establish whether there's a
+ domain master broswer registered with WINS. If not,
+ we can become a domain master browser.
+ */
+
+ DEBUG(0,("become_domain_master_browser_wins: querying WINS server at IP %s \
+for domain master browser name %s on workgroup %s\n",
+ inet_ntoa(unicast_subnet->myip), namestr(&nmbname), workgroup_name));
+
+ query_name(unicast_subnet, nmbname.name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+}
+
+/****************************************************************************
+ Add the domain logon server and domain master browser names
+ if we are set up to do so.
+ **************************************************************************/
+
+void add_domain_names(time_t t)
+{
+ static time_t lastrun = 0;
+
+ if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60)))
+ return;
+
+ lastrun = t;
+
+ /* Do the "internet group" - <1c> names. */
+ if (lp_domain_logons())
+ add_logon_names();
+
+ /* Do the domain master names. */
+ if(lp_domain_master())
+ {
+ if(we_are_a_wins_client())
+ {
+ /* We register the WORKGROUP<1b> name with the WINS
+ server first, and call add_domain_master_bcast()
+ only if this is successful.
+
+ This results in domain logon services being gracefully provided,
+ as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11.
+ 1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c,
+ cannot provide domain master / domain logon services.
+ */
+ become_domain_master_browser_wins(myworkgroup);
+ }
+ else
+ become_domain_master_browser_bcast(myworkgroup);
+ }
+}
diff --git a/source3/nmbd/nmbd_become_lmb.c b/source3/nmbd/nmbd_become_lmb.c
new file mode 100644
index 0000000000..7f54471a24
--- /dev/null
+++ b/source3/nmbd/nmbd_become_lmb.c
@@ -0,0 +1,534 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+extern pstring scope;
+extern pstring myname;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+static void insert_permanent_name_into_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname, uint16 nb_type )
+{
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL)
+ {
+ /* The name needs to be created on the unicast subnet. */
+ add_name_to_subnet( unicast_subnet, nmbname->name, nmbname->name_type,
+ nb_type, PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip);
+ }
+ else
+ {
+ /* The name already exists on the unicast subnet. Add our local
+ IP for the given broadcast subnet to the name. */
+ add_ip_to_name_record( namerec, subrec->myip);
+ }
+}
+
+/*******************************************************************
+ Utility function to remove a name from the unicast subnet.
+******************************************************************/
+
+static void remove_permanent_name_from_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname )
+{
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL)
+ {
+ /* Remove this broadcast subnet IP address from the name. */
+ remove_ip_from_name_record( namerec, subrec->myip);
+ if(namerec->num_ips == 0)
+ remove_name_from_namelist( unicast_subnet, namerec);
+ }
+}
+
+/*******************************************************************
+ Utility function always called to set our workgroup and server
+ state back to potential browser, or none.
+******************************************************************/
+
+static void reset_workgroup_state( struct subnet_record *subrec, char *workgroup_name )
+{
+ struct work_record *work;
+ struct server_record *servrec;
+ struct nmb_name nmbname;
+
+ if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL)
+ {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Update our server status - remove any master flag and replace
+ it with the potential browser flag. */
+ servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Reset our election flags. */
+ work->ElectionCriterion &= ~0x4;
+
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+
+ /* Forget who the local master browser was for
+ this workgroup. */
+
+ *work->local_master_browser_name = '\0';
+
+ /*
+ * Ensure the IP address of this subnet is not registered as one
+ * of the IP addresses of the WORKGROUP<1d> name on the unicast
+ * subnet. This undoes what we did below when we became a local
+ * master browser.
+ */
+
+ make_nmb_name(&nmbname, work->work_group, 0x1d, scope);
+
+ remove_permanent_name_from_unicast( subrec, &nmbname);
+
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release success function.
+******************************************************************/
+
+void unbecome_local_master_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ DEBUG(3,("unbecome_local_master_success: released name %s.\n",
+ namestr(released_name)));
+
+ /* Now reset the workgroup and server state. */
+ reset_workgroup_state( subrec, released_name->name );
+
+ DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \
+on subnet %s *****\n\n", myname, released_name->name, subrec->subnet_name));
+
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release fail function.
+******************************************************************/
+
+void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct name_record *namerec;
+
+ DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \
+Removing from namelist anyway.\n", namestr(fail_name)));
+
+ /* Do it anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Now reset the workgroup and server state. */
+ reset_workgroup_state( subrec, fail_name->name );
+
+ DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \
+on subnet %s *****\n\n", myname, fail_name->name, subrec->subnet_name));
+
+}
+
+/*******************************************************************
+ Utility function to remove the WORKGROUP<1d> name called by both
+ success and fail of releasing the MSBROWSE name.
+******************************************************************/
+
+void release_1d_name( struct subnet_record *subrec, char *workgroup_name)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, workgroup_name, 0x1d, scope);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL)
+ {
+ release_name(subrec, namerec,
+ unbecome_local_master_success,
+ unbecome_local_master_fail,
+ NULL);
+ }
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release success function.
+******************************************************************/
+
+static void release_msbrowse_name_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.",
+ namestr(released_name), subrec->subnet_name ));
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, released_name);
+
+ release_1d_name( subrec, userdata->data );
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release fail function.
+******************************************************************/
+
+static void release_msbrowse_name_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct name_record *namerec;
+
+ DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.",
+ namestr(fail_name), subrec->subnet_name ));
+
+ /* Release the name anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, fail_name);
+
+ release_1d_name( subrec, userdata->data );
+}
+
+/*******************************************************************
+ Unbecome the local master browser.
+******************************************************************/
+
+void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work)
+{
+ struct server_record *servrec;
+ struct name_record *namerec;
+ struct nmb_name nmbname;
+ struct userdata_struct *userdata;
+ char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1];
+
+ /* Sanity check. */
+
+ DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \
+on subnet %s\n",work->work_group, subrec->subnet_name));
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Set the state to unbecoming. */
+ work->mst_state = MST_UNBECOMING_MASTER;
+
+ /* Setup the userdata for the MSBROWSE name release. */
+ /* Setup the userdata_struct - this is copied so we can use
+ a stack variable for this. */
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ strcpy(userdata->data, work->work_group);
+
+ /* Deregister any browser names we may have. */
+ make_nmb_name(&nmbname, MSBROWSE, 0x1, scope);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL)
+ {
+ release_name(subrec, namerec,
+ release_msbrowse_name_success,
+ release_msbrowse_name_fail,
+ userdata);
+ }
+}
+
+/****************************************************************************
+ Success in registering the WORKGROUP<1d> name.
+ We are now *really* a local master browser.
+ ****************************************************************************/
+
+static void become_local_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ int i = 0;
+ struct server_record *sl;
+ struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, registered_name->name, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \
+on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
+
+ /* update our server status */
+ servrec->serv.type |= SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Add this name to the workgroup as local master browser. */
+ StrnCpy(work->local_master_browser_name, myname,
+ sizeof(work->local_master_browser_name)-1);
+
+ /* Count the number of servers we have on our list. If it's
+ less than 10 (just a heuristic) request the servers
+ to announce themselves.
+ */
+ for( sl = work->serverlist; sl != NULL; sl = sl->next)
+ i++;
+
+ if (i < 10)
+ {
+ /* Ask all servers on our local net to announce to us. */
+ broadcast_announce_request(subrec, work);
+ }
+
+ /*
+ * Now we are a local master on a broadcast subnet, we need to add
+ * the WORKGROUP<1d> name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We can create this name directly on
+ * the unicast subnet as a WINS server always returns true when registering
+ * this name, and discards the registration. We use the number of IP
+ * addresses registered to this name as a reference count, as we
+ * remove this broadcast subnet IP address from it when we stop becoming a local
+ * master browser for this broadcast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Reset the announce master browser timer so that we try and tell a domain
+ master browser as soon as possible that we are a local master browser. */
+ reset_announce_timer();
+
+ DEBUG(0,("\n***** Samba name server %s is now a local master browser for workgroup %s \
+on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name));
+
+}
+
+/****************************************************************************
+ Failed to register the WORKGROUP<1d> name.
+ ****************************************************************************/
+static void become_local_master_fail2(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, fail_name->name);
+
+ DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \
+Failed to become a local master browser.\n", namestr(fail_name), subrec->subnet_name));
+
+ if(!work)
+ {
+ DEBUG(0,("become_local_master_fail2: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+ return;
+ }
+
+ /* Roll back all the way by calling unbecome_local_master_browser(). */
+ unbecome_local_master_browser(subrec, work);
+}
+
+/****************************************************************************
+ Success in registering the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_stage1(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ char *work_name = userdata->data;
+ struct work_record *work = find_workgroup_on_subnet( subrec, work_name);
+
+ if(!work)
+ {
+ DEBUG(0,("become_local_master_stage1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n",
+ work->work_group));
+
+ work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */
+
+ /*
+ * We registered the MSBROWSE name on a broadcast subnet, now need to add
+ * the MSBROWSE name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We create this name directly on
+ * the unicast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Attempt to register the WORKGROUP<1d> name. */
+ register_name(subrec, work->work_group,0x1d,samba_nb_type,
+ become_local_master_stage2,
+ become_local_master_fail2,
+ NULL);
+}
+
+/****************************************************************************
+ Failed to register the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_fail1(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ char *work_name = rrec->userdata->data;
+ struct work_record *work = find_workgroup_on_subnet(subrec, work_name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup(work, myname)) == NULL)
+ {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ reset_workgroup_state( subrec, work->work_group );
+
+ DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, namestr(fail_name)));
+}
+
+/******************************************************************
+ Become the local master browser on a subnet.
+ This gets called if we win an election on this subnet.
+
+ Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1.
+ Stage 2: mst_state was MST_BACKUP - go to MST_MSB and register WORKGROUP<1d>.
+ Stage 3: mst_state was MST_MSB - go to MST_BROWSER.
+******************************************************************/
+
+void become_local_master_browser(struct subnet_record *subrec, struct work_record *work)
+{
+ struct server_record *servrec;
+ struct userdata_struct *userdata;
+ char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1];
+
+ /* Sanity check. */
+ if (!lp_local_master())
+ {
+ DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n"));
+ return;
+ }
+
+ if(!AM_POTENTIAL_MASTER_BROWSER(work))
+ {
+ DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n",
+ work->mst_state ));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \
+%s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n"));
+ work->mst_state = MST_BACKUP; /* an election win was successful */
+
+ work->ElectionCriterion |= 0x5;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Setup the userdata_struct - this is copied so we can use
+ a stack variable for this. */
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ strcpy(userdata->data, work->work_group);
+
+ /* Register the special browser group name. */
+ register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP,
+ become_local_master_stage1,
+ become_local_master_fail1,
+ userdata);
+}
diff --git a/source3/nmbd/nmbd_browserdb.c b/source3/nmbd/nmbd_browserdb.c
new file mode 100644
index 0000000000..b2db744370
--- /dev/null
+++ b/source3/nmbd/nmbd_browserdb.c
@@ -0,0 +1,184 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+#include "smb.h"
+
+extern int DEBUGLEVEL;
+
+/* This is our local master browser list database. */
+struct browse_cache_record *lmb_browserlist = NULL;
+
+/***************************************************************************
+Add a browser into the lmb list.
+**************************************************************************/
+
+static void add_to_lmb_browse_cache(struct browse_cache_record *browc)
+{
+ struct browse_cache_record *browc2;
+
+ if (lmb_browserlist == NULL)
+ {
+ lmb_browserlist = browc;
+ browc->prev = NULL;
+ browc->next = NULL;
+ return;
+ }
+
+ for (browc2 = lmb_browserlist; browc2->next; browc2 = browc2->next)
+ ;
+
+ browc2->next = browc;
+ browc->next = NULL;
+ browc->prev = browc2;
+}
+
+/*******************************************************************
+Remove a lmb browser entry.
+******************************************************************/
+
+void remove_lmb_browser_entry(struct browse_cache_record *browc)
+{
+ if (browc->prev)
+ browc->prev->next = browc->next;
+ if (browc->next)
+ browc->next->prev = browc->prev;
+
+ if (lmb_browserlist == browc)
+ lmb_browserlist = browc->next;
+
+ free((char *)browc);
+}
+
+/****************************************************************************
+Update a browser death time.
+****************************************************************************/
+
+void update_browser_death_time(struct browse_cache_record *browc)
+{
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = time(NULL) + (CHECK_TIME_MST_ANNOUNCE + 2)*60;
+}
+
+/****************************************************************************
+Create a browser entry.
+****************************************************************************/
+
+struct browse_cache_record *create_browser_in_lmb_cache(char *work_name, char *browser_name,
+ struct in_addr ip)
+{
+ struct browse_cache_record *browc;
+ time_t now = time(NULL);
+
+ browc = (struct browse_cache_record *)malloc(sizeof(*browc));
+
+ if (browc == NULL)
+ {
+ DEBUG(0,("create_browser_in_lmb_cache: malloc fail !\n"));
+ return(NULL);
+ }
+
+ bzero((char *)browc,sizeof(*browc));
+
+ /* For a new lmb entry we want to sync with it after one minute. This
+ will allow it time to send out a local announce and build its
+ browse list. */
+
+ browc->sync_time = now + 60;
+
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = now + (CHECK_TIME_MST_ANNOUNCE + 2)*60;
+
+ StrnCpy(browc->lmb_name, browser_name,sizeof(browc->lmb_name)-1);
+ StrnCpy(browc->work_group,work_name,sizeof(browc->work_group)-1);
+ strupper(browc->lmb_name);
+ strupper(browc->work_group);
+
+ browc->ip = ip;
+
+ add_to_lmb_browse_cache(browc);
+
+ DEBUG(3,("create_browser_in_lmb_cache: Added lmb cache entry for workgroup %s name %s IP %s ttl %d\n",
+ browc->work_group, browc->lmb_name, inet_ntoa(ip), browc->death_time));
+
+ return(browc);
+}
+
+/****************************************************************************
+Find a browser entry.
+****************************************************************************/
+
+struct browse_cache_record *find_browser_in_lmb_cache( char *browser_name )
+{
+ struct browse_cache_record *browc = NULL;
+
+ for( browc = lmb_browserlist; browc; browc = browc->next)
+ if(strequal( browser_name, browc->lmb_name))
+ break;
+
+ return browc;
+}
+
+/*******************************************************************
+ Expire timed out browsers in the browserlist.
+******************************************************************/
+
+void expire_lmb_browsers(time_t t)
+{
+ struct browse_cache_record *browc;
+ struct browse_cache_record *nextbrowc;
+
+ for (browc = lmb_browserlist; browc; browc = nextbrowc)
+ {
+ nextbrowc = browc->next;
+
+ if (browc->death_time < t)
+ {
+ DEBUG(3,("expire_lmb_browsers: Removing timed out lmb entry %s\n",browc->lmb_name));
+ remove_lmb_browser_entry(browc);
+ }
+ }
+}
+
+/*******************************************************************
+ Remove browsers from a named workgroup in the browserlist.
+******************************************************************/
+
+void remove_workgroup_lmb_browsers(char *work_group)
+{
+ struct browse_cache_record *browc;
+ struct browse_cache_record *nextbrowc;
+
+ for (browc = lmb_browserlist; browc; browc = nextbrowc)
+ {
+ nextbrowc = browc->next;
+
+ if (strequal(work_group, browc->work_group))
+ {
+ DEBUG(3,("remove_workgroup_browsers: Removing lmb entry %s\n",browc->lmb_name));
+ remove_lmb_browser_entry(browc);
+ }
+ }
+}
+
diff --git a/source3/nmbd/nmbd_browsesync.c b/source3/nmbd/nmbd_browsesync.c
new file mode 100644
index 0000000000..b899e2e8bc
--- /dev/null
+++ b/source3/nmbd/nmbd_browsesync.c
@@ -0,0 +1,458 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+#include "smb.h"
+
+extern int DEBUGLEVEL;
+extern pstring scope;
+extern struct in_addr ipzero;
+extern pstring myname;
+
+/* This is our local master browser list database. */
+extern struct browse_cache_record *lmb_browserlist;
+
+static struct work_record *call_work;
+static struct subnet_record *call_subrec;
+
+/*******************************************************************
+ This is the NetServerEnum callback.
+ ******************************************************************/
+
+static void callback(char *sname, uint32 stype, char *comment)
+{
+ struct work_record *work;
+
+ stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (stype & SV_TYPE_DOMAIN_ENUM)
+ {
+ /* See if we can find the workgroup on this subnet. */
+ if(( work = find_workgroup_on_subnet( call_subrec, sname )) != NULL)
+ {
+ /* We already know about this workgroup - update the ttl. */
+ update_workgroup_ttl( work, lp_max_ttl() );
+ }
+ else
+ {
+ /* Create the workgroup on the subnet. */
+ create_workgroup_on_subnet( call_subrec, sname, lp_max_ttl() );
+ }
+ }
+ else
+ {
+ /* Server entry. */
+ struct server_record *servrec;
+
+ work = call_work;
+
+ if(( servrec = find_server_in_workgroup( work, sname )) != NULL)
+ {
+ /* Check that this is not a locally known server - if so ignore the
+ entry. */
+ if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY))
+ {
+ /* We already know about this server - update the ttl. */
+ update_server_ttl(servrec, lp_max_ttl() );
+ /* Update the type. */
+ servrec->serv.type = stype;
+ }
+ }
+ else
+ {
+ /* Create the server in the workgroup. */
+ create_server_on_workgroup(work, sname,stype,lp_max_ttl(),comment);
+ }
+ }
+}
+
+/*******************************************************************
+ Synchronise browse lists with another browse server.
+ Log in on the remote server's SMB port to their IPC$ service,
+ do a NetServerEnum and update our server and workgroup databases.
+******************************************************************/
+
+static void sync_browse_lists(struct subnet_record *subrec, struct work_record *work,
+ char *name, int nm_type, struct in_addr ip, BOOL local)
+{
+ extern fstring local_machine;
+ static struct cli_state cli;
+ uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
+
+ DEBUG(2,("%s: sync_browse_lists: Sync browse lists with server %s<%02x> at IP %s for workgroup %s\n",
+ timestring(), name, nm_type, inet_ntoa(ip), work->work_group ));
+
+ /* Check we're not trying to sync with ourselves. This can happen if we are
+ a domain *and* a local master browser. */
+ if(ismyip(ip))
+ {
+ DEBUG(2,("sync_browse_lists: We are both a domain and a local master browser for workgroup %s. \
+Do not sync with ourselves.\n", work->work_group ));
+ return;
+ }
+
+ if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip))
+ {
+ DEBUG(0,("sync_browse_lists: Failed to start browse sync with %s\n", name));
+ return;
+ }
+
+ if (!cli_session_request(&cli, name, nm_type, local_machine))
+ {
+ DEBUG(0,("sync_browse_lists: %s rejected the browse sync session\n",name));
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_negprot(&cli))
+ {
+ DEBUG(0,("sync_browse_lists: %s rejected the negprot\n",name));
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_session_setup(&cli, "", "", 1, "", 0, work->work_group))
+ {
+ DEBUG(0,("sync_browse_lists: %s rejected the browse sync sessionsetup\n",
+ name));
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1))
+ {
+ DEBUG(0,("sync_browse_lists: %s refused browse sync IPC$ connect\n", name));
+ cli_shutdown(&cli);
+ return;
+ }
+
+ call_work = work;
+ call_subrec = subrec;
+
+ /* Fetch a workgroup list. */
+ cli_NetServerEnum(&cli, work->work_group,
+ local_type|SV_TYPE_DOMAIN_ENUM,
+ callback);
+
+ /* Now fetch a server list. */
+ cli_NetServerEnum(&cli, work->work_group,
+ local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
+ callback);
+
+ cli_shutdown(&cli);
+}
+
+/****************************************************************************
+As a domain master browser, do a sync with a local master browser.
+**************************************************************************/
+
+static void sync_with_lmb(struct browse_cache_record *browc)
+{
+ struct work_record *work;
+
+ if (!(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group))) {
+ DEBUG(0, ("sync_with_lmb: failed to get a \
+workgroup for a local master browser cache entry workgroup %s, server %s\n",
+ browc->work_group, browc->lmb_name));
+ return;
+ }
+
+ /* We should only be doing this if we are a domain master browser for
+ the given workgroup. Ensure this is so. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work))
+ {
+ DEBUG(0,("sync_with_lmb: We are trying to sync with a local master browser %s \
+for workgroup %s and we are not a domain master browser on this workgroup. Error !\n",
+ browc->lmb_name, browc->work_group));
+ return;
+ }
+
+ DEBUG(2, ("sync_with_lmb: Initiating sync with local master browser %s<0x20> at IP %s for \
+workgroup %s\n", browc->lmb_name, inet_ntoa(browc->ip), browc->work_group));
+
+ sync_browse_lists(unicast_subnet, work, browc->lmb_name, 0x20, browc->ip, True);
+
+ browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
+}
+
+/****************************************************************************
+Sync or expire any local master browsers.
+**************************************************************************/
+
+void dmb_expire_and_sync_browser_lists(time_t t)
+{
+ static time_t last_run = 0;
+ struct browse_cache_record *browc;
+
+ /* Only do this every 20 seconds. */
+ if (t - last_run < 20)
+ return;
+
+ last_run = t;
+
+ expire_lmb_browsers(t);
+
+ for (browc = lmb_browserlist; browc; browc = browc->next)
+ {
+ if (browc->sync_time < t)
+ sync_with_lmb(browc);
+ }
+}
+
+/****************************************************************************
+As a local master browser, send an announce packet to the domain master browser.
+**************************************************************************/
+
+static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
+{
+ pstring outbuf;
+ char *p;
+
+ if(ismyip(work->dmb_addr))
+ {
+ DEBUG(2,("announce_local_master_browser_to_domain_master_browser: We are both a domain \
+and a local master browser for workgroup %s. \
+Do not announce to ourselves.\n", work->work_group ));
+ return;
+ }
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_MasterAnnouncement;
+ p++;
+
+ StrnCpy(p,myname,15);
+ strupper(p);
+ p = skip_string(p,1);
+
+ DEBUG(4,("announce_local_master_browser_to_domain_master_browser: Sending local master announce \
+to %s for workgroup %s.\n", namestr(&work->dmb_name), work->work_group ));
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ myname, 0x0, work->dmb_name.name, 0x20, work->dmb_addr, FIRST_SUBNET->myip);
+
+}
+
+/****************************************************************************
+As a local master browser, do a sync with a domain master browser.
+**************************************************************************/
+
+static void sync_with_dmb(struct work_record *work)
+{
+ DEBUG(2, ("sync_with_dmb: Initiating sync with domain master browser %s at IP %s for \
+workgroup %s\n", namestr(&work->dmb_name), inet_ntoa(work->dmb_addr), work->work_group));
+
+ sync_browse_lists(unicast_subnet, work, work->dmb_name.name, work->dmb_name.name_type,
+ work->dmb_addr, False);
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP succeeds.
+****************************************************************************/
+
+static void domain_master_node_status_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct res_rec *answers,
+ struct in_addr from_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
+
+ if(work == NULL)
+ {
+ DEBUG(0,("domain_master_node_status_success: Unable to find workgroup %s on subnet %s.\n",
+ userdata->data, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("domain_master_node_status_success: Success in node status for workgroup %s from ip %s\n",
+ work->work_group, inet_ntoa(from_ip) ));
+
+ /* Go through the list of names found at answers->rdata and look for
+ the first SERVER<0x20> name. */
+
+ if(answers->rdata != NULL)
+ {
+ char *p = answers->rdata;
+ int numnames = CVAL(p, 0);
+
+ p += 1;
+
+ while (numnames--)
+ {
+ char qname[17];
+ uint16 nb_flags;
+ int name_type;
+
+ StrnCpy(qname,p,15);
+ name_type = CVAL(p,15);
+ nb_flags = get_nb_flags(&p[16]);
+ trim_string(qname,NULL," ");
+
+ p += 18;
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
+ {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, qname, name_type, scope);
+
+ /* Copy the dmb name and IP address
+ into the workgroup struct. */
+
+ work->dmb_name = nmbname;
+ putip((char *)&work->dmb_addr, &from_ip);
+
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ break;
+ }
+ }
+ }
+ else
+ DEBUG(0,("domain_master_node_status_success: Failed to find a SERVER<0x20> \
+name in reply from IP %s.\n", inet_ntoa(from_ip) ));
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void domain_master_node_status_fail(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+
+ DEBUG(0,("domain_master_node_status_fail: Doing a node status request to \
+the domain master browser for workgroup %s at IP %s failed. Cannot sync browser \
+lists.\n", userdata->data, inet_ntoa(rrec->packet->ip) ));
+
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+****************************************************************************/
+
+static void find_domain_master_name_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata_in,
+ struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+ /*
+ * Unfortunately, finding the IP address of the Domain Master Browser,
+ * as we have here, is not enough. We need to now do a sync to the
+ * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
+ * respond to the SMBSERVER name. To get this name from IP
+ * address we do a Node status request, and look for the first
+ * NAME<0x20> in the response, and take that as the server name.
+ * We also keep a cache of the Domain Master Browser name for this
+ * workgroup in the Workgroup struct, so that if the same IP addess
+ * is returned every time, we don't need to do the node status
+ * request.
+ */
+
+ struct work_record *work;
+ struct nmb_name nmbname;
+ struct userdata_struct *userdata;
+ char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1];
+
+ if (!(work = find_workgroup_on_subnet(subrec, q_name->name))) {
+ DEBUG(0, ("find_domain_master_name_query_success: failed to find \
+workgroup %s\n", q_name->name ));
+ return;
+ }
+
+ /* First check if we already have a dmb for this workgroup. */
+
+ if(!ip_equal(work->dmb_addr, ipzero) && ip_equal(work->dmb_addr, answer_ip))
+ {
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ return;
+ }
+ else
+ putip((char *)&work->dmb_addr, &ipzero);
+
+ /* Now initiate the node status request. */
+ bzero((char *)&nmbname, sizeof(nmbname));
+ nmbname.name[0] = '*';
+
+ /* Put the workgroup name into the userdata so we know
+ what workgroup we're talking to when the reply comes
+ back. */
+
+ /* Setup the userdata_struct - this is copied so we can use
+ a stack variable for this. */
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ strcpy(userdata->data, work->work_group);
+
+ node_status( subrec, &nmbname, answer_ip,
+ domain_master_node_status_success,
+ domain_master_node_status_fail,
+ userdata);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ ****************************************************************************/
+static void find_domain_master_name_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ DEBUG(0,("find_domain_master_name_query_fail: Unable to find the Domain Master \
+Browser name %s for the workgroup %s. Unable to sync browse lists in this workgroup.\n",
+ namestr(question_name), question_name->name ));
+}
+
+/****************************************************************************
+As a local master browser for a workgroup find the domain master browser
+name, announce ourselves as local master browser to it and then pull the
+full domain browse lists from it onto the given subnet.
+**************************************************************************/
+
+void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname,work->work_group,0x1b,scope);
+
+ /* First, query for the WORKGROUP<1b> name from the WINS server. */
+ query_name(unicast_subnet, nmbname.name, nmbname.name_type,
+ find_domain_master_name_query_success,
+ find_domain_master_name_query_fail,
+ NULL);
+
+}
diff --git a/source3/nmbd/nmbd_elections.c b/source3/nmbd/nmbd_elections.c
new file mode 100644
index 0000000000..5c3c4c7a01
--- /dev/null
+++ b/source3/nmbd/nmbd_elections.c
@@ -0,0 +1,348 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+
+/* Election parameters. */
+extern time_t StartupTime;
+
+/****************************************************************************
+ Send an election datagram packet.
+**************************************************************************/
+static void send_election_dgram(struct subnet_record *subrec, char *workgroup_name,
+ uint32 criterion, int timeup,char *server_name)
+{
+ pstring outbuf;
+ char *p;
+
+ DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
+ workgroup_name, subrec->subnet_name ));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_Election; /* Election opcode. */
+ p++;
+
+ CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
+ SIVAL(p,1,criterion);
+ SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
+ p += 13;
+ pstrcpy(p,server_name);
+ strupper(p);
+ p = skip_string(p,1);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ server_name, 0,
+ workgroup_name, 0x1e,
+ subrec->bcast_ip, subrec->myip);
+}
+
+/*******************************************************************
+ We found a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *answer_name,
+ struct in_addr answer_ip, struct res_rec *rrec)
+{
+ DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
+IP %s (just checking).\n", answer_name->name, inet_ntoa(answer_ip) ));
+}
+
+/*******************************************************************
+ We failed to find a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int fail_code)
+{
+ char *workgroup_name = question_name->name;
+ struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+ if(work == NULL)
+ {
+ DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
+ workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if (strequal(work->work_group, myworkgroup))
+ {
+
+ if (lp_local_master())
+ {
+ /* We have discovered that there is no local master
+ browser, and we are configured to initiate
+ an election that we will participate in.
+ */
+ DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
+ work->work_group, subrec->subnet_name ));
+
+ /* Setting this means we will participate when the
+ election is run in run_elections(). */
+ work->needelection = True;
+ }
+ else
+ {
+ /* We need to force an election, because we are configured
+ not to become the local master, but we still need one,
+ having detected that one doesn't exist.
+ */
+ send_election_dgram(subrec, work->work_group, 0, 0, myname);
+ }
+ }
+}
+
+/*******************************************************************
+ Ensure there is a local master browser for a workgroup on our
+ broadcast interfaces.
+******************************************************************/
+
+void check_master_browser_exists(time_t t)
+{
+ static time_t lastrun=0;
+ struct subnet_record *subrec;
+ char *workgroup_name = myworkgroup;
+
+ if (!lastrun)
+ lastrun = t;
+
+ if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
+ return;
+
+ lastrun = t;
+
+ dump_workgroups();
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work))
+ {
+ /* Do a name query for the local master browser on this net. */
+ query_name( subrec, work->work_group, 0x1d,
+ check_for_master_browser_success,
+ check_for_master_browser_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/*******************************************************************
+ Run an election.
+******************************************************************/
+
+void run_elections(time_t t)
+{
+ static time_t lastime = 0;
+
+ struct subnet_record *subrec;
+
+ /* Send election packets once a second - note */
+ if (lastime && (t - lastime <= 0))
+ return;
+
+ lastime = t;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ if (work->RunningElection)
+ {
+ send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
+ t - StartupTime, myname);
+
+ if (work->ElectionCount++ >= 4)
+ {
+ /* Won election (4 packets were sent out uncontested. */
+ DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->RunningElection = False;
+
+ become_local_master_browser(subrec, work);
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************
+ Determine if I win an election.
+******************************************************************/
+
+static BOOL win_election(struct work_record *work, int version,
+ uint32 criterion, int timeup, char *server_name)
+{
+ int mytimeup = time(NULL) - StartupTime;
+ uint32 mycriterion = work->ElectionCriterion;
+
+ /* If local master is false then never win
+ in election broadcasts. */
+ if(!lp_local_master())
+ {
+ DEBUG(3,("win_election: Losing election as local master == False\n"));
+ return False;
+ }
+
+ DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
+ version, ELECTION_VERSION,
+ criterion, mycriterion,
+ timeup, mytimeup,
+ server_name, myname));
+
+ if (version > ELECTION_VERSION)
+ return(False);
+ if (version < ELECTION_VERSION)
+ return(True);
+
+ if (criterion > mycriterion)
+ return(False);
+ if (criterion < mycriterion)
+ return(True);
+
+ if (timeup > mytimeup)
+ return(False);
+ if (timeup < mytimeup)
+ return(True);
+
+ if (strcasecmp(myname, server_name) > 0)
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ Process an incoming election datagram packet.
+******************************************************************/
+
+void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int version = CVAL(buf,0);
+ uint32 criterion = IVAL(buf,1);
+ int timeup = IVAL(buf,5)/1000;
+ char *server_name = buf+13;
+ struct work_record *work;
+ char *workgroup_name = dgram->dest_name.name;
+
+ server_name[15] = 0;
+
+ DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
+ server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
+
+ DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
+
+ if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+ {
+ DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
+ workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if (!strequal(work->work_group, myworkgroup))
+ {
+ DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
+is not my workgroup.\n", work->work_group, subrec->subnet_name ));
+ return;
+ }
+
+ if (win_election(work, version,criterion,timeup,server_name))
+ {
+ /* We take precedence over the requesting server. */
+ if (!work->RunningElection)
+ {
+ /* We weren't running an election - start running one. */
+
+ work->needelection = True;
+ work->ElectionCount=0;
+ }
+
+ /* Note that if we were running an election for this workgroup on this
+ subnet already, we just ignore the server we take precedence over. */
+ }
+ else
+ {
+ /* We lost. Stop participating. */
+ work->needelection = False;
+
+ if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work))
+ {
+ work->RunningElection = False;
+ DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ unbecome_local_master_browser(subrec, work);
+ }
+ }
+}
+
+/****************************************************************************
+ This function looks over all the workgroups known on all the broadcast
+ subnets and decides if a browser election is to be run on that workgroup.
+ It returns True if any election packets need to be sent (this will then
+ be done by run_elections().
+***************************************************************************/
+
+BOOL check_elections(void)
+{
+ struct subnet_record *subrec;
+ BOOL run_any_election = False;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ run_any_election |= work->RunningElection;
+
+ /* Only start an election if we are in the potential browser state. */
+ if (work->needelection && !work->RunningElection && AM_POTENTIAL_MASTER_BROWSER(work))
+ {
+ DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->ElectionCount = 0;
+ work->RunningElection = True;
+ work->needelection = False;
+ }
+ }
+ }
+ return run_any_election;
+}
diff --git a/source3/nmbd/nmbd_incomingdgrams.c b/source3/nmbd/nmbd_incomingdgrams.c
new file mode 100644
index 0000000000..24b4f33838
--- /dev/null
+++ b/source3/nmbd/nmbd_incomingdgrams.c
@@ -0,0 +1,625 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+
+#if 0
+
+/* XXXX note: This function is currently unsuitable for use, as it
+ does not properly check that a server is in a fit state to become
+ a backup browser before asking it to be one.
+ The code is left here to be worked on at a later date.
+*/
+
+/****************************************************************************
+Tell a server to become a backup browser
+**************************************************************************/
+
+void tell_become_backup(void)
+{
+ struct subnet_record *subrec;
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ struct server_record *servrec;
+ int num_servers = 0;
+ int num_backups = 0;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ num_servers++;
+
+ if (is_myname(servrec->serv.name))
+ continue;
+
+ if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER)
+ {
+ num_backups++;
+ continue;
+ }
+
+ if (servrec->serv.type & SV_TYPE_MASTER_BROWSER)
+ continue;
+
+ if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER))
+ continue;
+
+ DEBUG(3,("num servers: %d num backups: %d\n",
+ num_servers, num_backups));
+
+ /* make first server a backup server. thereafter make every
+ tenth server a backup server */
+ if (num_backups != 0 && (num_servers+9) / num_backups > 10)
+ continue;
+
+ DEBUG(2,("sending become backup to %s %s for %s\n",
+ servrec->serv.name, inet_ntoa(subrec->bcast_ip),
+ work->work_group));
+
+ /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+ do_announce_request(servrec->serv.name, work->work_group,
+ ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip);
+ }
+ }
+ }
+}
+#endif
+
+/*******************************************************************
+ Process an incoming host announcement packet.
+*******************************************************************/
+
+void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ char *announce_name = buf+5;
+ uint32 servertype = IVAL(buf,23);
+ char *comment = buf+31;
+ struct work_record *work;
+ struct server_record *servrec;
+ char *work_name;
+ char *source_name = dgram->source_name.name;
+
+ comment[43] = 0;
+
+ DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ namestr(&dgram->dest_name),announce_name));
+
+ DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype,comment));
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* A host announcement must be sent to the name WORKGROUP<1d>. */
+ if(dgram->dest_name.name_type != 0x1d)
+ {
+ DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1d. Allowing packet anyway.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ /* Change it so it was. */
+ dgram->dest_name.name_type = 0x1d;
+ }
+
+ /* For a host announce the workgroup name is the destination name. */
+ work_name = dgram->dest_name.name;
+
+ /*
+ * We are being very agressive here in adding a workgroup
+ * name on the basis of a host announcing itself as being
+ * in that workgroup. Maybe we should wait for the workgroup
+ * announce instead ? JRA.
+ */
+
+ if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL)
+ {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, announce_name))==NULL)
+ {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, announce_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ }
+ else
+ {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl( servrec, ttl);
+ StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+ }
+
+ subrec->work_changed = True;
+}
+
+/*******************************************************************
+ Process an incoming WORKGROUP announcement packet.
+*******************************************************************/
+
+void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ char *workgroup_announce_name = buf+5;
+ uint32 servertype = IVAL(buf,23);
+ char *master_name = buf+31;
+ struct work_record *work;
+ char *source_name = dgram->source_name.name;
+
+ master_name[43] = 0;
+
+ DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \
+%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ namestr(&dgram->dest_name),workgroup_announce_name));
+
+ DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n",
+ ttl, servertype, master_name));
+
+ /* Workgroup announcements must only go to the MSBROWSE name. */
+ if (!strequal(dgram->dest_name.name, MSBROWSE) || (dgram->dest_name.name_type != 0x1))
+ {
+ DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n",
+ inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+ return;
+ }
+
+ if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL)
+ {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL)
+ return;
+ }
+ else
+ {
+ /* Update the workgroup death_time. */
+ update_workgroup_ttl(work, ttl);
+ }
+
+ if(*work->local_master_browser_name == '\0')
+ {
+ /* Set the master browser name. */
+ StrnCpy(work->local_master_browser_name, master_name,
+ sizeof(work->local_master_browser_name)-1);
+
+ }
+
+ subrec->work_changed = True;
+}
+
+/*******************************************************************
+ Process an incoming local master browser announcement packet.
+*******************************************************************/
+
+void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ char *server_name = buf+5;
+ uint32 servertype = IVAL(buf,23);
+ char *comment = buf+31;
+ char *work_name;
+ struct work_record *work;
+ struct server_record *servrec;
+ char *source_name = dgram->source_name.name;
+
+ comment[43] = 0;
+
+ DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ namestr(&dgram->dest_name),server_name));
+
+ DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype, comment));
+
+ /* A local master announcement must be sent to the name WORKGROUP<1e>. */
+ if(dgram->dest_name.name_type != 0x1e)
+ {
+ DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1e. Ignoring packet.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ return;
+ }
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* For a local master announce the workgroup name is the destination name. */
+ work_name = dgram->dest_name.name;
+
+ if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL)
+ {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ return;
+ }
+
+ /* If we think we're the local master browser for this workgroup,
+ we should never have got this packet. We don't see our own
+ packets.
+ */
+ if(AM_LOCAL_MASTER_BROWSER(work))
+ {
+ DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \
+a local master browser for workgroup %s and we think we are master. Forcing election.\n",
+ server_name, inet_ntoa(p->ip), work_name));
+
+ /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when
+ they have become a local master browser once, they will never
+ stop sending local master announcements. To fix this we send
+ them a reset browser packet, with level 0x2 on the __SAMBA__
+ name that only they should be listening to. */
+
+ send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip);
+
+ /* We should demote ourself and force an election. */
+
+ unbecome_local_master_browser( subrec, work);
+
+ /* The actual election requests are handled in
+ nmbd_election.c */
+
+ work->needelection = True;
+ return;
+ }
+
+ /* Find the server record on this workgroup. If it doesn't exist, add it. */
+
+ if((servrec = find_server_in_workgroup( work, server_name))==NULL)
+ {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, server_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ }
+ else
+ {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl(servrec, ttl);
+ StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+ }
+
+ /* Set the master browser name. */
+ StrnCpy(work->local_master_browser_name, server_name,
+ sizeof(work->local_master_browser_name)-1);
+
+ subrec->work_changed = True;
+}
+
+/*******************************************************************
+ Process a domain master announcement frame.
+ Domain master browsers receive these from local masters. The Domain
+ master should then issue a sync with the local master, asking for
+ that machines local server list.
+******************************************************************/
+
+void process_master_browser_announce(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ char *local_master_name = buf;
+ struct work_record *work;
+ struct browse_cache_record *browrec;
+
+ local_master_name[15] = 0;
+
+ DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n",
+ local_master_name, inet_ntoa(p->ip)));
+
+ if (!lp_domain_master())
+ {
+ DEBUG(0,("process_master_browser_announce: Not configured as domain \
+master - ignoring master announce.\n"));
+ return;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, myworkgroup)) == NULL)
+ {
+ DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n",
+ myworkgroup, subrec->subnet_name));
+ return;
+ }
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work))
+ {
+ DEBUG(0,("process_master_browser_announce: Local master announce made to us from \
+%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip)));
+ return;
+ }
+
+ /* Add this host as a local master browser entry on the browse lists.
+ This causes a sync request to be made to it at a later date.
+ */
+
+ if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL)
+ {
+ /* Add it. */
+ create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip);
+ }
+ else
+ update_browser_death_time(browrec);
+}
+
+/****************************************************************************
+ Send a backup list response.
+*****************************************************************************/
+
+static void send_backup_list_response(struct subnet_record *subrec,
+ struct work_record *work,
+ struct nmb_name *send_to_name,
+ unsigned char max_number_requested,
+ uint32 token, struct in_addr sendto_ip)
+{
+ char outbuf[1024];
+ char *p, *countptr, *nameptr;
+ int count = 0;
+ int len;
+ struct server_record *servrec;
+
+ bzero(outbuf,sizeof(outbuf));
+
+ DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n",
+ work->work_group, namestr(send_to_name), inet_ntoa(sendto_ip)));
+
+ p = outbuf;
+
+ SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */
+ p++;
+
+ countptr = p;
+ p++;
+
+ SIVAL(p,0,token); /* The sender's unique info. */
+ p += 4;
+
+ nameptr = p;
+
+ /* We always return at least one name - our own. */
+ count = 1;
+ StrnCpy(p,myname,15);
+ strupper(p);
+ p = skip_string(p,1);
+
+ /* Look for backup browsers in this workgroup. */
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ len = PTR_DIFF(p, outbuf);
+ if((sizeof(outbuf) - len) < 16)
+ break;
+
+ if(count >= max_number_requested)
+ break;
+
+ if(strnequal(servrec->serv.name, myname,15))
+ continue;
+
+ if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER))
+ continue;
+
+ StrnCpy(p, servrec->serv.name, 15);
+ strupper(p);
+ count++;
+
+ DEBUG(5,("send_backup_list_response: Adding server %s number %d\n",
+ p, count));
+
+ p = skip_string(p,1);
+ }
+
+ SCVAL(countptr, 0, count);
+
+ len = PTR_DIFF(p, outbuf);
+
+ DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n",
+ send_to_name->name, inet_ntoa(sendto_ip), count));
+
+ send_mailslot(True, BROWSE_MAILSLOT,
+ outbuf,PTR_DIFF(p,outbuf),
+ myname, 0,
+ send_to_name->name,0,
+ sendto_ip, subrec->myip);
+}
+
+/*******************************************************************
+ Process a send backup list request packet.
+
+ A client sends a backup list request to ask for a list of servers on
+ the net that maintain server lists for a domain. A server is then
+ chosen from this list to send NetServerEnum commands to to list
+ available servers.
+
+********************************************************************/
+
+void process_get_backup_list_request(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ unsigned char max_number_requested = CVAL(buf,0);
+ uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */
+ int name_type = dgram->dest_name.name_type;
+ char *workgroup_name = dgram->dest_name.name;
+
+ DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n",
+ namestr(&dgram->source_name), inet_ntoa(p->ip),
+ namestr(&dgram->dest_name)));
+
+ /* We have to be a master browser, or a domain master browser
+ for the requested workgroup. That means it must be our
+ workgroup. */
+
+ if(strequal(workgroup_name, myworkgroup) == False)
+ {
+ DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ return;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+ {
+ DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, subrec->subnet_name));
+ return;
+ }
+
+ if(name_type == 0x1b)
+ {
+ /* We must be a domain master browser in order to
+ process this packet. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work))
+ {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a domain master browser.\n", workgroup_name));
+ return;
+ }
+ }
+ else if (name_type == 0x1d)
+ {
+ /* We must be a local master browser in order to
+ process this packet. */
+
+ if(!AM_LOCAL_MASTER_BROWSER(work))
+ {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a local master browser.\n", workgroup_name));
+ return;
+ }
+ }
+ else
+ {
+ DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n",
+ name_type));
+ return;
+ }
+
+ send_backup_list_response(subrec, work, &dgram->source_name,
+ max_number_requested, token, p->ip);
+}
+
+/*******************************************************************
+ Process a reset browser state packet.
+
+ Diagnostic packet:
+ 0x1 - Stop being a master browser and become a backup browser.
+ 0x2 - Discard browse lists, stop being a master browser, try again.
+ 0x4 - Stop being a master browser forever.
+
+******************************************************************/
+
+void process_reset_browser(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int state = CVAL(buf,0);
+ struct subnet_record *sr;
+
+ DEBUG(1,("process_reset_browser: received diagnostic browser reset \
+request from %s IP %s state=0x%X\n",
+ namestr(&dgram->source_name), inet_ntoa(p->ip), state));
+
+ /* Stop being a local master browser on all our broadcast subnets. */
+ if (state & 0x1)
+ {
+ for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr))
+ {
+ struct work_record *work;
+ for (work = sr->workgrouplist; work; work = work->next)
+ {
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ {
+ unbecome_local_master_browser(sr, work);
+ work->needelection = True;
+ }
+ }
+ }
+ }
+
+ /* Discard our browse lists. */
+ if (state & 0x2)
+ {
+ /*
+ * Calling expire_workgroups_and_servers with a -1
+ * time causes all servers not marked with a PERMANENT_TTL
+ * on the workgroup lists to be discarded, and all
+ * workgroups with empty server lists to be discarded.
+ * This means we keep our own server names and workgroup
+ * as these have a PERMANENT_TTL.
+ */
+
+ expire_workgroups_and_servers(-1);
+ }
+
+ /* Request to stop browsing altogether. */
+ if (state & 0x4)
+ DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n"));
+}
+
+/*******************************************************************
+ Process a announcement request packet.
+ We don't respond immediately, we just check it's a request for
+ out workgroup and then set the flag telling the announce code
+ in nmbd_sendannounce.c:announce_my_server_names that an
+ announcement is needed soon.
+ ******************************************************************/
+
+void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ char *workgroup_name = dgram->dest_name.name;
+
+ DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n",
+ namestr(&dgram->source_name), inet_ntoa(p->ip),
+ namestr(&dgram->dest_name)));
+
+ /* We only send announcement requests on our workgroup. */
+ if(strequal(workgroup_name, myworkgroup) == False)
+ {
+ DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ return;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+ {
+ DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+ workgroup_name));
+ return;
+ }
+
+ work->needannounce = True;
+}
diff --git a/source3/nmbd/nmbd_incomingrequests.c b/source3/nmbd/nmbd_incomingrequests.c
new file mode 100644
index 0000000000..ff4bb07a45
--- /dev/null
+++ b/source3/nmbd/nmbd_incomingrequests.c
@@ -0,0 +1,556 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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.
+
+ This file contains all the code to process NetBIOS requests coming
+ in on port 137. It does not deal with the code needed to service
+ WINS server requests, but only broadcast and unicast requests.
+
+*/
+
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+/****************************************************************************
+Send a name release response.
+**************************************************************************/
+
+static void send_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name release packet on a broadcast subnet.
+Ignore it if it's not one of our names.
+****************************************************************************/
+
+void process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct in_addr owner_ip;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec;
+ int rcode = 0;
+
+ putip((char *)&owner_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast)
+ {
+ /* We should only get broadcast name release packets here.
+ Anyone trying to release unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_release_request: unicast name release request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ namestr(question), inet_ntoa(owner_ip), subrec->subnet_name));
+
+ send_name_release_response(FMT_ERR, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_release_request: Name release on name %s, \
+subnet %s from owner IP %s\n",
+ namestr(&nmb->question.question_name),
+ subrec->subnet_name, inet_ntoa(owner_ip)));
+
+ /* If someone is releasing a broadcast group name, just ignore it. */
+ if( group && !ismyip(owner_ip) )
+ return;
+
+ namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_ANY_NAME);
+
+ /* We only care about someone trying to release one of our names. */
+ if (namerec && ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)))
+ {
+ rcode = ACT_ERR;
+ DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \
+on subnet %s being rejected as it is one of our names.\n",
+ namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name));
+ }
+
+ if(rcode == 0)
+ return;
+
+ /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+ send_name_release_response(rcode, p);
+}
+
+/****************************************************************************
+Send a name registration response.
+**************************************************************************/
+
+static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name refresh request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_refresh_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast)
+ {
+ /* We should only get broadcast name refresh packets here.
+ Anyone trying to refresh unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_refresh_request: unicast name registration request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ /* Just log a message. We really don't care about broadcast name
+ refreshes. */
+
+ DEBUG(3,("process_name_refresh_request: Name refresh for name %s \
+IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+}
+
+/****************************************************************************
+Process a name registration request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = nmb->additional->ttl;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast)
+ {
+ /* We should only get broadcast name registration packets here.
+ Anyone trying to register unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_registration_request: unicast name registration request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_registration_request: Name registration for name %s \
+IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ /* See if the name already exists. */
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if (!group)
+ {
+ /* Unique name. */
+
+ if ((namerec != NULL) &&
+ ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME) ||
+ NAME_GROUP(namerec))
+ )
+ {
+ /* No-one can register one of Samba's names, nor can they
+ register a name that's a group name as a unique name */
+
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ }
+ else if(namerec != NULL)
+ {
+ /* Update the namelist record with the new information. */
+ namerec->ip[0] = from_ip;
+ update_name_ttl(namerec, ttl);
+
+ DEBUG(3,("process_name_registration_request: Updated name record %s \
+with IP %s on subnet %s\n",namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+ }
+ else
+ {
+ /* Group name. */
+
+ if((namerec != NULL) && !NAME_GROUP(namerec) &&
+ ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))
+ )
+ {
+ /* Disallow group names when we have a unique name. */
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+This is used to sort names for a name status into a sensible order.
+We put our own names first, then in alphabetical order.
+**************************************************************************/
+
+static int status_compare(char *n1,char *n2)
+{
+ extern pstring myname;
+ int l1,l2,l3;
+
+ /* It's a bit tricky because the names are space padded */
+ for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ;
+ for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ;
+ l3 = strlen(myname);
+
+ if ((l1==l3) && strncmp(n1,myname,l3) == 0 &&
+ (l2!=l3 || strncmp(n2,myname,l3) != 0))
+ return -1;
+
+ if ((l2==l3) && strncmp(n2,myname,l3) == 0 &&
+ (l1!=l3 || strncmp(n1,myname,l3) != 0))
+ return 1;
+
+ return memcmp(n1,n2,18);
+}
+
+
+/****************************************************************************
+ Process a node status query
+ ****************************************************************************/
+
+void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char *qname = nmb->question.question_name.name;
+ int ques_type = nmb->question.question_name.name_type;
+ char rdata[MAX_DGRAM_SIZE];
+ char *countptr, *buf, *bufend, *buf0;
+ int names_added,i;
+ struct name_record *namerec;
+
+ DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s.\n", namestr(&nmb->question.question_name), inet_ntoa(p->ip),
+ subrec->subnet_name));
+
+ if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name,
+ FIND_SELF_NAME)) == 0)
+ {
+ DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s - name not found.\n", namestr(&nmb->question.question_name),
+ inet_ntoa(p->ip), subrec->subnet_name));
+
+ return;
+ }
+
+ /* XXXX hack, we should calculate exactly how many will fit. */
+ bufend = &rdata[MAX_DGRAM_SIZE] - 18;
+ countptr = buf = rdata;
+ buf += 1;
+ buf0 = buf;
+
+ names_added = 0;
+
+ namerec = subrec->namelist;
+
+ while (buf < bufend)
+ {
+ if ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))
+ {
+ int name_type = namerec->name.name_type;
+
+ if (!strequal(namerec->name.name,"*") &&
+ !strequal(namerec->name.name,"__SAMBA__") &&
+ (name_type < 0x1b || name_type >= 0x20 ||
+ ques_type < 0x1b || ques_type >= 0x20 ||
+ strequal(qname, namerec->name.name)))
+ {
+ /* Start with the name. */
+ bzero(buf,18);
+ sprintf(buf,"%-15.15s",namerec->name.name);
+ strupper(buf);
+
+ /* Put the name type and netbios flags in the buffer. */
+ buf[15] = name_type;
+ set_nb_flags(&buf[16],namerec->nb_flags);
+ buf[16] |= NB_ACTIVE; /* all our names are active */
+
+ buf += 18;
+
+ names_added++;
+ }
+ }
+
+ /* Remove duplicate names. */
+ qsort(buf0,names_added,18,QSORT_CAST status_compare);
+
+ for (i=1;i<names_added;i++)
+ {
+ if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0)
+ {
+ names_added--;
+ if (names_added == i)
+ break;
+ memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+ i--;
+ }
+ }
+
+ buf = buf0 + 18*names_added;
+
+ namerec = namerec->next;
+
+ if (!namerec)
+ {
+ /* End of the subnet specific name list. Now
+ add the names on the unicast subnet . */
+ struct subnet_record *uni_subrec = unicast_subnet;
+
+ if (uni_subrec != subrec)
+ {
+ subrec = uni_subrec;
+ namerec = subrec->namelist;
+ }
+ }
+ if (!namerec)
+ break;
+
+ }
+
+ SCVAL(countptr,0,names_added);
+
+ /* We don't send any stats as they could be used to attack
+ the protocol. */
+ bzero(buf,64);
+
+ buf += 46;
+
+ /* Send a NODE STATUS RESPONSE */
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_STATUS, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ PTR_DIFF(buf,rdata)); /* data length. */
+}
+
+
+/***************************************************************************
+Process a name query.
+
+For broadcast name queries:
+
+ - Only reply if the query is for one of YOUR names.
+ - NEVER send a negative response to a broadcast query.
+
+****************************************************************************/
+
+void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ int name_type = question->name_type;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ int ttl=0;
+ int rcode = 0;
+ char *prdata = NULL;
+ char rdata[6];
+ BOOL success = False;
+ struct name_record *namerec = NULL;
+ int reply_data_len = 0;
+ int i;
+
+ DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n",
+ inet_ntoa(p->ip), subrec->subnet_name, namestr(question)));
+
+ /* Look up the name in the cache - if the request is a broadcast request that
+ came from a subnet we don't know about then search all the broadcast subnets
+ for a match (as we don't know what interface the request came in on). */
+
+ if(subrec == remote_broadcast_subnet)
+ namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME);
+ else
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+
+ /* Check if it is a name that expired */
+ if (namerec && ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp)))
+ {
+ DEBUG(5,("process_name_query_request: expired name %s\n", namestr(&namerec->name)));
+ namerec = NULL;
+ }
+
+ if (namerec)
+ {
+
+ /*
+ * Always respond to unicast queries.
+ * Don't respond to broadcast queries unless the query is for
+ * a name we own, a Primary Domain Controller name, or a WINS_PROXY
+ * name with type 0 or 0x20. WINS_PROXY names are only ever added
+ * into the namelist if we were configured as a WINS proxy.
+ */
+
+ if (!bcast ||
+ (bcast && ((name_type == 0x1b) || (namerec->source == SELF_NAME) ||
+ (namerec->source == PERMANENT_NAME) ||
+ ((namerec->source == WINS_PROXY_NAME) && ((name_type == 0) || (name_type == 0x20)))))
+ )
+ {
+
+ /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY,
+ or it's a Domain Master type. */
+
+ ttl = (namerec->death_time != PERMANENT_TTL) ?
+ namerec->death_time - p->timestamp : lp_max_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so
+ we don't need a malloc. */
+
+ if(namerec->num_ips == 1 )
+ prdata = rdata;
+ else
+ {
+ if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL)
+ {
+ DEBUG(0,("process_name_query_request: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for( i = 0; i < namerec->num_ips; i++)
+ {
+ set_nb_flags(&prdata[i*6],namerec->nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->ip[i]);
+ }
+ reply_data_len = namerec->num_ips * 6;
+ success = True;
+ }
+ }
+
+ /*
+ * If a machine is broadcasting a name lookup request and we have lp_wins_proxy()
+ * set we should initiate a WINS query here. On success we add the resolved name
+ * into our namelist with a type of WINS_PROXY_NAME and then reply to the query.
+ */
+
+ if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() &&
+ bcast && (subrec != remote_broadcast_subnet))
+ {
+ make_wins_proxy_name_query_request( subrec, p, question );
+ return;
+ }
+
+ if (!success && bcast)
+ {
+ if((prdata != rdata) && (prdata != NULL))
+ free(rdata);
+ return; /* Never reply with a negative response to broadcasts. */
+ }
+
+ /*
+ * Final check. From observation, if a unicast packet is sent
+ * to a non-WINS server with the recursion desired bit set
+ * then never send a negative response.
+ */
+
+ if(!success && !bcast && nmb->header.nm_flags.recursion_desired)
+ {
+ if((prdata != rdata) && (prdata != NULL))
+ free(rdata);
+ return;
+ }
+
+ if (success)
+ {
+ rcode = 0;
+ DEBUG(3,("OK\n"));
+ }
+ else
+ {
+ rcode = NAM_ERR;
+ DEBUG(3,("UNKNOWN\n"));
+ }
+
+ /* See rfc1002.txt 4.2.13. */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if((prdata != rdata) && (prdata != NULL))
+ free(prdata);
+}
diff --git a/source3/nmbd/nmbd_lmhosts.c b/source3/nmbd/nmbd_lmhosts.c
new file mode 100644
index 0000000000..2dd1db81cd
--- /dev/null
+++ b/source3/nmbd/nmbd_lmhosts.c
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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.
+
+ Revision History:
+
+ Handle lmhosts file reading.
+
+*/
+
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+/****************************************************************************
+Load a lmhosts file.
+****************************************************************************/
+void load_lmhosts_file(char *fname)
+{
+ FILE *fp = fopen(fname,"r");
+ pstring line;
+ if (!fp) {
+ DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n",
+ fname, strerror(errno)));
+ return;
+ }
+
+ while (!feof(fp))
+ {
+ pstring ip,name,flags,extra;
+ struct subnet_record *subrec = NULL;
+ char *ptr;
+ int count = 0;
+ struct in_addr ipaddr;
+ enum name_source source = LMHOSTS_NAME;
+ int name_type = -1;
+
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ strcpy(ip,"");
+ strcpy(name,"");
+ strcpy(flags,"");
+
+ ptr = line;
+
+ if (next_token(&ptr,ip ,NULL))
+ ++count;
+ if (next_token(&ptr,name ,NULL))
+ ++count;
+ if (next_token(&ptr,flags,NULL))
+ ++count;
+ if (next_token(&ptr,extra,NULL))
+ ++count;
+
+ if (count <= 0)
+ continue;
+
+ if (count > 0 && count < 2)
+ {
+ DEBUG(0,("load_lmhosts_file: Ill formed hosts line [%s]\n",line));
+ continue;
+ }
+
+ if (count >= 4)
+ {
+ DEBUG(0,("load_lmhosts_file: too many columns in lmhosts file %s (obsolete syntax)\n",
+ fname));
+ continue;
+ }
+
+ DEBUG(4, ("load_lmhosts_file: lmhost entry: %s %s %s\n", ip, name, flags));
+
+ if (strchr(flags,'G') || strchr(flags,'S'))
+ {
+ DEBUG(0,("load_lmhosts_file: group flag in %s ignored (obsolete)\n",fname));
+ continue;
+ }
+
+ ipaddr = *interpret_addr2(ip);
+
+ /* Extra feature. If the name ends in '#XX', where XX is a hex number,
+ then only add that name type. */
+ if((ptr = strchr(name, '#')) != NULL)
+ {
+ char *endptr;
+
+ ptr++;
+ name_type = (int)strtol(ptr, &endptr,0);
+
+ if(!*ptr || (endptr == ptr))
+ {
+ DEBUG(0,("load_lmhosts_file: invalid name %s containing '#'.\n", name));
+ continue;
+ }
+
+ *(--ptr) = '\0'; /* Truncate at the '#' */
+ }
+
+ /* We find a relevent subnet to put this entry on, then add it. */
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ /* If none match add the name to the remote_broadcast_subnet. */
+ if(subrec == NULL)
+ subrec = remote_broadcast_subnet;
+
+ if(name_type == -1)
+ {
+ /* Add the (0) and (0x20) names directly into the namelist for this subnet. */
+ add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ }
+ else
+ {
+ /* Add the given name type to the subnet namelist. */
+ add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ }
+ }
+
+ fclose(fp);
+}
+
+/****************************************************************************
+ Find a name read from the lmhosts file. We secretly check the names on
+ the remote_broadcast_subnet as if the name was added to a regular broadcast
+ subnet it will be found by normal name query processing.
+****************************************************************************/
+
+BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname,
+ FIND_ANY_NAME))==NULL)
+ return False;
+
+ if(!NAME_IS_ACTIVE(namerec) || (namerec->source != LMHOSTS_NAME))
+ return False;
+
+ *namerecp = namerec;
+ return True;
+}
diff --git a/source3/nmbd/nmbd_logonnames.c b/source3/nmbd/nmbd_logonnames.c
new file mode 100644
index 0000000000..b2431ec0a7
--- /dev/null
+++ b/source3/nmbd/nmbd_logonnames.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+extern struct in_addr ipzero;
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail to become a Logon server on a subnet.
+ ****************************************************************************/
+static void become_logon_server_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, fail_name->name, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state back to LOGON_NONE. */
+ work->log_state = LOGON_NONE;
+
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL;
+
+ DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, namestr(fail_name)));
+
+}
+
+/****************************************************************************
+ Become a Logon server on a subnet.
+ ****************************************************************************/
+
+static void become_logon_server_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+ struct server_record *servrec;
+
+ if(!work)
+ {
+ DEBUG(0,("become_logon_server_success: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, myname)) == NULL)
+ {
+ DEBUG(0,("become_logon_server_success: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ myname, registered_name->name, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->log_state = LOGON_SRV; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER);
+ /* To allow Win95 policies to load we need to set type domain
+ controller.
+ */
+ servrec->serv.type |= SV_TYPE_DOMAIN_CTRL;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ DEBUG(0,("become_logon_server_success: Samba is now a logon server\
+for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+}
+
+/*******************************************************************
+ Become a logon server by attempting to register the WORKGROUP<1c>
+ group name.
+******************************************************************/
+
+static void become_logon_server(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \
+on subnet %s\n", work->work_group,subrec->subnet_name));
+
+ DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n",
+ work->work_group));
+ work->log_state = LOGON_WAIT;
+
+ register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP,
+ become_logon_server_success,
+ become_logon_server_fail, NULL);
+}
+
+/*****************************************************************************
+ Add the internet group <1c> logon names by unicast and broadcast.
+ ****************************************************************************/
+void add_logon_names(void)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup);
+
+ if (work && (work->log_state == LOGON_NONE))
+ {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,myworkgroup,0x1c,scope);
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL)
+ {
+ DEBUG(0,("add_domain_logon_names: At time %s attempting to become \
+logon server for workgroup %s on subnet %s\n", timestring(), myworkgroup,
+ subrec->subnet_name));
+ become_logon_server(subrec, work);
+ }
+ }
+ }
+}
diff --git a/source3/nmbd/nmbd_mynames.c b/source3/nmbd/nmbd_mynames.c
new file mode 100644
index 0000000000..660b545069
--- /dev/null
+++ b/source3/nmbd/nmbd_mynames.c
@@ -0,0 +1,165 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern char **my_netbios_names;
+extern pstring myname;
+extern fstring myworkgroup;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail funtion when registering my netbios names.
+ **************************************************************************/
+
+static void my_name_register_failed(struct subnet_record *subrec,
+ struct response_record *rrec, struct nmb_name *nmbname)
+{
+ DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n",
+ namestr(nmbname), subrec->subnet_name));
+}
+
+/****************************************************************************
+ Add my workgroup and my given names to the subnet lists.
+ Also add the magic Samba names.
+ **************************************************************************/
+
+BOOL register_my_workgroup_and_names()
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+ int i;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ /* Create the workgroup on the subnet. */
+ if((work = create_workgroup_on_subnet(subrec, myworkgroup, PERMANENT_TTL)) == NULL)
+ {
+ DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \
+Exiting.\n", myworkgroup, subrec->subnet_name));
+ return False;
+ }
+
+ /* Each subnet entry, except for the remote_announce_broadcast subnet
+ and the wins_server_subnet has the magic Samba names. */
+ add_samba_names_to_subnet(subrec);
+
+ /* Register all our names including aliases. */
+ for (i=0; my_netbios_names[i]; i++)
+ {
+ register_name(subrec, my_netbios_names[i],0x20,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names[i],0x03,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names[i],0x00,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ }
+
+ /* Initiate election processing, register the workgroup names etc. */
+ initiate_myworkgroup_startup(subrec, work);
+ }
+
+ /* If we are not a WINS client, we still need to add the magic Samba
+ names and the netbios names to the unicast subnet directly. This is
+ to allow unicast node status requests and queries to still work
+ in a broadcast only environment. */
+
+ if(we_are_a_wins_client() == False)
+ {
+ add_samba_names_to_subnet(unicast_subnet);
+
+ for (i=0; my_netbios_names[i]; i++)
+ {
+ add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x20,samba_nb_type, PERMANENT_TTL,
+ SELF_NAME, 1, &FIRST_SUBNET->myip);
+
+ add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x3,samba_nb_type, PERMANENT_TTL,
+ SELF_NAME, 1, &FIRST_SUBNET->myip);
+
+ add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x0,samba_nb_type, PERMANENT_TTL,
+ SELF_NAME, 1, &FIRST_SUBNET->myip);
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Remove all the names we registered.
+**************************************************************************/
+
+void release_my_names()
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct name_record *namerec, *nextnamerec;
+
+ for (namerec = subrec->namelist; namerec; namerec = nextnamerec)
+ {
+ nextnamerec = namerec->next;
+ if ((namerec->source == SELF_NAME) && !NAME_IS_DEREGISTERING(namerec))
+ release_name(subrec, namerec, standard_success_release,
+ NULL, NULL);
+ }
+ }
+}
+
+/*******************************************************************
+ Refresh our registered names.
+ ******************************************************************/
+
+void refresh_my_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct name_record *namerec;
+
+ for (namerec = subrec->namelist; namerec; namerec = namerec->next)
+ {
+ /* Each SELF name has an individual time to be refreshed. */
+ if ((namerec->source == SELF_NAME) && (namerec->refresh_time < t) &&
+ (namerec->death_time != PERMANENT_TTL))
+ {
+ /* We cheat here and pretend the refresh is going to be
+ successful & update the refresh times. This stops
+ multiple refresh calls being done. We actually
+ deal with refresh failure in the fail_fn.
+ */
+ refresh_name(subrec, namerec, NULL, NULL, NULL);
+ namerec->death_time += lp_max_ttl();
+ namerec->refresh_time += lp_max_ttl();
+ }
+ }
+ }
+}
diff --git a/source3/nmbd/nmbd_namelistdb.c b/source3/nmbd/nmbd_namelistdb.c
new file mode 100644
index 0000000000..dfd8a80baa
--- /dev/null
+++ b/source3/nmbd/nmbd_namelistdb.c
@@ -0,0 +1,586 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern char **my_netbios_names;
+
+uint16 samba_nb_type = 0; /* samba's NetBIOS name type */
+
+
+/****************************************************************************
+ Set Samba's NetBIOS name type.
+ ****************************************************************************/
+
+void set_samba_nb_type(void)
+{
+ if (lp_wins_support() || (*lp_wins_server()))
+ samba_nb_type = NB_MFLAG; /* samba is a 'hybrid' node type */
+ else
+ samba_nb_type = NB_BFLAG; /* samba is broadcast-only node type */
+}
+
+/****************************************************************************
+ Returns True if the netbios name is ^1^2__MSBROWSE__^2^1.
+
+ Note: This name is registered if as a master browser or backup browser
+ you are responsible for a workgroup (when you announce a domain by
+ broadcasting on your local subnet, you announce it as coming from this
+ name: see announce_host()).
+
+ **************************************************************************/
+
+BOOL ms_browser_name(char *name, int type)
+{
+ return (strequal(name,MSBROWSE) && (type == 0x01));
+}
+
+/****************************************************************************
+ Add a netbios name into a namelist.
+ **************************************************************************/
+
+static void add_name_to_namelist(struct subnet_record *subrec,
+ struct name_record *namerec)
+{
+ struct name_record *namerec2;
+
+ if (!subrec->namelist)
+ {
+ subrec->namelist = namerec;
+ namerec->prev = NULL;
+ namerec->next = NULL;
+ return;
+ }
+
+ for (namerec2 = subrec->namelist; namerec2->next; namerec2 = namerec2->next)
+ ;
+
+ namerec2->next = namerec;
+ namerec->next = NULL;
+ namerec->prev = namerec2;
+ namerec->subnet = subrec;
+
+ subrec->namelist_changed = True;
+}
+
+/****************************************************************************
+ Remove a name from the namelist.
+ **************************************************************************/
+
+void remove_name_from_namelist(struct subnet_record *subrec,
+ struct name_record *namerec)
+{
+ if (namerec->next)
+ namerec->next->prev = namerec->prev;
+ if (namerec->prev)
+ namerec->prev->next = namerec->next;
+
+ if(namerec == subrec->namelist)
+ subrec->namelist = namerec->next;
+
+ if(namerec->ip != NULL)
+ free((char *)namerec->ip);
+ free((char *)namerec);
+
+ subrec->namelist_changed = True;
+}
+
+
+/****************************************************************************
+ Find a name in a subnet.
+ **************************************************************************/
+
+struct name_record *find_name_on_subnet(struct subnet_record *subrec,
+ struct nmb_name *nmbname, BOOL self_only)
+{
+ struct name_record *namerec = subrec->namelist;
+ struct name_record *name_ret;
+
+ for (name_ret = namerec; name_ret; name_ret = name_ret->next)
+ {
+ if (nmb_name_equal(&name_ret->name, nmbname))
+ {
+ /* Self names only - these include permanent names. */
+ if (self_only && (name_ret->source != SELF_NAME) &&
+ (name_ret->source != PERMANENT_NAME) )
+ {
+ continue;
+ }
+ DEBUG(9,("find_name_on_subnet: on subnet %s - found name %s source=%d\n",
+ subrec->subnet_name, namestr(nmbname), name_ret->source));
+ return name_ret;
+ }
+ }
+ DEBUG(9,("find_name_on_subnet: on subnet %s - name %s NOT FOUND\n",
+ subrec->subnet_name, namestr(nmbname)));
+ return NULL;
+}
+
+/****************************************************************************
+ Find a name over all known broadcast subnets.
+**************************************************************************/
+
+struct name_record *find_name_for_remote_broadcast_subnet( struct nmb_name *nmbname,
+ BOOL self_only)
+{
+ struct subnet_record *subrec;
+ struct name_record *namerec = NULL;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ if((namerec = find_name_on_subnet(subrec, nmbname, self_only))!= NULL)
+ break;
+ }
+
+ return namerec;
+}
+
+/****************************************************************************
+ Update the ttl of an entry in a subnet name list.
+ ****************************************************************************/
+
+void update_name_ttl(struct name_record *namerec, int ttl)
+{
+ time_t time_now = time(NULL);
+
+ if(namerec->death_time != PERMANENT_TTL)
+ namerec->death_time = time_now + ttl;
+
+ namerec->refresh_time = time_now + (ttl/2);
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/****************************************************************************
+ Add an entry to a subnet name list.
+ ****************************************************************************/
+
+struct name_record *add_name_to_subnet(struct subnet_record *subrec,
+ char *name, int type, uint16 nb_flags, int ttl,
+ enum name_source source, int num_ips, struct in_addr *iplist)
+{
+ struct name_record *namerec;
+ time_t time_now = time(NULL);
+
+ if((namerec = (struct name_record *)malloc(sizeof(*namerec))) == NULL)
+ {
+ DEBUG(0,("add_name_to_subnet: malloc fail.\n"));
+ return NULL;
+ }
+
+ bzero((char *)namerec,sizeof(*namerec));
+
+ namerec->subnet = subrec;
+
+ namerec->num_ips = num_ips;
+ namerec->ip = (struct in_addr *)malloc(sizeof(struct in_addr) * namerec->num_ips);
+ if (!namerec->ip)
+ {
+ DEBUG(0,("add_name_to_subnet: malloc fail when creating ip_flgs.\n"));
+ free((char *)namerec);
+ return NULL;
+ }
+
+ bzero((char *)namerec->ip, sizeof(struct in_addr) * namerec->num_ips);
+
+ memcpy(&namerec->ip[0], iplist, num_ips * sizeof(struct in_addr));
+
+ make_nmb_name(&namerec->name,name,type,scope);
+
+ /* Setup the death_time and refresh_time. */
+ if(ttl == PERMANENT_TTL)
+ namerec->death_time = PERMANENT_TTL;
+ else
+ namerec->death_time = time_now + ttl;
+
+ namerec->refresh_time = time_now + (ttl/2);
+
+ /* Enter the name as active. */
+ namerec->nb_flags = nb_flags | NB_ACTIVE;
+
+ /* If it's our primary name, flag it as so. */
+ if(strequal(my_netbios_names[0],name))
+ namerec->nb_flags |= NB_PERM;
+
+ namerec->source = source;
+
+ add_name_to_namelist(subrec,namerec);
+
+ DEBUG(3,("add_name_to_subnet: Added netbios name %s with first IP %s ttl=%d nb_flags=%2x to subnet %s\n",
+ namestr(&namerec->name),inet_ntoa(*iplist),ttl,(unsigned int)nb_flags,
+ subrec->subnet_name));
+
+ subrec->namelist_changed = True;
+
+ return(namerec);
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ succeeds. By definition this is a SELF_NAME (or we wouldn't be registering
+ it).
+ ******************************************************************/
+
+void standard_success_register(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, uint16 nb_flags, int ttl,
+ struct in_addr registered_ip)
+{
+ struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME);
+
+ if(namerec == NULL)
+ add_name_to_subnet(subrec, nmbname->name, nmbname->name_type,
+ nb_flags, ttl, SELF_NAME, 1, &registered_ip);
+ else
+ update_name_ttl(namerec, ttl);
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ fails.
+ ******************************************************************/
+
+void standard_fail_register(struct subnet_record *subrec,
+ struct response_record *rrec, struct nmb_name *nmbname)
+{
+ struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME);
+
+ DEBUG(0,("standard_fail_register: Failed to register/refresh name %s on subnet %s\n",
+ namestr(nmbname), subrec->subnet_name));
+
+ /* Remove the name from the subnet. */
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+static void remove_nth_ip_in_record( struct name_record *namerec, int ind)
+{
+ if(ind != namerec->num_ips)
+ memmove( (char *)(&namerec->ip[ind]), (char *)(&namerec->ip[ind+1]),
+ ( namerec->num_ips - ind - 1) * sizeof(struct in_addr));
+
+ namerec->num_ips--;
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to check if an IP address exists in a name record.
+ ******************************************************************/
+
+BOOL find_ip_in_name_record(struct name_record *namerec, struct in_addr ip)
+{
+ int i;
+
+ for(i = 0; i < namerec->num_ips; i++)
+ if(ip_equal( namerec->ip[i], ip))
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Utility function to add an IP address to a name record.
+ ******************************************************************/
+
+void add_ip_to_name_record(struct name_record *namerec, struct in_addr new_ip)
+{
+ struct in_addr *new_list;
+
+ /* Don't add one we already have. */
+ if(find_ip_in_name_record( namerec, new_ip))
+ return;
+
+ if((new_list = (struct in_addr *)malloc( (namerec->num_ips + 1)*sizeof(struct in_addr)) )== NULL)
+ {
+ DEBUG(0,("add_ip_to_name_record: Malloc fail !\n"));
+ return;
+ }
+
+ memcpy((char *)new_list, (char *)namerec->ip, namerec->num_ips *sizeof(struct in_addr));
+ new_list[namerec->num_ips] = new_ip;
+
+ free((char *)namerec->ip);
+ namerec->ip = new_list;
+ namerec->num_ips += 1;
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+void remove_ip_from_name_record( struct name_record *namerec, struct in_addr remove_ip)
+{
+ /* Try and find the requested ip address - remove it. */
+ int i;
+ int orig_num = namerec->num_ips;
+
+ for(i = 0; i < orig_num; i++)
+ if( ip_equal( remove_ip, namerec->ip[i]) )
+ {
+ remove_nth_ip_in_record( namerec, i);
+ break;
+ }
+}
+
+/*******************************************************************
+ Utility function that release_name callers can plug into as the
+ success function when a name release is successful. Used to save
+ duplication of success_function code.
+ ******************************************************************/
+
+void standard_success_release(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr released_ip)
+{
+ struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME);
+
+ if(namerec == NULL)
+ {
+ DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. Name \
+was not found on subnet.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name));
+ return;
+ }
+ else
+ {
+ int orig_num = namerec->num_ips;
+
+ remove_ip_from_name_record( namerec, released_ip);
+
+ if(namerec->num_ips == orig_num)
+ DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. This ip \
+is not known for this name.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name ));
+ }
+
+ if (namerec->num_ips == 0)
+ remove_name_from_namelist(subrec, namerec);
+}
+
+/*******************************************************************
+ Expires old names in a subnet namelist.
+ ******************************************************************/
+
+void expire_names_on_subnet(struct subnet_record *subrec, time_t t)
+{
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+
+ for (namerec = subrec->namelist; namerec; namerec = next_namerec)
+ {
+ next_namerec = namerec->next;
+ if ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < t))
+ {
+ if (namerec->source == SELF_NAME)
+ {
+ DEBUG(3,("expire_names_on_subnet: Subnet %s not expiring SELF name %s\n",
+ subrec->subnet_name, namestr(&namerec->name)));
+ namerec->death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+ DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n",
+ subrec->subnet_name, namestr(&namerec->name)));
+
+ remove_name_from_namelist(subrec, namerec);
+ }
+ }
+}
+
+/*******************************************************************
+ Expires old names in all subnet namelists.
+ ******************************************************************/
+
+void expire_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ expire_names_on_subnet(subrec, t);
+ }
+}
+
+/****************************************************************************
+ Add the magic samba names, useful for finding samba servers.
+ These go directly into the name list for a particular subnet,
+ without going through the normal registration process.
+ When adding them to the unicast subnet, add them as a list of
+ all broadcast subnet IP addresses.
+**************************************************************************/
+
+void add_samba_names_to_subnet(struct subnet_record *subrec)
+{
+ struct in_addr *iplist = &subrec->myip;
+ int num_ips = 1;
+
+ /* These names are added permanently (ttl of zero) and will NOT be
+ refreshed. */
+
+ if((subrec == unicast_subnet) || (subrec == wins_server_subnet))
+ {
+ struct subnet_record *bcast_subrecs;
+ int i;
+ /* Create an IP list containing all our known subnets. */
+
+ num_ips = iface_count();
+ if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL)
+ {
+ DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n"));
+ return;
+ }
+
+ for(bcast_subrecs = FIRST_SUBNET, i = 0; bcast_subrecs;
+ bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++)
+ iplist[i] = bcast_subrecs->myip;
+
+ }
+
+ add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+
+ if(iplist != &subrec->myip)
+ free((char *)iplist);
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+static void dump_subnet_namelist( struct subnet_record *subrec, FILE *fp)
+{
+ struct name_record *namerec;
+ char *src_type;
+ struct tm *tm;
+ int i;
+
+ fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name);
+ for (namerec = subrec->namelist; namerec; namerec = namerec->next)
+ {
+ fprintf(fp,"\tName = %s\t", namestr(&namerec->name));
+ switch(namerec->source)
+ {
+ case LMHOSTS_NAME:
+ src_type = "LMHOSTS_NAME";
+ break;
+ case WINS_PROXY_NAME:
+ src_type = "WINS_PROXY_NAME";
+ break;
+ case REGISTER_NAME:
+ src_type = "REGISTER_NAME";
+ break;
+ case SELF_NAME:
+ src_type = "SELF_NAME";
+ break;
+ case DNS_NAME:
+ src_type = "DNS_NAME";
+ break;
+ case DNSFAIL_NAME:
+ src_type = "DNSFAIL_NAME";
+ break;
+ case PERMANENT_NAME:
+ src_type = "PERMANENT_NAME";
+ break;
+ default:
+ src_type = "unknown!";
+ break;
+ }
+ fprintf(fp, "Source = %s\nb_flags = %x\t", src_type, namerec->nb_flags);
+
+ if(namerec->death_time != PERMANENT_TTL)
+ {
+ tm = LocalTime(&namerec->death_time);
+ fprintf(fp, "death_time = %s\t", asctime(tm));
+ }
+ else
+ fprintf(fp, "death_time = PERMANENT\t");
+
+ if(namerec->refresh_time != PERMANENT_TTL)
+ {
+ tm = LocalTime(&namerec->refresh_time);
+ fprintf(fp, "refresh_time = %s\n", asctime(tm));
+ }
+ else
+ fprintf(fp, "refresh_time = PERMANENT\n");
+
+ fprintf(fp, "\t\tnumber of IPS = %d", namerec->num_ips);
+ for(i = 0; i < namerec->num_ips; i++)
+ fprintf(fp, "\t%s", inet_ntoa(namerec->ip[i]));
+
+ fprintf(fp, "\n\n");
+ }
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+void dump_all_namelists()
+{
+ fstring fname;
+ FILE *fp;
+ struct subnet_record *subrec;
+
+ pstrcpy(fname,lp_lockdir());
+ trim_string(fname,NULL,"/");
+ strcat(fname,"/");
+ strcat(fname,"namelist.debug");
+
+ fp = fopen(fname,"w");
+
+ if (!fp)
+ {
+ DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n",
+ fname,strerror(errno)));
+ return;
+ }
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ dump_subnet_namelist( subrec, fp);
+
+ if(!we_are_a_wins_client())
+ dump_subnet_namelist(unicast_subnet, fp);
+
+ if(remote_broadcast_subnet->namelist != NULL)
+ dump_subnet_namelist(remote_broadcast_subnet, fp);
+
+ if(wins_server_subnet != NULL)
+ dump_subnet_namelist( wins_server_subnet, fp);
+ fclose(fp);
+}
diff --git a/source3/nmbd/nmbd_namequery.c b/source3/nmbd/nmbd_namequery.c
new file mode 100644
index 0000000000..5d98935fec
--- /dev/null
+++ b/source3/nmbd/nmbd_namequery.c
@@ -0,0 +1,234 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+
+/****************************************************************************
+ Deal with a response packet when querying a name.
+****************************************************************************/
+
+static void query_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL success = False;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct in_addr answer_ip;
+
+ /* Ensure we don't retry the query but leave the response record cleanup
+ to the timeout code. We may get more answer responses in which case
+ we should mark the name in conflict.. */
+ rrec->repeat_count = 0;
+
+ if(rrec->num_msgs == 1)
+ {
+ /* This is the first response. */
+
+ if(nmb->header.opcode == NMB_WACK_OPCODE)
+ {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more query requests. */
+
+ DEBUG(5,("query_name_response: WACK from WINS server %s in querying \
+name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(question_name), subrec->subnet_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ }
+ else if(nmb->header.rcode != 0)
+ {
+ success = False;
+
+ DEBUG(5,("query_name_response: On subnet %s - negative response \
+from IP %s for name %s. Error code was %d.\n", subrec->subnet_name, inet_ntoa(p->ip),
+ namestr(question_name), nmb->header.rcode));
+ }
+ else
+ {
+ success = True;
+
+ putip((char *)&answer_ip,&nmb->answers->rdata[2]);
+ DEBUG(5,("query_name_response: On subnet %s - positive response from IP %s\
+for name %s. IP of that name is %s\n", subrec->subnet_name, inet_ntoa(p->ip),
+ namestr(question_name), inet_ntoa(answer_ip)));
+
+ /* Interestingly, we could add these names to our namelists, and
+ change nmbd to a model that checked its own name cache first,
+ before sending out a query. This is a task for another day, though.
+ */
+ }
+ }
+ else if( rrec->num_msgs > 1)
+ {
+ DEBUG(0,("query_name_response: Multiple (%d) responses received for a query on \
+subnet %s for name %s. This response was from IP %s\n",
+ rrec->num_msgs, subrec->subnet_name, namestr(question_name),
+ inet_ntoa(rrec->packet->ip) ));
+
+ /* We have already called the success or fail function, so we
+ don't call again here. Leave the response record around in
+ case we get more responses. */
+
+ return;
+ }
+
+ if(success && rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers);
+ else if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode);
+
+}
+
+/****************************************************************************
+ Deal with a timeout when querying a name.
+****************************************************************************/
+
+static void query_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ /* We can only fail here, never succeed. */
+ BOOL failed = True;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ if(rrec->num_msgs != 0)
+ {
+ /* We got at least one response, and have called the success/fail
+ function already. */
+
+ failed = False;
+ }
+
+ if(failed)
+ {
+ DEBUG(5,("query_name_timeout_response: No response to querying name %s on subnet %s.\n",
+ namestr(question_name), subrec->subnet_name));
+
+ if(rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, question_name, 0);
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Lookup a name on our local namelists. We check the lmhosts file first. If the
+ name is not there we look for the name on the given subnet.
+****************************************************************************/
+
+static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if(find_name_in_lmhosts(nmbname, namerecp))
+ return True;
+
+ if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL)
+ return False;
+
+ if(NAME_IS_ACTIVE(namerec) && ((namerec->source == SELF_NAME) ||
+ (namerec->source == LMHOSTS_NAME)) )
+ {
+ *namerecp = namerec;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and query for a name.
+****************************************************************************/
+
+BOOL query_name(struct subnet_record *subrec, char *name, int type,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, name, type, scope);
+
+ /*
+ * We need to check our local namelists first.
+ * It may be an magic name, lmhosts name or just
+ * a name we have registered.
+ */
+
+ if(query_local_namelists(subrec, &nmbname, &namerec) == True)
+ {
+ struct res_rec rrec;
+ int i;
+
+ bzero((char *)&rrec, sizeof(struct res_rec));
+
+ /* Fake up the needed res_rec just in case it's used. */
+ rrec.rr_name = nmbname;
+ rrec.rr_type = RR_TYPE_NB;
+ rrec.rr_class = RR_CLASS_IN;
+ rrec.ttl = PERMANENT_TTL;
+ rrec.rdlength = namerec->num_ips * 6;
+ if(rrec.rdlength > MAX_DGRAM_SIZE)
+ {
+ DEBUG(0,("query_name: nmbd internal error - there are %d ip addresses for name %s.\n",
+ namerec->num_ips, namestr(&nmbname) ));
+ return False;
+ }
+
+ for( i = 0; i < namerec->num_ips; i++)
+ {
+ set_nb_flags( &rrec.rdata[i*6], namerec->nb_flags );
+ putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->ip[i]);
+ }
+
+ /* Call the success function directly. */
+ if(success_fn)
+ (*success_fn)(subrec, userdata, &nmbname, namerec->ip[0], &rrec);
+ return False;
+ }
+
+ if(queue_query_name( subrec,
+ query_name_response,
+ query_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ &nmbname) == NULL)
+ {
+ DEBUG(0,("query_name: Failed to send packet trying to query name %s\n",
+ namestr(&nmbname)));
+ return True;
+ }
+ return False;
+}
diff --git a/source3/nmbd/nmbd_nameregister.c b/source3/nmbd/nmbd_nameregister.c
new file mode 100644
index 0000000000..603daaa531
--- /dev/null
+++ b/source3/nmbd/nmbd_nameregister.c
@@ -0,0 +1,390 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern fstring myworkgroup;
+
+/****************************************************************************
+ Deal with a response packet when registering one of our names.
+****************************************************************************/
+
+static void register_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are registering broadcast, then getting a response is an
+ * error - we do not have the name. If we are registering unicast,
+ * then we expect to get a response.
+ */
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ int ttl;
+ uint16 nb_flags;
+ struct in_addr registered_ip;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!nmb_name_equal(question_name, answer_name))
+ {
+ DEBUG(0,("register_name_response: Answer name %s differs from question \
+name %s.\n", namestr(answer_name), namestr(question_name)));
+ return;
+ }
+
+ if(bcast)
+ {
+ /*
+ * Special hack to cope with old Samba nmbd's.
+ * Earlier versions of Samba (up to 1.9.16p11) respond
+ * to a broadcast name registration of WORKGROUP<1b> when
+ * they should not. Hence, until these versions are gone,
+ * we should treat such errors as success for this particular
+ * case only. jallison@whistle.com.
+ */
+
+#if 1 /* OLD_SAMBA_SERVER_HACK */
+ if((nmb->header.rcode == ACT_ERR) && strequal(myworkgroup, answer_name->name) &&
+ (answer_name->name_type == 0x1b))
+ {
+ /* Pretend we did not get this. */
+ rrec->num_msgs--;
+
+ DEBUG(5,("register_name_response: Ignoring broadcast response to \
+registration of name %s due to old Samba server bug.\n", namestr(answer_name)));
+ return;
+ }
+#endif /* OLD_SAMBA_SERVER_HACK */
+
+ /* Someone else has the name. Log the problem. */
+ DEBUG(1,("register_name_response: Failed to register \
+name %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n",
+ namestr(answer_name),
+ subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
+ success = False;
+ }
+ else
+ {
+ /* Unicast - check to see if the response allows us to have the name. */
+ if(nmb->header.rcode != 0)
+ {
+ /* Error code - we didn't get the name. */
+ success = False;
+
+ DEBUG(0,("register_name_response: server at IP %s rejected our \
+name registration of %s with error code %d.\n", inet_ntoa(p->ip),
+ namestr(answer_name), nmb->header.rcode));
+
+ }
+ else if(nmb->header.opcode == NMB_WACK_OPCODE)
+ {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more register requests. */
+
+ DEBUG(5,("register_name_response: WACK from WINS server %s in registering \
+name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ }
+ else
+ {
+ success = True;
+ /* Get the data we need to pass to the success function. */
+ nb_flags = get_nb_flags(nmb->answers->rdata);
+ putip((char*)&registered_ip,&nmb->answers->rdata[2]);
+ ttl = nmb->answers->ttl;
+ }
+ }
+
+ DEBUG(5,("register_name_response: %s in registering name %s on subnet %s.\n",
+ success ? "success" : "failure", namestr(answer_name), subrec->subnet_name));
+
+ if(success)
+ {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip);
+ if( rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip);
+ }
+ else
+ {
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when registering one of our names.
+****************************************************************************/
+
+static void register_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /*
+ * If we are registering unicast, then NOT getting a response is an
+ * error - we do not have the name. If we are registering broadcast,
+ * then we don't expect to get a response.
+ */
+
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ BOOL success = False;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ uint16 nb_flags;
+ int ttl;
+ struct in_addr registered_ip;
+
+ if(bcast)
+ {
+ if(rrec->num_msgs == 0)
+ {
+ /* Not receiving a message is success for broadcast registration. */
+ success = True;
+
+ /* Pull the success values from the original request packet. */
+ nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ ttl = sent_nmb->additional->ttl;
+ putip(&registered_ip,&sent_nmb->additional->rdata[2]);
+ }
+ }
+ else
+ {
+ /* Unicast - if no responses then it's an error. */
+ if(rrec->num_msgs == 0)
+ {
+ DEBUG(2,("register_name_timeout_response: WINS server at address %s is not \
+responding.\n", inet_ntoa(rrec->packet->ip)));
+
+ /* Keep trying to contact the WINS server periodically. This allows
+ us to work correctly if the WINS server is down temporarily when
+ we come up. */
+
+ /* Reset the number of attempts to zero and double the interval between
+ retries. Max out at 5 minutes. */
+ rrec->repeat_count = 3;
+ rrec->repeat_interval *= 2;
+ if(rrec->repeat_interval > (5 * 60))
+ rrec->repeat_interval = (5 * 60);
+ rrec->repeat_time = time(NULL) + rrec->repeat_interval;
+
+ DEBUG(5,("register_name_timeout_response: increasing WINS timeout to %d seconds.\n",
+ rrec->repeat_interval));
+ return; /* Don't remove the response record. */
+ }
+ }
+
+ DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n",
+ success ? "success" : "failure", namestr(question_name), subrec->subnet_name));
+ if(success)
+ {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ if( rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ }
+ else
+ {
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and register one of our names on the unicast subnet - multihomed.
+****************************************************************************/
+
+static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ /*
+ If we are adding a group name, we just send multiple
+ register name packets to the WINS server (this is an
+ internet group name.
+
+ If we are adding a unique name, We need first to add
+ our names to the unicast subnet namelist. This is
+ because when a WINS server receives a multihomed
+ registration request, the first thing it does is to
+ send a name query to the registering machine, to see
+ if it has put the name in it's local namelist.
+ We need the name there so the query response code in
+ nmbd_incomingrequests.c will find it.
+
+ We are adding this name prematurely (we don't really
+ have it yet), but as this is on the unicast subnet
+ only we will get away with this (only the WINS server
+ will ever query names from us on this subnet).
+ */
+
+ int num_ips=0;
+ int i;
+ struct in_addr *ip_list = NULL;
+ struct subnet_record *subrec;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
+ num_ips++;
+
+ if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL)
+ {
+ DEBUG(0,("multihomed_register_name: malloc fail !\n"));
+ return True;
+ }
+
+ for(subrec = FIRST_SUBNET, i = 0; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ )
+ ip_list[i] = subrec->myip;
+
+ add_name_to_subnet(unicast_subnet, nmbname->name, nmbname->name_type,
+ nb_flags, lp_max_ttl(), SELF_NAME, num_ips, ip_list);
+
+ free((char *)ip_list);
+
+ /* Now try and register the name, num_ips times. On the last time use
+ the given success and fail functions. */
+
+ for( i = 0; i < num_ips; i++)
+ {
+ if(queue_register_multihomed_name( unicast_subnet,
+ register_name_response,
+ register_name_timeout_response,
+ (i == num_ips - 1) ? success_fn : NULL,
+ (i == num_ips - 1) ? fail_fn : NULL,
+ (i == num_ips - 1) ? userdata : NULL,
+ nmbname,
+ nb_flags,
+ ip_list[i]) == NULL)
+ {
+ DEBUG(0,("multihomed_register_name: Failed to send packet trying to \
+register name %s IP %s\n", namestr(nmbname), inet_ntoa(ip_list[i]) ));
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Try and register one of our names.
+****************************************************************************/
+
+BOOL register_name(struct subnet_record *subrec,
+ char *name, int type, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, name, type, scope);
+
+ /* Always set the NB_ACTIVE flag on the name we are
+ registering. Doesn't make sense without it.
+ */
+
+ nb_flags |= NB_ACTIVE;
+
+ /* If this is the unicast subnet, and we are a multi-homed
+ host, then register a multi-homed name. */
+
+ if( (subrec == unicast_subnet) && we_are_multihomed())
+ return multihomed_register_name(&nmbname, nb_flags,
+ success_fn, fail_fn,
+ userdata);
+
+ if(queue_register_name( subrec,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ &nmbname,
+ nb_flags) == NULL)
+ {
+ DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
+ namestr(&nmbname)));
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and refresh one of our names.
+****************************************************************************/
+
+BOOL refresh_name(struct subnet_record *subrec, struct name_record *namerec,
+ refresh_name_success_function success_fn,
+ refresh_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int i;
+
+ /*
+ * Go through and refresh the name for all known ip addresses.
+ * Only call the success/fail function on the last one (it should
+ * only be done once).
+ */
+
+ for( i = 0; i < namerec->num_ips; i++)
+ {
+ if(queue_refresh_name( subrec,
+ register_name_response,
+ register_name_timeout_response,
+ (i == (namerec->num_ips - 1)) ? success_fn : NULL,
+ (i == (namerec->num_ips - 1)) ? fail_fn : NULL,
+ (i == (namerec->num_ips - 1)) ? userdata : NULL,
+ namerec,
+ namerec->ip[i]) == NULL)
+ {
+ DEBUG(0,("refresh_name: Failed to send packet trying to refresh name %s\n",
+ namestr(&namerec->name)));
+ return True;
+ }
+ }
+ return False;
+}
diff --git a/source3/nmbd/nmbd_namerelease.c b/source3/nmbd/nmbd_namerelease.c
new file mode 100644
index 0000000000..8632dd7655
--- /dev/null
+++ b/source3/nmbd/nmbd_namerelease.c
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+
+/****************************************************************************
+ Deal with a response packet when releasing one of our names.
+****************************************************************************/
+
+static void release_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are releasing broadcast, then getting a response is an
+ * error. If we are releasing unicast, then we expect to get a response.
+ */
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct in_addr released_ip;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!nmb_name_equal(question_name, answer_name))
+ {
+ DEBUG(0,("release_name_response: Answer name %s differs from question \
+name %s.\n", namestr(answer_name), namestr(question_name)));
+ return;
+ }
+
+ if(bcast)
+ {
+ /* Someone sent a response. This shouldn't happen/ */
+ DEBUG(1,("release_name_response: A response for releasing name %s was received on a\
+broadcast subnet %s. This should not happen !\n", namestr(answer_name), subrec->subnet_name));
+ return;
+ }
+ else
+ {
+ /* Unicast - check to see if the response allows us to release the name. */
+ if(nmb->header.rcode != 0)
+ {
+ /* Error code - we were told not to release the name ! What now ! */
+ success = False;
+
+ DEBUG(0,("release_name_response: WINS server at IP %s rejected our \
+name release of name %s with error code %d.\n", inet_ntoa(p->ip),
+ namestr(answer_name), nmb->header.rcode));
+
+ }
+ else if(nmb->header.opcode == NMB_WACK_OPCODE)
+ {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more release requests. */
+
+ DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \
+name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ }
+ }
+
+ DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n",
+ success ? "success" : "failure", namestr(answer_name), subrec->subnet_name));
+
+ if(success)
+ {
+ putip((char*)&released_ip ,&nmb->answers->rdata[2]);
+
+ if(rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip);
+ standard_success_release( subrec, rrec->userdata, answer_name, released_ip);
+ }
+ else
+ {
+ /* We have no standard_fail_release - maybe we should add one ? */
+ if(rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, answer_name);
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when releasing one of our names.
+****************************************************************************/
+
+static void release_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /*
+ * If we are releasing unicast, then NOT getting a response is an
+ * error - we could not release the name. If we are releasing broadcast,
+ * then we don't expect to get a response.
+ */
+
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ BOOL success = False;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ struct in_addr released_ip;
+
+ if(bcast)
+ {
+ if(rrec->num_msgs == 0)
+ {
+ /* Not receiving a message is success for broadcast release. */
+ success = True;
+
+ /* Get the ip address we were trying to release. */
+ putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]);
+ }
+ }
+ else
+ {
+ /* Unicast - if no responses then it's an error. */
+ if(rrec->num_msgs == 0)
+ {
+ DEBUG(2,("release_name_timeout_response: WINS server at address %s is not \
+responding.\n", inet_ntoa(rrec->packet->ip)));
+
+ /* Keep trying to contact the WINS server periodically. This allows
+ us to work correctly if the WINS server is down temporarily when
+ we want to delete the name. */
+
+ /* Reset the number of attempts to zero and double the interval between
+ retries. Max out at 5 minutes. */
+ rrec->repeat_count = 3;
+ rrec->repeat_interval *= 2;
+ if(rrec->repeat_interval > (5 * 60))
+ rrec->repeat_interval = (5 * 60);
+ rrec->repeat_time = time(NULL) + rrec->repeat_interval;
+
+ DEBUG(5,("release_name_timeout_response: increasing WINS timeout to %d seconds.\n",
+ rrec->repeat_interval));
+ return; /* Don't remove the response record. */
+ }
+ }
+
+ DEBUG(5,("release_name_timeout_response: %s in releasing name %s on subnet %s.\n",
+ success ? "success" : "failure", namestr(question_name), subrec->subnet_name));
+
+ if(success && rrec->success_fn)
+ {
+ if(rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip);
+ standard_success_release( subrec, rrec->userdata, question_name, released_ip);
+ }
+ else
+ {
+ /* We have no standard_fail_release - maybe we should add one ? */
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec, question_name);
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and release one of our names.
+****************************************************************************/
+
+BOOL release_name(struct subnet_record *subrec, struct name_record *namerec,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int i;
+
+ /* Ensure it's a SELF name, and in the ACTIVE state. */
+ if((namerec->source != SELF_NAME) || !NAME_IS_ACTIVE(namerec))
+ {
+ DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n",
+ namestr(&namerec->name), subrec->subnet_name, namerec->source));
+ return True;
+ }
+
+ /* Set the name into the deregistering state. */
+ namerec->nb_flags |= NB_DEREG;
+
+ /*
+ * Go through and release the name for all known ip addresses.
+ * Only call the success/fail function on the last one (it should
+ * only be done once).
+ */
+
+ for( i = 0; i < namerec->num_ips; i++)
+ {
+ if(queue_release_name( subrec,
+ release_name_response,
+ release_name_timeout_response,
+ (i == (namerec->num_ips - 1)) ? success_fn : NULL,
+ (i == (namerec->num_ips - 1)) ? fail_fn : NULL,
+ (i == (namerec->num_ips - 1)) ? userdata : NULL,
+ &namerec->name,
+ namerec->nb_flags,
+ namerec->ip[i]) == NULL)
+ {
+ DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+ namestr(&namerec->name), inet_ntoa(namerec->ip[i]) ));
+ return True;
+ }
+ }
+ return False;
+}
diff --git a/source3/nmbd/nmbd_nodestatus.c b/source3/nmbd/nmbd_nodestatus.c
new file mode 100644
index 0000000000..267446c69d
--- /dev/null
+++ b/source3/nmbd/nmbd_nodestatus.c
@@ -0,0 +1,99 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+
+/****************************************************************************
+ Deal with a successful node status response.
+****************************************************************************/
+static void node_status_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!nmb_name_equal(question_name, answer_name))
+ {
+ DEBUG(0,("node_status_response: Answer name %s differs from question \
+name %s.\n", namestr(answer_name), namestr(question_name)));
+ return;
+ }
+
+ DEBUG(5,("node_status_response: response from name %s on subnet %s.\n",
+ namestr(answer_name), subrec->subnet_name));
+
+ /* Just send the whole answer resource record for the success function
+ to parse. */
+ if(rrec->success_fn)
+ (*rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when requesting a node status.
+****************************************************************************/
+static void node_status_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n",
+ namestr(question_name), subrec->subnet_name));
+
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and do a node status to a name - given the name & IP address.
+****************************************************************************/
+
+BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct in_addr send_ip, node_status_success_function success_fn,
+ node_status_fail_function fail_fn, struct userdata_struct *userdata)
+{
+ if(queue_node_status( subrec,
+ node_status_response, node_status_timeout_response,
+ success_fn, fail_fn, userdata, nmbname, send_ip)==NULL)
+ {
+ DEBUG(0,("node_status: Failed to send packet trying to get node status for \
+name %s, IP address %s\n", namestr(nmbname), inet_ntoa(send_ip)));
+ return True;
+ }
+ return False;
+}
diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c
new file mode 100644
index 0000000000..43249cc0a3
--- /dev/null
+++ b/source3/nmbd/nmbd_packets.c
@@ -0,0 +1,1775 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+extern int DEBUGLEVEL;
+
+extern int num_response_packets;
+
+extern pstring scope;
+extern struct in_addr loopback_ip;
+
+/*******************************************************************
+ The global packet linked-list. Incoming entries are
+ added to the end of this list. It is supposed to remain fairly
+ short so we won't bother with an end pointer.
+******************************************************************/
+
+static struct packet_struct *packet_queue = NULL;
+
+/***************************************************************************
+Utility function to find the specific fd to send a packet out on.
+**************************************************************************/
+
+static int find_subnet_fd_for_address( struct in_addr local_ip )
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ if(ip_equal(local_ip, subrec->myip))
+ return subrec->nmb_sock;
+
+ return ClientNMB;
+}
+
+/***************************************************************************
+Get/Set problematic nb_flags as network byte order 16 bit int.
+**************************************************************************/
+
+uint16 get_nb_flags(char *buf)
+{
+ return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
+}
+
+void set_nb_flags(char *buf, uint16 nb_flags)
+{
+ *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF);
+ *buf = '\0';
+}
+
+/***************************************************************************
+Dumps out the browse packet data.
+**************************************************************************/
+
+static void debug_browse_data(char *outbuf, int len)
+{
+ int i,j;
+ for (i = 0; i < len; i+= 16)
+ {
+ DEBUG(4, ("%3x char ", i));
+
+ for (j = 0; j < 16; j++)
+ {
+ unsigned char x = outbuf[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ if (i+j >= len)
+ break;
+ DEBUG(4, ("%c", x));
+ }
+
+ DEBUG(4, (" hex ", i));
+
+ for (j = 0; j < 16; j++)
+ {
+ if (i+j >= len)
+ break;
+ DEBUG(4, (" %02x", (unsigned char)outbuf[i+j]));
+ }
+
+ DEBUG(4, ("\n"));
+ }
+}
+
+/***************************************************************************
+ Generates the unique transaction identifier
+**************************************************************************/
+
+static uint16 name_trn_id=0;
+
+static uint16 generate_name_trn_id(void)
+{
+
+ if (!name_trn_id)
+ {
+ name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100);
+ }
+ name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+ return name_trn_id;
+}
+
+/***************************************************************************
+ Either loops back or sends out a completed NetBIOS packet.
+**************************************************************************/
+
+static BOOL send_netbios_packet(struct packet_struct *p)
+{
+ BOOL loopback_this_packet = False;
+
+ /* Check if we are sending to or from ourselves as a WINS server. */
+ if(ismyip(p->ip) && (p->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ if(loopback_this_packet)
+ {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ }
+ else if (!send_packet(p))
+ {
+ DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n",
+ inet_ntoa(p->ip),p->port));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Sets up the common elements of an outgoing NetBIOS packet.
+**************************************************************************/
+
+static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
+ BOOL bcast,
+ struct in_addr to_ip)
+{
+ struct packet_struct *packet = NULL;
+ struct nmb_packet *nmb = NULL;
+
+ /* Allocate the packet_struct we will return. */
+ if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL)
+ {
+ DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n"));
+ return NULL;
+ }
+
+ bzero((char *)packet,sizeof(*packet));
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.name_trn_id = generate_name_trn_id();
+ nmb->header.response = False;
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.nm_flags.bcast = bcast;
+
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+
+ nmb->question.question_name = *nmbname;
+ nmb->question.question_type = QUESTION_TYPE_NB_QUERY;
+ nmb->question.question_class = QUESTION_CLASS_IN;
+
+ packet->ip = to_ip;
+ packet->port = NMB_PORT;
+ packet->fd = ClientNMB;
+ packet->timestamp = time(NULL);
+ packet->packet_type = NMB_PACKET;
+ packet->locked = False;
+
+ return packet; /* Caller must free. */
+}
+
+/***************************************************************************
+ Sets up the common elements of register, refresh or release packet.
+**************************************************************************/
+
+static BOOL create_and_init_additional_record(struct packet_struct *packet,
+ uint16 nb_flags,
+ struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL)
+ {
+ DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n"));
+ return False;
+ }
+
+ bzero((char *)nmb->additional,sizeof(struct res_rec));
+
+ nmb->additional->rr_name = nmb->question.question_name;
+ nmb->additional->rr_type = RR_TYPE_NB;
+ nmb->additional->rr_class = RR_CLASS_IN;
+
+ nmb->additional->ttl = lp_max_ttl();
+
+ nmb->additional->rdlength = 6;
+
+ set_nb_flags(nmb->additional->rdata,nb_flags);
+
+ /* Set the address for the name we are registering. */
+ putip(&nmb->additional->rdata[2], register_ip);
+
+ /* Ensure that we send out the file descriptor to give us the
+ the specific source address we are registering as our
+ IP source address. */
+
+ packet->fd = find_subnet_fd_for_address( *register_ip );
+
+ return True;
+}
+
+/***************************************************************************
+ Sends out a name query.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet( struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = NULL;
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n",
+ namestr(&nmb->question.question_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name register.
+**************************************************************************/
+
+static BOOL initiate_name_register_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n",
+ namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a multihomed name register.
+**************************************************************************/
+
+static BOOL initiate_multihomed_name_register_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+ char second_ip_buf[25];
+
+ strcpy(second_ip_buf, inet_ntoa(packet->ip));
+
+ nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \
+for name %s IP %s (bcast=%s) to IP %s\n",
+ namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
+ BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf ));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name refresh.
+**************************************************************************/
+
+static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *refresh_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n",
+ namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name release.
+**************************************************************************/
+
+static BOOL initiate_name_release_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *release_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_RELEASE_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, release_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n",
+ namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a node status.
+**************************************************************************/
+
+static BOOL initiate_node_status_packet( struct packet_struct *packet )
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ nmb->question.question_type = QUESTION_TYPE_NB_STATUS;
+
+ DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n",
+ namestr(&nmb->question.question_name),
+ inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/****************************************************************************
+ Simplification functions for queuing standard packets.
+ These should be the only publicly callable functions for sending
+ out packets.
+****************************************************************************/
+
+/****************************************************************************
+ Assertion - we should never be sending nmbd packets on the remote
+ broadcast subnet.
+****************************************************************************/
+
+static BOOL assert_check_subnet(struct subnet_record *subrec)
+{
+ if( subrec == remote_broadcast_subnet)
+ {
+ DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
+This is a bug.\n"));
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Queue a register name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_register_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL bcast = (subrec == unicast_subnet) ? False : True;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, bcast,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_register_packet( p, nb_flags,
+ iface_ip(subrec->bcast_ip)) == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a multihomed register name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_register_multihomed_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr register_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL bcast = False;
+ BOOL ret;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet)
+ {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, bcast,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if (nb_flags & NB_GROUP)
+ ret = initiate_name_register_packet( p, nb_flags, &register_ip);
+ else
+ ret = initiate_multihomed_name_register_packet( p, nb_flags, &register_ip);
+
+ if(ret == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a release name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_release_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr release_ip)
+{
+ BOOL bcast = (subrec == unicast_subnet) ? False : True;
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, bcast,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_release_packet( p, nb_flags, &release_ip) == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a refresh name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_refresh_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ refresh_name_success_function success_fn,
+ refresh_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct name_record *namerec,
+ struct in_addr refresh_ip)
+{
+ BOOL bcast = (subrec == unicast_subnet) ? False : True;
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(&namerec->name, bcast,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_refresh_packet( p, namerec->nb_flags, &refresh_ip) == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_query_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL bcast = True;
+
+ if ((subrec == unicast_subnet) || (subrec == wins_server_subnet))
+ bcast = False;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, bcast,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_query_packet( p ) == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a node status packet to a given name and address.
+****************************************************************************/
+
+struct response_record *queue_node_status( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ node_status_success_function success_fn,
+ node_status_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ struct in_addr send_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL bcast = False;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet)
+ {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, bcast,
+ send_ip)) == NULL)
+ return NULL;
+
+ if(initiate_node_status_packet(p) == False)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL)
+ {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Reply to a netbios name packet. see rfc1002.txt
+****************************************************************************/
+
+void reply_netbios_packet(struct packet_struct *orig_packet,
+ int rcode, enum netbios_reply_type_code rcv_code, int opcode,
+ int ttl, char *data,int len)
+{
+ struct packet_struct packet;
+ struct nmb_packet *nmb = NULL;
+ struct res_rec answers;
+ struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
+ BOOL loopback_this_packet = False;
+ char *packet_type = "unknown";
+
+ /* Check if we are sending to or from ourselves. */
+ if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ nmb = &packet.packet.nmb;
+
+ /* Do a partial copy of the packet. We clear the locked flag and
+ the resource record pointers. */
+ packet = *orig_packet; /* Full structure copy. */
+ packet.locked = False;
+ nmb->answers = NULL;
+ nmb->nsrecs = NULL;
+ nmb->additional = NULL;
+
+ switch (rcv_code)
+ {
+ case NMB_STATUS:
+ {
+ packet_type = "nmb_status";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ }
+ case NMB_QUERY:
+ {
+ packet_type = "nmb_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ }
+ case NMB_REG:
+ case NMB_REG_REFRESH:
+ {
+ packet_type = "nmb_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ }
+ case NMB_REL:
+ {
+ packet_type = "nmb_rel";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ }
+ case NMB_WAIT_ACK:
+ {
+ packet_type = "nmb_wack";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ }
+ case WINS_REG:
+ {
+ packet_type = "wins_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ }
+ case WINS_QUERY:
+ {
+ packet_type = "wins_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ }
+
+ default:
+ {
+ DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n",
+ packet_type, namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip)));
+
+ return;
+ }
+ }
+
+ DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \
+for id %hu\n",
+ packet_type, namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
+
+ nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
+ nmb->header.opcode = opcode;
+ nmb->header.response = True;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = True;
+
+ nmb->header.rcode = rcode;
+ nmb->header.qdcount = 0;
+ nmb->header.ancount = 1;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ bzero((char*)&nmb->question,sizeof(nmb->question));
+
+ nmb->answers = &answers;
+ bzero((char*)nmb->answers,sizeof(*nmb->answers));
+
+ nmb->answers->rr_name = orig_nmb->question.question_name;
+ nmb->answers->rr_type = orig_nmb->question.question_type;
+ nmb->answers->rr_class = orig_nmb->question.question_class;
+ nmb->answers->ttl = ttl;
+
+ if (data && len)
+ {
+ nmb->answers->rdlength = len;
+ memcpy(nmb->answers->rdata, data, len);
+ }
+
+ packet.packet_type = NMB_PACKET;
+ /* Ensure we send out on the same fd that the original
+ packet came in on to give the correct source IP address. */
+ packet.fd = orig_packet->fd;
+ packet.timestamp = time(NULL);
+
+ debug_nmb_packet(&packet);
+
+ if(loopback_this_packet)
+ {
+ struct packet_struct *lo_packet;
+ DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&packet)) == NULL)
+ return;
+ queue_packet(lo_packet);
+ }
+ else if (!send_packet(&packet))
+ {
+ DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n",
+ inet_ntoa(packet.ip),packet.port));
+ }
+}
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+
+void queue_packet(struct packet_struct *packet)
+{
+ struct packet_struct *p;
+
+ if (!packet_queue)
+ {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Try and find a matching subnet record for a datagram port 138 packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p)
+{
+ struct subnet_record *subrec;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If the subnet record is the remote announce broadcast subnet,
+ hack it here to be the first subnet. This is really gross and
+ is needed due to people turning on port 137/138 broadcast
+ forwarding on their routers. May fire and brimstone rain
+ down upon them...
+ */
+
+ return FIRST_SUBNET;
+}
+
+/****************************************************************************
+Dispatch a browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+void process_browse_packet(struct packet_struct *p, char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int command = CVAL(buf,0);
+ struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+
+ /* Drop the packet if it's a different NetBIOS scope, or
+ the source is from one of our names. */
+
+ if (!strequal(dgram->dest_name.scope,scope ))
+ {
+ DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scope));
+ return;
+ }
+
+ if (is_myname(dgram->source_name.name))
+ {
+ DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name)));
+ return;
+ }
+
+ switch (command)
+ {
+ case ANN_HostAnnouncement:
+ {
+ debug_browse_data(buf, len);
+ process_host_announce(subrec, p, buf+1);
+ break;
+ }
+ case ANN_DomainAnnouncement:
+ {
+ debug_browse_data(buf, len);
+ process_workgroup_announce(subrec, p, buf+1);
+ break;
+ }
+ case ANN_LocalMasterAnnouncement:
+ {
+ debug_browse_data(buf, len);
+ process_local_master_announce(subrec, p, buf+1);
+ break;
+ }
+ case ANN_AnnouncementRequest:
+ {
+ process_announce_request(subrec, p, buf+1);
+ break;
+ }
+ case ANN_Election:
+ {
+ process_election(subrec, p, buf+1);
+ break;
+ }
+ case ANN_GetBackupListReq:
+ {
+ debug_browse_data(buf, len);
+
+ /* This is one occasion where we change a subnet that is
+ given to us. If the packet was sent to WORKGROUP<1b> instead
+ of WORKGROUP<1d> then it was unicast to us a domain master
+ browser. Change subrec to unicast.
+ */
+ if(dgram->dest_name.name_type == 0x1b)
+ subrec = unicast_subnet;
+
+ process_get_backup_list_request(subrec, p, buf+1);
+ break;
+ }
+ case ANN_GetBackupListResp:
+ {
+ debug_browse_data(buf, len);
+ /* We never send ANN_GetBackupListReq so we
+ should never get these. */
+ DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \
+packet from %s IP %s\n", namestr(&dgram->source_name), inet_ntoa(p->ip)));
+ break;
+ }
+ case ANN_ResetBrowserState:
+ {
+ process_reset_browser(subrec, p, buf+1);
+ break;
+ }
+ case ANN_MasterAnnouncement:
+ {
+ /* Master browser datagrams must be processed
+ on the unicast subnet. */
+ subrec = unicast_subnet;
+
+ process_master_browser_announce(subrec, p, buf+1);
+ break;
+ }
+ default:
+ {
+ DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n",
+ subrec->subnet_name, command, namestr(&dgram->source_name),
+ inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+ }
+ }
+}
+
+/****************************************************************************
+ Determine if a packet is for us on port 138. Note that to have any chance of
+ being efficient we need to drop as many packets as possible at this
+ stage as subsequent processing is expensive.
+****************************************************************************/
+
+static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+{
+ struct subnet_record *subrec = NULL;
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ if(subrec == NULL)
+ subrec = unicast_subnet;
+
+ return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL);
+}
+
+/****************************************************************************
+ Process udp 138 datagrams
+****************************************************************************/
+
+static void process_dgram(struct packet_struct *p)
+{
+ char *buf;
+ char *buf2;
+ int len;
+ struct dgram_packet *dgram = &p->packet.dgram;
+
+ /* If we aren't listening to the destination name then ignore the packet */
+ if (!listening(p,&dgram->dest_name))
+ {
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n",
+ namestr(&dgram->dest_name), inet_ntoa(p->ip)));
+ return;
+ }
+
+ if (dgram->header.msg_type != 0x10 &&
+ dgram->header.msg_type != 0x11 &&
+ dgram->header.msg_type != 0x12)
+ {
+ /* Don't process error packets etc yet */
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \
+ an error packet of type %x\n",
+ namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
+ return;
+ }
+
+ buf = &dgram->data[0];
+ buf -= 4; /* XXXX for the pseudo tcp length -
+ someday I need to get rid of this */
+
+ if (CVAL(buf,smb_com) != SMBtrans)
+ return;
+
+ len = SVAL(buf,smb_vwv11);
+ buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
+
+ DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n",
+ namestr(&dgram->source_name),namestr(&dgram->dest_name),
+ inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len));
+
+
+ if (len <= 0)
+ return;
+
+ /* Datagram packet received for the browser mailslot */
+ if (strequal(smb_buf(buf),BROWSE_MAILSLOT))
+ {
+ process_browse_packet(p,buf2,len);
+ return;
+ }
+
+ /* Datagram packet received for the domain logon mailslot */
+ if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT))
+ {
+ process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT);
+ return;
+ }
+
+ /* Datagram packet received for the NT domain logon mailslot */
+ if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT))
+ {
+ process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT);
+ return;
+ }
+}
+
+/****************************************************************************
+ Validate a response nmb packet.
+****************************************************************************/
+
+BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode)
+ {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if (nmb->header.ancount == 0)
+ {
+ DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1))
+ {
+ DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.ancount == 0)
+ {
+ DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+ case NMB_WACK_OPCODE:
+ /* Check WACK response here. */
+ if (nmb->header.ancount != 1)
+ {
+ DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Validate a request nmb packet.
+****************************************************************************/
+
+BOOL validate_nmb_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode)
+ {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+ {
+ DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.qdcount == 0) ||
+ ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) &&
+ (nmb->question.question_type != QUESTION_TYPE_NB_STATUS)))
+ {
+ DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+ {
+ DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Find a subnet (and potentially a response record) for a packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p,
+ struct response_record **pprrec)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct response_record *rrec = NULL;
+ struct subnet_record *subrec = NULL;
+
+ if(pprrec != NULL)
+ *pprrec = NULL;
+
+ if(nmb->header.response)
+ {
+ /* It's a response packet. Find a record for it or it's an error. */
+
+ rrec = find_response_record( &subrec, nmb->header.name_trn_id);
+ if(rrec == NULL)
+ {
+ DEBUG(0,("find_subnet_for_nmb_packet: response record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ return NULL;
+ }
+
+ if(subrec == NULL)
+ {
+ DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ return NULL;
+ }
+
+ if(pprrec != NULL)
+ *pprrec = rrec;
+ return subrec;
+ }
+
+ /* Try and see what subnet this packet belongs to. */
+
+ /* WINS server ? */
+ if(packet_is_for_wins_server(p))
+ return wins_server_subnet;
+
+ /* If it wasn't a broadcast packet then send to the UNICAST subnet. */
+ if(nmb->header.nm_flags.bcast == False)
+ return unicast_subnet;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If none match it must have been a directed broadcast - assign
+ the remote_broadcast_subnet. */
+ return remote_broadcast_subnet;
+}
+
+/****************************************************************************
+ Process a nmb request packet - validate the packet and route it.
+****************************************************************************/
+
+static void process_nmb_request(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+
+ debug_nmb_packet(p);
+
+ /* Ensure we have a good packet. */
+ if(validate_nmb_packet(nmb))
+ return;
+
+ /* Allocate a subnet to this packet - if we cannot - fail. */
+ if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL)
+ return;
+
+ switch (nmb->header.opcode)
+ {
+ case NMB_NAME_REG_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_registration_request(subrec, p);
+ else
+ process_name_registration_request(subrec, p);
+ break;
+
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9:
+ if(subrec == wins_server_subnet)
+ wins_process_name_refresh_request(subrec, p);
+ else
+ process_name_refresh_request(subrec, p);
+ break;
+
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_multihomed_name_registration_request(subrec, p);
+ else
+ {
+ DEBUG(0,("process_nmb_request: Multihomed registration request must be \
+directed at a WINS server.\n"));
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ switch (nmb->question.question_type)
+ {
+ case QUESTION_TYPE_NB_QUERY:
+ {
+ if(subrec == wins_server_subnet)
+ wins_process_name_query_request(subrec, p);
+ else
+ process_name_query_request(subrec, p);
+ break;
+ }
+ case QUESTION_TYPE_NB_STATUS:
+ {
+ if(subrec == wins_server_subnet)
+ {
+ DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \
+not allowed.\n"));
+ break;
+ }
+ else
+ process_node_status_request(subrec, p);
+ break;
+ }
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_release_request(subrec, p);
+ else
+ process_name_release_request(subrec, p);
+ break;
+ }
+}
+
+/****************************************************************************
+ Process a nmb response packet - validate the packet and route it.
+ to either the WINS server or a normal response.
+****************************************************************************/
+
+static void process_nmb_response(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+ struct response_record *rrec = NULL;
+
+ debug_nmb_packet(p);
+
+ if(validate_nmb_response_packet(nmb))
+ return;
+
+ if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL)
+ return;
+
+ if(rrec == NULL)
+ {
+ DEBUG(0,("process_nmb_response: response packet received but no response record \
+found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
+ return;
+ }
+
+ /* Increment the number of responses received for this record. */
+ rrec->num_msgs++;
+ /* Ensure we don't re-send the request. */
+ rrec->repeat_count = 0;
+
+ /* Call the response received function for this packet. */
+ (*rrec->resp_fn)(subrec, rrec, p);
+}
+
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+void run_packet_queue()
+{
+ struct packet_struct *p;
+
+ while ((p = packet_queue))
+ {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ switch (p->packet_type)
+ {
+ case NMB_PACKET:
+ if(p->packet.nmb.header.response)
+ process_nmb_response(p);
+ else
+ process_nmb_request(p);
+ break;
+
+ case DGRAM_PACKET:
+ process_dgram(p);
+ break;
+ }
+ free_packet(p);
+ }
+}
+
+/*******************************************************************
+ Retransmit or timeout elements from all the outgoing subnet response
+ record queues.
+******************************************************************/
+
+void retransmit_or_expire_response_records(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct response_record *rrec, *nextrrec;
+
+ for (rrec = subrec->responselist; rrec; rrec = nextrrec)
+ {
+ nextrrec = rrec->next;
+
+ if (rrec->repeat_time <= t)
+ {
+ if (rrec->repeat_count > 0)
+ {
+ /* Resend while we have a non-zero repeat_count. */
+ if(!send_packet(rrec->packet))
+ {
+ DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \
+to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip),
+ subrec->subnet_name));
+ }
+ rrec->repeat_time += rrec->repeat_interval;
+ rrec->repeat_count--;
+ }
+ else
+ {
+ DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \
+on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip),
+ subrec->subnet_name));
+
+ /* Call the timeout function. This will deal with removing the
+ timed out packet. */
+ if(rrec->timeout_fn)
+ (*rrec->timeout_fn)(subrec, rrec);
+ else
+ {
+ /* We must remove the record ourself if there is
+ no timeout function. */
+ remove_response_record(subrec, rrec);
+ }
+ } /* rrec->repeat_count > 0 */
+ } /* rrec->repeat_time <= t */
+ } /* end for rrec */
+ } /* end for subnet */
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number)
+{
+ int *sock_array = NULL;
+ struct subnet_record *subrec = NULL;
+ int count = 0;
+ int num = 0;
+ fd_set *pset = (fd_set *)malloc(sizeof(fd_set));
+
+ if(pset == NULL)
+ {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+ /* Check that we can add all the fd's we need. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ count++;
+
+ if((count*2) + 2 > FD_SETSIZE)
+ {
+ DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
+only use %d.\n", (count*2) + 2, FD_SETSIZE));
+ return True;
+ }
+
+ if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL)
+ {
+ DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n"));
+ return True;
+ }
+
+ FD_ZERO(pset);
+
+ /* Add in the broadcast socket on 137. */
+ FD_SET(ClientNMB,pset);
+ sock_array[num++] = ClientNMB;
+
+ /* Add in the 137 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ FD_SET(subrec->nmb_sock,pset);
+ sock_array[num++] = subrec->nmb_sock;
+ }
+
+ /* Add in the broadcast socket on 138. */
+ FD_SET(ClientDGRAM,pset);
+ sock_array[num++] = ClientDGRAM;
+
+ /* Add in the 138 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ FD_SET(subrec->dgram_sock,pset);
+ sock_array[num++] = subrec->dgram_sock;
+ }
+
+ *listen_number = (count*2) + 2;
+ *ppset = pset;
+ *psock_array = sock_array;
+
+ return False;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+***************************************************************************/
+
+BOOL listen_for_packets(BOOL run_election)
+{
+ static fd_set *listen_set = NULL;
+ static int listen_number = 0;
+ static int *sock_array = NULL;
+
+ fd_set fds;
+ int selrtn;
+ struct timeval timeout;
+#ifndef SYNC_DNS
+ int dns_fd;
+#endif
+
+ if(listen_set == NULL)
+ {
+ if(create_listen_fdset(&listen_set, &sock_array, &listen_number))
+ {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+#ifndef SYNC_DNS
+ dns_fd = asyncdns_fd();
+ if (dns_fd != -1) {
+ FD_SET(dns_fd, &fds);
+ }
+#endif
+
+
+ /*
+ * During elections and when expecting a netbios response packet we
+ * need to send election packets at tighter intervals.
+ * Ideally it needs to be the interval (in ms) between time now and
+ * the time we are expecting the next netbios packet.
+ */
+
+ timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* We can only take term signals when we are in the select. */
+ BlockSignals(False, SIGTERM);
+ selrtn = sys_select(&fds,&timeout);
+ BlockSignals(True, SIGTERM);
+
+ if(selrtn > 0)
+ {
+ int i;
+
+#ifndef SYNC_DNS
+ if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) {
+ run_dns_queue();
+ }
+#endif
+
+ for(i = 0; i < listen_number; i++)
+ {
+ if(i < (listen_number/2))
+ {
+ /* Processing a 137 socket. */
+ if (FD_ISSET(sock_array[i],&fds))
+ {
+ struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET);
+ if (packet)
+ {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) &&
+ (!is_local_net(packet->ip)))
+ {
+ DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ }
+ else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == global_nmb_port)
+ {
+ DEBUG(7,("discarding own packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ }
+ else
+ {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Processing a 138 socket. */
+
+ if (FD_ISSET(sock_array[i],&fds))
+ {
+ struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET);
+ if (packet)
+ {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) &&
+ (!is_local_net(packet->ip)))
+ {
+ DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ }
+ else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == DGRAM_PORT)
+ {
+ DEBUG(7,("discarding own packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ }
+ else
+ {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ } /* end processing 138 socket. */
+ } /* end for */
+ } /* end if selret > 0 */
+ return False;
+}
+
+/****************************************************************************
+ Construct and send a netbios DGRAM.
+ Note that this currently sends all packets to port 138.
+**************************************************************************/
+
+BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len,
+ char *srcname, int src_type,
+ char *dstname, int dest_type,
+ struct in_addr dest_ip,struct in_addr src_ip)
+{
+ BOOL loopback_this_packet = False;
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr,*p2;
+ char tmp[4];
+
+ bzero((char *)&p,sizeof(p));
+
+ if(ismyip(dest_ip))
+ loopback_this_packet = True;
+
+ generate_name_trn_id();
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = name_trn_id;
+ dgram->header.source_ip = src_ip;
+ dgram->header.source_port = DGRAM_PORT;
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,0,scope);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type,scope);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+ set_message(ptr,17,17 + len,True);
+ memcpy(ptr,tmp,4);
+
+ CVAL(ptr,smb_com) = SMBtrans;
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,1);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ strcpy(p2,mailslot);
+ p2 = skip_string(p2,1);
+
+ memcpy(p2,buf,len);
+ p2 += len;
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.ip = dest_ip;
+ p.port = DGRAM_PORT;
+ p.fd = ClientDGRAM;
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+ namestr(&dgram->source_name), inet_ntoa(src_ip)));
+ DEBUG(4,("to %s IP %s\n", namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+ debug_browse_data(buf, len);
+
+ if(loopback_this_packet)
+ {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_mailslot: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ return True;
+ }
+ else
+ return(send_packet(&p));
+}
diff --git a/source3/nmbd/nmbd_processlogon.c b/source3/nmbd/nmbd_processlogon.c
new file mode 100644
index 0000000000..ae917564fe
--- /dev/null
+++ b/source3/nmbd/nmbd_processlogon.c
@@ -0,0 +1,250 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+
+/****************************************************************************
+Process a domain logon packet
+**************************************************************************/
+
+void process_logon_packet(struct packet_struct *p,char *buf,int len,
+ char *mailslot)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ pstring my_name;
+ fstring reply_name;
+ BOOL add_slashes = False;
+ pstring outbuf;
+ int code,reply_code;
+ char unknown_byte = 0;
+ uint16 request_count = 0;
+ uint16 token = 0;
+
+ uint32 ntversion;
+ uint16 lmnttoken;
+ uint16 lm20token;
+ uint32 allowableaccount; /* Control bits, i.e. 0x80 == workstation trust a/c. */
+ uint32 domainsidsize;
+ uint16 requestcount;
+ char *domainsid;
+ char *getdc;
+ char *uniuser; /* Unicode user name. */
+ pstring ascuser;
+ char *unicomp; /* Unicode computer name. */
+ struct smb_passwd *smb_pass; /* To check if machine account exists */
+
+ if (!lp_domain_logons())
+ {
+ DEBUG(3,("process_logon_packet: Logon packet received from IP %S and domain \
+logons are not enabled.\n", inet_ntoa(p->ip) ));
+ return;
+ }
+
+ strcpy(my_name, myname);
+ strupper(my_name);
+
+ code = SVAL(buf,0);
+ DEBUG(1,("process_logon_packet: Logon from %s: code = %x\n", inet_ntoa(p->ip), code));
+
+ switch (code)
+ {
+ case 0:
+ {
+ char *q = buf + 2;
+ char *machine = q;
+ char *user = skip_string(machine,1);
+
+ getdc = skip_string(user,1);
+ q = skip_string(getdc,1);
+ unknown_byte = CVAL(q,0);
+ request_count = SVAL(q,1);
+ token = SVAL(q,3);
+
+ reply_code = 0x6;
+ strcpy(reply_name,my_name);
+ add_slashes = True;
+
+ DEBUG(3,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n",
+ machine,inet_ntoa(p->ip),user,token));
+
+ q = outbuf;
+ SSVAL(q, 0, 6); q += 2;
+
+ strcpy(reply_name, "\\\\");
+ strcat(reply_name, my_name);
+ strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */
+
+ SSVAL(q, 0, token); q += 2;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ send_mailslot(True, getdc,
+ outbuf,PTR_DIFF(q,outbuf),
+ dgram->dest_name.name,
+ dgram->dest_name.name_type,
+ dgram->source_name.name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip));
+ break;
+ }
+
+ case QUERYFORPDC:
+ {
+ char *q = buf + 2;
+ char *machine = q;
+
+ getdc = skip_string(machine,1);
+ unicomp = skip_string(getdc,1);
+
+ q = align2(unicomp, buf);
+
+ q = skip_unicode_string(q, 1);
+
+ ntversion = IVAL(q, 0); q += 4;
+ lmnttoken = SVAL(q, 0); q += 2;
+ lm20token = SVAL(q, 0); q += 2;
+
+ /* Construct reply. */
+
+ q = outbuf;
+ SSVAL(q, 0, QUERYFORPDC_R); q += 2;
+
+ strcpy(reply_name,my_name);
+ strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */
+
+ if (strcmp(mailslot, NT_LOGON_MAILSLOT)==0) {
+ q = align2(q, buf);
+
+ PutUniCode(q, my_name); /* PDC name */
+ q = skip_unicode_string(q, 1);
+ PutUniCode(q, myworkgroup); /* Domain name*/
+ q = skip_unicode_string(q, 1);
+
+ SIVAL(q, 0, ntversion); q += 4;
+ SSVAL(q, 0, lmnttoken); q += 2;
+ SSVAL(q, 0, lm20token); q += 2;
+ }
+
+ DEBUG(3,("process_logon_packet: GETDC request from %s at IP %s, \
+reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
+ machine,inet_ntoa(p->ip), reply_name, lp_workgroup(),
+ QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken,
+ (uint32)lm20token ));
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ send_mailslot(True, getdc,
+ outbuf,PTR_DIFF(q,outbuf),
+ dgram->dest_name.name,
+ dgram->dest_name.name_type,
+ dgram->source_name.name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip));
+ return;
+ }
+
+ case SAMLOGON:
+ {
+ char *q = buf + 2;
+
+ requestcount = SVAL(q, 0); q += 2;
+ unicomp = q;
+ uniuser = skip_unicode_string(unicomp,1);
+ getdc = skip_unicode_string(uniuser,1);
+ q = skip_string(getdc,1);
+ allowableaccount = IVAL(q, 0); q += 4;
+ domainsidsize = IVAL(q, 0); q += 4;
+ domainsid = q;
+ q += domainsidsize + 3;
+ ntversion = IVAL(q, 0); q += 4;
+ lmnttoken = SVAL(q, 0); q += 2;
+ lm20token = SVAL(q, 0); q += 2;
+
+ DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion));
+
+ /*
+ * If MACHINE$ is in our password database then respond, else ignore.
+ * Let's ignore the SID.
+ */
+
+ strcpy(ascuser, unistr(uniuser));
+ DEBUG(3,("process_logon_packet: SAMLOGON user %s\n", ascuser));
+
+ strcpy(reply_name,"\\\\"); /* Here it wants \\LOGONSERVER. */
+ strcpy(reply_name+2,my_name);
+
+ smb_pass = get_smbpwd_entry(ascuser, 0);
+
+ if(!smb_pass)
+ {
+ DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, not in password file\n",
+ unistr(unicomp),inet_ntoa(p->ip), ascuser));
+ return;
+ }
+ else
+ {
+ DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n",
+ unistr(unicomp),inet_ntoa(p->ip), ascuser, reply_name, myworkgroup,
+ SAMLOGON_R ,lmnttoken));
+ }
+
+ /* Construct reply. */
+
+ q = outbuf;
+ SSVAL(q, 0, SAMLOGON_R); q += 2;
+
+ PutUniCode(q, reply_name); q = skip_unicode_string(q, 1);
+ unistrcpy(q, uniuser); q = skip_unicode_string(q, 1); /* User name (workstation trust account) */
+ PutUniCode(q, lp_workgroup()); q = skip_unicode_string(q, 1); /* Domain name. */
+
+ SIVAL(q, 0, ntversion); q += 4;
+ SSVAL(q, 0, lmnttoken); q += 2;
+ SSVAL(q, 0, lm20token); q += 2;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ send_mailslot(True, getdc,
+ outbuf,PTR_DIFF(q,outbuf),
+ dgram->dest_name.name,
+ dgram->dest_name.name_type,
+ dgram->source_name.name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip));
+ break;
+ }
+
+ default:
+ {
+ DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code));
+ return;
+ }
+ }
+}
diff --git a/source3/nmbd/nmbd_responserecordsdb.c b/source3/nmbd/nmbd_responserecordsdb.c
new file mode 100644
index 0000000000..bc0c0745f5
--- /dev/null
+++ b/source3/nmbd/nmbd_responserecordsdb.c
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int ClientNMB;
+
+extern int DEBUGLEVEL;
+
+extern pstring scope;
+extern pstring myname;
+extern struct in_addr ipzero;
+
+int num_response_packets = 0;
+
+/***************************************************************************
+ Add an expected response record into the list
+ **************************************************************************/
+
+void add_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct response_record *rrec2;
+
+ num_response_packets++; /* count of total number of packets still around */
+
+ DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n",
+ rrec->response_id, subrec->subnet_name, num_response_packets));
+
+ if (!subrec->responselist)
+ {
+ subrec->responselist = rrec;
+ rrec->prev = NULL;
+ rrec->next = NULL;
+ return;
+ }
+
+ for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next)
+ ;
+
+ rrec2->next = rrec;
+ rrec->next = NULL;
+ rrec->prev = rrec2;
+}
+
+/***************************************************************************
+ Remove an expected response record from the list
+ **************************************************************************/
+
+void remove_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ if (rrec->prev)
+ rrec->prev->next = rrec->next;
+ if (rrec->next)
+ rrec->next->prev = rrec->prev;
+
+ if (subrec->responselist == rrec)
+ subrec->responselist = rrec->next;
+
+ if(rrec->userdata)
+ {
+ if(rrec->userdata->free_fn)
+ (*rrec->userdata->free_fn)(rrec->userdata);
+ else
+ free((char *)rrec->userdata);
+ }
+
+ /* Ensure we can delete. */
+ rrec->packet->locked = False;
+ free_packet(rrec->packet);
+
+ free((char *)rrec);
+
+ num_response_packets--; /* count of total number of packets still around */
+}
+
+/****************************************************************************
+ Create a response record for an outgoing packet.
+ **************************************************************************/
+
+struct response_record *make_response_record( struct subnet_record *subrec,
+ struct packet_struct *p,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ success_function success_fn,
+ fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct response_record *rrec;
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if (!(rrec = (struct response_record *)malloc(sizeof(*rrec))))
+ {
+ DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n"));
+ return NULL;
+ }
+
+ bzero((char *)rrec, sizeof(*rrec));
+
+ rrec->response_id = nmb->header.name_trn_id;
+
+ rrec->resp_fn = resp_fn;
+ rrec->timeout_fn = timeout_fn;
+ rrec->success_fn = success_fn;
+ rrec->fail_fn = fail_fn;
+
+ rrec->packet = p;
+
+ if(userdata)
+ {
+ /* Intelligent userdata. */
+ if(userdata->copy_fn)
+ {
+ if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL)
+ {
+ DEBUG(0,("make_response_queue_record: copy fail for userdata.\n"));
+ free(rrec);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Primitive userdata, do a memcpy. */
+ if((rrec->userdata = (struct userdata_struct *)
+ malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL)
+ {
+ DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n"));
+ free(rrec);
+ return NULL;
+ }
+ rrec->userdata->copy_fn = userdata->copy_fn;
+ rrec->userdata->free_fn = userdata->free_fn;
+ rrec->userdata->userdata_len = userdata->userdata_len;
+ memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len);
+ }
+ }
+ else
+ rrec->userdata = NULL;
+
+ rrec->num_msgs = 0;
+
+ if(!nmb->header.nm_flags.bcast)
+ rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */
+ else
+ rrec->repeat_interval = 1; /* XXXX should be in ms */
+ rrec->repeat_count = 3; /* 3 retries */
+ rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */
+
+ /* Lock the packet so we won't lose it while it's on the list. */
+ p->locked = True;
+
+ add_response_record(subrec, rrec);
+
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in a subnet's name query response list.
+ **************************************************************************/
+
+static struct response_record *find_response_record_on_subnet(
+ struct subnet_record *subrec, uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for (rrec = subrec->responselist; rrec; rrec = rrec->next)
+ {
+ if (rrec->response_id == id)
+ {
+ DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n",
+ id, subrec->subnet_name));
+ break;
+ }
+ }
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in any subnet's name query response list.
+ **************************************************************************/
+
+struct response_record *find_response_record(struct subnet_record **ppsubrec,
+ uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec);
+ (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec))
+ {
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL)
+ return rrec;
+ }
+
+ /* There should never be response records on the remote_broadcast subnet.
+ Sanity check to ensure this is so. */
+ if(remote_broadcast_subnet->responselist != NULL)
+ {
+ DEBUG(0,("find_response_record: response record found on subnet %s. This should \
+never happen !\n", remote_broadcast_subnet->subnet_name));
+ }
+
+ /* Now check the WINS server subnet if it exists. */
+ if(wins_server_subnet != NULL)
+ {
+ *ppsubrec = wins_server_subnet;
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL)
+ return rrec;
+ }
+
+ DEBUG(0,("find_response_record: repsonse packet id %hu received with no \
+matching record.\n", id));
+
+ *ppsubrec = NULL;
+
+ return NULL;
+}
diff --git a/source3/nmbd/nmbd_sendannounce.c b/source3/nmbd/nmbd_sendannounce.c
new file mode 100644
index 0000000000..e4b288aea5
--- /dev/null
+++ b/source3/nmbd/nmbd_sendannounce.c
@@ -0,0 +1,477 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+extern int updatecount;
+
+/****************************************************************************
+ Send a browser reset packet.
+**************************************************************************/
+
+void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip)
+{
+ pstring outbuf;
+ char *p;
+
+ DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n",
+ reset_type, to_name, to_type, inet_ntoa(to_ip) ));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_ResetBrowserState;
+ p++;
+ CVAL(p,0) = reset_type;
+ p++;
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ myname, 0x0, to_name, to_type, to_ip, FIRST_SUBNET->myip);
+}
+
+/****************************************************************************
+ Broadcast a packet to the local net requesting that all servers in this
+ workgroup announce themselves to us.
+ **************************************************************************/
+
+void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work)
+{
+ pstring outbuf;
+ char *p;
+
+ work->needannounce = True;
+
+ DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \
+to subnet %s\n", work->work_group, subrec->subnet_name));
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_AnnouncementRequest;
+ p++;
+
+ CVAL(p,0) = work->token; /* (local) Unique workgroup token id. */
+ p++;
+ StrnCpy(p,myname,15);
+ strupper(p);
+ p = skip_string(p,1);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ myname, 0x0, work->work_group,0x1e, subrec->bcast_ip, subrec->myip);
+}
+
+/****************************************************************************
+ Broadcast an announcement.
+ **************************************************************************/
+
+static void send_announcement(struct subnet_record *subrec, int announce_type,
+ char *from_name, char *to_name, int to_type, struct in_addr to_ip,
+ time_t announce_interval,
+ char *server_name, int server_type, char *server_comment)
+{
+ pstring outbuf;
+ char *p;
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf+1;
+
+ CVAL(outbuf,0) = announce_type;
+
+ /* Announcement parameters. */
+ CVAL(p,0) = updatecount;
+ SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */
+
+ StrnCpy(p+5,server_name,15);
+ strupper(p+5);
+
+ CVAL(p,21) = lp_major_announce_version(); /* Major version. */
+ CVAL(p,22) = lp_minor_announce_version(); /* Minor version. */
+
+ SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+ /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */
+ SSVAL(p,27,BROWSER_ELECTION_VERSION);
+ SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */
+
+ pstrcpy(p+31,server_comment);
+ p += 31;
+ p = skip_string(p,1);
+
+ send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ from_name, 0x0, to_name, to_type, to_ip, subrec->myip);
+}
+
+/****************************************************************************
+ We are a local master browser. Announce this to WORKGROUP<1e>.
+****************************************************************************/
+
+static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bit set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n",
+ type, myname, subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_LocalMasterAnnouncement,
+ myname, /* From nbt name. */
+ work->work_group, 0x1e, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ myname, /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the workgroup WORKGROUP to MSBROWSE<01>.
+****************************************************************************/
+
+static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work)
+{
+ DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n",
+ subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_DomainAnnouncement,
+ myname, /* From nbt name. */
+ MSBROWSE, 0x1, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ work->work_group, /* Name to announce. */
+ SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */
+ myname); /* From name as comment. */
+}
+
+/****************************************************************************
+ Announce the given host to WORKGROUP<1d>.
+****************************************************************************/
+
+static void send_host_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bits set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n",
+ type, servrec->serv.name, subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_HostAnnouncement,
+ servrec->serv.name, /* From nbt name. */
+ work->work_group, 0x1d, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ servrec->serv.name, /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce a server record.
+ ****************************************************************************/
+
+static void announce_server(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Only do domain announcements if we are a master and it's
+ our primary name we're being asked to announce. */
+
+ if (AM_LOCAL_MASTER_BROWSER(work) && strequal(myname,servrec->serv.name))
+ {
+ send_local_master_announcement(subrec, work, servrec);
+ send_workgroup_announcement(subrec, work);
+ }
+ else
+ {
+ send_host_announcement(subrec, work, servrec);
+ }
+}
+
+/****************************************************************************
+ Go through all my registered names on all broadcast subnets and announce
+ them if the timeout requires it.
+ **************************************************************************/
+
+void announce_my_server_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup);
+
+ if(work)
+ {
+ struct server_record *servrec;
+
+ if (work->needannounce)
+ {
+ /* Drop back to a max 3 minute announce. This is to prevent a
+ single lost packet from breaking things for too long. */
+
+ work->announce_interval = MIN(work->announce_interval,
+ CHECK_TIME_MIN_HOST_ANNCE*60);
+ work->lastannounce_time = t - (work->announce_interval+1);
+ work->needannounce = False;
+ }
+
+ /* Announce every minute at first then progress to every 12 mins */
+ if ((t - work->lastannounce_time) < work->announce_interval)
+ continue;
+
+ if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60))
+ work->announce_interval += 60;
+
+ work->lastannounce_time = t;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ if (is_myname(servrec->serv.name))
+ announce_server(subrec, work, servrec);
+ }
+ } /* if work */
+ } /* for subrec */
+}
+
+/* Announce timer. Moved into global static so it can be reset
+ when a machine becomes a local master browser. */
+static time_t announce_timer_last=0;
+
+/****************************************************************************
+ Reset the announce_timer so that a local master browser announce will be done
+ immediately.
+ ****************************************************************************/
+
+void reset_announce_timer()
+{
+ announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
+}
+
+/****************************************************************************
+ Announce myself as a local master browser to a domain master browser.
+ **************************************************************************/
+
+void announce_myself_to_domain_master_browser(time_t t)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+
+ if(!we_are_a_wins_client())
+ {
+ DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n"));
+ return;
+ }
+
+ if (!announce_timer_last)
+ announce_timer_last = t;
+
+ if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60))
+ {
+ DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n",
+ t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
+ return;
+ }
+
+ announce_timer_last = t;
+
+ /* Look over all our broadcast subnets to see if any of them
+ has the state set as local master browser. */
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ {
+ DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ /* Look in nmbd_browsersync.c for the rest of this code. */
+ announce_and_sync_with_domain_master_browser(subrec, work);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+Announce all samba's server entries as 'gone'.
+This must *only* be called on shutdown.
+****************************************************************************/
+
+void announce_my_servers_removed(void)
+{
+ struct subnet_record *subrec;
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ struct server_record *servrec;
+
+ work->announce_interval = 0;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ if (!is_myname(servrec->serv.name))
+ continue;
+ servrec->serv.type = 0;
+ if(AM_LOCAL_MASTER_BROWSER(work))
+ send_local_master_announcement(subrec, work, servrec);
+ send_host_announcement(subrec, work, servrec);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Do all the "remote" announcements. These are used to put ourselves
+ on a remote browse list. They are done blind, no checking is done to
+ see if there is actually a local master browser at the other end.
+ **************************************************************************/
+
+void announce_remote(time_t t)
+{
+ char *s,*ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ char *comment;
+ int stype = lp_default_server_announce();
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_announce();
+ if (!*s)
+ return;
+
+ comment = lp_serverstring();
+
+ for (ptr=s; next_token(&ptr,s2,NULL); )
+ {
+ /* The entries are of the form a.b.c.d/WORKGROUP with
+ WORKGROUP being optional */
+ char *wgroup;
+ int i;
+
+ wgroup = strchr(s2,'/');
+ if (wgroup)
+ *wgroup++ = 0;
+ if (!wgroup || !*wgroup)
+ wgroup = myworkgroup;
+
+ addr = *interpret_addr2(s2);
+
+ /* Announce all our names including aliases */
+ /* Give the ip address as the address of our first
+ broadcast subnet. */
+
+ for(i=0; my_netbios_names[i]; i++)
+ {
+ char *name = my_netbios_names[i];
+
+ DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n",
+ name, inet_ntoa(addr) ));
+
+ send_announcement(FIRST_SUBNET, ANN_HostAnnouncement,
+ name, /* From nbt name. */
+ wgroup, 0x1e, /* To nbt name. */
+ addr, /* To ip. */
+ REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */
+ name, /* Name to announce. */
+ stype, /* Type field. */
+ comment);
+ }
+ }
+}
+
+/****************************************************************************
+ Implement the 'remote browse sync' feature Andrew added.
+ These are used to put our browse lists into remote browse lists.
+ **************************************************************************/
+
+void browse_sync_remote(time_t t)
+{
+ char *s,*ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ struct work_record *work;
+ pstring outbuf;
+ char *p;
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_browse_sync();
+ if (!*s)
+ return;
+
+ /*
+ * We only do this if we are the local master browser
+ * for our workgroup on the firsst subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL)
+ {
+ DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n",
+ myworkgroup, FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ if(!AM_LOCAL_MASTER_BROWSER(work))
+ {
+ DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \
+for workgroup %s on subnet %s.\n", myworkgroup, FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+ CVAL(p,0) = ANN_MasterAnnouncement;
+ p++;
+
+ StrnCpy(p,myname,15);
+ strupper(p);
+ p = skip_string(p,1);
+
+ for (ptr=s; next_token(&ptr,s2,NULL); )
+ {
+ /* The entries are of the form a.b.c.d */
+ addr = *interpret_addr2(s2);
+
+ DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n",
+ myname, inet_ntoa(addr) ));
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ myname, 0x0, "*", 0x0, addr, FIRST_SUBNET->myip);
+ }
+}
diff --git a/source3/nmbd/nmbd_serverlistdb.c b/source3/nmbd/nmbd_serverlistdb.c
new file mode 100644
index 0000000000..1281fe2ee3
--- /dev/null
+++ b/source3/nmbd/nmbd_serverlistdb.c
@@ -0,0 +1,454 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+#include "smb.h"
+
+extern int ClientNMB;
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+
+int updatecount = 0;
+
+/*******************************************************************
+ Remove all the servers in a work group.
+ ******************************************************************/
+
+void remove_all_servers(struct work_record *work)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts)
+ {
+ DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
+ nexts = servrec->next;
+
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ free((char *)servrec);
+
+ }
+
+ work->subnet->work_changed = True;
+}
+
+/***************************************************************************
+ Add a server into the a workgroup serverlist.
+ **************************************************************************/
+
+static void add_server_to_workgroup(struct work_record *work,
+ struct server_record *servrec)
+{
+ struct server_record *servrec2;
+
+ if (!work->serverlist)
+ {
+ work->serverlist = servrec;
+ servrec->prev = NULL;
+ servrec->next = NULL;
+ return;
+ }
+
+ for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
+ ;
+
+ servrec2->next = servrec;
+ servrec->next = NULL;
+ servrec->prev = servrec2;
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Find a server in a server list.
+ **************************************************************************/
+
+struct server_record *find_server_in_workgroup(struct work_record *work, char *name)
+{
+ struct server_record *ret;
+
+ for (ret = work->serverlist; ret; ret = ret->next)
+ {
+ if (strequal(ret->serv.name,name))
+ return ret;
+ }
+ return NULL;
+}
+
+
+/****************************************************************************
+ Remove a server entry from this workgroup.
+ ****************************************************************************/
+
+static void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
+{
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ free((char *)servrec);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Create a server entry on this workgroup.
+ ****************************************************************************/
+
+struct server_record *create_server_on_workgroup(struct work_record *work,
+ char *name,int servertype,
+ int ttl,char *comment)
+{
+ struct server_record *servrec;
+
+ if (name[0] == '*')
+ {
+ DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
+ name));
+ return (NULL);
+ }
+
+ if((servrec = find_server_in_workgroup(work, name)) != NULL)
+ {
+ DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
+workgroup %s. This is a bug.\n", name, work->work_group));
+ return NULL;
+ }
+
+ if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL)
+ {
+ DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+
+ bzero((char *)servrec,sizeof(*servrec));
+
+ servrec->subnet = work->subnet;
+
+ StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1);
+ StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+ strupper(servrec->serv.name);
+ servrec->serv.type = servertype;
+
+ update_server_ttl(servrec, ttl);
+
+ add_server_to_workgroup(work, servrec);
+
+ DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
+workgroup %s.\n", name,servertype,comment, work->work_group));
+
+ work->subnet->work_changed = True;
+
+ return(servrec);
+}
+
+/*******************************************************************
+ Update the ttl field of a server record.
+*******************************************************************/
+
+void update_server_ttl(struct server_record *servrec, int ttl)
+{
+ if(ttl > lp_max_ttl())
+ ttl = lp_max_ttl();
+
+ if(is_myname(servrec->serv.name))
+ servrec->death_time = PERMANENT_TTL;
+ else
+ servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ servrec->subnet->work_changed = True;
+}
+
+/*******************************************************************
+ Expire old servers in the serverlist. A time of -1 indicates
+ everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
+ This should only be called from expire_workgroups_and_servers().
+ ******************************************************************/
+
+void expire_servers(struct work_record *work, time_t t)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts)
+ {
+ nexts = servrec->next;
+
+ if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t)))
+ {
+ DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
+ remove_server_from_workgroup(work, servrec);
+ work->subnet->work_changed = True;
+ }
+ }
+}
+
+/*******************************************************************
+ Decide if we should write out a server record for this server.
+ We return zero if we should not. Check if we've already written
+ out this server record from an earlier subnet.
+******************************************************************/
+
+static uint32 write_this_server_name( struct subnet_record *subrec,
+ struct work_record *work,
+ struct server_record *servrec)
+{
+ struct subnet_record *ssub;
+ struct work_record *iwork;
+ struct server_record *sserv;
+
+ /* Go through all the subnets we have already seen. */
+ for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub))
+ {
+ for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next)
+ {
+ if((sserv = find_server_in_workgroup( iwork, servrec->serv.name)) != NULL)
+ {
+ /*
+ * We have already written out this server record, don't
+ * do it again. This gives precedence to servers we have seen
+ * on the broadcast subnets over servers that may have been
+ * added via a sync on the unicast_subet.
+ *
+ * The correct way to do this is to have a serverlist file
+ * per subnet - this means changes to smbd as well. I may
+ * add this at a later date (JRA).
+ */
+
+ return 0;
+ }
+ }
+ }
+
+ return servrec->serv.type;
+}
+
+/*******************************************************************
+ Decide if we should write out a workgroup record for this workgroup.
+ We return zero if we should not. Don't write out myworkgroup (we've
+ already done it) and also don't write out a second workgroup record
+ on the unicast subnet that we've already written out on one of the
+ broadcast subnets.
+******************************************************************/
+
+static uint32 write_this_workgroup_name( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct subnet_record *ssub;
+
+ if(strequal(myworkgroup, work->work_group))
+ return 0;
+
+ /* This is a workgroup we have seen on a broadcast subnet. All
+ these have the same type. */
+
+ if(subrec != unicast_subnet)
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+
+ for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub))
+ {
+ /* This is the unicast subnet so check if we've already written out
+ this subnet when we passed over the broadcast subnets. */
+
+ if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
+ return 0;
+ }
+
+ /* All workgroups on the unicast subnet (except our own, which we
+ have already written out) cannot be local. */
+
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
+}
+
+/*******************************************************************
+ Write out the browse.dat file.
+ ******************************************************************/
+
+void write_browse_list(time_t t, BOOL force_write)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+ struct server_record *servrec;
+ pstring fname,fnamenew;
+ uint32 stype;
+ fstring tmp;
+ int i;
+ FILE *fp;
+ BOOL list_changed = force_write;
+ static time_t lasttime = 0;
+
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 5)
+ return;
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ if(subrec->work_changed)
+ {
+ list_changed = True;
+ break;
+ }
+ }
+
+ if(!list_changed)
+ return;
+
+ lasttime = t;
+ updatecount++;
+
+ dump_workgroups();
+
+ pstrcpy(fname,lp_lockdir());
+ trim_string(fname,NULL,"/");
+ strcat(fname,"/");
+ strcat(fname,SERVER_LIST);
+ pstrcpy(fnamenew,fname);
+ strcat(fnamenew,".");
+
+ fp = fopen(fnamenew,"w");
+
+ if (!fp)
+ {
+ DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
+ fnamenew,strerror(errno)));
+ return;
+ }
+
+ /*
+ * Write out a record for our workgroup. Use the record from the first
+ * subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL)
+ {
+ DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
+ myworkgroup));
+ fclose(fp);
+ return;
+ }
+
+ sprintf(tmp, "\"%s\"", work->work_group);
+ fprintf(fp, "%-25s ", tmp);
+ fprintf(fp, "%08x ", SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+ sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ? work->local_master_browser_name : "Unknown");
+ fprintf(fp, "%-30s", tmp);
+ fprintf(fp, "\"%s\"\n", work->work_group);
+
+ /*
+ * We need to do something special for our own names.
+ * This is due to the fact that we may be a local master browser on
+ * one of our broadcast subnets, and a domain master on the unicast
+ * subnet. We iterate over the subnets and only write out the name
+ * once.
+ */
+
+ for (i=0; my_netbios_names[i]; i++)
+ {
+ stype = 0;
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ if((work = find_workgroup_on_subnet( subrec, myworkgroup )) == NULL)
+ continue;
+ if((servrec = find_server_in_workgroup( work, my_netbios_names[i])) == NULL)
+ continue;
+
+ stype |= servrec->serv.type;
+ }
+
+ /* Output server details, plus what workgroup they're in. */
+ sprintf(tmp, "\"%s\"", my_netbios_names[i]);
+ fprintf(fp, "%-25s ", tmp);
+ fprintf(fp, "%08x ", stype);
+ sprintf(tmp, "\"%s\" ", lp_serverstring());
+ fprintf(fp, "%-30s", tmp);
+ fprintf(fp, "\"%s\"\n", myworkgroup);
+ }
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ subrec->work_changed = False;
+
+ for (work = subrec->workgrouplist; work ; work = work->next)
+ {
+ /* Write out a workgroup record for a workgroup. */
+ uint32 wg_type = write_this_workgroup_name( subrec, work);
+
+ if(wg_type)
+ {
+ sprintf(tmp, "\"%s\"", work->work_group);
+ fprintf(fp, "%-25s ", tmp);
+
+ fprintf(fp, "%08x ", wg_type);
+ sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ?
+ work->local_master_browser_name : "Unknown" );
+ fprintf(fp, "%-30s", tmp);
+ fprintf(fp, "\"%s\"\n", work->work_group);
+ }
+
+ /* Now write out any server records a workgroup may have. */
+
+ for (servrec = work->serverlist; servrec ; servrec = servrec->next)
+ {
+ uint32 serv_type;
+
+ /* We have already written our names here. */
+ if(is_myname(servrec->serv.name))
+ continue;
+
+ serv_type = write_this_server_name(subrec, work, servrec);
+
+ if(serv_type)
+ {
+ /* Output server details, plus what workgroup they're in. */
+ sprintf(tmp, "\"%s\"", servrec->serv.name);
+ fprintf(fp, "%-25s ", tmp);
+ fprintf(fp, "%08x ", serv_type);
+ sprintf(tmp, "\"%s\" ", servrec->serv.comment);
+ fprintf(fp, "%-30s", tmp);
+ fprintf(fp, "\"%s\"\n", work->work_group);
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+ unlink(fname);
+ chmod(fnamenew,0644);
+ rename(fnamenew,fname);
+ DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+}
diff --git a/source3/nmbd/nmbd_subnetdb.c b/source3/nmbd/nmbd_subnetdb.c
new file mode 100644
index 0000000000..93aecc21f2
--- /dev/null
+++ b/source3/nmbd/nmbd_subnetdb.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+#include "smb.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+extern struct in_addr ipzero;
+
+/* This is the broadcast subnets database. */
+struct subnet_record *subnetlist = NULL;
+
+/* Extra subnets - keep these separate so enumeration code doesn't
+ run onto it by mistake. */
+
+struct subnet_record *unicast_subnet = NULL;
+struct subnet_record *remote_broadcast_subnet = NULL;
+struct subnet_record *wins_server_subnet = NULL;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/****************************************************************************
+ Add a subnet into the list.
+ **************************************************************************/
+
+static void add_subnet(struct subnet_record *subrec)
+{
+ struct subnet_record *subrec2;
+
+ if (!subnetlist)
+ {
+ subnetlist = subrec;
+ subrec->prev = NULL;
+ subrec->next = NULL;
+ return;
+ }
+
+ for (subrec2 = subnetlist; subrec2->next; subrec2 = subrec2->next)
+ ;
+
+ subrec2->next = subrec;
+ subrec->next = NULL;
+ subrec->prev = subrec2;
+}
+
+/****************************************************************************
+ Create a subnet entry.
+ ****************************************************************************/
+
+static struct subnet_record *make_subnet(char *name, enum subnet_type type,
+ struct in_addr myip, struct in_addr bcast_ip,
+ struct in_addr mask_ip)
+{
+ struct subnet_record *subrec = NULL;
+ int nmb_sock, dgram_sock;
+
+ /* Check if we are creating a non broadcast subnet - if so don't create
+ sockets.
+ */
+
+ if(type != NORMAL_SUBNET)
+ {
+ nmb_sock = -1;
+ dgram_sock = -1;
+ }
+ else
+ {
+ /*
+ * Attempt to open the sockets on port 137/138 for this interface
+ * and bind them.
+ * Fail the subnet creation if this fails.
+ */
+
+ if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr)) == -1)
+ {
+ DEBUG(0,("make_subnet: Failed to open nmb socket on interface %s \
+for port %d. Error was %s\n", inet_ntoa(myip), global_nmb_port, strerror(errno)));
+ return NULL;
+ }
+
+ if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr)) == -1)
+ {
+ DEBUG(0,("make_subnet: Failed to open dgram socket on interface %s \
+for port %d. Error was %s\n", inet_ntoa(myip), DGRAM_PORT, strerror(errno)));
+ return NULL;
+ }
+
+ /* Make sure we can broadcast from these sockets. */
+ set_socket_options(nmb_sock,"SO_BROADCAST");
+ set_socket_options(dgram_sock,"SO_BROADCAST");
+
+ }
+
+ subrec = (struct subnet_record *)malloc(sizeof(*subrec));
+
+ if (!subrec)
+ {
+ DEBUG(0,("make_subnet: malloc fail !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ return(NULL);
+ }
+
+ bzero((char *)subrec,sizeof(*subrec));
+
+ if((subrec->subnet_name = strdup(name)) == NULL)
+ {
+ DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ free((char *)subrec);
+ return(NULL);
+ }
+
+ DEBUG(2, ("making subnet name:%s ", name ));
+ DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip)));
+ DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip)));
+
+ subrec->namelist_changed = False;
+ subrec->work_changed = False;
+
+ subrec->bcast_ip = bcast_ip;
+ subrec->mask_ip = mask_ip;
+ subrec->myip = myip;
+ subrec->type = type;
+ subrec->nmb_sock = nmb_sock;
+ subrec->dgram_sock = dgram_sock;
+
+ return subrec;
+}
+
+/****************************************************************************
+ Create subnet entries.
+**************************************************************************/
+
+BOOL create_subnets()
+{
+ int num_interfaces = iface_count();
+ int i;
+ struct in_addr unicast_ip;
+
+ if(num_interfaces == 0)
+ {
+ DEBUG(0,("create_subnets: No local interfaces !\n"));
+ return False;
+ }
+
+ /*
+ * Create subnets from all the local interfaces and thread them onto
+ * the linked list.
+ */
+
+ for (i = 0 ; i < num_interfaces; i++)
+ {
+ struct subnet_record *subrec;
+ struct interface *iface = get_interface(i);
+
+ if((subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET,
+ iface->ip, iface->bcast,iface->nmask)) == NULL)
+ return False;
+ add_subnet(subrec);
+ }
+
+ /*
+ * If we have been configured to use a WINS server, then try and
+ * get the ip address of it here. If we are the WINS server then
+ * set the unicast subnet address to be the first of our own real
+ * addresses.
+ */
+
+ if(*lp_wins_server())
+ {
+ struct in_addr real_wins_ip;
+ real_wins_ip = *interpret_addr2(lp_wins_server());
+
+ if (!zero_ip(real_wins_ip))
+ {
+ unicast_ip = real_wins_ip;
+ }
+ else
+ {
+ /* The smb.conf's wins server parameter MUST be a host_name
+ or an ip_address. */
+ DEBUG(0,("invalid smb.conf parameter 'wins server'\n"));
+ return False;
+ }
+ }
+ else if(lp_we_are_a_wins_server())
+ {
+ /* Pick the first interface ip address as the WINS server ip. */
+ unicast_ip = *iface_n_ip(0);
+ }
+ else
+ {
+ /* We should not be using a WINS server at all. Set the
+ ip address of the subnet to be zero. */
+ unicast_ip = ipzero;
+ }
+
+ /*
+ * Create the unicast and remote broadcast subnets.
+ * Don't put these onto the linked list.
+ * The ip address of the unicast subnet is set to be
+ * the WINS server address, if it exists, or ipzero if not.
+ */
+
+ unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET,
+ unicast_ip, unicast_ip, unicast_ip);
+
+ remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET",
+ REMOTE_BROADCAST_SUBNET,
+ ipzero, ipzero, ipzero);
+
+ if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL))
+ return False;
+
+ /*
+ * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on
+ * the linked list.
+ */
+
+ if (lp_we_are_a_wins_server())
+ {
+ if((wins_server_subnet = make_subnet("WINS_SERVER_SUBNET",
+ WINS_SERVER_SUBNET,
+ ipzero, ipzero, ipzero)) == NULL)
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+Function to tell us if we can use the unicast subnet.
+******************************************************************/
+
+BOOL we_are_a_wins_client()
+{
+ static int cache_we_are_a_wins_client = -1;
+
+ if(cache_we_are_a_wins_client == -1)
+ cache_we_are_a_wins_client = (ip_equal(ipzero, unicast_subnet->myip) ?
+ False : True);
+
+ return cache_we_are_a_wins_client;
+}
+
+/*******************************************************************
+Access function used by NEXT_SUBNET_INCLUDING_UNICAST
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec)
+{
+ if(subrec == unicast_subnet)
+ return NULL;
+ else if((subrec->next == NULL) && we_are_a_wins_client())
+ return unicast_subnet;
+ else
+ return subrec->next;
+}
diff --git a/source3/nmbd/nmbd_winsproxy.c b/source3/nmbd/nmbd_winsproxy.c
new file mode 100644
index 0000000000..ed8653b8bf
--- /dev/null
+++ b/source3/nmbd/nmbd_winsproxy.c
@@ -0,0 +1,195 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+extern int DEBUGLEVEL;
+
+/****************************************************************************
+Function called when the name lookup succeeded.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_success( struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec)
+{
+ struct packet_struct *original_packet;
+ struct subnet_record *orig_broadcast_subnet;
+ uint16 nb_flags;
+ int num_ips;
+ int i;
+ int ttl;
+ struct in_addr *iplist;
+
+ /* Extract the original packet and the original broadcast subnet from
+ the userdata. */
+
+ memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) );
+ memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *) );
+
+ nb_flags = get_nb_flags( rrec->rdata );
+
+ num_ips = rrec->rdlength / 6;
+ if(num_ips == 0)
+ {
+ DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \
+returned for name %s.\n", namestr(nmbname) ));
+ return;
+ }
+
+ if(num_ips == 1)
+ iplist = &ip;
+ else
+ {
+ if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL)
+ {
+ DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n"));
+ return;
+ }
+
+ for(i = 0; i < num_ips; i++)
+ putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]);
+ }
+
+ /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */
+
+ if(rrec == PERMANENT_TTL)
+ ttl = lp_max_ttl();
+
+ add_name_to_subnet( orig_broadcast_subnet, nmbname->name, nmbname->name_type,
+ nb_flags, ttl, WINS_PROXY_NAME, num_ips, iplist);
+
+ if(iplist != &ip)
+ free((char *)iplist);
+
+ /* Finally reply to the original name query. */
+ reply_netbios_packet(original_packet, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rrec->rdata, /* data to send. */
+ rrec->rdlength); /* data length. */
+}
+
+/****************************************************************************
+Function called when the name lookup failed.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \
+of name %s.\n", fail_code, namestr(question_name) ));
+}
+
+/****************************************************************************
+Function to make a deep copy of the userdata we will need when the WINS
+proxy query returns.
+****************************************************************************/
+
+static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p, *copy_of_p;
+ struct userdata_struct *new_userdata =
+ (struct userdata_struct *)malloc( userdata->userdata_len );
+
+ if(new_userdata == NULL)
+ return NULL;
+
+ new_userdata->copy_fn = userdata->copy_fn;
+ new_userdata->free_fn = userdata->free_fn;
+ new_userdata->userdata_len = userdata->userdata_len;
+
+ /* Copy the subnet_record pointer. */
+ memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) );
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *) );
+
+ /* Do a deep copy of the packet. */
+ if((copy_of_p = copy_packet(p)) == NULL)
+ {
+ free((char *)new_userdata);
+ return NULL;
+ }
+
+ /* Lock the copy. */
+ copy_of_p->locked = True;
+
+ memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)&copy_of_p,
+ sizeof(struct packet_struct *) );
+
+ return new_userdata;
+}
+
+/****************************************************************************
+Function to free the deep copy of the userdata we used when the WINS
+proxy query returned.
+****************************************************************************/
+
+static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p;
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *));
+
+ /* Unlock the packet. */
+ p->locked = False;
+
+ free_packet(p);
+ free((char *)userdata);
+}
+
+/****************************************************************************
+ Make a WINS query on behalf of a broadcast client name query request.
+****************************************************************************/
+
+void make_wins_proxy_name_query_request( struct subnet_record *subrec,
+ struct packet_struct *incoming_packet,
+ struct nmb_name *question_name)
+{
+ char ud[sizeof(struct userdata_struct) + sizeof(struct subrec *) +
+ sizeof(struct packet_struct *)];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ bzero(ud, sizeof(ud));
+
+ userdata->copy_fn = wins_proxy_userdata_copy_fn;
+ userdata->free_fn = wins_proxy_userdata_free_fn;
+ userdata->userdata_len = sizeof(ud);
+ memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *));
+ memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet,
+ sizeof(struct packet_struct *));
+
+ /* Now use the unicast subnet to query the name with the WINS server. */
+ query_name( unicast_subnet, question_name->name, question_name->name_type,
+ wins_proxy_name_query_request_success,
+ wins_proxy_name_query_request_fail,
+ userdata);
+}
diff --git a/source3/nmbd/nmbd_winsserver.c b/source3/nmbd/nmbd_winsserver.c
new file mode 100644
index 0000000000..ba7b62e5ab
--- /dev/null
+++ b/source3/nmbd/nmbd_winsserver.c
@@ -0,0 +1,1565 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+
+#define WINS_LIST "wins.dat"
+
+extern int DEBUGLEVEL;
+extern struct in_addr ipzero;
+
+/****************************************************************************
+Determine if this packet should be allocated to the WINS server.
+*****************************************************************************/
+
+BOOL packet_is_for_wins_server(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ /* Only unicast packets go to a WINS server. */
+ if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True))
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
+ return False;
+ }
+
+ /* Check for node status requests. */
+ if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY)
+ return False;
+
+ switch(nmb->header.opcode)
+ {
+ /*
+ * A WINS server issues WACKS, not receives them.
+ */
+ case NMB_WACK_OPCODE:
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
+ return False;
+ /*
+ * A WINS server only processes registration and
+ * release requests, not responses.
+ */
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if(nmb->header.response)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(nmb->header.response)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ /*
+ * Only process unicast name queries with rd = 1.
+ */
+ case NMB_NAME_QUERY_OPCODE:
+ if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
+ return False;
+ }
+ break;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Utility function to decide what ttl to give a register/refresh request.
+*****************************************************************************/
+
+static int get_ttl_from_packet(struct nmb_packet *nmb)
+{
+ int ttl = nmb->additional->ttl;
+
+ if(ttl < lp_min_wins_ttl() )
+ ttl = lp_min_wins_ttl();
+
+ if(ttl > lp_max_wins_ttl() )
+ ttl = lp_max_wins_ttl();
+
+ return ttl;
+}
+
+/****************************************************************************
+Load or create the WINS database.
+*****************************************************************************/
+
+BOOL initialise_wins(void)
+{
+ fstring fname;
+ time_t time_now = time(NULL);
+ FILE *fp;
+ pstring line;
+
+ if(!lp_we_are_a_wins_server())
+ return True;
+
+ add_samba_names_to_subnet(wins_server_subnet);
+
+#ifndef SYNC_DNS
+ /* Setup the async dns. */
+ start_async_dns();
+#endif
+
+ fstrcpy(fname,lp_lockdir());
+ trim_string(fname,NULL,"/");
+ strcat(fname,"/");
+ strcat(fname,WINS_LIST);
+
+ if((fp = fopen(fname,"r")) == NULL)
+ {
+ DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
+ fname, strerror(errno) ));
+ return True;
+ }
+
+ while (!feof(fp))
+ {
+ pstring name_str, ip_str, ttl_str, nb_flags_str;
+ unsigned int num_ips;
+ pstring name;
+ struct in_addr *ip_list;
+ int type = 0;
+ uint16 nb_flags;
+ time_t ttl;
+ enum name_source source;
+ char *ptr;
+ char *p;
+ BOOL got_token;
+ BOOL was_ip;
+ int i;
+
+ /* Read a line from the wins.dat file. Strips whitespace
+ from the beginning and end of the line.
+ */
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ ptr = line;
+
+ /*
+ * Now we handle multiple IP addresses per name we need
+ * to iterate over the line twice. The first time to
+ * determine how many IP addresses there are, the second
+ * time to actually parse them into the ip_list array.
+ */
+
+ if (!next_token(&ptr,name_str,NULL))
+ {
+ DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line ));
+ continue;
+ }
+
+ if (!next_token(&ptr,ttl_str,NULL))
+ {
+ DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line ));
+ continue;
+ }
+
+ /*
+ * Determine the number of IP addresses per line.
+ */
+ num_ips = 0;
+ do
+ {
+ got_token = next_token(&ptr,ip_str,NULL);
+ was_ip = False;
+
+ if(got_token && strchr(ip_str, '.'))
+ {
+ num_ips++;
+ was_ip = True;
+ }
+ } while( got_token && was_ip);
+
+ if(num_ips == 0)
+ {
+ DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line ));
+ continue;
+ }
+
+ if(!got_token)
+ {
+ DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line ));
+ continue;
+ }
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL)
+ {
+ DEBUG(0,("initialise_wins: Malloc fail !\n"));
+ return False;
+ }
+
+ /* Reset and re-parse the line. */
+ ptr = line;
+ next_token(&ptr,name_str,NULL);
+ next_token(&ptr,ttl_str,NULL);
+ for(i = 0; i < num_ips; i++)
+ {
+ next_token(&ptr, ip_str, NULL);
+ ip_list[i] = *interpret_addr2(ip_str);
+ if (ip_equal(ip_list[i], ipzero))
+ source = SELF_NAME;
+ }
+ next_token(&ptr,nb_flags_str,NULL);
+
+ /*
+ * Deal with SELF or REGISTER name encoding. Default is REGISTER
+ * for compatibility with old nmbds.
+ */
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'S')
+ {
+ DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line));
+ free((char *)ip_list);
+ continue;
+ }
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'R')
+ nb_flags_str[strlen(nb_flags_str)-1] = '\0';
+
+ /* Netbios name. # divides the name from the type (hex): netbios#xx */
+ pstrcpy(name,name_str);
+
+ if((p = strchr(name,'#')) != NULL)
+ {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ /* Decode the netbios flags (hex) and the time-to-live (in seconds). */
+ sscanf(nb_flags_str,"%hx",&nb_flags);
+ sscanf(ttl_str,"%ld",&ttl);
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL)
+ {
+ struct name_record *namerec;
+
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG(4, ("initialise_wins: add name: %s#%02x ttl = %ld first IP %s flags = %2hx\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ namerec = add_name_to_subnet(wins_server_subnet, name, type, nb_flags,
+ ttl, REGISTER_NAME, num_ips, ip_list);
+
+ }
+ else
+ {
+ DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %ld first IP %s flags = %2hx\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+
+ free((char *)ip_list);
+ }
+
+ fclose(fp);
+ return True;
+}
+
+/****************************************************************************
+Send a WINS WACK (Wait ACKnowledgement) response.
+**************************************************************************/
+
+static void send_wins_wack_response(int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char rdata[2];
+
+ rdata[0] = rdata[1] = 0;
+
+ /* Taken from nmblib.c - we need to send back almost
+ identical bytes from the requesting packet header. */
+
+ rdata[0] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response) rdata[0] |= 0x4;
+ if (nmb->header.nm_flags.trunc) rdata[0] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired) rdata[0] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response) rdata[1] |= 0x80;
+ if (nmb->header.nm_flags.bcast) rdata[1] |= 0x10;
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_WAIT_ACK, /* nmbd type code. */
+ NMB_WACK_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ (char *)rdata, /* data to send. */
+ 2); /* data length. */
+}
+
+/****************************************************************************
+Send a WINS name registration response.
+**************************************************************************/
+
+static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name refresh request to a WINS server.
+************************************************************************/
+
+void wins_process_name_refresh_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = get_ttl_from_packet(nmb);
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name refresh packets here.
+ * Anyone trying to refresh broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \
+IP %s\n", namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If this is a refresh request and the name doesn't exist then
+ * fail it.
+ */
+
+ if(namerec == NULL)
+ {
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \
+the name does not exist.\n", namestr(question) ));
+ send_wins_name_registration_response(NAM_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Check that the group bits for the refreshing name and the
+ * name in our database match.
+ */
+
+ if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) )
+ {
+ DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \
+does not match group bit in WINS for this name.\n", namestr(question), group ? "True" : "False" ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * For a unique name check that the person refreshing the name is one of the registered IP
+ * addresses. If not - fail the refresh. Do the same for group names with a type of 0x1c.
+ * Just return success for unique 0x1d refreshes. For normal group names update the ttl
+ * and return success.
+ */
+
+ if((!group || (group && (question->name_type == 0x1c))) && find_ip_in_name_record(namerec, from_ip ))
+ {
+ /*
+ * Update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else if(group)
+ {
+ /*
+ * Normal groups are all registered with an IP address of 255.255.255.255
+ * so we can't search for the IP address.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else if(!group && (question->name_type == 0x1d))
+ {
+ /*
+ * Special name type - just pretend the refresh succeeded.
+ */
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else
+ {
+ /*
+ * Fail the refresh.
+ */
+
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \
+is IP is not known to the name.\n", namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+}
+
+/***********************************************************************
+ Deal with a name registration request query success to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The success here is actually a failure as it means
+ the client we queried wants to keep the name, so we must return
+ a registration failure to the original requestor.
+************************************************************************/
+
+static void wins_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
+name %s. Rejecting registration request.\n", inet_ntoa(ip), namestr(question_name) ));
+
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The failure here is actually a success as it means
+ the client we queried didn't want to keep the name, so we can remove
+ the old name record and then successfully add the new name.
+************************************************************************/
+
+static void wins_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+ struct nmb_packet *nmb;
+ struct name_record *namerec = NULL;
+ uint16 nb_flags;
+ BOOL group;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ nmb = &orig_reg_packet->packet.nmb;
+
+ nb_flags = get_nb_flags(nmb->additional->rdata);
+ group = (nb_flags & NB_GROUP) ? True : False;
+
+ /*
+ * We want to just add the name, as we now know the original owner
+ * didn't want it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this timeout/error response. So we check that
+ * the name still exists and is in the same state - if so
+ * we remove it and call wins_process_name_registration_request()
+ * as we know it will do the right thing now.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if((namerec != NULL) && (namerec->source == REGISTER_NAME) &&
+ ip_equal(rrec->packet->ip, *namerec->ip) )
+ {
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ if(namerec == NULL)
+ wins_process_name_registration_request(subrec, orig_reg_packet);
+ else
+ DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
+querying for name %s in order to replace it and this reply.\n", namestr(question_name) ));
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request to a WINS server.
+
+ Use the following pseudocode :
+
+ registering_group
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- add name (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ registering_unique
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- fail add (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ As can be seen from the above, the two cases may be collapsed onto each
+ other with the exception of the case where the name already exists and
+ is a group name. This case we handle with an if statement.
+
+************************************************************************/
+
+void wins_process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
+IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME)))
+ {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", namestr(question) ));
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if((namerec != NULL) && (namerec->source != REGISTER_NAME))
+ {
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS with source type %d.\n", namestr(question), namerec->source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Special policy decisions based on MS documentation.
+ * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255.
+ * 2). All unique names ending in 0x1d are ignored, although a positive response is sent.
+ */
+
+ /*
+ * A group name is always added as the local broadcast address, except
+ * for group names ending in 0x1c.
+ * Group names with type 0x1c are registered with individual IP addresses.
+ */
+
+ if(registering_group_name && (question->name_type != 0x1c))
+ from_ip = *interpret_addr2("255.255.255.255");
+
+ /*
+ * Ignore all attempts to register a unique 0x1d name, although return success.
+ */
+
+ if(!registering_group_name && (question->name_type == 0x1d))
+ {
+ DEBUG(3,("wins_process_name_registration_request: Ignoring request \
+to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * Next two cases are the 'if statement' mentioned above.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec))
+ {
+ if(registering_group_name)
+ {
+ /*
+ * If we are adding a group name, the name exists and is also a group entry just add this
+ * IP address to it and update the ttl.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
+ inet_ntoa(from_ip), namestr(question) ));
+ /*
+ * Check the ip address is not already in the group.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else
+ {
+ /*
+ * If we are adding a unique name, the name exists in the WINS db
+ * and is a group name then reject the registration.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) )
+ {
+ if(!ismyip(from_ip))
+ {
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ else
+ {
+ /*
+ * It's one of our names and one of our IP's - update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and it is a unique registration and the registering IP
+ * is the same as the the (single) already registered IP then just update the ttl.
+ */
+
+ if(!registering_group_name && (namerec != NULL) && (namerec->num_ips == 1) &&
+ ip_equal(namerec->ip[0], from_ip))
+ {
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * Finally if the name exists do a query to the registering machine
+ * to see if they still claim to have the name.
+ */
+
+ if(namerec != NULL)
+ {
+ char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)];
+ struct userdata_struct *userdata = (struct userdata_struct *)&ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * As query_name uses the subnet broadcast address as the destination
+ * of the packet we temporarily change the subnet broadcast address to
+ * be the first IP address of the name owner and send the packet. This
+ * is a *horrible* hack but the alternative is to add the destination
+ * address parameter to all query_name() calls. I hate this code :-).
+ */
+
+ subrec->bcast_ip = *namerec->ip;
+
+ query_name( subrec, question->name, question->name_type,
+ wins_register_query_success,
+ wins_register_query_fail,
+ userdata);
+ subrec->bcast_ip = ipzero;
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ add_name_to_subnet(subrec, question->name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with a mutihomed name query success to the machine that
+ requested the multihomed name registration.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+ struct nmb_packet *nmb;
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ int ttl;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ nmb = &orig_reg_packet->packet.nmb;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+ ttl = get_ttl_from_packet(nmb);
+
+ /*
+ * We want to just add the new IP, as we now know the requesting
+ * machine claims to own it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this response. So we check that
+ * the name still exists and is in the same state - if so
+ * we just add the extra IP and update the ttl.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || (namerec->source != REGISTER_NAME) )
+ {
+ DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
+a subsequent IP addess.\n", namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+ return;
+ }
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, orig_reg_packet);
+
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+ return;
+}
+
+/***********************************************************************
+ Deal with a multihomed name registration request to a WINS server.
+ These cannot be group name registrations.
+***********************************************************************/
+
+void wins_process_multihomed_name_registration_request( struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ /*
+ * Only unique names should be registered multihomed.
+ */
+
+ if(group)
+ {
+ DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
+received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
+IP %s\n", namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(question->name_type == 0x1d)
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
+to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME)))
+ {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", namestr(question) ));
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if((namerec != NULL) && (namerec->source != REGISTER_NAME))
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS with source type %d.\n", namestr(question), namerec->source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Reject if the name exists and is a GROUP name.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec))
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) )
+ {
+ if(!ismyip(from_ip))
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ else
+ {
+ /*
+ * It's one of our names and one of our IP's. Ensure the IP is in the record and
+ * update the ttl.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists do a query to the owner
+ * to see if they still want the name.
+ */
+
+ if(namerec != NULL)
+ {
+ char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)];
+ struct userdata_struct *userdata = (struct userdata_struct *)&ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * As query_name uses the subnet broadcast address as the destination
+ * of the packet we temporarily change the subnet broadcast address to
+ * be the IP address of the requesting machine and send the packet. This
+ * is a *horrible* hack but the alternative is to add the destination
+ * address parameter to all query_name() calls. I hate this code :-).
+ */
+
+ subrec->bcast_ip = p->ip;
+ query_name( subrec, question->name, question->name_type,
+ wins_multihomed_register_query_success,
+ wins_multihomed_register_query_fail,
+ userdata);
+ subrec->bcast_ip = ipzero;
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ add_name_to_subnet(subrec, question->name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with the special name query for *<1b>.
+***********************************************************************/
+
+static void process_wins_dmb_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct name_record *namerec = NULL;
+ char *prdata;
+ int num_ips;
+
+ /*
+ * Go through all the names in the WINS db looking for those
+ * ending in <1b>. Use this to calculate the number of IP
+ * addresses we need to return.
+ */
+
+ num_ips = 0;
+ for(namerec = subrec->namelist; namerec; namerec = namerec->next)
+ {
+ if(namerec->name.name_type == 0x1b)
+ num_ips += namerec->num_ips;
+ }
+
+ if(num_ips == 0)
+ {
+ /*
+ * There are no 0x1b names registered. Return name query fail.
+ */
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ return;
+ }
+
+ if((prdata = (char *)malloc( num_ips * 6 )) == NULL)
+ {
+ DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
+ return;
+ }
+
+ /*
+ * Go through all the names again in the WINS db looking for those
+ * ending in <1b>. Add their IP addresses into the list we will
+ * return.
+ */
+
+ num_ips = 0;
+ for(namerec = subrec->namelist; namerec; namerec = namerec->next)
+ {
+ if(namerec->name.name_type == 0x1b)
+ {
+ int i;
+ for(i = 0; i < namerec->num_ips; i++)
+ {
+ set_nb_flags(&prdata[num_ips * 6],namerec->nb_flags);
+ putip((char *)&prdata[(num_ips * 6) + 2], &namerec->ip[i]);
+ num_ips++;
+ }
+ }
+ }
+
+ /*
+ * Send back the reply containing the IP list.
+ */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ lp_min_wins_ttl(), /* ttl. */
+ prdata, /* data to send. */
+ num_ips*6); /* data length. */
+
+ free(prdata);
+}
+
+/****************************************************************************
+Send a WINS name query response.
+**************************************************************************/
+
+void send_wins_name_query_response(int rcode, struct packet_struct *p,
+ struct name_record *namerec)
+{
+ char rdata[6];
+ char *prdata = rdata;
+ int reply_data_len = 0;
+ int ttl = 0;
+ int i = 0;
+ int j;
+
+ bzero(rdata,6);
+
+ if(rcode == 0)
+ {
+ int same_net_index;
+
+ ttl = (namerec->death_time != PERMANENT_TTL) ?
+ namerec->death_time - p->timestamp : lp_max_wins_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so
+ we don't need a malloc. */
+
+ if(namerec->num_ips == 1 )
+ prdata = rdata;
+ else
+ {
+ if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL)
+ {
+ DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
+ return;
+ }
+
+ /*
+ * Look over the known IP addresses and see if one of them
+ * is on the same (local) net as the requesting IP address. If so then
+ * put that IP address into the packet as the first IP.
+ * We can only do this for local nets as they're the only
+ * ones we know the netmask for.
+ */
+
+ same_net_index = -1;
+ i = 0;
+
+ if(is_local_net(p->ip))
+ {
+ struct in_addr *n_mask = iface_nmask(p->ip);
+
+ for( j = 0; j < namerec->num_ips; j++)
+ {
+ if(same_net( namerec->ip[j], p->ip, *n_mask))
+ {
+ set_nb_flags(&prdata[0],namerec->nb_flags);
+ putip((char *)&prdata[2], &namerec->ip[j]);
+ same_net_index = j;
+ i = 1;
+ }
+ }
+ }
+ }
+
+ for(; i < namerec->num_ips; i++)
+ {
+ if(i == same_net_index)
+ continue;
+ set_nb_flags(&prdata[i*6],namerec->nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->ip[i]);
+ }
+ reply_data_len = namerec->num_ips * 6;
+
+ }
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if((prdata != rdata) && (prdata != NULL))
+ free(prdata);
+}
+
+/***********************************************************************
+ Deal with a name query.
+***********************************************************************/
+
+void wins_process_name_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ struct name_record *namerec = NULL;
+
+ DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n",
+ namestr(question), inet_ntoa(p->ip) ));
+
+ /*
+ * Special name code. If the queried name is *<1b> then search
+ * the entire WINS database and return a list of all the IP addresses
+ * registered to any <1b> name. This is to allow domain master browsers
+ * to discover other domains that may not have a presence on their subnet.
+ */
+
+ if(strequal( question->name, "*") && (question->name_type == 0x1b))
+ {
+ process_wins_dmb_query_request( subrec, p);
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if(namerec != NULL)
+ {
+ /*
+ * If it's a DNSFAIL_NAME then reply name not found.
+ */
+
+ if(namerec->source == DNSFAIL_NAME)
+ {
+ DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
+ namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If the name has expired then reply name not found.
+ */
+
+ if((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp))
+ {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
+ namestr(question), inet_ntoa(namerec->ip[0]) ));
+
+ send_wins_name_query_response(0, p, namerec);
+ return;
+ }
+
+ /*
+ * Name not found in WINS - try a dns query if it's a 0x20 name.
+ */
+
+ if(lp_dns_proxy() && (question->name_type == 0x20))
+ {
+
+ DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
+ namestr(question) ));
+
+ queue_dns_query(p, question, &namerec);
+ return;
+ }
+
+ /*
+ * Name not found - return error.
+ */
+
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+}
+
+/****************************************************************************
+Send a WINS name release response.
+**************************************************************************/
+
+static void send_wins_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name release.
+***********************************************************************/
+
+void wins_process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
+IP %s\n", releasing_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(!releasing_group_name && (question->name_type == 0x1d))
+ {
+ DEBUG(3,("wins_process_name_release_request: Ignoring request \
+to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if((namerec == NULL) || ((namerec != NULL) && (namerec->source != REGISTER_NAME)) )
+ {
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check that the sending machine has permission to release this name.
+ * If it's a group name not ending in 0x1c then just say yes and let
+ * the group time out.
+ */
+
+ if(releasing_group_name && (question->name_type != 0x1c))
+ {
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Check that the releasing node is on the list of IP addresses
+ * for this name. Disallow the release if not.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as IP %s is not one of the known IP's for this name.\n",
+ namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Release the name and then remove the IP from the known list.
+ */
+
+ send_wins_name_release_response(0, p);
+ remove_ip_from_name_record(namerec, from_ip);
+
+ /*
+ * Remove the name entirely if no IP addresses left.
+ */
+ if (namerec->num_ips == 0)
+ remove_name_from_namelist(subrec, namerec);
+
+}
+
+/*******************************************************************
+ WINS time dependent processing.
+******************************************************************/
+
+void initiate_wins_processing(time_t t)
+{
+ static time_t lasttime = 0;
+
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 5)
+ return;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ expire_names_on_subnet(wins_server_subnet, t);
+
+ if(wins_server_subnet->namelist_changed)
+ wins_write_database();
+
+ wins_server_subnet->namelist_changed = False;
+}
+
+/*******************************************************************
+ Write out the current WINS database.
+******************************************************************/
+
+void wins_write_database(void)
+{
+ struct name_record *namerec;
+ fstring fname, fnamenew;
+
+ FILE *fp;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ fstrcpy(fname,lp_lockdir());
+ trim_string(fname,NULL,"/");
+ strcat(fname,"/");
+ strcat(fname,WINS_LIST);
+ fstrcpy(fnamenew,fname);
+ strcat(fnamenew,".");
+
+ if((fp = fopen(fnamenew,"w")) == NULL)
+ {
+ DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+ return;
+ }
+
+ DEBUG(4,("wins_write_database: Dump of WINS name list.\n"));
+
+ for (namerec = wins_server_subnet->namelist; namerec; namerec = namerec->next)
+ {
+ int i;
+ struct tm *tm;
+
+ DEBUG(4,("%-19s ", namestr(&namerec->name) ));
+
+ if(namerec->death_time != PERMANENT_TTL)
+ {
+ tm = LocalTime(&namerec->death_time);
+ DEBUG(4,("TTL = %s", asctime(tm) ));
+ }
+ else
+ DEBUG(4,("TTL = PERMANENT\t"));
+
+ for (i = 0; i < namerec->num_ips; i++)
+ DEBUG(4,("%15s ", inet_ntoa(namerec->ip[i]) ));
+ DEBUG(4,("%2x\n", namerec->nb_flags ));
+
+ if (namerec->source == REGISTER_NAME)
+ {
+ fprintf(fp, "%s#%02x %ld ",
+ namerec->name.name,namerec->name.name_type, /* Ignore scope. */
+ namerec->death_time);
+
+ for (i = 0; i < namerec->num_ips; i++)
+ fprintf(fp, "%s ", inet_ntoa(namerec->ip[i]));
+ fprintf(fp, "%2xR\n", namerec->nb_flags);
+ }
+ }
+
+ fclose(fp);
+ unlink(fname);
+ chmod(fnamenew,0644);
+ rename(fnamenew,fname);
+}
diff --git a/source3/nmbd/nmbd_workgroupdb.c b/source3/nmbd/nmbd_workgroupdb.c
new file mode 100644
index 0000000000..828e29a024
--- /dev/null
+++ b/source3/nmbd/nmbd_workgroupdb.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1997
+ Copyright (C) Jeremy Allison 1994-1997
+
+ 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"
+#include "smb.h"
+
+extern int ClientNMB;
+
+extern int DEBUGLEVEL;
+
+extern pstring myname;
+extern fstring myworkgroup;
+extern char **my_netbios_names;
+extern uint16 samba_nb_type;
+extern struct in_addr ipzero;
+
+int workgroup_count = 0; /* unique index key: one for each workgroup */
+
+/****************************************************************************
+ Add a workgroup into the list.
+ **************************************************************************/
+
+static void add_workgroup(struct subnet_record *subrec, struct work_record *work)
+{
+ struct work_record *w2;
+
+ work->subnet = subrec;
+
+ if (!subrec->workgrouplist)
+ {
+ subrec->workgrouplist = work;
+ work->prev = NULL;
+ work->next = NULL;
+ return;
+ }
+
+ for (w2 = subrec->workgrouplist; w2->next; w2 = w2->next)
+ ;
+
+ w2->next = work;
+ work->next = NULL;
+ work->prev = w2;
+
+ subrec->work_changed = True;
+}
+
+/****************************************************************************
+ Create an empty workgroup.
+ **************************************************************************/
+
+static struct work_record *create_workgroup(char *name, int ttl)
+{
+ struct work_record *work;
+ struct subnet_record *subrec;
+ int t = -1;
+
+ if((work = (struct work_record *)malloc(sizeof(*work))) == NULL)
+ {
+ DEBUG(0,("create_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+ bzero((char *)work, sizeof(*work));
+
+ StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
+ work->serverlist = NULL;
+
+ work->RunningElection = False;
+ work->ElectionCount = 0;
+ work->announce_interval = 0;
+ work->needelection = False;
+ work->needannounce = True;
+ work->lastannounce_time = time(NULL);
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ work->dom_state = DOMAIN_NONE;
+ work->log_state = LOGON_NONE;
+
+ work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ /* Make sure all token representations of workgroups are unique. */
+
+ for (subrec = FIRST_SUBNET; subrec && (t == -1);
+ subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct work_record *w;
+ for (w = subrec->workgrouplist; w && t == -1; w = w->next)
+ {
+ if (strequal(w->work_group, work->work_group))
+ t = w->token;
+ }
+ }
+
+ if (t == -1)
+ work->token = ++workgroup_count;
+ else
+ work->token = t;
+
+ /* No known local master browser as yet. */
+ *work->local_master_browser_name = '\0';
+
+ /* No known domain master browser as yet. */
+ *work->dmb_name.name = '\0';
+ putip((char *)&work->dmb_addr, &ipzero);
+
+ /* WfWg uses 01040b01 */
+ /* Win95 uses 01041501 */
+ /* NTAS uses ???????? */
+ work->ElectionCriterion = (MAINTAIN_LIST)|(ELECTION_VERSION<<8);
+ work->ElectionCriterion |= (lp_os_level() << 24);
+ if (lp_domain_master())
+ work->ElectionCriterion |= 0x80;
+
+ return work;
+}
+
+/*******************************************************************
+ Remove a workgroup.
+ ******************************************************************/
+
+static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct work_record *ret_work = NULL;
+
+ DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group));
+
+ ret_work = work->next;
+
+ remove_all_servers(work);
+
+ if (!work->serverlist)
+ {
+ if (work->prev)
+ work->prev->next = work->next;
+ if (work->next)
+ work->next->prev = work->prev;
+
+ if (subrec->workgrouplist == work)
+ subrec->workgrouplist = work->next;
+
+ free((char *)work);
+ }
+
+ subrec->work_changed = True;
+
+ return ret_work;
+}
+
+
+/****************************************************************************
+ Find a workgroup in the workgroup list of a subnet.
+ **************************************************************************/
+
+struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec,
+ fstring name)
+{
+ struct work_record *ret;
+
+ DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ",
+ name, subrec->subnet_name));
+
+ for (ret = subrec->workgrouplist; ret; ret = ret->next)
+ {
+ if (!strcmp(ret->work_group,name))
+ {
+ DEBUG(4, ("found\n"));
+ return(ret);
+ }
+ }
+ DEBUG(4, ("not found\n"));
+ return NULL;
+}
+
+/****************************************************************************
+ Create a workgroup in the workgroup list of the subnet.
+ **************************************************************************/
+
+struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec,
+ fstring name, int ttl)
+{
+ struct work_record *work = NULL;
+
+ DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n",
+ name, subrec->subnet_name));
+
+ if ((work = create_workgroup(name, ttl)))
+ {
+ add_workgroup(subrec, work);
+
+ subrec->work_changed = True;
+
+ return(work);
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Update a workgroup ttl.
+ **************************************************************************/
+
+void update_workgroup_ttl(struct work_record *work, int ttl)
+{
+ if(work->death_time != PERMANENT_TTL)
+ work->death_time = time(NULL)+(ttl*3);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Fail function called if we cannot register the WORKGROUP<0> and
+ WORKGROUP<1e> names on the net.
+**************************************************************************/
+
+static void fail_register(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *nmbname)
+{
+ DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n",
+ namestr(nmbname), subrec->subnet_name));
+}
+
+/****************************************************************************
+ If the workgroup is our primary workgroup, add the required names to it.
+**************************************************************************/
+
+void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work)
+{
+ int i;
+
+ if(!strequal(myworkgroup, work->work_group))
+ return;
+
+ /* If this is a broadcast subnet then start elections on it
+ if we are so configured. */
+
+ if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) &&
+ (subrec != wins_server_subnet) && lp_preferred_master() &&
+ lp_local_master())
+ {
+ DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+ work->needelection = True;
+ work->ElectionCriterion |= (1<<3);
+ }
+
+ /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */
+
+ register_name(subrec,myworkgroup,0x0,samba_nb_type|NB_GROUP,
+ NULL,
+ fail_register,NULL);
+
+ register_name(subrec,myworkgroup,0x1e,samba_nb_type|NB_GROUP,
+ NULL,
+ fail_register,NULL);
+
+ for( i = 0; my_netbios_names[i]; i++)
+ {
+ char *name = my_netbios_names[i];
+ int stype = lp_default_server_announce() | (lp_local_master() ?
+ SV_TYPE_POTENTIAL_BROWSER : 0 );
+
+ if(!strequal(myname, name))
+ stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|
+ SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
+
+ create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY,
+ PERMANENT_TTL, lp_serverstring());
+ DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \
+on subnet %s\n", name, subrec->subnet_name));
+ }
+}
+
+/****************************************************************************
+ Dump a copy of the workgroup database into the log file.
+ **************************************************************************/
+
+void dump_workgroups(void)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ if (subrec->workgrouplist)
+ {
+ struct work_record *work;
+
+ DEBUG(4,("dump_workgroups: dump workgroup on subnet %15s: ", subrec->subnet_name));
+ DEBUG(4,(" netmask=%15s:\n", inet_ntoa(subrec->mask_ip)));
+
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ DEBUG(4,("\t%s(%d) current master browser = %s\n", work->work_group,
+ work->token,
+ *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" ));
+ if (work->serverlist)
+ {
+ struct server_record *servrec;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ DEBUG(4,("\t\t%s %8x (%s)\n",
+ servrec->serv.name, servrec->serv.type, servrec->serv.comment));
+ }
+ }
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Expire any dead servers on all workgroups. If the workgroup has expired
+ remove it.
+ **************************************************************************/
+
+void expire_workgroups_and_servers(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ struct work_record *nextwork;
+
+ for (work = subrec->workgrouplist; work; work = nextwork)
+ {
+ nextwork = work->next;
+ expire_servers(work, t);
+
+ if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) &&
+ ((t == -1) || (work->death_time < t)))
+ {
+ DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n",
+ work->work_group));
+ remove_workgroup_from_subnet(subrec, work);
+ }
+ }
+ }
+}