0001-01-01 02:30:17 +02:30
/*
0001-01-01 02:30:17 +02:30
Unix SMB / CIFS implementation .
0001-01-01 02:30:17 +02:30
NBT netbios routines and daemon - version 2
0001-01-01 02:30:17 +02:30
Copyright ( C ) Andrew Tridgell 1994 - 1998
Copyright ( C ) Luke Kenneth Casson Leighton 1994 - 1998
0001-01-01 02:30:17 +02:30
Copyright ( C ) Jeremy Allison 1994 - 2003
0001-01-01 02:30:17 +02:30
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
0001-01-01 02:30:17 +02:30
( 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
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
0001-01-01 02:30:17 +02:30
*/
# include "includes.h"
/* Election parameters. */
extern time_t StartupTime ;
/****************************************************************************
Send an election datagram packet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
static void send_election_dgram ( struct subnet_record * subrec , const char * workgroup_name ,
uint32 criterion , int timeup , const char * server_name )
0001-01-01 02:30:17 +02:30
{
2007-11-20 02:15:09 +03:00
char outbuf [ 1024 ] ;
0001-01-01 02:30:17 +02:30
unstring srv_name ;
0001-01-01 02:30:17 +02:30
char * p ;
DEBUG ( 2 , ( " send_election_dgram: Sending election packet for workgroup %s on subnet %s \n " ,
workgroup_name , subrec - > subnet_name ) ) ;
memset ( outbuf , ' \0 ' , sizeof ( outbuf ) ) ;
p = outbuf ;
SCVAL ( p , 0 , ANN_Election ) ; /* Election opcode. */
p + + ;
SCVAL ( 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 ;
0001-01-01 02:30:17 +02:30
unstrcpy ( srv_name , server_name ) ;
0001-01-01 02:30:17 +02:30
strupper_m ( srv_name ) ;
0001-01-01 02:30:17 +02:30
/* The following call does UNIX -> DOS charset conversion. */
2007-11-20 02:15:09 +03:00
push_ascii ( p , srv_name , sizeof ( outbuf ) - PTR_DIFF ( p , outbuf ) - 1 , STR_TERMINATE ) ;
2007-04-03 00:10:21 +04:00
p = skip_string ( outbuf , sizeof ( outbuf ) , p ) ;
2007-11-20 02:15:09 +03:00
0001-01-01 02:30:17 +02:30
send_mailslot ( False , BROWSE_MAILSLOT , outbuf , PTR_DIFF ( p , outbuf ) ,
global_myname ( ) , 0 ,
workgroup_name , 0x1e ,
subrec - > bcast_ip , subrec - > myip , DGRAM_PORT ) ;
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
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 )
{
0001-01-01 02:30:17 +02:30
unstring aname ;
0001-01-01 02:30:17 +02:30
pull_ascii_nstring ( aname , sizeof ( aname ) , answer_name - > name ) ;
0001-01-01 02:30:17 +02:30
DEBUG ( 3 , ( " check_for_master_browser_success: Local master browser for workgroup %s exists at \
IP % s ( just checking ) . \ n " , aname, inet_ntoa(answer_ip) ));
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
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 )
{
0001-01-01 02:30:17 +02:30
unstring workgroup_name ;
0001-01-01 02:30:17 +02:30
struct work_record * work ;
0001-01-01 02:30:17 +02:30
pull_ascii_nstring ( workgroup_name , sizeof ( workgroup_name ) , question_name - > name ) ;
0001-01-01 02:30:17 +02:30
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 ;
}
0001-01-01 02:30:17 +02:30
if ( strequal ( work - > work_group , lp_workgroup ( ) ) ) {
0001-01-01 02:30:17 +02:30
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 , " " ) ;
}
}
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
Ensure there is a local master browser for a workgroup on our
broadcast interfaces .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void check_master_browser_exists ( time_t t )
{
0001-01-01 02:30:17 +02:30
static time_t lastrun = 0 ;
struct subnet_record * subrec ;
const char * workgroup_name = lp_workgroup ( ) ;
if ( ! lastrun )
lastrun = t ;
if ( t < ( lastrun + ( CHECK_TIME_MST_BROWSE * 60 ) ) )
return ;
lastrun = t ;
dump_workgroups ( False ) ;
for ( subrec = FIRST_SUBNET ; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST ( subrec ) ) {
struct work_record * work ;
for ( work = subrec - > workgrouplist ; work ; work = work - > next ) {
0001-01-01 02:30:17 +02:30
if ( strequal ( work - > work_group , workgroup_name ) & & ! AM_LOCAL_MASTER_BROWSER ( work ) ) {
0001-01-01 02:30:17 +02:30
/* 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 ) ;
}
}
}
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
Run an election .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void run_elections ( time_t t )
{
0001-01-01 02:30:17 +02:30
static time_t lastime = 0 ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
struct subnet_record * subrec ;
0001-01-01 02:30:17 +02:30
2006-05-07 13:21:39 +04:00
START_PROFILE ( run_elections ) ;
0001-01-01 02:30:17 +02:30
/* Send election packets once every 2 seconds - note */
2006-05-07 13:21:39 +04:00
if ( lastime & & ( t - lastime < 2 ) ) {
END_PROFILE ( run_elections ) ;
0001-01-01 02:30:17 +02:30
return ;
2006-05-07 13:21:39 +04:00
}
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
lastime = t ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
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 ) {
/*
* 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 ;
make_nmb_name ( & nmbname , work - > work_group , 0x1e ) ;
if ( find_name_on_subnet ( subrec , & nmbname , FIND_SELF_NAME ) = = NULL ) {
DEBUG ( 8 , ( " run_elections: Cannot send election packet yet as name %s not \
0001-01-01 02:30:17 +02:30
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
0001-01-01 02:30:17 +02:30
continue ;
}
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
send_election_dgram ( subrec , work - > work_group , work - > ElectionCriterion ,
t - StartupTime , global_myname ( ) ) ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
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 ) ;
}
}
}
}
END_PROFILE ( run_elections ) ;
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
Determine if I win an election .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
static bool win_election ( struct work_record * work , int version ,
0001-01-01 02:30:17 +02:30
uint32 criterion , int timeup , const char * server_name )
0001-01-01 02:30:17 +02:30
{
0001-01-01 02:30:17 +02:30
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 ;
}
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
DEBUG ( 4 , ( " win_election: election comparison: %x:%x %x:%x %d:%d %s:%s \n " ,
version , ELECTION_VERSION ,
criterion , mycriterion ,
timeup , mytimeup ,
server_name , global_myname ( ) ) ) ;
if ( version > ELECTION_VERSION )
return ( False ) ;
if ( version < ELECTION_VERSION )
return ( True ) ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
if ( criterion > mycriterion )
return ( False ) ;
if ( criterion < mycriterion )
return ( True ) ;
if ( timeup > mytimeup )
return ( False ) ;
if ( timeup < mytimeup )
return ( True ) ;
0001-01-01 02:30:17 +02:30
if ( StrCaseCmp ( global_myname ( ) , server_name ) > 0 )
0001-01-01 02:30:17 +02:30
return ( False ) ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
return ( True ) ;
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
Process an incoming election datagram packet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_election ( struct subnet_record * subrec , struct packet_struct * p , char * buf )
{
0001-01-01 02:30:17 +02:30
struct dgram_packet * dgram = & p - > packet . dgram ;
int version = CVAL ( buf , 0 ) ;
uint32 criterion = IVAL ( buf , 1 ) ;
int timeup = IVAL ( buf , 5 ) / 1000 ;
0001-01-01 02:30:17 +02:30
unstring server_name ;
0001-01-01 02:30:17 +02:30
struct work_record * work ;
0001-01-01 02:30:17 +02:30
unstring workgroup_name ;
0001-01-01 02:30:17 +02:30
2006-05-07 13:21:39 +04:00
START_PROFILE ( election ) ;
0001-01-01 02:30:17 +02:30
pull_ascii_nstring ( server_name , sizeof ( server_name ) , buf + 13 ) ;
pull_ascii_nstring ( workgroup_name , sizeof ( workgroup_name ) , dgram - > dest_name . name ) ;
0001-01-01 02:30:17 +02:30
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 ) ) ;
goto done ;
}
0001-01-01 02:30:17 +02:30
if ( ! strequal ( work - > work_group , lp_workgroup ( ) ) ) {
0001-01-01 02:30:17 +02:30
DEBUG ( 3 , ( " process_election: ignoring election request for workgroup %s on subnet %s as this \
0001-01-01 02:30:17 +02:30
is not my workgroup . \ n " , work->work_group, subrec->subnet_name ));
0001-01-01 02:30:17 +02:30
goto done ;
}
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 , False ) ;
}
}
0001-01-01 02:30:17 +02:30
done :
0001-01-01 02:30:17 +02:30
END_PROFILE ( election ) ;
0001-01-01 02:30:17 +02:30
}
/****************************************************************************
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 ( ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool check_elections ( void )
0001-01-01 02:30:17 +02:30
{
0001-01-01 02:30:17 +02:30
struct subnet_record * subrec ;
2007-10-19 04:40:25 +04:00
bool run_any_election = False ;
0001-01-01 02:30:17 +02:30
for ( subrec = FIRST_SUBNET ; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST ( subrec ) ) {
struct work_record * work ;
for ( work = subrec - > workgrouplist ; work ; work = work - > next ) {
2008-01-05 10:26:47 +03:00
if ( work - > RunningElection ) {
run_any_election = work - > RunningElection ;
}
0001-01-01 02:30:17 +02:30
/*
* 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 ( ) ) {
/*
* 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 ;
make_nmb_name ( & nmbname , work - > work_group , 0x1e ) ;
if ( find_name_on_subnet ( subrec , & nmbname , FIND_SELF_NAME ) = = NULL ) {
DEBUG ( 8 , ( " check_elections: Cannot send election packet yet as name %s not \
0001-01-01 02:30:17 +02:30
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
0001-01-01 02:30:17 +02:30
continue ;
}
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
DEBUG ( 3 , ( " check_elections: >>> Starting election for workgroup %s on subnet %s <<< \n " ,
work - > work_group , subrec - > subnet_name ) ) ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
work - > ElectionCount = 0 ;
work - > RunningElection = True ;
work - > needelection = False ;
}
}
}
return run_any_election ;
}
0001-01-01 02:30:17 +02:30
/****************************************************************************
0001-01-01 02:30:17 +02:30
Process a internal Samba message forcing an election .
0001-01-01 02:30:17 +02:30
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
0001-01-01 02:30:17 +02:30
2007-05-20 01:53:28 +04:00
void nmbd_message_election ( struct messaging_context * msg ,
void * private_data ,
uint32_t msg_type ,
struct server_id server_id ,
DATA_BLOB * data )
0001-01-01 02:30:17 +02:30
{
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 ) {
0001-01-01 02:30:17 +02:30
if ( strequal ( work - > work_group , lp_workgroup ( ) ) ) {
0001-01-01 02:30:17 +02:30
work - > needelection = True ;
work - > ElectionCount = 0 ;
work - > mst_state = lp_local_master ( ) ? MST_POTENTIAL : MST_NONE ;
}
}
}
}