1997-12-13 17:16:07 +03:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1997-12-13 17:16:07 +03:00
NBT netbios routines and daemon - version 2
1998-01-22 16:27:43 +03:00
Copyright ( C ) Andrew Tridgell 1994 - 1998
Copyright ( C ) Luke Kenneth Casson Leighton 1994 - 1998
Copyright ( C ) Jeremy Allison 1994 - 1998
1997-12-13 17:16:07 +03:00
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 0213 9 , USA .
*/
# include "includes.h"
1998-04-25 05:12:08 +04:00
extern pstring global_myname ;
extern fstring global_myworkgroup ;
1997-12-13 17:16:07 +03:00
/* 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 ) ) ;
1999-12-13 16:27:58 +03:00
memset ( outbuf , ' \0 ' , sizeof ( outbuf ) ) ;
1997-12-13 17:16:07 +03:00
p = outbuf ;
2002-01-11 22:10:25 +03:00
SCVAL ( p , 0 , ANN_Election ) ; /* Election opcode. */
1997-12-13 17:16:07 +03:00
p + + ;
2002-01-11 22:10:25 +03:00
SCVAL ( p , 0 , ( ( criterion = = 0 & & timeup = = 0 ) ? 0 : ELECTION_VERSION ) ) ;
1997-12-13 17:16:07 +03:00
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 ) ,
1998-06-27 04:27:44 +04:00
global_myname , 0 ,
1997-12-13 17:16:07 +03:00
workgroup_name , 0x1e ,
1998-08-30 21:04:24 +04:00
subrec - > bcast_ip , subrec - > myip , DGRAM_PORT ) ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
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 ;
}
1998-04-25 05:12:08 +04:00
if ( strequal ( work - > work_group , global_myworkgroup ) )
1997-12-13 17:16:07 +03:00
{
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 .
*/
1998-06-27 04:27:44 +04:00
send_election_dgram ( subrec , work - > work_group , 0 , 0 , " " ) ;
1997-12-13 17:16:07 +03:00
}
}
}
/*******************************************************************
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 ;
1998-04-25 05:12:08 +04:00
char * workgroup_name = global_myworkgroup ;
1997-12-13 17:16:07 +03:00
1999-12-13 16:27:58 +03:00
if ( ! lastrun )
lastrun = t ;
1997-12-13 17:16:07 +03:00
if ( t < ( lastrun + ( CHECK_TIME_MST_BROWSE * 60 ) ) )
return ;
lastrun = t ;
1997-12-24 11:49:44 +03:00
dump_workgroups ( False ) ;
1997-12-13 17:16:07 +03:00
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 ;
1998-06-27 04:27:44 +04:00
/* Send election packets once every 2 seconds - note */
if ( lastime & & ( t - lastime < 2 ) )
1997-12-13 17:16:07 +03:00
return ;
lastime = t ;
2001-09-05 22:43:55 +04:00
START_PROFILE ( run_elections ) ;
1997-12-13 17:16:07 +03:00
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 )
{
1998-06-27 04:27:44 +04:00
/*
* We can only run an election for a workgroup if we have
* registered the WORKGROUP < 1 e > name , as that ' s the name
* we must listen to .
*/
struct nmb_name nmbname ;
2000-01-07 09:55:36 +03:00
make_nmb_name ( & nmbname , work - > work_group , 0x1e ) ;
1998-06-27 04:27:44 +04:00
if ( find_name_on_subnet ( subrec , & nmbname , FIND_SELF_NAME ) = = NULL ) {
DEBUG ( 8 , ( " run_elections: Cannot send election packet yet as name %s not \
1998-11-14 04:04:13 +03:00
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
1998-06-27 04:27:44 +04:00
continue ;
}
1997-12-13 17:16:07 +03:00
send_election_dgram ( subrec , work - > work_group , work - > ElectionCriterion ,
1998-04-25 05:12:08 +04:00
t - StartupTime , global_myname ) ;
1997-12-13 17:16:07 +03:00
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 ) ;
}
}
}
}
2001-09-05 22:43:55 +04:00
END_PROFILE ( run_elections ) ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
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 ,
1998-04-25 05:12:08 +04:00
server_name , global_myname ) ) ;
1997-12-13 17:16:07 +03:00
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 ) ;
1998-04-25 05:12:08 +04:00
if ( strcasecmp ( global_myname , server_name ) > 0 )
1997-12-13 17:16:07 +03:00
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 ;
2001-09-05 22:43:55 +04:00
START_PROFILE ( election ) ;
1997-12-13 17:16:07 +03:00
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 ) ) ;
2001-09-05 22:43:55 +04:00
goto done ;
1997-12-13 17:16:07 +03:00
}
1998-04-25 05:12:08 +04:00
if ( ! strequal ( work - > work_group , global_myworkgroup ) )
1997-12-13 17:16:07 +03:00
{
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 ));
2001-09-05 22:43:55 +04:00
goto done ;
1997-12-13 17:16:07 +03:00
}
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 ) )
1998-06-27 04:27:44 +04:00
unbecome_local_master_browser ( subrec , work , False ) ;
1997-12-13 17:16:07 +03:00
}
}
2001-09-05 22:43:55 +04:00
done :
END_PROFILE ( election ) ;
1997-12-13 17:16:07 +03:00
}
/****************************************************************************
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 ;
1998-06-27 05:07:30 +04:00
/*
* Start an election if we have any chance of winning .
* Note this is a change to the previous code , that would
* only run an election if nmbd was in the potential browser
* state . We need to run elections in any state if we ' re told
* to . JRA .
*/
if ( work - > needelection & & ! work - > RunningElection & & lp_local_master ( ) )
1997-12-13 17:16:07 +03:00
{
1998-06-27 04:27:44 +04:00
/*
* We can only run an election for a workgroup if we have
* registered the WORKGROUP < 1 e > name , as that ' s the name
* we must listen to .
*/
struct nmb_name nmbname ;
2000-01-07 09:55:36 +03:00
make_nmb_name ( & nmbname , work - > work_group , 0x1e ) ;
1998-06-27 04:27:44 +04:00
if ( find_name_on_subnet ( subrec , & nmbname , FIND_SELF_NAME ) = = NULL ) {
DEBUG ( 8 , ( " check_elections: Cannot send election packet yet as name %s not \
1998-11-14 04:04:13 +03:00
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
1998-06-27 04:27:44 +04:00
continue ;
}
1997-12-13 17:16:07 +03:00
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 ;
}
2000-09-12 10:13:25 +04:00
/****************************************************************************
process a internal Samba message forcing an election
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nmbd_message_election ( int msg_type , pid_t src , void * buf , size_t len )
{
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 ) {
if ( strequal ( work - > work_group , global_myworkgroup ) ) {
work - > needelection = True ;
work - > ElectionCount = 0 ;
work - > mst_state = lp_local_master ( ) ? MST_POTENTIAL : MST_NONE ;
}
}
}
}