1996-05-04 11:50:46 +04:00
/*
Unix SMB / Netbios implementation .
Version 1.9 .
NBT netbios routines and daemon - version 2
Copyright ( C ) Andrew Tridgell 1994 - 1995
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"
# include "loadparm.h"
# include "nameserv.h"
static void queue_packet ( struct packet_struct * packet ) ;
void process ( void ) ;
static void dump_names ( void ) ;
static void announce_request ( char * group ) ;
void sync_browse_lists ( char * name , int name_type , char * myname ,
char * domain , struct in_addr ip ) ;
extern int DEBUGLEVEL ;
extern pstring debugf ;
pstring servicesf = CONFIGFILE ;
extern pstring scope ;
extern BOOL CanRecurse ;
extern struct in_addr myip ;
extern struct in_addr bcast_ip ;
extern struct in_addr Netmask ;
extern pstring myhostname ;
static pstring host_file ;
static pstring myname = " " ;
static int ClientNMB = - 1 ;
static int ClientDGRAM = - 1 ;
static BOOL needannounce = True ;
/* this is our name database */
static struct name_record * namelist = NULL ;
/* list of servers to be returned by NetServerEnum */
static struct server_record * serverlist = NULL ;
/* this is the domain list. For the moment we will assume that our
primary domain is the first one listed in this list */
static struct domain_record * domainlist = NULL ;
/* are we running as a daemon ? */
static BOOL is_daemon = False ;
/* machine comment for host announcements */
static pstring ServerComment = " " ;
static BOOL got_bcast = False ;
static BOOL got_myip = False ;
static BOOL got_nmask = False ;
static BOOL updatedlists = False ;
static int updatecount = 0 ;
/* what server type are we currently */
static int ServerType =
SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE |
SV_TYPE_SERVER_UNIX |
SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER ;
/* here are my election parameters */
/* NTAS uses 2, NT uses 1, WfWg uses 0 */
# define MAINTAIN_LIST 1
# define ELECTION_VERSION 1
static BOOL RunningElection = False ;
static BOOL needelection = False ;
static int ElectionCount = 0 ;
static int StartupTime = 0 ;
/* WfWg uses 01040b01 */
/* Win95 uses 01041501 */
/* NTAS uses ?? */
static uint32 ElectionCriterion = ( MAINTAIN_LIST < < 1 ) | ( ELECTION_VERSION < < 8 ) ;
/* we currently support being the master for just one group. Being the
master for more than one group might be tricky as NetServerEnum is
often asked for a list without naming the group */
static fstring PrimaryGroup = " " ;
# define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER))
# define MSBROWSE "\001\002__MSBROWSE__\002"
# define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
# define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
/****************************************************************************
catch a sighup
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int sig_hup ( )
{
BlockSignals ( True ) ;
DEBUG ( 0 , ( " Got SIGHUP (reload not implemented) \n " ) ) ;
dump_names ( ) ;
reload_services ( True ) ;
BlockSignals ( False ) ;
# ifndef DONT_REINSTALL_SIG
signal ( SIGHUP , SIGNAL_CAST sig_hup ) ;
# endif
return ( 0 ) ;
}
/****************************************************************************
catch a sigpipe
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int sig_pipe ( )
{
BlockSignals ( True ) ;
DEBUG ( 0 , ( " Got SIGPIPE \n " ) ) ;
if ( ! is_daemon )
exit ( 1 ) ;
BlockSignals ( False ) ;
return ( 0 ) ;
}
# if DUMP_CORE
/*******************************************************************
prepare to dump a core file - carefully !
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL dump_core ( void )
{
char * p ;
pstring dname ;
strcpy ( dname , debugf ) ;
if ( ( p = strrchr ( dname , ' / ' ) ) ) * p = 0 ;
strcat ( dname , " /corefiles " ) ;
mkdir ( dname , 0700 ) ;
sys_chown ( dname , getuid ( ) , getgid ( ) ) ;
chmod ( dname , 0700 ) ;
if ( chdir ( dname ) ) return ( False ) ;
umask ( ~ ( 0700 ) ) ;
# ifndef NO_GETRLIMIT
# ifdef RLIMIT_CORE
{
struct rlimit rlp ;
getrlimit ( RLIMIT_CORE , & rlp ) ;
rlp . rlim_cur = MAX ( 4 * 1024 * 1024 , rlp . rlim_cur ) ;
setrlimit ( RLIMIT_CORE , & rlp ) ;
getrlimit ( RLIMIT_CORE , & rlp ) ;
DEBUG ( 3 , ( " Core limits now %d %d \n " , rlp . rlim_cur , rlp . rlim_max ) ) ;
}
# endif
# endif
DEBUG ( 0 , ( " Dumping core in %s \n " , dname ) ) ;
return ( True ) ;
}
# endif
/****************************************************************************
possibly continue after a fault
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void fault_continue ( void )
{
static int errcount = 1 ;
errcount - - ;
if ( is_daemon & & errcount )
process ( ) ;
# if DUMP_CORE
if ( dump_core ( ) ) return ;
# endif
return ;
}
/*******************************************************************
wrapper to get the DC
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * domain_controller ( void )
{
char * dc = lp_domain_controller ( ) ;
/* so many people mistake this for a bool that we need to handle it. sigh. */
if ( ! * dc | | strequal ( dc , " yes " ) | | strequal ( dc , " true " ) )
strcpy ( dc , myname ) ;
return ( dc ) ;
}
/****************************************************************************
true if two netbios names are equal
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL name_equal ( struct nmb_name * n1 , struct nmb_name * n2 )
{
if ( n1 - > name_type ! = n2 - > name_type ) return ( False ) ;
return ( strequal ( n1 - > name , n2 - > name ) & & strequal ( n1 - > scope , n2 - > scope ) ) ;
}
/****************************************************************************
add a netbios name into the namelist
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_name ( struct name_record * n )
{
struct name_record * n2 ;
if ( ! namelist ) {
namelist = n ;
n - > prev = NULL ;
n - > next = NULL ;
return ;
}
for ( n2 = namelist ; n2 - > next ; n2 = n2 - > next ) ;
n2 - > next = n ;
n - > next = NULL ;
n - > prev = n2 ;
}
/****************************************************************************
add a domain into the list
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_domain ( struct domain_record * d )
{
struct domain_record * d2 ;
if ( ! domainlist ) {
domainlist = d ;
d - > prev = NULL ;
d - > next = NULL ;
return ;
}
for ( d2 = domainlist ; d2 - > next ; d2 = d2 - > next ) ;
d2 - > next = d ;
d - > next = NULL ;
d - > prev = d2 ;
}
/****************************************************************************
add a server into the list
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_server ( struct server_record * s )
{
struct server_record * s2 ;
if ( ! serverlist ) {
serverlist = s ;
s - > prev = NULL ;
s - > next = NULL ;
return ;
}
for ( s2 = serverlist ; s2 - > next ; s2 = s2 - > next ) ;
s2 - > next = s ;
s - > next = NULL ;
s - > prev = s2 ;
}
/****************************************************************************
remove a name from the namelist . The pointer must be an element just
retrieved
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void remove_name ( struct name_record * n )
{
struct name_record * nlist = namelist ;
while ( nlist & & nlist ! = n ) nlist = nlist - > next ;
if ( nlist ) {
if ( nlist - > next ) nlist - > next - > prev = nlist - > prev ;
if ( nlist - > prev ) nlist - > prev - > next = nlist - > next ;
free ( nlist ) ;
}
}
/****************************************************************************
find a name in the namelist
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct name_record * find_name ( struct nmb_name * n )
{
struct name_record * ret ;
for ( ret = namelist ; ret ; ret = ret - > next )
if ( name_equal ( & ret - > name , n ) ) return ( ret ) ;
return ( NULL ) ;
}
/****************************************************************************
dump a copy of the name table
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void dump_names ( void )
{
time_t t = time ( NULL ) ;
struct name_record * n ;
struct domain_record * d ;
DEBUG ( 3 , ( " Dump of local name table: \n " ) ) ;
for ( n = namelist ; n ; n = n - > next ) {
DEBUG ( 3 , ( " %s %s TTL=%d Unique=%s \n " ,
namestr ( & n - > name ) ,
inet_ntoa ( n - > ip ) ,
n - > death_time ? n - > death_time - t : 0 ,
BOOLSTR ( n - > unique ) ) ) ;
}
DEBUG ( 3 , ( " \n Dump of domain list: \n " ) ) ;
for ( d = domainlist ; d ; d = d - > next )
DEBUG ( 3 , ( " %s %s \n " , d - > name , inet_ntoa ( d - > bcast_ip ) ) ) ;
}
/****************************************************************************
add a host entry to the name list
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct name_record * add_host_entry ( char * name , int type , BOOL unique , int ttl ,
enum name_source source ,
struct in_addr ip )
{
struct name_record * n ;
struct name_record * n2 = NULL ;
n = ( struct name_record * ) malloc ( sizeof ( * n ) ) ;
if ( ! n ) return ( NULL ) ;
bzero ( ( char * ) n , sizeof ( * n ) ) ;
make_nmb_name ( & n - > name , name , type , scope ) ;
if ( ( n2 = find_name ( & n - > name ) ) ) {
free ( n ) ;
n = n2 ;
}
if ( ttl ) n - > death_time = time ( NULL ) + ttl * 3 ;
n - > ip = ip ;
n - > unique = unique ;
n - > source = source ;
if ( ! n2 ) add_name ( n ) ;
DEBUG ( 3 , ( " Added host entry %s at %s ttl=%d unique=%s \n " ,
namestr ( & n - > name ) , inet_ntoa ( ip ) , ttl , BOOLSTR ( unique ) ) ) ;
return ( n ) ;
}
/****************************************************************************
add a domain entry
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct domain_record * add_domain_entry ( char * name , struct in_addr ip )
{
struct domain_record * d ;
d = ( struct domain_record * ) malloc ( sizeof ( * d ) ) ;
if ( ! d ) return ( NULL ) ;
bzero ( ( char * ) d , sizeof ( * d ) ) ;
if ( zero_ip ( ip ) ) ip = bcast_ip ;
StrnCpy ( d - > name , name , sizeof ( d - > name ) - 1 ) ;
d - > bcast_ip = ip ;
if ( ! PrimaryGroup [ 0 ] & & ip_equal ( bcast_ip , ip ) & & name [ 0 ] ! = ' * ' ) {
strcpy ( PrimaryGroup , name ) ;
strupper ( PrimaryGroup ) ;
DEBUG ( 3 , ( " Setting primary group to %s (%s) \n " , PrimaryGroup , inet_ntoa ( ip ) ) ) ;
}
add_domain ( d ) ;
ip = * interpret_addr2 ( " 255.255.255.255 " ) ;
if ( name [ 0 ] ! = ' * ' ) add_host_entry ( name , 0x1e , False , 0 , SELF , ip ) ;
DEBUG ( 3 , ( " Added domain entry %s at %s \n " ,
name , inet_ntoa ( ip ) ) ) ;
return ( d ) ;
}
/****************************************************************************
add a server entry
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct server_record * add_server_entry ( char * name , int servertype ,
int ttl , char * comment , BOOL replace )
{
BOOL newentry = False ;
struct server_record * s ;
for ( s = serverlist ; s ; s = s - > next )
if ( strequal ( name , s - > name ) ) break ;
if ( s & & ! replace ) {
DEBUG ( 4 , ( " Not replacing %s \n " , name ) ) ;
return ( s ) ;
}
updatedlists = True ;
if ( ! s ) {
newentry = True ;
s = ( struct server_record * ) malloc ( sizeof ( * s ) ) ;
if ( ! s ) return ( NULL ) ;
bzero ( ( char * ) s , sizeof ( * s ) ) ;
}
/* update the entry */
StrnCpy ( s - > name , name , sizeof ( s - > name ) - 1 ) ;
StrnCpy ( s - > comment , comment , sizeof ( s - > comment ) - 1 ) ;
s - > servertype = servertype ;
s - > death_time = ttl ? time ( NULL ) + ttl * 3 : 0 ;
strupper ( s - > name ) ;
if ( s - > servertype & SV_TYPE_DOMAIN_ENUM ) strupper ( s - > comment ) ;
if ( ! newentry ) return ( s ) ;
add_server ( s ) ;
if ( newentry ) {
DEBUG ( 3 , ( " Added server entry %s of type %x (%s) \n " ,
name , servertype , comment ) ) ;
} else {
DEBUG ( 3 , ( " Updated server entry %s of type %x (%s) \n " ,
name , servertype , comment ) ) ;
}
return ( s ) ;
}
/****************************************************************************
add the magic samba names , useful for finding samba servers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_my_names ( void )
{
struct in_addr ip ;
ip = * interpret_addr2 ( " 0.0.0.0 " ) ;
add_host_entry ( myname , 0x20 , True , 0 , SELF , ip ) ;
add_host_entry ( myname , 0x0 , True , 0 , SELF , ip ) ;
add_host_entry ( myname , 0x1f , True , 0 , SELF , ip ) ; /* used for chat?? */
add_host_entry ( myname , 0x3 , True , 0 , SELF , ip ) ; /* used for winpopup */
if ( ! domainlist )
add_domain_entry ( lp_workgroup ( ) , bcast_ip ) ;
add_server_entry ( myname ,
ServerType ,
0 , ServerComment , True ) ;
add_host_entry ( " __SAMBA__ " , 0x20 , True , 0 , SELF , ip ) ;
add_host_entry ( " __SAMBA__ " , 0x0 , True , 0 , SELF , ip ) ;
if ( lp_preferred_master ( ) ) {
DEBUG ( 3 , ( " Preferred master startup \n " ) ) ;
needelection = True ;
ElectionCriterion | = ( 1 < < 3 ) ;
}
ElectionCriterion | = ( lp_os_level ( ) < < 24 ) ;
}
/*******************************************************************
write out browse . dat
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void write_browse_list ( void )
{
struct server_record * s ;
pstring fname , fnamenew ;
FILE * f ;
updatecount + + ;
strcpy ( fname , lp_lockdir ( ) ) ;
trim_string ( fname , NULL , " / " ) ;
strcat ( fname , " / " ) ;
strcat ( fname , SERVER_LIST ) ;
strcpy ( fnamenew , fname ) ;
strcat ( fnamenew , " . " ) ;
f = fopen ( fnamenew , " w " ) ;
if ( ! f ) {
DEBUG ( 4 , ( " Can't open %s - %s \n " , fnamenew , strerror ( errno ) ) ) ;
return ;
}
for ( s = serverlist ; s ; s = s - > next ) {
/* don't list domains I don't have a master for */
if ( ( s - > servertype & SV_TYPE_DOMAIN_ENUM ) & & ! s - > comment [ 0 ] ) continue ;
fprintf ( f , " \" %s \" \t %08x \t \" %s \" \n " , s - > name , s - > servertype , s - > comment ) ;
}
fclose ( f ) ;
chmod ( fnamenew , 0644 ) ;
/* unlink(fname); */
rename ( fnamenew , fname ) ;
DEBUG ( 3 , ( " Wrote browse list %s \n " , fname ) ) ;
}
/*******************************************************************
expire old names in the namelist and serverlist
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void expire_names ( void )
{
static time_t lastrun = 0 ;
time_t t = time ( NULL ) ;
struct name_record * n ;
struct name_record * next ;
struct server_record * s ;
struct server_record * nexts ;
if ( ! lastrun ) lastrun = t ;
if ( t < lastrun + 5 ) return ;
lastrun = t ;
/* expire old names */
for ( n = namelist ; n ; n = next ) {
if ( n - > death_time & & n - > death_time < t ) {
DEBUG ( 3 , ( " Removing dead name %s \n " ,
namestr ( & n - > name ) ) ) ;
next = n - > next ;
if ( n - > prev ) n - > prev - > next = n - > next ;
if ( n - > next ) n - > next - > prev = n - > prev ;
if ( namelist = = n ) namelist = n - > next ;
free ( n ) ;
} else {
next = n - > next ;
}
}
/* expire old entries in the serverlist */
for ( s = serverlist ; s ; s = nexts ) {
if ( s - > death_time & & s - > death_time < t ) {
DEBUG ( 3 , ( " Removing dead server %s \n " , s - > name ) ) ;
updatedlists = True ;
nexts = s - > next ;
if ( s - > prev ) s - > prev - > next = s - > next ;
if ( s - > next ) s - > next - > prev = s - > prev ;
if ( serverlist = = s ) serverlist = s - > next ;
free ( s ) ;
} else {
nexts = s - > next ;
}
}
}
/*******************************************************************
delete old names from the namelist
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void housekeeping ( void )
{
time_t t = time ( NULL ) ;
expire_names ( ) ;
/* write out the browse.dat database for smbd to get */
if ( updatedlists ) {
write_browse_list ( ) ;
updatedlists = False ;
}
{
/* occasionally check to see if the master browser is around */
static time_t lastrun = 0 ;
if ( ! lastrun ) lastrun = t ;
if ( t < lastrun + 5 * 60 ) return ;
lastrun = t ;
if ( ! AM_MASTER & & PrimaryGroup [ 0 ] & &
! name_query ( ClientNMB , PrimaryGroup , 0x1d , True , False ,
bcast_ip , NULL , queue_packet ) ) {
DEBUG ( 2 , ( " Forcing election on %s \n " , PrimaryGroup ) ) ;
needelection = True ;
}
}
}
/****************************************************************************
reload the services file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL reload_services ( BOOL test )
{
BOOL ret ;
extern fstring remote_machine ;
strcpy ( remote_machine , " nmbd " ) ;
if ( lp_loaded ( ) )
{
pstring fname ;
strcpy ( fname , lp_configfile ( ) ) ;
if ( file_exist ( fname , NULL ) & & ! strcsequal ( fname , servicesf ) )
{
strcpy ( servicesf , fname ) ;
test = False ;
}
}
if ( test & & ! lp_file_list_changed ( ) )
return ( True ) ;
ret = lp_load ( servicesf , True ) ;
/* perhaps the config filename is now set */
if ( ! test )
reload_services ( True ) ;
return ( ret ) ;
}
/****************************************************************************
load a netbios hosts file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void load_hosts_file ( char * fname )
{
FILE * f = fopen ( fname , " r " ) ;
pstring line ;
if ( ! f ) {
DEBUG ( 2 , ( " Can't open lmhosts file %s \n " , fname ) ) ;
return ;
}
while ( ! feof ( f ) )
{
if ( ! fgets_slash ( line , sizeof ( pstring ) , f ) ) continue ;
if ( * line = = ' # ' ) continue ;
{
BOOL group = False ;
string ip , name , flags , extra ;
char * ptr ;
int count = 0 ;
struct in_addr ipaddr ;
enum name_source source = LMHOSTS ;
* ip = * name = * flags = * extra = 0 ;
ptr = line ;
if ( next_token ( & ptr , ip , NULL ) ) + + count ;
if ( next_token ( & ptr , name , NULL ) ) + + count ;
if ( next_token ( & ptr , flags , NULL ) ) + + count ;
if ( next_token ( & ptr , extra , NULL ) ) + + count ;
if ( count < = 0 ) continue ;
if ( count > 0 & & count < 2 )
{
DEBUG ( 0 , ( " Ill formed hosts line [%s] \n " , line ) ) ;
continue ;
}
if ( strchr ( flags , ' G ' ) | | strchr ( flags , ' S ' ) )
group = True ;
if ( strchr ( flags , ' M ' ) & & ! group ) {
source = SELF ;
strcpy ( myname , name ) ;
}
ipaddr = * interpret_addr2 ( ip ) ;
if ( group ) {
add_domain_entry ( name , ipaddr ) ;
} else {
add_host_entry ( name , 0x20 , True , 0 , source , ipaddr ) ;
}
}
}
fclose ( f ) ;
}
/*******************************************************************
check if 2 IPs are on the same net
we will assume the local netmask , although this could be wrong XXXX
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL same_net ( struct in_addr ip1 , struct in_addr ip2 )
{
unsigned long net1 , net2 , nmask ;
nmask = ntohl ( Netmask . s_addr ) ;
net1 = ntohl ( ip1 . s_addr ) ;
net2 = ntohl ( ip2 . s_addr ) ;
return ( ( net1 & nmask ) = = ( net2 & nmask ) ) ;
}
/****************************************************************************
send an election packet
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void send_election ( char * group , uint32 criterion , int timeup , char * name )
{
pstring outbuf ;
char * p ;
DEBUG ( 2 , ( " Sending election to %s for workgroup %s \n " ,
inet_ntoa ( bcast_ip ) , group ) ) ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
p = outbuf ;
CVAL ( p , 0 ) = 8 ; /* election */
p + + ;
CVAL ( p , 0 ) = ELECTION_VERSION ;
SIVAL ( p , 1 , criterion ) ;
SIVAL ( p , 5 , timeup * 1000 ) ; /* ms - despite the spec */
p + = 13 ;
strcpy ( p , name ) ;
strupper ( p ) ;
p = skip_string ( p , 1 ) ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
name , group , 0 , 0x1e , bcast_ip , myip ) ;
}
/****************************************************************************
send a backup list response
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void send_backup_list ( char * name , int token , struct nmb_name * to ,
struct in_addr ip )
{
pstring outbuf ;
char * p ;
DEBUG ( 2 , ( " Sending backup list to %s for workgroup %s \n " ,
inet_ntoa ( ip ) , PrimaryGroup ) ) ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
p = outbuf ;
CVAL ( p , 0 ) = 10 ; /* backup list response */
p + + ;
CVAL ( p , 0 ) = 1 ; /* count */
SIVAL ( p , 1 , token ) ;
p + = 5 ;
strcpy ( p , name ) ;
strupper ( p ) ;
p = skip_string ( p , 1 ) + 1 ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
myname , to - > name , 0 , to - > name_type , ip , myip ) ;
}
/*******************************************************************
become the master browser
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void become_master ( void )
{
uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX ;
DEBUG ( 2 , ( " Becoming master for %s \n " , PrimaryGroup ) ) ;
ServerType | = SV_TYPE_MASTER_BROWSER ;
ServerType | = SV_TYPE_BACKUP_BROWSER ;
ElectionCriterion | = 0x5 ;
add_host_entry ( PrimaryGroup , 0x1d , True , 0 , SELF , myip ) ;
add_host_entry ( PrimaryGroup , 0x0 , False , 0 , SELF , myip ) ;
add_host_entry ( MSBROWSE , 1 , False , 0 , SELF , myip ) ;
if ( lp_domain_master ( ) ) {
add_host_entry ( myname , 0x1b , True , 0 , SELF , myip ) ;
add_host_entry ( PrimaryGroup , 0x1b , True , 0 , SELF , myip ) ;
add_host_entry ( PrimaryGroup , 0x1c , False , 0 , SELF , myip ) ;
ServerType | = SV_TYPE_DOMAIN_MASTER ;
if ( lp_domain_logons ( ) ) {
ServerType | = SV_TYPE_DOMAIN_CTRL ;
ServerType | = SV_TYPE_DOMAIN_MEMBER ;
domain_type | = SV_TYPE_DOMAIN_CTRL ;
}
}
add_server_entry ( PrimaryGroup , domain_type , 0 , myname , True ) ;
add_server_entry ( myname , ServerType , 0 , ServerComment , True ) ;
announce_request ( PrimaryGroup ) ;
needannounce = True ;
}
/*******************************************************************
unbecome the master browser
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void become_nonmaster ( void )
{
struct name_record * n ;
struct nmb_name nn ;
DEBUG ( 2 , ( " Becoming non-master for %s \n " , PrimaryGroup ) ) ;
ServerType & = ~ SV_TYPE_MASTER_BROWSER ;
ServerType & = ~ SV_TYPE_DOMAIN_CTRL ;
ServerType & = ~ SV_TYPE_DOMAIN_MASTER ;
ElectionCriterion & = ~ 0x4 ;
make_nmb_name ( & nn , PrimaryGroup , 0x1d , scope ) ;
n = find_name ( & nn ) ;
if ( n & & n - > source = = SELF ) remove_name ( n ) ;
make_nmb_name ( & nn , PrimaryGroup , 0x1b , scope ) ;
n = find_name ( & nn ) ;
if ( n & & n - > source = = SELF ) remove_name ( n ) ;
make_nmb_name ( & nn , MSBROWSE , 1 , scope ) ;
n = find_name ( & nn ) ;
if ( n & & n - > source = = SELF ) remove_name ( n ) ;
}
/*******************************************************************
run the election
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void run_election ( void )
{
time_t t = time ( NULL ) ;
static time_t lastime = 0 ;
if ( ! PrimaryGroup [ 0 ] | | ! RunningElection ) return ;
/* send election packets once a second */
if ( lastime & &
t - lastime < = 0 ) return ;
lastime = t ;
send_election ( PrimaryGroup , ElectionCriterion , t - StartupTime , myname ) ;
if ( ElectionCount + + < 4 ) return ;
/* I won! now what :-) */
RunningElection = False ;
DEBUG ( 2 , ( " >>> Won election on %s <<< \n " , PrimaryGroup ) ) ;
become_master ( ) ;
}
/****************************************************************************
construct a host announcement unicast
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void announce_host ( struct domain_record * d , char * my_name , char * comment )
{
time_t t = time ( NULL ) ;
pstring outbuf ;
char * p ;
char * namep ;
char * stypep ;
char * commentp ;
uint32 stype = ServerType ;
if ( needannounce ) {
/* drop back to a max 3 minute announce - this is to prevent a
single lost packet from stuffing things up for too long */
d - > announce_interval = MIN ( d - > announce_interval , 3 * 60 ) ;
d - > lastannounce_time = t - ( d - > announce_interval + 1 ) ;
}
/* announce every minute at first then progress to every 12 mins */
if ( d - > lastannounce_time & &
( t - d - > lastannounce_time ) < d - > announce_interval )
return ;
if ( d - > announce_interval < 12 * 60 ) d - > announce_interval + = 60 ;
d - > lastannounce_time = t ;
DEBUG ( 2 , ( " Sending announcement to %s for workgroup %s \n " ,
inet_ntoa ( d - > bcast_ip ) , d - > name ) ) ;
if ( ! strequal ( PrimaryGroup , d - > name ) | |
! ip_equal ( bcast_ip , d - > bcast_ip ) ) {
stype & = ~ ( SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER ) ;
}
if ( ! * comment ) comment = " NoComment " ;
if ( ! * my_name ) my_name = " NoName " ;
if ( strlen ( comment ) > 43 ) comment [ 43 ] = 0 ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
CVAL ( outbuf , 0 ) = 1 ; /* host announce */
p = outbuf + 1 ;
CVAL ( p , 0 ) = updatecount ;
SIVAL ( p , 1 , d - > announce_interval * 1000 ) ; /* ms - despite the spec */
namep = p + 5 ;
StrnCpy ( p + 5 , my_name , 16 ) ;
strupper ( p + 5 ) ;
CVAL ( p , 21 ) = 2 ; /* major version */
CVAL ( p , 22 ) = 2 ; /* minor version */
stypep = p + 23 ;
SIVAL ( p , 23 , stype ) ;
SSVAL ( p , 27 , 0xaa55 ) ; /* browse signature */
SSVAL ( p , 29 , 1 ) ; /* browse version */
commentp = p + 31 ;
strcpy ( p + 31 , comment ) ;
p + = 31 ;
p = skip_string ( p , 1 ) ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
my_name , d - > name , 0 , 0x1d , d - > bcast_ip , myip ) ;
/* if I'm the master then I also need to do a local master and
domain announcement */
if ( AM_MASTER & &
strequal ( d - > name , PrimaryGroup ) & &
ip_equal ( bcast_ip , d - > bcast_ip ) ) {
/* do master announcements as well */
SIVAL ( stypep , 0 , ServerType ) ;
CVAL ( outbuf , 0 ) = 15 ; /* local master announce */
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
my_name , PrimaryGroup , 0 , 0x1e , d - > bcast_ip , myip ) ;
CVAL ( outbuf , 0 ) = 12 ; /* domain announce */
StrnCpy ( namep , PrimaryGroup , 15 ) ;
strupper ( namep ) ;
StrnCpy ( commentp , myname , 15 ) ;
strupper ( commentp ) ;
SIVAL ( stypep , 0 , ( unsigned ) 0x80000000 ) ;
p = commentp + strlen ( commentp ) + 1 ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
my_name , MSBROWSE , 0 , 1 , d - > bcast_ip , myip ) ;
}
}
/****************************************************************************
send a announce request to the local net
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void announce_request ( char * group )
{
pstring outbuf ;
char * p ;
DEBUG ( 2 , ( " Sending announce request to %s for workgroup %s \n " ,
inet_ntoa ( bcast_ip ) , group ) ) ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
p = outbuf ;
CVAL ( p , 0 ) = 2 ; /* announce request */
p + + ;
CVAL ( p , 0 ) = 0 ; /* flags?? */
p + + ;
StrnCpy ( p , myname , 16 ) ;
strupper ( p ) ;
p = skip_string ( p , 1 ) ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
myname , group , 0 , 0 , bcast_ip , myip ) ;
}
/****************************************************************************
announce myself as a master to the PDC
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void announce_master ( char * group )
{
static time_t last = 0 ;
time_t t = time ( NULL ) ;
pstring outbuf ;
char * p ;
struct in_addr ip , pdc_ip ;
fstring pdcname ;
* pdcname = 0 ;
if ( strequal ( domain_controller ( ) , myname ) ) return ;
if ( ! AM_MASTER | | ( last & & ( t - last < 10 * 60 ) ) ) return ;
last = t ;
ip = * interpret_addr2 ( domain_controller ( ) ) ;
if ( zero_ip ( ip ) ) ip = bcast_ip ;
if ( ! name_query ( ClientNMB , PrimaryGroup ,
0x1b , False , False , ip , & pdc_ip , queue_packet ) ) {
DEBUG ( 2 , ( " Failed to find PDC at %s \n " , domain_controller ( ) ) ) ;
return ;
}
name_status ( ClientNMB , PrimaryGroup , 0x1b , False ,
pdc_ip , NULL , pdcname , queue_packet ) ;
if ( ! pdcname [ 0 ] ) {
DEBUG ( 3 , ( " Can't find netbios name of PDC at %s \n " , inet_ntoa ( pdc_ip ) ) ) ;
} else {
sync_browse_lists ( pdcname , 0x20 , myname , PrimaryGroup , pdc_ip ) ;
}
DEBUG ( 2 , ( " Sending master announce to %s for workgroup %s \n " ,
inet_ntoa ( pdc_ip ) , group ) ) ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
p = outbuf ;
CVAL ( p , 0 ) = 13 ; /* announce request */
p + + ;
StrnCpy ( p , myname , 16 ) ;
strupper ( p ) ;
p = skip_string ( p , 1 ) ;
send_mailslot_reply ( BROWSE_MAILSLOT , ClientDGRAM , outbuf , PTR_DIFF ( p , outbuf ) ,
myname , PrimaryGroup , 0x1b , 0 , pdc_ip , myip ) ;
}
/*******************************************************************
am I listening on a name . Should check name_type as well
This is primarily used to prevent us gathering server lists from
other workgroups we aren ' t a part of
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL listening ( struct nmb_name * n )
{
if ( ! strequal ( n - > scope , scope ) ) return ( False ) ;
if ( strequal ( n - > name , myname ) | |
strequal ( n - > name , PrimaryGroup ) | |
strequal ( n - > name , MSBROWSE ) )
return ( True ) ;
return ( False ) ;
}
/*******************************************************************
process a domain announcement frame
Announce frames come in 3 types . Servers send host announcements
( command = 1 ) to let the master browswer know they are
available . Master browsers send local master announcements
( command = 15 ) to let other masters and backups that they are the
master . They also send domain announcements ( command = 12 ) to register
the domain
The comment field of domain announcements contains the master
browser name . The servertype is used by NetServerEnum to select
resources . We just have to pass it to smbd ( via browser . dat ) and let
the client choose using bit masks .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_announce ( struct packet_struct * p , int command , char * buf )
{
struct dgram_packet * dgram = & p - > packet . dgram ;
int update_count = CVAL ( buf , 0 ) ;
int ttl = IVAL ( buf , 1 ) / 1000 ;
char * name = buf + 5 ;
int osmajor = CVAL ( buf , 21 ) ;
int osminor = CVAL ( buf , 22 ) ;
uint32 servertype = IVAL ( buf , 23 ) ;
char * comment = buf + 31 ;
name [ 15 ] = 0 ;
comment [ 43 ] = 0 ;
DEBUG ( 3 , ( " Announce(%d) %s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s \n " ,
command , name , update_count , ttl , osmajor , osminor ,
servertype , comment ) ) ;
if ( strequal ( dgram - > source_name . name , myname ) ) return ;
if ( ! listening ( & dgram - > dest_name ) ) return ;
ttl = GET_TTL ( ttl ) ;
/* add them to our browse list */
add_server_entry ( name , servertype , ttl , comment , True ) ;
}
/*******************************************************************
process a master announcement frame
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_master_announce ( struct packet_struct * p , char * buf )
{
struct dgram_packet * dgram = & p - > packet . dgram ;
char * name = buf ;
name [ 15 ] = 0 ;
DEBUG ( 3 , ( " Master Announce from %s (%s) \n " , name , inet_ntoa ( p - > ip ) ) ) ;
if ( strequal ( dgram - > source_name . name , myname ) ) return ;
if ( ! AM_MASTER | | ! listening ( & dgram - > dest_name ) ) return ;
/* merge browse lists with them */
if ( lp_domain_master ( ) )
sync_browse_lists ( name , 0x20 , myname , PrimaryGroup , p - > ip ) ;
}
/*******************************************************************
process a backup list request
A client send a backup list request to ask for a list of servers on
the net that maintain server lists for a domain . A server is then
chosen from this list to send NetServerEnum commands to to list
available servers .
Currently samba only sends back one name in the backup list , its
wn . For larger nets we ' ll have to add backups and send " become
backup " requests occasionally.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_backup_list ( struct packet_struct * p , char * buf )
{
struct dgram_packet * dgram = & p - > packet . dgram ;
int count = CVAL ( buf , 0 ) ;
int token = IVAL ( buf , 1 ) ;
DEBUG ( 3 , ( " Backup request to %s token=%d \n " ,
namestr ( & dgram - > dest_name ) ,
token ) ) ;
if ( strequal ( dgram - > source_name . name , myname ) ) return ;
if ( count < = 0 ) return ;
if ( ! AM_MASTER | |
! strequal ( PrimaryGroup , dgram - > dest_name . name ) )
return ;
if ( ! listening ( & dgram - > dest_name ) ) return ;
send_backup_list ( myname , token ,
& dgram - > source_name ,
p - > ip ) ;
}
/*******************************************************************
work out if I win an election
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL win_election ( int version , uint32 criterion , int timeup , char * name )
{
time_t t = time ( NULL ) ;
uint32 mycriterion ;
if ( version > ELECTION_VERSION ) return ( False ) ;
if ( version < ELECTION_VERSION ) return ( True ) ;
mycriterion = ElectionCriterion ;
if ( criterion > mycriterion ) return ( False ) ;
if ( criterion < mycriterion ) return ( True ) ;
if ( timeup > ( t - StartupTime ) ) return ( False ) ;
if ( timeup < ( t - StartupTime ) ) return ( True ) ;
if ( strcasecmp ( myname , name ) > 0 ) return ( False ) ;
return ( True ) ;
}
/*******************************************************************
process a election packet
An election dynamically decides who will be the master .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_election ( 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 * name = buf + 13 ;
name [ 15 ] = 0 ;
DEBUG ( 3 , ( " Election request from %s vers=%d criterion=%08x timeup=%d \n " ,
name , version , criterion , timeup ) ) ;
if ( strequal ( dgram - > source_name . name , myname ) ) return ;
if ( ! listening ( & dgram - > dest_name ) ) return ;
if ( win_election ( version , criterion , timeup , name ) ) {
if ( ! RunningElection ) {
needelection = True ;
ElectionCount = 0 ;
}
} else {
needelection = False ;
if ( RunningElection ) {
RunningElection = False ;
DEBUG ( 3 , ( " >>> Lost election on %s <<< \n " , PrimaryGroup ) ) ;
/* if we are the master then remove our masterly names */
if ( AM_MASTER )
become_nonmaster ( ) ;
}
}
}
/*******************************************************************
process a announcement request
clients send these when they want everyone to send an announcement
immediately . This can cause quite a storm of packets !
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_announce_request ( struct packet_struct * p , char * buf )
{
struct dgram_packet * dgram = & p - > packet . dgram ;
int flags = CVAL ( buf , 0 ) ;
char * name = buf + 1 ;
name [ 15 ] = 0 ;
DEBUG ( 3 , ( " Announce request from %s flags=0x%X \n " , name , flags ) ) ;
if ( strequal ( dgram - > source_name . name , myname ) ) return ;
needannounce = True ;
}
/****************************************************************************
process a browse frame
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_browse_packet ( struct packet_struct * p , char * buf , int len )
{
int command = CVAL ( buf , 0 ) ;
switch ( command )
{
case 1 : /* host announce */
case 12 : /* domain announce */
case 15 : /* local master announce */
process_announce ( p , command , buf + 1 ) ;
break ;
case 2 : /* announce request */
process_announce_request ( p , buf + 1 ) ;
break ;
case 8 : /* election */
process_election ( p , buf + 1 ) ;
break ;
case 9 : /* get backup list */
process_backup_list ( p , buf + 1 ) ;
break ;
case 13 : /* master announcement */
process_master_announce ( p , buf + 1 ) ;
break ;
}
}
/****************************************************************************
process a domain logon packet
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_logon_packet ( struct packet_struct * p , char * buf , int len )
{
char * logname , * q ;
pstring outbuf ;
struct dgram_packet * dgram = & p - > packet . dgram ;
int code ;
if ( ! lp_domain_logons ( ) ) {
DEBUG ( 3 , ( " No domain logons \n " ) ) ;
return ;
}
if ( ! listening ( & dgram - > dest_name ) ) {
DEBUG ( 4 , ( " Not listening to that domain \n " ) ) ;
return ;
}
q = outbuf ;
bzero ( outbuf , sizeof ( outbuf ) ) ;
code = SVAL ( buf , 0 ) ;
switch ( code ) {
case 0 :
{
char * machine = buf + 2 ;
char * user = skip_string ( machine , 1 ) ;
logname = skip_string ( user , 1 ) ;
SSVAL ( q , 0 , 6 ) ;
q + = 2 ;
strcpy ( q , " \\ \\ " ) ;
q + = 2 ;
StrnCpy ( q , myname , 16 ) ;
strupper ( q ) ;
q = skip_string ( q , 1 ) ;
SSVAL ( q , 0 , 0xFFFF ) ;
q + = 2 ;
DEBUG ( 3 , ( " Domain login request from %s(%s) user=%s \n " ,
machine , inet_ntoa ( p - > ip ) , user ) ) ;
}
break ;
case 7 :
{
char * machine = buf + 2 ;
logname = skip_string ( machine , 1 ) ;
SSVAL ( q , 0 , 0xc ) ;
q + = 2 ;
StrnCpy ( q , domain_controller ( ) , 16 ) ;
strupper ( q ) ;
q = skip_string ( q , 1 ) ;
q + = PutUniCode ( q , domain_controller ( ) ) ;
q + = PutUniCode ( q , dgram - > dest_name . name ) ;
SSVAL ( q , 0 , 0xFFFF ) ;
q + = 2 ;
DEBUG ( 3 , ( " GETDC request from %s(%s) \n " ,
machine , inet_ntoa ( p - > ip ) ) ) ;
}
break ;
default :
DEBUG ( 3 , ( " Unknown domain request %d \n " , code ) ) ;
return ;
}
send_mailslot_reply ( logname , ClientDGRAM , outbuf , PTR_DIFF ( q , outbuf ) ,
myname , & dgram - > source_name . name [ 0 ] , 0 , 0 , p - > ip , myip ) ;
}
/****************************************************************************
process udp 138 datagrams
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_dgram ( struct packet_struct * p )
{
char * buf ;
char * buf2 ;
int len ;
struct dgram_packet * dgram = & p - > packet . dgram ;
if ( dgram - > header . msg_type ! = 0x10 & &
dgram - > header . msg_type ! = 0x11 & &
dgram - > header . msg_type ! = 0x12 ) {
/* don't process error packets etc yet */
return ;
}
buf = & dgram - > data [ 0 ] ;
buf - = 4 ; /* XXXX for the pseudo tcp length -
someday I need to get rid of this */
if ( CVAL ( buf , smb_com ) ! = SMBtrans ) return ;
len = SVAL ( buf , smb_vwv11 ) ;
buf2 = smb_base ( buf ) + SVAL ( buf , smb_vwv12 ) ;
DEBUG ( 3 , ( " datagram from %s to %s for %s of type %d len=%d \n " ,
namestr ( & dgram - > source_name ) , namestr ( & dgram - > dest_name ) ,
smb_buf ( buf ) , CVAL ( buf2 , 0 ) , len ) ) ;
if ( len < = 0 ) return ;
if ( strequal ( smb_buf ( buf ) , " \\ MAILSLOT \\ BROWSE " ) ) {
process_browse_packet ( p , buf2 , len ) ;
} else if ( strequal ( smb_buf ( buf ) , " \\ MAILSLOT \\ NET \\ NETLOGON " ) ) {
process_logon_packet ( p , buf2 , len ) ;
}
}
/*******************************************************************
find a workgroup using the specified broadcast
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL find_workgroup ( char * name , struct in_addr ip )
{
fstring name1 ;
BOOL ret ;
struct in_addr ipout ;
strcpy ( name1 , MSBROWSE ) ;
ret = name_query ( ClientNMB , name1 , 0x1 , True , False , ip , & ipout , queue_packet ) ;
if ( ! ret ) return ( False ) ;
name_status ( ClientNMB , name1 , 0x1 , False , ipout , name , NULL , queue_packet ) ;
if ( name [ 0 ] ! = ' * ' ) {
DEBUG ( 2 , ( " Found workgroup %s on broadcast %s \n " , name , inet_ntoa ( ip ) ) ) ;
} else {
DEBUG ( 3 , ( " Failed to find workgroup %s on broadcast %s \n " , name , inet_ntoa ( ip ) ) ) ;
}
return ( name [ 0 ] ! = ' * ' ) ;
}
/****************************************************************************
a hook for announce handling - called every minute
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void do_announcements ( void )
{
struct domain_record * d ;
for ( d = domainlist ; d ; d = d - > next ) {
/* if the ip address is 0 then set to the broadcast */
if ( zero_ip ( d - > bcast_ip ) ) d - > bcast_ip = bcast_ip ;
/* if the workgroup is '*' then find a workgroup to be part of */
if ( d - > name [ 0 ] = = ' * ' ) {
if ( ! find_workgroup ( d - > name , d - > bcast_ip ) ) continue ;
add_host_entry ( d - > name , 0x1e , False , 0 , SELF ,
* interpret_addr2 ( " 255.255.255.255 " ) ) ;
if ( ! PrimaryGroup [ 0 ] & & ip_equal ( bcast_ip , d - > bcast_ip ) ) {
strcpy ( PrimaryGroup , d - > name ) ;
strupper ( PrimaryGroup ) ;
}
}
announce_host ( d , myname , ServerComment ) ;
}
/* if I have a domain controller then announce to it */
if ( AM_MASTER )
announce_master ( PrimaryGroup ) ;
needannounce = False ;
}
/*******************************************************************
check if someone still owns a name
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL confirm_name ( struct name_record * n )
{
struct in_addr ipout ;
BOOL ret = name_query ( ClientNMB , n - > name . name ,
n - > name . name_type , False ,
False , n - > ip , & ipout , queue_packet ) ;
return ( ret & & ip_equal ( ipout , n - > ip ) ) ;
}
/****************************************************************************
reply to a name release
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void reply_name_release ( struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
struct packet_struct p2 ;
struct nmb_packet * nmb2 ;
struct res_rec answer_rec ;
struct in_addr ip ;
int rcode = 0 ;
int nb_flags = nmb - > additional - > rdata [ 0 ] ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
putip ( ( char * ) & ip , & nmb - > additional - > rdata [ 2 ] ) ;
{
struct name_record * n = find_name ( & nmb - > question . question_name ) ;
if ( n & & n - > unique & & n - > source = = REGISTER & &
ip_equal ( ip , n - > ip ) ) {
remove_name ( n ) ; n = NULL ;
}
/* XXXX under what conditions should we reject the removal?? */
}
DEBUG ( 3 , ( " Name release on name %s rcode=%d \n " ,
namestr ( & nmb - > question . question_name ) , rcode ) ) ;
if ( bcast ) return ;
/* Send a NAME RELEASE RESPONSE */
p2 = * p ;
nmb2 = & p2 . packet . nmb ;
nmb2 - > header . response = True ;
nmb2 - > header . nm_flags . bcast = False ;
nmb2 - > header . nm_flags . recursion_available = CanRecurse ;
nmb2 - > header . nm_flags . trunc = False ;
nmb2 - > header . nm_flags . authoritative = True ;
nmb2 - > header . qdcount = 0 ;
nmb2 - > header . ancount = 1 ;
nmb2 - > header . nscount = 0 ;
nmb2 - > header . arcount = 0 ;
nmb2 - > header . rcode = rcode ;
nmb2 - > answers = & answer_rec ;
bzero ( ( char * ) nmb2 - > answers , sizeof ( * nmb2 - > answers ) ) ;
nmb2 - > answers - > rr_name = nmb - > question . question_name ;
nmb2 - > answers - > rr_type = nmb - > question . question_type ;
nmb2 - > answers - > rr_class = nmb - > question . question_class ;
nmb2 - > answers - > ttl = 0 ;
nmb2 - > answers - > rdlength = 6 ;
nmb2 - > answers - > rdata [ 0 ] = nb_flags ;
putip ( & nmb2 - > answers - > rdata [ 2 ] , ( char * ) & ip ) ;
send_packet ( & p2 ) ;
}
/****************************************************************************
reply to a reg request
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void reply_name_reg ( struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char * qname = nmb - > question . question_name . name ;
BOOL wildcard = ( qname [ 0 ] = = ' * ' ) ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
int ttl = GET_TTL ( nmb - > additional - > ttl ) ;
int name_type = nmb - > question . question_name . name_type ;
int nb_flags = nmb - > additional - > rdata [ 0 ] ;
struct packet_struct p2 ;
struct nmb_packet * nmb2 ;
struct res_rec answer_rec ;
struct in_addr ip ;
BOOL group = ( nb_flags & 0x80 ) ? True : False ;
int rcode = 0 ;
if ( wildcard ) return ;
putip ( ( char * ) & ip , & nmb - > additional - > rdata [ 2 ] ) ;
if ( group ) {
/* apparently we should return 255.255.255.255 for group queries (email from MS) */
ip = * interpret_addr2 ( " 255.255.255.255 " ) ;
}
{
struct name_record * n = find_name ( & nmb - > question . question_name ) ;
if ( n ) {
if ( ! group & & ! ip_equal ( ip , n - > ip ) ) {
/* check if the previous owner still wants it,
if so reject the registration , otherwise change the owner
and refresh */
if ( n - > source ! = REGISTER | | confirm_name ( n ) ) {
rcode = 6 ;
} else {
n - > ip = ip ;
n - > death_time = ttl ? p - > timestamp + ttl * 3 : 0 ;
DEBUG ( 3 , ( " %s changed owner to %s \n " ,
namestr ( & n - > name ) , inet_ntoa ( n - > ip ) ) ) ;
}
} else {
/* refresh the name */
if ( n - > source ! = SELF )
n - > death_time = ttl ? p - > timestamp + ttl * 3 : 0 ;
}
} else {
/* add the name to our database */
n = add_host_entry ( qname , name_type , ! group , ttl , REGISTER , ip ) ;
}
}
if ( bcast ) return ;
DEBUG ( 3 , ( " Name registration for name %s at %s rcode=%d \n " ,
namestr ( & nmb - > question . question_name ) ,
inet_ntoa ( ip ) , rcode ) ) ;
/* Send a NAME REGISTRATION RESPONSE */
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = * p ;
nmb2 = & p2 . packet . nmb ;
nmb2 - > header . opcode = 5 ;
nmb2 - > header . response = True ;
nmb2 - > header . nm_flags . bcast = False ;
nmb2 - > header . nm_flags . recursion_available = CanRecurse ;
nmb2 - > header . nm_flags . trunc = False ;
nmb2 - > header . nm_flags . authoritative = True ;
nmb2 - > header . qdcount = 0 ;
nmb2 - > header . ancount = 1 ;
nmb2 - > header . nscount = 0 ;
nmb2 - > header . arcount = 0 ;
nmb2 - > header . rcode = rcode ;
nmb2 - > answers = & answer_rec ;
bzero ( ( char * ) nmb2 - > answers , sizeof ( * nmb2 - > answers ) ) ;
nmb2 - > answers - > rr_name = nmb - > question . question_name ;
nmb2 - > answers - > rr_type = nmb - > question . question_type ;
nmb2 - > answers - > rr_class = nmb - > question . question_class ;
nmb2 - > answers - > ttl = ttl ;
nmb2 - > answers - > rdlength = 6 ;
nmb2 - > answers - > rdata [ 0 ] = nb_flags ;
putip ( & nmb2 - > answers - > rdata [ 2 ] , ( char * ) & ip ) ;
send_packet ( & p2 ) ;
}
/****************************************************************************
reply to a name status query
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void reply_name_status ( struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char * qname = nmb - > question . question_name . name ;
BOOL wildcard = ( qname [ 0 ] = = ' * ' ) ;
struct packet_struct p2 ;
struct nmb_packet * nmb2 ;
struct res_rec answer_rec ;
char * buf ;
int count ;
int rcode = 0 ;
struct name_record * n = find_name ( & nmb - > question . question_name ) ;
DEBUG ( 3 , ( " Name status for name %s \n " ,
namestr ( & nmb - > question . question_name ) ) ) ;
if ( ! wildcard & & ( ! n | | n - > source ! = SELF ) )
return ;
/* Send a POSITIVE NAME STATUS RESPONSE */
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = * p ;
nmb2 = & p2 . packet . nmb ;
nmb2 - > header . response = True ;
nmb2 - > header . nm_flags . bcast = False ;
nmb2 - > header . nm_flags . recursion_available = CanRecurse ;
nmb2 - > header . nm_flags . trunc = False ;
nmb2 - > header . nm_flags . authoritative = True ; /* WfWg ignores
non - authoritative answers */
nmb2 - > header . qdcount = 0 ;
nmb2 - > header . ancount = 1 ;
nmb2 - > header . nscount = 0 ;
nmb2 - > header . arcount = 0 ;
nmb2 - > header . rcode = rcode ;
nmb2 - > answers = & answer_rec ;
bzero ( ( char * ) nmb2 - > answers , sizeof ( * nmb2 - > answers ) ) ;
nmb2 - > answers - > rr_name = nmb - > question . question_name ;
nmb2 - > answers - > rr_type = nmb - > question . question_type ;
nmb2 - > answers - > rr_class = nmb - > question . question_class ;
nmb2 - > answers - > ttl = 0 ;
for ( count = 0 , n = namelist ; n ; n = n - > next ) {
if ( n - > source ! = SELF ) continue ;
count + + ;
}
count = MIN ( count , 400 / 18 ) ; /* XXXX hack, we should calculate exactly
how many will fit */
buf = & nmb2 - > answers - > rdata [ 0 ] ;
SCVAL ( buf , 0 , count ) ;
buf + = 1 ;
for ( n = namelist ; n ; n = n - > next )
{
if ( n - > source ! = SELF ) continue ;
bzero ( buf , 18 ) ;
strcpy ( buf , n - > name . name ) ;
strupper ( buf ) ;
buf [ 15 ] = n - > name . name_type ;
buf + = 16 ;
buf [ 0 ] = 0x4 ; /* active */
if ( ! n - > unique ) buf [ 0 ] | = 0x80 ; /* group */
buf + = 2 ;
count - - ;
}
/* XXXXXXX we should fill in more fields of the statistics structure */
bzero ( buf , 64 ) ;
{
extern int num_good_sends , num_good_receives ;
SIVAL ( buf , 20 , num_good_sends ) ;
SIVAL ( buf , 24 , num_good_receives ) ;
}
SIVAL ( buf , 46 , 0xFFB8E5 ) ; /* undocumented - used by NT */
buf + = 64 ;
nmb2 - > answers - > rdlength = PTR_DIFF ( buf , & nmb2 - > answers - > rdata [ 0 ] ) ;
send_packet ( & p2 ) ;
}
/****************************************************************************
reply to a name query
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void reply_name_query ( struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char * qname = nmb - > question . question_name . name ;
BOOL wildcard = ( qname [ 0 ] = = ' * ' ) ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
struct in_addr retip ;
int name_type = nmb - > question . question_name . name_type ;
struct packet_struct p2 ;
struct nmb_packet * nmb2 ;
struct res_rec answer_rec ;
int ttl = 0 ;
int rcode = 0 ;
BOOL unique = True ;
DEBUG ( 3 , ( " Name query for %s from %s (bcast=%s) - " ,
namestr ( & nmb - > question . question_name ) ,
inet_ntoa ( p - > ip ) ,
BOOLSTR ( bcast ) ) ) ;
if ( wildcard )
retip = myip ;
if ( ! wildcard ) {
struct name_record * n = find_name ( & nmb - > question . question_name ) ;
if ( ! n ) {
struct in_addr ip ;
unsigned long a ;
/* only do DNS lookups if the query is for type 0x20 or type 0x0 */
if ( name_type ! = 0x20 & & name_type ! = 0 ) {
DEBUG ( 3 , ( " not found \n " ) ) ;
return ;
}
/* look it up with DNS */
a = interpret_addr ( qname ) ;
putip ( ( char * ) & ip , ( char * ) & a ) ;
if ( ! a ) {
/* no luck with DNS. We could possibly recurse here XXXX */
/* if this isn't a bcast then we should send a negative reply XXXX */
DEBUG ( 3 , ( " no recursion \n " ) ) ;
add_host_entry ( qname , name_type , True , 60 * 60 , DNSFAIL , ip ) ;
return ;
}
/* add it to our cache of names. give it 2 hours in the cache */
n = add_host_entry ( qname , name_type , True , 2 * 60 * 60 , DNS , ip ) ;
/* failed to add it? yikes! */
if ( ! n ) return ;
}
/* don't respond to bcast queries for group names unless we own them */
if ( bcast & & ! n - > unique & & ! n - > source = = SELF ) {
DEBUG ( 3 , ( " no bcast replies \n " ) ) ;
return ;
}
1996-05-31 19:13:29 +04:00
if ( ! lp_proxy_name_resolution ( ) & & n - > source ! = SELF ) {
DEBUG ( 3 , ( " no proxy resolution \n " ) ) ;
return ;
}
1996-05-04 11:50:46 +04:00
/* don't respond to bcast queries for addresses on the same net as the
machine doing the querying unless its our IP */
if ( bcast & &
n - > source ! = SELF & &
same_net ( n - > ip , p - > ip ) ) {
DEBUG ( 3 , ( " same net \n " ) ) ;
return ;
}
/* is our entry already dead? */
if ( n - > death_time ) {
if ( n - > death_time < p - > timestamp ) return ;
ttl = n - > death_time - p - > timestamp ;
}
retip = n - > ip ;
unique = n - > unique ;
/* it may have been an earlier failure */
if ( n - > source = = DNSFAIL ) {
DEBUG ( 3 , ( " DNSFAIL \n " ) ) ;
return ;
}
}
/* if the IP is 0 then substitute my IP - we should see which one is on the
right interface for the caller to do this right XXX */
if ( zero_ip ( retip ) ) retip = myip ;
DEBUG ( 3 , ( " OK %s rcode=%d \n " , inet_ntoa ( retip ) , rcode ) ) ;
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = * p ;
nmb2 = & p2 . packet . nmb ;
nmb2 - > header . response = True ;
nmb2 - > header . nm_flags . bcast = False ;
nmb2 - > header . nm_flags . recursion_available = CanRecurse ;
nmb2 - > header . nm_flags . trunc = False ;
nmb2 - > header . nm_flags . authoritative = True ; /* WfWg ignores
non - authoritative answers */
nmb2 - > header . qdcount = 0 ;
nmb2 - > header . ancount = 1 ;
nmb2 - > header . nscount = 0 ;
nmb2 - > header . arcount = 0 ;
nmb2 - > header . rcode = rcode ;
nmb2 - > answers = & answer_rec ;
bzero ( ( char * ) nmb2 - > answers , sizeof ( * nmb2 - > answers ) ) ;
nmb2 - > answers - > rr_name = nmb - > question . question_name ;
nmb2 - > answers - > rr_type = nmb - > question . question_type ;
nmb2 - > answers - > rr_class = nmb - > question . question_class ;
nmb2 - > answers - > ttl = ttl ;
nmb2 - > answers - > rdlength = 6 ;
nmb2 - > answers - > rdata [ 0 ] = unique ? 0 : 0x80 ;
nmb2 - > answers - > rdata [ 1 ] = 0 ;
putip ( & nmb2 - > answers - > rdata [ 2 ] , ( char * ) & retip ) ;
send_packet ( & p2 ) ;
}
/* the global packet linked-list. incoming entries are added to the
end of this list . it is supposed to remain fairly short so we
won ' t bother with an end pointer . */
static struct packet_struct * packet_queue = NULL ;
/*******************************************************************
queue a packet into the packet queue
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void queue_packet ( struct packet_struct * packet )
{
struct packet_struct * p ;
if ( ! packet_queue ) {
packet - > prev = NULL ;
packet - > next = NULL ;
packet_queue = packet ;
return ;
}
/* find the bottom */
for ( p = packet_queue ; p - > next ; p = p - > next ) ;
p - > next = packet ;
packet - > next = NULL ;
packet - > prev = p ;
}
/****************************************************************************
process a nmb packet
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void process_nmb ( struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
/* if this is a response then ignore it */
if ( nmb - > header . response ) return ;
switch ( nmb - > header . opcode )
{
case 5 :
case 8 :
case 9 :
if ( nmb - > header . qdcount > 0 & &
nmb - > header . arcount > 0 ) {
reply_name_reg ( p ) ;
return ;
}
break ;
case 0 :
if ( nmb - > header . qdcount > 0 )
{
switch ( nmb - > question . question_type )
{
case 0x20 :
reply_name_query ( p ) ;
break ;
case 0x21 :
reply_name_status ( p ) ;
break ;
}
return ;
}
break ;
case 6 :
if ( nmb - > header . qdcount > 0 & &
nmb - > header . arcount > 0 ) {
reply_name_release ( p ) ;
return ;
}
break ;
}
}
/*******************************************************************
run elements off the packet queue till its empty
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void run_packet_queue ( void )
{
struct packet_struct * p ;
while ( ( p = packet_queue ) ) {
switch ( p - > packet_type )
{
case NMB_PACKET :
process_nmb ( p ) ;
break ;
case DGRAM_PACKET :
process_dgram ( p ) ;
break ;
}
packet_queue = packet_queue - > next ;
if ( packet_queue ) packet_queue - > prev = NULL ;
free_packet ( p ) ;
}
}
/****************************************************************************
The main select loop , listen for packets and respond
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process ( void )
{
while ( True )
{
fd_set fds ;
int selrtn ;
struct timeval timeout ;
if ( needelection & & PrimaryGroup [ 0 ] & & ! RunningElection ) {
DEBUG ( 3 , ( " >>> Starting election on %s <<< \n " , PrimaryGroup ) ) ;
ElectionCount = 0 ;
RunningElection = True ;
needelection = False ;
}
FD_ZERO ( & fds ) ;
FD_SET ( ClientNMB , & fds ) ;
FD_SET ( ClientDGRAM , & fds ) ;
/* during elections we need to send election packets at one
second intervals */
timeout . tv_sec = RunningElection ? 1 : NMBD_SELECT_LOOP ;
timeout . tv_usec = 0 ;
selrtn = sys_select ( & fds , & timeout ) ;
if ( FD_ISSET ( ClientNMB , & fds ) ) {
struct packet_struct * packet = read_packet ( ClientNMB , NMB_PACKET ) ;
if ( packet ) queue_packet ( packet ) ;
}
if ( FD_ISSET ( ClientDGRAM , & fds ) ) {
struct packet_struct * packet = read_packet ( ClientDGRAM , DGRAM_PACKET ) ;
if ( packet ) queue_packet ( packet ) ;
}
if ( RunningElection )
run_election ( ) ;
run_packet_queue ( ) ;
do_announcements ( ) ;
housekeeping ( ) ;
}
}
/****************************************************************************
open the socket communication
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL open_sockets ( BOOL isdaemon , int port )
{
struct hostent * hp ;
/* get host info */
if ( ( hp = Get_Hostbyname ( myhostname ) ) = = 0 )
{
DEBUG ( 0 , ( " Get_Hostbyname: Unknown host. %s \n " , myhostname ) ) ;
return False ;
}
if ( isdaemon )
ClientNMB = open_socket_in ( SOCK_DGRAM , port , 0 ) ;
else
ClientNMB = 0 ;
ClientDGRAM = open_socket_in ( SOCK_DGRAM , DGRAM_PORT , 3 ) ;
if ( ClientNMB = = - 1 )
return ( False ) ;
signal ( SIGPIPE , SIGNAL_CAST sig_pipe ) ;
set_socket_options ( ClientNMB , " SO_BROADCAST " ) ;
set_socket_options ( ClientDGRAM , " SO_BROADCAST " ) ;
DEBUG ( 3 , ( " Socket opened. \n " ) ) ;
return True ;
}
/*******************************************************************
check that a IP , bcast and netmask and consistent . Must be a 1 s
broadcast
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL ip_consistent ( struct in_addr ip , struct in_addr bcast ,
struct in_addr nmask )
{
unsigned long a_ip , a_bcast , a_nmask ;
a_ip = ntohl ( ip . s_addr ) ;
a_bcast = ntohl ( bcast . s_addr ) ;
a_nmask = ntohl ( nmask . s_addr ) ;
/* check the netmask is sane */
if ( ( ( a_nmask > > 24 ) & 0xFF ) ! = 0xFF ) {
DEBUG ( 0 , ( " Insane netmask %s \n " , inet_ntoa ( nmask ) ) ) ;
return ( False ) ;
}
/* check the IP and bcast are on the same net */
if ( ( a_ip & a_nmask ) ! = ( a_bcast & a_nmask ) ) {
DEBUG ( 0 , ( " IP and broadcast are on different nets! \n " ) ) ;
return ( False ) ;
}
/* check the IP and bcast are on the same net */
if ( ( a_bcast | a_nmask ) ! = 0xFFFFFFFF ) {
DEBUG ( 0 , ( " Not a ones based broadcast %s \n " , inet_ntoa ( bcast ) ) ) ;
return ( False ) ;
}
return ( True ) ;
}
/****************************************************************************
initialise connect , service and file structs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL init_structs ( void )
{
if ( ! get_myname ( myhostname , got_myip ? NULL : & myip ) )
return ( False ) ;
/* Read the broadcast address from the interface */
{
struct in_addr ip0 , ip1 , ip2 ;
ip0 = myip ;
if ( ! ( got_bcast & & got_nmask ) )
{
get_broadcast ( & ip0 , & ip1 , & ip2 ) ;
if ( ! got_myip )
myip = ip0 ;
if ( ! got_bcast )
bcast_ip = ip1 ;
if ( ! got_nmask )
Netmask = ip2 ;
}
DEBUG ( 1 , ( " Using IP %s " , inet_ntoa ( myip ) ) ) ;
DEBUG ( 1 , ( " broadcast %s " , inet_ntoa ( bcast_ip ) ) ) ;
DEBUG ( 1 , ( " netmask %s \n " , inet_ntoa ( Netmask ) ) ) ;
if ( ! ip_consistent ( myip , bcast_ip , Netmask ) ) {
DEBUG ( 0 , ( " WARNING: The IP address, broadcast and Netmask are not consistent \n " ) ) ;
DEBUG ( 0 , ( " You are likely to experience problems with this setup! \n " ) ) ;
}
}
if ( ! * myname ) {
char * p ;
strcpy ( myname , myhostname ) ;
p = strchr ( myname , ' . ' ) ;
if ( p ) * p = 0 ;
}
{
extern fstring local_machine ;
strcpy ( local_machine , myname ) ;
strupper ( local_machine ) ;
}
return True ;
}
/****************************************************************************
usage on the program
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void usage ( char * pname )
{
DEBUG ( 0 , ( " Incorrect program usage - is the command line correct? \n " ) ) ;
printf ( " Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename] \n " , pname ) ;
printf ( " Version %s \n " , VERSION ) ;
printf ( " \t -D become a daemon \n " ) ;
printf ( " \t -p port listen on the specified port \n " ) ;
printf ( " \t -d debuglevel set the debuglevel \n " ) ;
printf ( " \t -l log basename. Basename for log/debug files \n " ) ;
printf ( " \t -n netbiosname. the netbios name to advertise for this host \n " ) ;
printf ( " \t -B broadcast address the address to use for broadcasts \n " ) ;
printf ( " \t -N netmask the netmask to use for subnet determination \n " ) ;
printf ( " \t -H hosts file load a netbios hosts file \n " ) ;
printf ( " \t -I ip-address override the IP address \n " ) ;
printf ( " \t -G group name add a group name to be part of \n " ) ;
printf ( " \t -C comment sets the machine comment that appears in browse lists \n " ) ;
printf ( " \n " ) ;
}
/****************************************************************************
main program
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int main ( int argc , char * argv [ ] )
{
int port = NMB_PORT ;
int opt ;
extern FILE * dbf ;
extern char * optarg ;
* host_file = 0 ;
#if 0
sleep ( 10 ) ;
# endif
StartupTime = time ( NULL ) ;
TimeInit ( ) ;
strcpy ( debugf , NMBLOGFILE ) ;
setup_logging ( argv [ 0 ] , False ) ;
charset_initialise ( ) ;
# ifdef LMHOSTSFILE
strcpy ( host_file , LMHOSTSFILE ) ;
# endif
/* this is for people who can't start the program correctly */
while ( argc > 1 & & ( * argv [ 1 ] ! = ' - ' ) )
{
argv + + ;
argc - - ;
}
fault_setup ( fault_continue ) ;
signal ( SIGHUP , SIGNAL_CAST sig_hup ) ;
bcast_ip = * interpret_addr2 ( " 0.0.0.0 " ) ;
myip = * interpret_addr2 ( " 0.0.0.0 " ) ;
while ( ( opt = getopt ( argc , argv , " s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G: " ) ) ! = EOF )
switch ( opt )
{
case ' s ' :
strcpy ( servicesf , optarg ) ;
break ;
case ' C ' :
strcpy ( ServerComment , optarg ) ;
break ;
case ' G ' :
add_domain_entry ( optarg , bcast_ip ) ;
break ;
case ' H ' :
strcpy ( host_file , optarg ) ;
break ;
case ' I ' :
myip = * interpret_addr2 ( optarg ) ;
got_myip = True ;
break ;
case ' B ' :
bcast_ip = * interpret_addr2 ( optarg ) ;
got_bcast = True ;
break ;
case ' N ' :
Netmask = * interpret_addr2 ( optarg ) ;
got_nmask = True ;
break ;
case ' n ' :
strcpy ( myname , optarg ) ;
break ;
case ' l ' :
sprintf ( debugf , " %s.nmb " , optarg ) ;
break ;
case ' i ' :
strcpy ( scope , optarg ) ;
strupper ( scope ) ;
break ;
case ' D ' :
is_daemon = True ;
break ;
case ' d ' :
DEBUGLEVEL = atoi ( optarg ) ;
break ;
case ' p ' :
port = atoi ( optarg ) ;
break ;
case ' h ' :
usage ( argv [ 0 ] ) ;
exit ( 0 ) ;
break ;
default :
if ( ! is_a_socket ( 0 ) )
usage ( argv [ 0 ] ) ;
break ;
}
DEBUG ( 1 , ( " %s netbios nameserver version %s started \n " , timestring ( ) , VERSION ) ) ;
DEBUG ( 1 , ( " Copyright Andrew Tridgell 1994 \n " ) ) ;
init_structs ( ) ;
if ( ! reload_services ( False ) )
return ( - 1 ) ;
if ( * host_file )
{
load_hosts_file ( host_file ) ;
DEBUG ( 3 , ( " Loaded hosts file \n " ) ) ;
}
if ( ! * ServerComment )
strcpy ( ServerComment , " Samba %v " ) ;
string_sub ( ServerComment , " %v " , VERSION ) ;
string_sub ( ServerComment , " %h " , myhostname ) ;
add_my_names ( ) ;
DEBUG ( 3 , ( " Checked names \n " ) ) ;
dump_names ( ) ;
DEBUG ( 3 , ( " Dumped names \n " ) ) ;
if ( ! is_daemon & & ! is_a_socket ( 0 ) ) {
DEBUG ( 0 , ( " standard input is not a socket, assuming -D option \n " ) ) ;
is_daemon = True ;
}
if ( is_daemon ) {
DEBUG ( 2 , ( " %s becoming a daemon \n " , timestring ( ) ) ) ;
become_daemon ( ) ;
}
DEBUG ( 3 , ( " Opening sockets \n " ) ) ;
if ( open_sockets ( is_daemon , port ) )
{
process ( ) ;
close_sockets ( ) ;
}
if ( dbf )
fclose ( dbf ) ;
return ( 0 ) ;
}