mirror of
https://github.com/samba-team/samba.git
synced 2024-12-29 11:21:54 +03:00
9b41ada7c8
loadparm.c: Fixed bug with 'valid chars' param.
nameelect.c:Unregister the 1b name from the WINS server when unbecoming master.
nameservresp.c: Fix nasty browse bug where we were sending the query
to the wrong ip address.
Jeremy (jallison@whistle.com)
(This used to be commit 927d1875f0
)
859 lines
26 KiB
C
859 lines
26 KiB
C
/*
|
|
Unix SMB/Netbios implementation.
|
|
Version 1.9.
|
|
NBT netbios routines and daemon - version 2
|
|
Copyright (C) Andrew Tridgell 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.
|
|
|
|
Module name: nameelect.c
|
|
|
|
Revision History:
|
|
|
|
14 jan 96: lkcl@pires.co.uk
|
|
added multiple workgroup domain master support
|
|
|
|
04 jul 96: lkcl@pires.co.uk
|
|
added system to become a master browser by stages.
|
|
|
|
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
extern int ClientNMB;
|
|
extern int ClientDGRAM;
|
|
|
|
extern int DEBUGLEVEL;
|
|
extern pstring scope;
|
|
|
|
extern pstring myname;
|
|
extern fstring myworkgroup;
|
|
extern struct in_addr ipzero;
|
|
extern struct in_addr wins_ip;
|
|
|
|
/* here are my election parameters */
|
|
|
|
extern time_t StartupTime;
|
|
|
|
extern struct subnet_record *subnetlist;
|
|
|
|
extern uint16 nb_type; /* samba's NetBIOS name type */
|
|
|
|
|
|
/*******************************************************************
|
|
occasionally check to see if the master browser is around
|
|
******************************************************************/
|
|
void check_master_browser(time_t t)
|
|
{
|
|
static time_t lastrun=0;
|
|
struct subnet_record *d;
|
|
|
|
if (!lastrun) lastrun = t;
|
|
if (t < lastrun + CHECK_TIME_MST_BROWSE * 60) return;
|
|
|
|
lastrun = t;
|
|
|
|
dump_workgroups();
|
|
|
|
for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
|
|
{
|
|
struct work_record *work;
|
|
|
|
for (work = d->workgrouplist; work; work = work->next)
|
|
{
|
|
if (strequal(work->work_group, myworkgroup) && !AM_MASTER(work))
|
|
{
|
|
if (lp_local_master())
|
|
{
|
|
/* potential master browser - not a master browser. force
|
|
becoming a master browser, hence the log message.
|
|
*/
|
|
|
|
DEBUG(2,("%s potential master for %s %s - force election\n",
|
|
timestring(), work->work_group,
|
|
inet_ntoa(d->bcast_ip)));
|
|
|
|
browser_gone(work->work_group, d->bcast_ip);
|
|
}
|
|
else
|
|
{
|
|
/* if we are not the browse master of a workgroup,
|
|
and we can't find a browser on the subnet, do
|
|
something about it.
|
|
*/
|
|
|
|
queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
|
|
work->work_group,0x1d,0,0,0,NULL,NULL,
|
|
True,False,d->bcast_ip,d->bcast_ip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
what to do if a master browser DOESN't exist.
|
|
|
|
option 1: force an election, and participate in it
|
|
option 2: force an election, and let everyone else participate.
|
|
|
|
******************************************************************/
|
|
void browser_gone(char *work_name, struct in_addr ip)
|
|
{
|
|
struct subnet_record *d = find_subnet(ip);
|
|
struct work_record *work = find_workgroupstruct(d, work_name, False);
|
|
|
|
/* i don't know about this workgroup, therefore i don't care */
|
|
if (!work || !d) return;
|
|
|
|
/* don't do election stuff on the WINS subnet */
|
|
if (ip_equal(d->bcast_ip,wins_ip))
|
|
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 under exactly such circumstances.
|
|
*/
|
|
DEBUG(2,("Forcing election on %s %s\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip)));
|
|
|
|
/* we can attempt to become master browser */
|
|
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.
|
|
*/
|
|
|
|
/* local interfaces: force an election */
|
|
send_election(d, work->work_group, 0, 0, myname);
|
|
|
|
/* only removes workgroup completely on a local interface
|
|
persistent lmhosts entries on a local interface _will_ be removed).
|
|
*/
|
|
remove_workgroup(d, work,True);
|
|
add_workgroup_to_subnet(d, work->work_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
send an election packet
|
|
**************************************************************************/
|
|
void send_election(struct subnet_record *d, char *group,uint32 criterion,
|
|
int timeup,char *name)
|
|
{
|
|
pstring outbuf;
|
|
char *p;
|
|
|
|
if (!d) return;
|
|
|
|
DEBUG(2,("Sending election to %s for workgroup %s\n",
|
|
inet_ntoa(d->bcast_ip),group));
|
|
|
|
bzero(outbuf,sizeof(outbuf));
|
|
p = outbuf;
|
|
CVAL(p,0) = ANN_Election; /* election */
|
|
p++;
|
|
|
|
CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
|
|
SIVAL(p,1,criterion);
|
|
SIVAL(p,5,timeup*1000); /* ms - despite the spec */
|
|
p += 13;
|
|
strcpy(p,name);
|
|
strupper(p);
|
|
p = skip_string(p,1);
|
|
|
|
send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
|
|
outbuf,PTR_DIFF(p,outbuf),
|
|
name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
un-register a SELF name that got rejected.
|
|
|
|
if this name happens to be rejected when samba is in the process
|
|
of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
|
|
or WORKGROUP(1b)) then we must stop being a master browser. sad.
|
|
|
|
**************************************************************************/
|
|
void name_unregister_work(struct subnet_record *d, char *name, int name_type)
|
|
{
|
|
struct work_record *work;
|
|
int remove_type_local = 0;
|
|
int remove_type_domain = 0;
|
|
int remove_type_logon = 0;
|
|
|
|
remove_netbios_name(d,name,name_type,SELF,ipzero);
|
|
|
|
if (!(work = find_workgroupstruct(d, name, False))) return;
|
|
|
|
/* work out what to unbecome, from the name type being removed */
|
|
|
|
if (ms_browser_name(name, name_type))
|
|
{
|
|
remove_type_local |= SV_TYPE_MASTER_BROWSER;
|
|
}
|
|
if (AM_MASTER(work) && strequal(name, myworkgroup) && name_type == 0x1d)
|
|
{
|
|
remove_type_local |= SV_TYPE_MASTER_BROWSER;
|
|
}
|
|
if (AM_DOMMST(work) && strequal(name, myworkgroup) && name_type == 0x1b)
|
|
{
|
|
remove_type_domain |= SV_TYPE_DOMAIN_MASTER;
|
|
}
|
|
if (AM_DOMMEM(work) && strequal(name, myworkgroup) && name_type == 0x1c)
|
|
{
|
|
remove_type_logon|= SV_TYPE_DOMAIN_MEMBER;
|
|
}
|
|
|
|
if (remove_type_local ) unbecome_local_master (d, work, remove_type_local );
|
|
if (remove_type_domain) unbecome_domain_master(d, work, remove_type_domain);
|
|
if (remove_type_logon ) unbecome_logon_server (d, work, remove_type_logon );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
registers a name.
|
|
|
|
if the name being added is a SELF name, we must additionally check
|
|
whether to proceed to the next stage in samba becoming a master browser.
|
|
|
|
**************************************************************************/
|
|
void name_register_work(struct subnet_record *d, char *name, int name_type,
|
|
int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast)
|
|
{
|
|
enum name_source source = (ismyip(ip) || ip_equal(ip, ipzero)) ?
|
|
SELF : REGISTER;
|
|
|
|
if (source == SELF)
|
|
{
|
|
struct work_record *work = find_workgroupstruct(d,
|
|
myworkgroup, False);
|
|
|
|
add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
|
|
|
|
if (work)
|
|
{
|
|
int add_type_local = False;
|
|
int add_type_domain = False;
|
|
int add_type_logon = False;
|
|
|
|
DEBUG(4,("checking next stage: name_register_work %s\n", name));
|
|
|
|
/* work out what to become, from the name type being added */
|
|
|
|
if (ms_browser_name(name, name_type))
|
|
{
|
|
add_type_local = True;
|
|
}
|
|
if (strequal(name, myworkgroup) && name_type == 0x1d)
|
|
{
|
|
add_type_local = True;
|
|
}
|
|
if (strequal(name, myworkgroup) && name_type == 0x1b)
|
|
{
|
|
add_type_domain = True;
|
|
}
|
|
if (strequal(name, myworkgroup) && name_type == 0x1c)
|
|
{
|
|
add_type_logon = True;
|
|
}
|
|
|
|
if (add_type_local ) become_local_master (d, work);
|
|
if (add_type_domain) become_domain_master(d, work);
|
|
if (add_type_logon ) become_logon_server (d, work);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
become the local master browser.
|
|
|
|
this is done in stages. note that this could take a while,
|
|
particularly on a broadcast subnet, as we have to wait for
|
|
the implicit registration of each name to be accepted.
|
|
|
|
as each name is successfully registered, become_local_master() is
|
|
called again, in order to initiate the next stage. see
|
|
dead_netbios_entry() - deals with implicit name registration
|
|
and response_name_reg() - deals with explicit registration
|
|
with a WINS server.
|
|
|
|
stage 1: was MST_POTENTIAL - go to MST_POTENTIAL and register ^1^2__MSBROWSE__^2^1.
|
|
stage 2: was MST_BACK - go to MST_MSB and register WORKGROUP(0x1d)
|
|
stage 3: was MST_MSB - go to MST_BROWSER and stay there
|
|
|
|
XXXX note: this code still does not cope with the distinction
|
|
between different types of nodes, particularly between M and P
|
|
nodes. that comes later.
|
|
|
|
******************************************************************/
|
|
void become_local_master(struct subnet_record *d, struct work_record *work)
|
|
{
|
|
/* domain type must be limited to domain enum + server type. it must
|
|
not have SV_TYPE_SERVER or anything else with SERVER in it, else
|
|
clients get confused and start thinking this entry is a server
|
|
not a workgroup
|
|
*/
|
|
uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
|
|
|
|
if (!work || !d)
|
|
return;
|
|
|
|
if (!lp_local_master())
|
|
{
|
|
DEBUG(0,("Samba not configured as a local master browser.\n"));
|
|
return;
|
|
}
|
|
|
|
DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip),work->mst_state));
|
|
|
|
switch (work->mst_state)
|
|
{
|
|
case MST_POTENTIAL: /* while we were nothing but a server... */
|
|
{
|
|
DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
|
|
work->mst_state = MST_BACK; /* an election win was successful */
|
|
|
|
work->ElectionCriterion |= 0x5;
|
|
|
|
/* update our server status */
|
|
work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
|
|
add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY,
|
|
0,lp_serverstring(),True);
|
|
|
|
/* add special browser name */
|
|
add_my_name_entry(d,MSBROWSE,0x01,nb_type|NB_ACTIVE|NB_GROUP);
|
|
|
|
/* DON'T do anything else after calling add_my_name_entry() */
|
|
break;
|
|
}
|
|
|
|
case MST_BACK: /* while nothing had happened except we won an election... */
|
|
{
|
|
DEBUG(3,("go to second stage: register as master browser\n"));
|
|
work->mst_state = MST_MSB; /* registering MSBROWSE was successful */
|
|
|
|
/* add server entry on successful registration of MSBROWSE */
|
|
add_server_entry(d,work,work->work_group,domain_type|SV_TYPE_LOCAL_LIST_ONLY,
|
|
0,myname,True);
|
|
|
|
/* add master name */
|
|
add_my_name_entry(d,work->work_group,0x1d,nb_type|NB_ACTIVE);
|
|
|
|
/* DON'T do anything else after calling add_my_name_entry() */
|
|
break;
|
|
}
|
|
|
|
case MST_MSB: /* while we were still only registered MSBROWSE state... */
|
|
{
|
|
int i = 0;
|
|
struct server_record *sl;
|
|
|
|
DEBUG(3,("2nd stage complete: registered as master browser for workgroup %s \
|
|
on subnet %s\n", work->work_group, inet_ntoa(d->bcast_ip)));
|
|
work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
|
|
|
|
/* update our server status */
|
|
work->ServerType |= SV_TYPE_MASTER_BROWSER;
|
|
|
|
DEBUG(3,("become_local_master: updating our server %s to type %x\n",
|
|
myname, work->ServerType));
|
|
|
|
add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY,
|
|
0,lp_serverstring(),True);
|
|
|
|
/* 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 */
|
|
announce_request(work, d->bcast_ip);
|
|
}
|
|
|
|
/* Reset the announce master timer so that we do an announce as soon as possible
|
|
now we are a master. */
|
|
reset_announce_timer();
|
|
|
|
DEBUG(0,("Samba is now a local master browser for workgroup %s on subnet %s\n",
|
|
work->work_group, inet_ntoa(d->bcast_ip)));
|
|
|
|
break;
|
|
}
|
|
|
|
case MST_BROWSER:
|
|
{
|
|
/* don't have to do anything: just report success */
|
|
DEBUG(3,("3rd stage: become master browser!\n"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
become the domain master browser.
|
|
|
|
this is done in stages. note that this could take a while,
|
|
particularly on a broadcast subnet, as we have to wait for
|
|
the implicit registration of each name to be accepted.
|
|
|
|
as each name is successfully registered, become_domain_master() is
|
|
called again, in order to initiate the next stage. see
|
|
dead_netbios_entry() - deals with implicit name registration
|
|
and response_name_reg() - deals with explicit registration
|
|
with a WINS server.
|
|
|
|
stage 1: was DOMAIN_NONE - go to DOMAIN_MST
|
|
|
|
XXXX note: this code still does not cope with the distinction
|
|
between different types of nodes, particularly between M and P
|
|
nodes. that comes later.
|
|
|
|
******************************************************************/
|
|
void become_domain_master(struct subnet_record *d, struct work_record *work)
|
|
{
|
|
/* domain type must be limited to domain enum + server type. it must
|
|
not have SV_TYPE_SERVER or anything else with SERVER in it, else
|
|
clients get confused and start thinking this entry is a server
|
|
not a workgroup
|
|
*/
|
|
|
|
if (!work || !d) return;
|
|
|
|
if (!lp_domain_master())
|
|
{
|
|
DEBUG(0,("Samba not configured as a domain master browser.\n"));
|
|
return;
|
|
}
|
|
|
|
DEBUG(2,("Becoming domain master for %s %s (currently at stage %d)\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip),work->dom_state));
|
|
|
|
switch (work->dom_state)
|
|
{
|
|
case DOMAIN_NONE: /* while we were nothing but a server... */
|
|
{
|
|
DEBUG(3,("become_domain_master: go to first stage: register <1b> name\n"));
|
|
work->dom_state = DOMAIN_WAIT;
|
|
|
|
/* XXXX the 0x1b is domain master browser name */
|
|
add_my_name_entry(d, work->work_group,0x1b,nb_type|NB_ACTIVE);
|
|
|
|
/* DON'T do anything else after calling add_my_name_entry() */
|
|
break;
|
|
}
|
|
|
|
case DOMAIN_WAIT:
|
|
{
|
|
work->dom_state = DOMAIN_MST; /* ... become domain master */
|
|
DEBUG(3,("become_domain_master: first stage - register as domain member\n"));
|
|
|
|
/* update our server status */
|
|
work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER;
|
|
add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY,
|
|
0, lp_serverstring(),True);
|
|
|
|
DEBUG(0,("Samba is now a domain master browser for workgroup %s on subnet %s\n",
|
|
work->work_group, inet_ntoa(d->bcast_ip)));
|
|
|
|
if (d == wins_subnet)
|
|
{
|
|
/* ok! 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 DOMAIN.txt.
|
|
|
|
*/
|
|
add_domain_master_bcast();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DOMAIN_MST:
|
|
{
|
|
/* don't have to do anything: just report success */
|
|
DEBUG(3,("domain second stage: there isn't one!\n"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
become a logon server.
|
|
******************************************************************/
|
|
void become_logon_server(struct subnet_record *d, struct work_record *work)
|
|
{
|
|
if (!work || !d) return;
|
|
|
|
if (!lp_domain_logons())
|
|
{
|
|
DEBUG(0,("samba not configured as a logon master.\n"));
|
|
return;
|
|
}
|
|
|
|
DEBUG(2,("Becoming logon server for %s %s (currently at stage %d)\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip),work->log_state));
|
|
|
|
switch (work->log_state)
|
|
{
|
|
case LOGON_NONE: /* while we were nothing but a server... */
|
|
{
|
|
DEBUG(3,("go to first stage: register <1c> name\n"));
|
|
work->log_state = LOGON_WAIT;
|
|
|
|
/* XXXX the 0x1c is apparently something to do with domain logons */
|
|
add_my_name_entry(d, myworkgroup,0x1c,nb_type|NB_ACTIVE|NB_GROUP);
|
|
|
|
/* DON'T do anything else after calling add_my_name_entry() */
|
|
break;
|
|
}
|
|
|
|
case LOGON_WAIT:
|
|
{
|
|
work->log_state = LOGON_SRV; /* ... become logon server */
|
|
DEBUG(3,("logon second stage: register \n"));
|
|
|
|
/* update our server status */
|
|
work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER;
|
|
add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY
|
|
,0, lp_serverstring(),True);
|
|
|
|
/* DON'T do anything else after calling add_my_name_entry() */
|
|
break;
|
|
}
|
|
|
|
case LOGON_SRV:
|
|
{
|
|
DEBUG(3,("logon third stage: there isn't one!\n"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
unbecome the local master browser. initates removal of necessary netbios
|
|
names, and tells the world that we are no longer a master browser.
|
|
|
|
XXXX this _should_ be used to demote to a backup master browser, without
|
|
going straight to non-master browser. another time.
|
|
|
|
******************************************************************/
|
|
void unbecome_local_master(struct subnet_record *d, struct work_record *work,
|
|
int remove_type)
|
|
{
|
|
int new_server_type = work->ServerType;
|
|
|
|
/* can only remove master types with this function */
|
|
remove_type &= SV_TYPE_MASTER_BROWSER;
|
|
|
|
new_server_type &= ~remove_type;
|
|
|
|
if (remove_type)
|
|
{
|
|
DEBUG(2,("Becoming local non-master for %s\n",work->work_group));
|
|
|
|
/* no longer a master browser of any sort */
|
|
|
|
work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
|
|
work->ElectionCriterion &= ~0x4;
|
|
work->mst_state = MST_POTENTIAL;
|
|
|
|
/* announce ourselves as no longer active as a master browser. */
|
|
announce_server(d, work, work->work_group, myname, 0, 0);
|
|
remove_name_entry(d,MSBROWSE ,0x01);
|
|
remove_name_entry(d,work->work_group,0x1d);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
unbecome the domain master browser. initates removal of necessary netbios
|
|
names, and tells the world that we are no longer a domain browser.
|
|
******************************************************************/
|
|
void unbecome_domain_master(struct subnet_record *d, struct work_record *work,
|
|
int remove_type)
|
|
{
|
|
int new_server_type = work->ServerType;
|
|
|
|
DEBUG(2,("Becoming domain non-master for %s\n",work->work_group));
|
|
|
|
/* can only remove master or domain types with this function */
|
|
remove_type &= SV_TYPE_DOMAIN_MASTER;
|
|
|
|
new_server_type &= ~remove_type;
|
|
|
|
if (remove_type)
|
|
{
|
|
/* no longer a domain master browser of any sort */
|
|
|
|
work->dom_state = DOMAIN_NONE;
|
|
|
|
/* announce ourselves as no longer active as a master browser on
|
|
all our local subnets. */
|
|
for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
|
|
{
|
|
work = find_workgroupstruct(d, myworkgroup, False);
|
|
|
|
announce_server(d, work, work->work_group, myname, 0, 0);
|
|
/* Remove the name entry without any NetBIOS traffic as that's
|
|
how it was registered. */
|
|
remove_name_entry(d,work->work_group,0x1b);
|
|
}
|
|
|
|
/* Unregister the 1b name from the WINS server. */
|
|
if(wins_subnet != NULL)
|
|
remove_name_entry(wins_subnet, myworkgroup, 0x1b);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
unbecome the logon server. initates removal of necessary netbios
|
|
names, and tells the world that we are no longer a logon server.
|
|
******************************************************************/
|
|
void unbecome_logon_server(struct subnet_record *d, struct work_record *work,
|
|
int remove_type)
|
|
{
|
|
int new_server_type = work->ServerType;
|
|
|
|
DEBUG(2,("Becoming logon non-server for %s\n",work->work_group));
|
|
|
|
/* can only remove master or domain types with this function */
|
|
remove_type &= SV_TYPE_DOMAIN_MEMBER;
|
|
|
|
new_server_type &= ~remove_type;
|
|
|
|
if (remove_type)
|
|
{
|
|
/* no longer a master browser of any sort */
|
|
|
|
work->log_state = LOGON_NONE;
|
|
|
|
/* announce ourselves as no longer active as a master browser. */
|
|
announce_server(d, work, work->work_group, myname, 0, 0);
|
|
remove_name_entry(d,work->work_group,0x1c);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
run the election
|
|
******************************************************************/
|
|
void run_elections(time_t t)
|
|
{
|
|
static time_t lastime = 0;
|
|
|
|
struct subnet_record *d;
|
|
|
|
/* send election packets once a second */
|
|
if (lastime && t-lastime <= 0) return;
|
|
|
|
lastime = t;
|
|
|
|
for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
|
|
{
|
|
struct work_record *work;
|
|
|
|
for (work = d->workgrouplist; work; work = work->next)
|
|
{
|
|
if (work->RunningElection)
|
|
{
|
|
send_election(d,work->work_group, work->ElectionCriterion,
|
|
t-StartupTime,myname);
|
|
|
|
if (work->ElectionCount++ >= 4)
|
|
{
|
|
/* I won! now what :-) */
|
|
DEBUG(2,(">>> Won election on %s %s <<<\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip)));
|
|
|
|
work->RunningElection = False;
|
|
work->mst_state = MST_POTENTIAL;
|
|
|
|
become_local_master(d, work);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
work out if I win an election
|
|
******************************************************************/
|
|
static BOOL win_election(struct work_record *work,int version,uint32 criterion,
|
|
int timeup,char *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,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
|
|
version,ELECTION_VERSION,
|
|
criterion,mycriterion,
|
|
timeup,mytimeup,
|
|
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,name) > 0) return(False);
|
|
|
|
return(True);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
process a election packet
|
|
|
|
An election dynamically decides who will be the master.
|
|
******************************************************************/
|
|
void process_election(struct packet_struct *p,char *buf)
|
|
{
|
|
struct dgram_packet *dgram = &p->packet.dgram;
|
|
struct in_addr ip = dgram->header.source_ip;
|
|
struct subnet_record *d = find_subnet(ip);
|
|
int version = CVAL(buf,0);
|
|
uint32 criterion = IVAL(buf,1);
|
|
int timeup = IVAL(buf,5)/1000;
|
|
char *name = buf+13;
|
|
struct work_record *work;
|
|
|
|
if (!d) return;
|
|
|
|
if (ip_equal(d->bcast_ip,wins_ip))
|
|
{
|
|
DEBUG(0,("Unexpected election request from %s %s on WINS net\n",
|
|
name, inet_ntoa(p->ip)));
|
|
return;
|
|
}
|
|
|
|
name[15] = 0;
|
|
|
|
DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
|
|
name,inet_ntoa(p->ip),version,criterion,timeup));
|
|
|
|
if (same_context(dgram)) return;
|
|
|
|
for (work = d->workgrouplist; work; work = work->next)
|
|
{
|
|
if (!strequal(work->work_group, myworkgroup))
|
|
continue;
|
|
|
|
if (win_election(work, version,criterion,timeup,name))
|
|
{
|
|
if (!work->RunningElection)
|
|
{
|
|
work->needelection = True;
|
|
work->ElectionCount=0;
|
|
work->mst_state = MST_POTENTIAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
work->needelection = False;
|
|
|
|
if (work->RunningElection || AM_MASTER(work))
|
|
{
|
|
work->RunningElection = False;
|
|
DEBUG(3,(">>> Lost election on %s %s <<<\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip)));
|
|
if (AM_MASTER(work))
|
|
{
|
|
unbecome_local_master(d, work, SV_TYPE_MASTER_BROWSER);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
checks whether a browser election is to be run on any workgroup
|
|
|
|
this function really ought to return the time between election
|
|
packets (which depends on whether samba intends to be a domain
|
|
master or a master browser) in milliseconds.
|
|
|
|
***************************************************************************/
|
|
BOOL check_elections(void)
|
|
{
|
|
struct subnet_record *d;
|
|
BOOL run_any_election = False;
|
|
|
|
for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
|
|
{
|
|
struct work_record *work;
|
|
for (work = d->workgrouplist; work; work = work->next)
|
|
{
|
|
run_any_election |= work->RunningElection;
|
|
|
|
if (work->needelection && !work->RunningElection)
|
|
{
|
|
DEBUG(3,(">>> Starting election on %s %s <<<\n",
|
|
work->work_group,inet_ntoa(d->bcast_ip)));
|
|
work->ElectionCount = 0;
|
|
work->RunningElection = True;
|
|
work->needelection = False;
|
|
}
|
|
}
|
|
}
|
|
return run_any_election;
|
|
}
|
|
|