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
2003-08-23 05:59:14 +04:00
Copyright ( C ) Jeremy Allison 1994 - 2003
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
1997-12-13 17:16:07 +03:00
( 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/>.
1997-12-13 17:16:07 +03:00
*/
# include "includes.h"
2010-08-18 17:22:09 +04:00
# include "nmbd/nmbd.h"
2020-08-07 21:17:34 +03:00
# include "lib/util/string_wrappers.h"
1997-12-13 17:16:07 +03:00
/* Election parameters. */
extern time_t StartupTime ;
/****************************************************************************
Send an election datagram packet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-08-23 05:59:14 +04:00
2002-11-13 02:20:50 +03:00
static void send_election_dgram ( struct subnet_record * subrec , const char * workgroup_name ,
2015-04-30 06:14:34 +03:00
uint32_t criterion , int timeup , const char * server_name )
1997-12-13 17:16:07 +03:00
{
2007-11-20 02:15:09 +03:00
char outbuf [ 1024 ] ;
2004-03-16 00:45:45 +03:00
unstring srv_name ;
2003-08-23 05:59:14 +04:00
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 ;
2004-03-16 00:45:45 +03:00
unstrcpy ( srv_name , server_name ) ;
2012-08-09 02:35:28 +04:00
if ( ! strupper_m ( srv_name ) ) {
DEBUG ( 2 , ( " strupper_m failed for %s \n " , srv_name ) ) ;
return ;
}
2003-08-27 05:25:01 +04:00
/* 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
2003-08-23 05:59:14 +04:00
send_mailslot ( False , BROWSE_MAILSLOT , outbuf , PTR_DIFF ( p , outbuf ) ,
2011-06-09 09:31:03 +04:00
lp_netbios_name ( ) , 0 ,
2003-08-23 05:59:14 +04:00
workgroup_name , 0x1e ,
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 )
{
2004-03-16 00:45:45 +03:00
unstring aname ;
2004-03-13 05:16:21 +03:00
pull_ascii_nstring ( aname , sizeof ( aname ) , answer_name - > name ) ;
2003-08-23 05:59:14 +04:00
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) ));
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
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 )
{
2004-03-16 00:45:45 +03:00
unstring workgroup_name ;
2003-08-23 05:59:14 +04:00
struct work_record * work ;
2004-03-13 05:16:21 +03:00
pull_ascii_nstring ( workgroup_name , sizeof ( workgroup_name ) , question_name - > name ) ;
2003-08-23 05:59:14 +04:00
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 ;
}
2004-03-13 05:47:21 +03:00
if ( strequal ( work - > work_group , lp_workgroup ( ) ) ) {
2003-08-23 05:59:14 +04: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 .
*/
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 )
{
2003-08-23 05:59:14 +04:00
static time_t lastrun = 0 ;
struct subnet_record * subrec ;
const char * workgroup_name = lp_workgroup ( ) ;
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 ) {
2004-03-13 05:47:21 +03:00
if ( strequal ( work - > work_group , workgroup_name ) & & ! AM_LOCAL_MASTER_BROWSER ( work ) ) {
2003-08-23 05:59:14 +04:00
/* 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 ) ;
}
}
}
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Run an election .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void run_elections ( time_t t )
{
2003-08-23 05:59:14 +04:00
static time_t lastime = 0 ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
struct subnet_record * subrec ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
/* Send election packets once every 2 seconds - note */
2006-05-07 13:21:39 +04:00
if ( lastime & & ( t - lastime < 2 ) ) {
2003-08-23 05:59:14 +04:00
return ;
2006-05-07 13:21:39 +04:00
}
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
lastime = t ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04: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 ) {
/*
* 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 \
1998-11-14 04:04:13 +03:00
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
2003-08-23 05:59:14 +04:00
continue ;
}
1998-06-27 04:27:44 +04:00
2003-08-23 05:59:14 +04:00
send_election_dgram ( subrec , work - > work_group , work - > ElectionCriterion ,
2011-06-09 09:31:03 +04:00
t - StartupTime , lp_netbios_name ( ) ) ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04: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 ) ;
}
}
}
}
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Determine if I win an election .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
static bool win_election ( struct work_record * work , int version ,
2015-04-30 06:14:34 +03:00
uint32_t criterion , int timeup , const char * server_name )
1997-12-13 17:16:07 +03:00
{
2003-08-23 05:59:14 +04:00
int mytimeup = time ( NULL ) - StartupTime ;
2015-04-30 06:14:34 +03:00
uint32_t mycriterion = work - > ElectionCriterion ;
2003-08-23 05:59:14 +04:00
/* 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 ;
}
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
DEBUG ( 4 , ( " win_election: election comparison: %x:%x %x:%x %d:%d %s:%s \n " ,
version , ELECTION_VERSION ,
criterion , mycriterion ,
timeup , mytimeup ,
2011-06-09 09:31:03 +04:00
server_name , lp_netbios_name ( ) ) ) ;
2003-08-23 05:59:14 +04:00
if ( version > ELECTION_VERSION )
return ( False ) ;
if ( version < ELECTION_VERSION )
return ( True ) ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
if ( criterion > mycriterion )
return ( False ) ;
if ( criterion < mycriterion )
return ( True ) ;
if ( timeup > mytimeup )
return ( False ) ;
if ( timeup < mytimeup )
return ( True ) ;
2011-06-09 09:31:03 +04:00
if ( strcasecmp_m ( lp_netbios_name ( ) , server_name ) > 0 )
2003-08-23 05:59:14 +04:00
return ( False ) ;
1997-12-13 17:16:07 +03:00
2003-08-23 05:59:14 +04:00
return ( True ) ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Process an incoming election datagram packet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-05-05 22:29:01 +04:00
void process_election ( struct subnet_record * subrec , struct packet_struct * p , const char * buf )
1997-12-13 17:16:07 +03:00
{
2003-08-23 05:59:14 +04:00
struct dgram_packet * dgram = & p - > packet . dgram ;
int version = CVAL ( buf , 0 ) ;
2015-04-30 06:14:34 +03:00
uint32_t criterion = IVAL ( buf , 1 ) ;
2003-08-23 05:59:14 +04:00
int timeup = IVAL ( buf , 5 ) / 1000 ;
2004-03-16 00:45:45 +03:00
unstring server_name ;
2003-08-23 05:59:14 +04:00
struct work_record * work ;
2004-03-16 00:45:45 +03:00
unstring workgroup_name ;
2003-08-23 05:59:14 +04:00
2004-03-13 05:16:21 +03:00
pull_ascii_nstring ( server_name , sizeof ( server_name ) , buf + 13 ) ;
pull_ascii_nstring ( workgroup_name , sizeof ( workgroup_name ) , dgram - > dest_name . name ) ;
2003-08-23 05:59:14 +04: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 ) ) ;
goto done ;
}
2004-03-13 05:47:21 +03:00
if ( ! strequal ( work - > work_group , lp_workgroup ( ) ) ) {
2003-08-23 05:59:14 +04:00
DEBUG ( 3 , ( " process_election: ignoring election request for workgroup %s on subnet %s as this \
1997-12-13 17:16:07 +03:00
is not my workgroup . \ n " , work->work_group, subrec->subnet_name ));
2003-08-23 05:59:14 +04:00
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 ) ;
}
}
2001-09-05 22:43:55 +04:00
done :
2014-11-05 15:10:49 +03:00
return ;
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 ( ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool check_elections ( void )
1997-12-13 17:16:07 +03:00
{
2003-08-23 05:59:14 +04:00
struct subnet_record * subrec ;
2007-10-19 04:40:25 +04:00
bool run_any_election = False ;
2003-08-23 05:59:14 +04:00
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 ;
}
2003-08-23 05:59:14 +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 ( ) ) {
/*
* 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 \
1998-11-14 04:04:13 +03:00
yet registered on subnet % s \ n " , nmb_namestr(&nmbname), subrec->subnet_name ));
2003-08-23 05:59:14 +04:00
continue ;
}
2000-09-12 10:13:25 +04:00
2003-08-23 05:59:14 +04:00
DEBUG ( 3 , ( " check_elections: >>> Starting election for workgroup %s on subnet %s <<< \n " ,
work - > work_group , subrec - > subnet_name ) ) ;
2000-09-12 10:13:25 +04:00
2003-08-23 05:59:14 +04:00
work - > ElectionCount = 0 ;
work - > RunningElection = True ;
work - > needelection = False ;
}
}
}
return run_any_election ;
}
2000-09-12 10:13:25 +04:00
/****************************************************************************
2003-08-23 05:59:14 +04:00
Process a internal Samba message forcing an election .
2000-09-12 10:13:25 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-08-23 05:59:14 +04:00
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 )
2000-09-12 10:13:25 +04:00
{
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 ) {
2004-03-13 05:47:21 +03:00
if ( strequal ( work - > work_group , lp_workgroup ( ) ) ) {
2000-09-12 10:13:25 +04:00
work - > needelection = True ;
work - > ElectionCount = 0 ;
work - > mst_state = lp_local_master ( ) ? MST_POTENTIAL : MST_NONE ;
}
}
}
}