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
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"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2010-08-03 00:48:16 +04:00
# include "../librpc/gen_ndr/svcctl.h"
2010-08-18 17:22:09 +04:00
# include "nmbd/nmbd.h"
1997-12-13 17:16:07 +03:00
int updatecount = 0 ;
/*******************************************************************
Remove all the servers in a work group .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void remove_all_servers ( struct work_record * work )
{
2003-08-27 05:25:01 +04:00
struct server_record * servrec ;
struct server_record * nexts ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
for ( servrec = work - > serverlist ; servrec ; servrec = nexts ) {
DEBUG ( 7 , ( " remove_all_servers: Removing server %s \n " , servrec - > serv . name ) ) ;
nexts = servrec - > next ;
2010-02-06 04:38:24 +03:00
DLIST_REMOVE ( work - > serverlist , servrec ) ;
2003-08-27 05:25:01 +04:00
ZERO_STRUCTP ( servrec ) ;
SAFE_FREE ( servrec ) ;
}
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
work - > subnet - > work_changed = True ;
1997-12-13 17:16:07 +03:00
}
/***************************************************************************
Add a server into the a workgroup serverlist .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_server_to_workgroup ( struct work_record * work ,
struct server_record * servrec )
{
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( work - > serverlist , servrec ) ;
2003-08-27 05:25:01 +04:00
work - > subnet - > work_changed = True ;
1997-12-13 17:16:07 +03:00
}
/****************************************************************************
Find a server in a server list .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-11-13 02:20:50 +03:00
struct server_record * find_server_in_workgroup ( struct work_record * work , const char * name )
1997-12-13 17:16:07 +03:00
{
2003-08-27 05:25:01 +04:00
struct server_record * ret ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
for ( ret = work - > serverlist ; ret ; ret = ret - > next ) {
if ( strequal ( ret - > serv . name , name ) )
return ret ;
}
return NULL ;
1997-12-13 17:16:07 +03:00
}
/****************************************************************************
Remove a server entry from this workgroup .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1997-12-24 11:49:44 +03:00
void remove_server_from_workgroup ( struct work_record * work , struct server_record * servrec )
1997-12-13 17:16:07 +03:00
{
2010-02-06 04:38:24 +03:00
DLIST_REMOVE ( work - > serverlist , servrec ) ;
2003-08-27 05:25:01 +04:00
ZERO_STRUCTP ( servrec ) ;
SAFE_FREE ( servrec ) ;
work - > subnet - > work_changed = True ;
1997-12-13 17:16:07 +03:00
}
/****************************************************************************
Create a server entry on this workgroup .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct server_record * create_server_on_workgroup ( struct work_record * work ,
2002-11-13 02:20:50 +03:00
const char * name , int servertype ,
int ttl , const char * comment )
1997-12-13 17:16:07 +03:00
{
2003-08-27 05:25:01 +04:00
struct server_record * servrec ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
if ( name [ 0 ] = = ' * ' ) {
DEBUG ( 7 , ( " create_server_on_workgroup: not adding name starting with '*' (%s) \n " ,
name ) ) ;
return ( NULL ) ;
}
1997-12-13 17:16:07 +03:00
2009-01-20 02:09:51 +03:00
if ( find_server_in_workgroup ( work , name ) ! = NULL ) {
2003-08-27 05:25:01 +04:00
DEBUG ( 0 , ( " create_server_on_workgroup: Server %s already exists on \
1997-12-13 17:16:07 +03:00
workgroup % s . This is a bug . \ n " , name, work->work_group));
2003-08-27 05:25:01 +04:00
return NULL ;
}
1997-12-13 17:16:07 +03:00
2004-12-07 21:25:53 +03:00
if ( ( servrec = SMB_MALLOC_P ( struct server_record ) ) = = NULL ) {
2003-08-27 05:25:01 +04:00
DEBUG ( 0 , ( " create_server_entry_on_workgroup: malloc fail ! \n " ) ) ;
return NULL ;
}
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
memset ( ( char * ) servrec , ' \0 ' , sizeof ( * servrec ) ) ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
servrec - > subnet = work - > subnet ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
fstrcpy ( servrec - > serv . name , name ) ;
fstrcpy ( servrec - > serv . comment , comment ) ;
2012-08-09 02:35:28 +04:00
if ( ! strupper_m ( servrec - > serv . name ) ) {
DEBUG ( 2 , ( " strupper_m %s failed \n " , servrec - > serv . name ) ) ;
SAFE_FREE ( servrec ) ;
return NULL ;
}
2003-08-27 05:25:01 +04:00
servrec - > serv . type = servertype ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
update_server_ttl ( servrec , ttl ) ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
add_server_to_workgroup ( work , servrec ) ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
DEBUG ( 3 , ( " create_server_on_workgroup: Created server entry %s of type %x (%s) on \
1997-12-13 17:16:07 +03:00
workgroup % s . \ n " , name,servertype,comment, work->work_group));
2003-08-27 05:25:01 +04:00
return ( servrec ) ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Update the ttl field of a server record .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void update_server_ttl ( struct server_record * servrec , int ttl )
{
2003-08-27 05:25:01 +04:00
if ( ttl > lp_max_ttl ( ) )
ttl = lp_max_ttl ( ) ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
if ( is_myname ( servrec - > serv . name ) )
servrec - > death_time = PERMANENT_TTL ;
else
servrec - > death_time = ( ttl ! = PERMANENT_TTL ) ? time ( NULL ) + ( ttl * 3 ) : PERMANENT_TTL ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Expire old servers in the serverlist . A time of - 1 indicates
everybody dies except those with a death_time of PERMANENT_TTL ( which is 0 ) .
This should only be called from expire_workgroups_and_servers ( ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void expire_servers ( struct work_record * work , time_t t )
{
2003-08-27 05:25:01 +04:00
struct server_record * servrec ;
struct server_record * nexts ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
for ( servrec = work - > serverlist ; servrec ; servrec = nexts ) {
nexts = servrec - > next ;
if ( ( servrec - > death_time ! = PERMANENT_TTL ) & & ( ( t = = - 1 ) | | ( servrec - > death_time < t ) ) ) {
DEBUG ( 3 , ( " expire_old_servers: Removing timed out server %s \n " , servrec - > serv . name ) ) ;
remove_server_from_workgroup ( work , servrec ) ;
}
}
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Decide if we should write out a server record for this server .
We return zero if we should not . Check if we ' ve already written
out this server record from an earlier subnet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-04-30 06:14:34 +03:00
static uint32_t write_this_server_name ( struct subnet_record * subrec ,
1997-12-13 17:16:07 +03:00
struct work_record * work ,
struct server_record * servrec )
{
2003-08-27 05:25:01 +04:00
struct subnet_record * ssub ;
struct work_record * iwork ;
/* Go through all the subnets we have already seen. */
2006-06-28 04:05:53 +04:00
for ( ssub = FIRST_SUBNET ; ssub & & ( ssub ! = subrec ) ; ssub = NEXT_SUBNET_INCLUDING_UNICAST ( ssub ) ) {
2003-08-27 05:25:01 +04:00
for ( iwork = ssub - > workgrouplist ; iwork ; iwork = iwork - > next ) {
if ( find_server_in_workgroup ( iwork , servrec - > serv . name ) ! = NULL ) {
/*
* We have already written out this server record , don ' t
* do it again . This gives precedence to servers we have seen
* on the broadcast subnets over servers that may have been
* added via a sync on the unicast_subet .
*
* The correct way to do this is to have a serverlist file
* per subnet - this means changes to smbd as well . I may
* add this at a later date ( JRA ) .
*/
return 0 ;
}
}
}
return servrec - > serv . type ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Decide if we should write out a workgroup record for this workgroup .
2002-11-13 02:20:50 +03:00
We return zero if we should not . Don ' t write out lp_workgroup ( ) ( we ' ve
1997-12-13 17:16:07 +03:00
already done it ) and also don ' t write out a second workgroup record
on the unicast subnet that we ' ve already written out on one of the
broadcast subnets .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-04-30 06:14:34 +03:00
static uint32_t write_this_workgroup_name ( struct subnet_record * subrec ,
1997-12-13 17:16:07 +03:00
struct work_record * work )
{
2003-08-27 05:25:01 +04:00
struct subnet_record * ssub ;
1997-12-13 17:16:07 +03:00
2004-03-13 05:47:21 +03:00
if ( strequal ( lp_workgroup ( ) , work - > work_group ) )
2003-08-27 05:25:01 +04:00
return 0 ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
/* This is a workgroup we have seen on a broadcast subnet. All
these have the same type . */
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
if ( subrec ! = unicast_subnet )
return ( SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT | SV_TYPE_LOCAL_LIST_ONLY ) ;
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
for ( ssub = FIRST_SUBNET ; ssub ; ssub = NEXT_SUBNET_EXCLUDING_UNICAST ( ssub ) ) {
/* This is the unicast subnet so check if we've already written out
this subnet when we passed over the broadcast subnets . */
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
if ( find_workgroup_on_subnet ( ssub , work - > work_group ) ! = NULL )
return 0 ;
}
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
/* All workgroups on the unicast subnet (except our own, which we
have already written out ) cannot be local . */
1997-12-13 17:16:07 +03:00
2003-08-27 05:25:01 +04:00
return ( SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT ) ;
1997-12-13 17:16:07 +03:00
}
/*******************************************************************
Write out the browse . dat file .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2016-11-26 11:50:33 +03:00
void write_browse_list_entry ( FILE * fp , const char * name , uint32_t rec_type ,
2002-11-13 02:20:50 +03:00
const char * local_master_browser_name , const char * description )
2002-01-18 05:30:37 +03:00
{
fstring tmp ;
slprintf ( tmp , sizeof ( tmp ) - 1 , " \" %s \" " , name ) ;
2016-11-26 11:50:33 +03:00
fprintf ( fp , " %-25s " , tmp ) ;
fprintf ( fp , " %08x " , rec_type ) ;
2002-01-18 05:30:37 +03:00
slprintf ( tmp , sizeof ( tmp ) - 1 , " \" %s \" " , local_master_browser_name ) ;
2016-11-26 11:50:33 +03:00
fprintf ( fp , " %-30s " , tmp ) ;
fprintf ( fp , " \" %s \" \n " , description ) ;
2002-01-18 05:30:37 +03:00
}
2007-10-19 04:40:25 +04:00
void write_browse_list ( time_t t , bool force_write )
2007-11-20 02:15:09 +03:00
{
2003-08-27 05:25:01 +04:00
struct subnet_record * subrec ;
struct work_record * work ;
struct server_record * servrec ;
2007-11-20 02:15:09 +03:00
char * fname ;
char * fnamenew ;
2015-04-30 06:14:34 +03:00
uint32_t stype ;
2003-08-27 05:25:01 +04:00
int i ;
2016-11-26 11:50:33 +03:00
int fd ;
FILE * fp ;
2007-10-19 04:40:25 +04:00
bool list_changed = force_write ;
2003-08-27 05:25:01 +04:00
static time_t lasttime = 0 ;
2007-11-20 02:15:09 +03:00
TALLOC_CTX * ctx = talloc_tos ( ) ;
2003-08-27 05:25:01 +04:00
/* Always dump if we're being told to by a signal. */
if ( force_write = = False ) {
if ( ! lasttime )
lasttime = t ;
if ( t - lasttime < 5 )
return ;
}
lasttime = t ;
dump_workgroups ( force_write ) ;
2007-11-20 02:15:09 +03:00
2003-08-27 05:25:01 +04:00
for ( subrec = FIRST_SUBNET ; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST ( subrec ) ) {
if ( subrec - > work_changed ) {
list_changed = True ;
break ;
}
}
if ( ! list_changed )
return ;
updatecount + + ;
2007-11-20 02:15:09 +03:00
2009-01-16 02:40:48 +03:00
fname = cache_path ( SERVER_LIST ) ;
2007-11-20 02:15:09 +03:00
if ( ! fname ) {
return ;
}
fnamenew = talloc_asprintf ( ctx , " %s. " ,
fname ) ;
if ( ! fnamenew ) {
2014-10-06 20:21:16 +04:00
talloc_free ( fname ) ;
2007-11-20 02:15:09 +03:00
return ;
}
2016-11-26 11:50:33 +03:00
fd = open ( fnamenew , O_WRONLY | O_CREAT | O_TRUNC , 0644 ) ;
if ( fd = = - 1 ) {
DBG_ERR ( " Can't open file %s: %s \n " , fnamenew ,
strerror ( errno ) ) ;
talloc_free ( fnamenew ) ;
talloc_free ( fname ) ;
return ;
}
2007-11-20 02:15:09 +03:00
2016-11-26 11:50:33 +03:00
fp = fdopen ( fd , " w " ) ;
2003-08-27 05:25:01 +04:00
if ( ! fp ) {
2016-11-26 11:50:33 +03:00
DBG_ERR ( " fdopen failed: %s \n " , strerror ( errno ) ) ;
close ( fd ) ;
2014-10-06 20:21:16 +04:00
talloc_free ( fnamenew ) ;
talloc_free ( fname ) ;
2003-08-27 05:25:01 +04:00
return ;
2007-11-20 02:15:09 +03:00
}
2016-11-26 11:50:33 +03:00
fd = - 1 ;
2007-11-20 02:15:09 +03:00
2003-08-27 05:25:01 +04:00
/*
* Write out a record for our workgroup . Use the record from the first
* subnet .
*/
if ( ( work = find_workgroup_on_subnet ( FIRST_SUBNET , lp_workgroup ( ) ) ) = = NULL ) {
DEBUG ( 0 , ( " write_browse_list: Fatal error - cannot find my workgroup %s \n " ,
lp_workgroup ( ) ) ) ;
2016-11-26 11:50:33 +03:00
fclose ( fp ) ;
2014-10-06 20:21:16 +04:00
talloc_free ( fnamenew ) ;
talloc_free ( fname ) ;
2003-08-27 05:25:01 +04:00
return ;
}
write_browse_list_entry ( fp , work - > work_group ,
SV_TYPE_DOMAIN_ENUM | SV_TYPE_NT | SV_TYPE_LOCAL_LIST_ONLY ,
work - > local_master_browser_name , work - > work_group ) ;
2007-11-20 02:15:09 +03:00
/*
2003-08-27 05:25:01 +04:00
* We need to do something special for our own names .
* This is due to the fact that we may be a local master browser on
* one of our broadcast subnets , and a domain master on the unicast
* subnet . We iterate over the subnets and only write out the name
* once .
*/
for ( i = 0 ; my_netbios_names ( i ) ; i + + ) {
stype = 0 ;
for ( subrec = FIRST_SUBNET ; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST ( subrec ) ) {
if ( ( work = find_workgroup_on_subnet ( subrec , lp_workgroup ( ) ) ) = = NULL )
continue ;
if ( ( servrec = find_server_in_workgroup ( work , my_netbios_names ( i ) ) ) = = NULL )
continue ;
stype | = servrec - > serv . type ;
}
/* Output server details, plus what workgroup they're in. */
write_browse_list_entry ( fp , my_netbios_names ( i ) , stype ,
2014-02-04 06:08:58 +04:00
string_truncate ( lp_server_string ( talloc_tos ( ) ) , MAX_SERVER_STRING_LENGTH ) , lp_workgroup ( ) ) ;
2003-08-27 05:25:01 +04:00
}
2007-11-20 02:15:09 +03:00
2003-08-27 05:25:01 +04:00
for ( subrec = FIRST_SUBNET ; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST ( subrec ) ) {
subrec - > work_changed = False ;
for ( work = subrec - > workgrouplist ; work ; work = work - > next ) {
/* Write out a workgroup record for a workgroup. */
2015-04-30 06:14:34 +03:00
uint32_t wg_type = write_this_workgroup_name ( subrec , work ) ;
2003-08-27 05:25:01 +04:00
if ( wg_type ) {
write_browse_list_entry ( fp , work - > work_group , wg_type ,
work - > local_master_browser_name ,
work - > work_group ) ;
}
/* Now write out any server records a workgroup may have. */
for ( servrec = work - > serverlist ; servrec ; servrec = servrec - > next ) {
2015-04-30 06:14:34 +03:00
uint32_t serv_type ;
2003-08-27 05:25:01 +04:00
/* We have already written our names here. */
if ( is_myname ( servrec - > serv . name ) )
2007-11-20 02:15:09 +03:00
continue ;
2003-08-27 05:25:01 +04:00
serv_type = write_this_server_name ( subrec , work , servrec ) ;
if ( serv_type ) {
/* Output server details, plus what workgroup they're in. */
write_browse_list_entry ( fp , servrec - > serv . name , serv_type ,
servrec - > serv . comment , work - > work_group ) ;
}
}
}
2007-11-20 02:15:09 +03:00
}
2016-11-26 11:50:33 +03:00
fclose ( fp ) ;
2003-08-27 05:25:01 +04:00
unlink ( fname ) ;
chmod ( fnamenew , 0644 ) ;
rename ( fnamenew , fname ) ;
DEBUG ( 3 , ( " write_browse_list: Wrote browse list into file %s \n " , fname ) ) ;
2014-10-06 20:21:16 +04:00
talloc_free ( fnamenew ) ;
talloc_free ( fname ) ;
1997-12-13 17:16:07 +03:00
}