0001-01-01 02:30:17 +02:30
/*
Unix SMB / Netbios implementation .
Version 1.9 .
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
Copyright ( C ) Jeremy Allison 1994 - 1998
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
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"
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
0001-01-01 02:30:17 +02:30
void insert_permanent_name_into_unicast ( struct subnet_record * subrec ,
0001-01-01 02:30:17 +02:30
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 . */
0001-01-01 02:30:17 +02:30
set_workgroup_local_master_browser_name ( work , " " ) ;
0001-01-01 02:30:17 +02:30
/*
* Ensure the IP address of this subnet is not registered as one
* of the IP addresses of the WORKGROUP < 1 d > 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 ) ;
0001-01-01 02:30:17 +02:30
DEBUG ( 0 , ( " \n %s ***** Samba name server %s has stopped being a local master browser for workgroup %s \
on subnet % s * * * * * \ n \ n " , timestring(), myname, released_name->name, subrec->subnet_name));
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
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 ) ;
0001-01-01 02:30:17 +02:30
DEBUG ( 0 , ( " \n %s ***** Samba name server %s has stopped being a local master browser for workgroup %s \
on subnet % s * * * * * \ n \ n " , timestring(), myname, fail_name->name, subrec->subnet_name));
0001-01-01 02:30:17 +02:30
}
/*******************************************************************
Utility function to remove the WORKGROUP < 1 d > 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 ;
/* 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. */
0001-01-01 02:30:17 +02:30
if ( ( userdata = ( struct userdata_struct * ) malloc ( sizeof ( struct userdata_struct ) + sizeof ( fstring ) + 1 ) ) = = NULL )
{
DEBUG ( 0 , ( " unbecome_local_master_browser: malloc fail. \n " ) ) ;
return ;
}
0001-01-01 02:30:17 +02:30
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 ) ;
}
0001-01-01 02:30:17 +02:30
free ( ( char * ) userdata ) ;
0001-01-01 02:30:17 +02:30
}
/****************************************************************************
Success in registering the WORKGROUP < 1 d > 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. */
0001-01-01 02:30:17 +02:30
set_workgroup_local_master_browser_name ( work , myname ) ;
0001-01-01 02:30:17 +02:30
/* 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 < 1 d > 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 ( ) ;
0001-01-01 02:30:17 +02:30
DEBUG ( 0 , ( " \n %s ***** Samba name server %s is now a local master browser for workgroup %s \
on subnet % s * * * * * \ n \ n " , timestring(), myname, work->work_group, subrec->subnet_name));
0001-01-01 02:30:17 +02:30
}
/****************************************************************************
Failed to register the WORKGROUP < 1 d > 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 < 1 d > .
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 ;
/* 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 ;
0001-01-01 02:30:17 +02:30
/* Setup the userdata_struct. */
if ( ( userdata = ( struct userdata_struct * ) malloc ( sizeof ( struct userdata_struct ) + sizeof ( fstring ) + 1 ) ) = = NULL )
{
DEBUG ( 0 , ( " become_local_master_browser: malloc fail. \n " ) ) ;
return ;
}
0001-01-01 02:30:17 +02:30
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 ) ;
0001-01-01 02:30:17 +02:30
free ( ( char * ) userdata ) ;
0001-01-01 02:30:17 +02:30
}
0001-01-01 02:30:17 +02:30
/***************************************************************
Utility function to set the local master browser name . Does
some sanity checking as old versions of Samba seem to sometimes
say that the master browser name for a workgroup is the same
as the workgroup name .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void set_workgroup_local_master_browser_name ( struct work_record * work , char * newname )
{
DEBUG ( 5 , ( " set_workgroup_local_master_browser_name: setting local master name to '%s' \
for workgroup % s . \ n " , newname, work->work_group ));
0001-01-01 02:30:17 +02:30
#if 0
/*
* Apparently some sites use the workgroup name as the local
* master browser name . Arrrrggghhhhh ! ( JRA ) .
*/
0001-01-01 02:30:17 +02:30
if ( strequal ( work - > work_group , newname ) )
{
DEBUG ( 5 , ( " set_workgroup_local_master_browser_name: Refusing to set \
local_master_browser_name for workgroup % s to workgroup name . \ n " ,
work - > work_group ) ) ;
return ;
}
0001-01-01 02:30:17 +02:30
# endif
0001-01-01 02:30:17 +02:30
StrnCpy ( work - > local_master_browser_name , newname ,
sizeof ( work - > local_master_browser_name ) - 1 ) ;
}