/* 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 02139, USA. Revision History: 14 jan 96: lkcl@pires.co.uk added multiple workgroup domain master support */ #include "includes.h" #include "loadparm.h" extern int ClientNMB; extern int ClientDGRAM; extern int DEBUGLEVEL; extern pstring scope; extern pstring myname; /* machine comment for host announcements */ extern pstring ServerComment; /* here are my election parameters */ extern time_t StartupTime; #define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER) #define MSBROWSE "\001\002__MSBROWSE__\002" #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" extern struct domain_record *domainlist; /******************************************************************* occasionally check to see if the master browser is around ******************************************************************/ void check_master_browser(void) { static time_t lastrun=0; time_t t = time(NULL); struct domain_record *d; if (!lastrun) lastrun = t; if (t < lastrun + 5*60) return; lastrun = t; dump_workgroups(); for (d = domainlist; d; d = d->next) { struct work_record *work; for (work = d->workgrouplist; work; work = work->next) { /* if we are not the browse master of a workgroup, and we can't find a browser on the subnet, do something about it. */ if (!AM_MASTER(work)) { queue_netbios_packet(ClientNMB,NMB_QUERY,CHECK_MASTER, work->work_group,0x1d,0, True,False,d->bcast_ip); } } } } /******************************************************************* what to do if a master browser DOESN't exist ******************************************************************/ void browser_gone(char *work_name, struct in_addr ip) { struct domain_record *d = find_domain(ip); struct work_record *work = find_workgroupstruct(d, work_name, False); if (!work || !d) return; if (strequal(work->work_group, lp_workgroup()) && ismybcast(d->bcast_ip)) { DEBUG(2,("Forcing election on %s %s\n", work->work_group,inet_ntoa(d->bcast_ip))); /* we can attempt to become master browser */ work->needelection = True; } else { DEBUG(2,("no master browser for persistent entry %s %s\n", work->work_group, inet_ntoa(d->bcast_ip))); /* XXXX oh dear. we are going to have problems here. the entry is a persistent one, there isn't anyone responsible for this workgroup up and running, yet we can't find it and we are going to continually have name_queries until a master browser is found for this workgroup on the remote subnet. */ } } /**************************************************************************** send an election packet **************************************************************************/ void send_election(struct domain_record *d, char *group,uint32 criterion, int timeup,char *name) { pstring outbuf; char *p; if (!d) return; DEBUG(2,("Sending election to %s for workgroup %s\n", inet_ntoa(d->bcast_ip),group)); bzero(outbuf,sizeof(outbuf)); p = outbuf; CVAL(p,0) = 8; /* election */ p++; CVAL(p,0) = (criterion == 0 && timeup == 0) ? 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,d->bcast_ip,*iface_ip(d->bcast_ip)); } /******************************************************************* become the master browser ******************************************************************/ static void become_master(struct domain_record *d, struct work_record *work) { uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000; if (!work) return; DEBUG(2,("Becoming master for %s\n",work->work_group)); work->ServerType |= SV_TYPE_MASTER_BROWSER; work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER; work->ElectionCriterion |= 0x5; /* add browse, master and general names to database or register with WINS */ add_name_entry(MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP); add_name_entry(work->work_group,0x1d,NB_ACTIVE ); if (lp_domain_master()) { DEBUG(4,("Domain master: adding names...\n")); /* add domain master and domain member names or register with WINS */ add_name_entry(work->work_group,0x1b,NB_ACTIVE ); work->ServerType |= SV_TYPE_DOMAIN_MASTER; if (lp_domain_logons()) { work->ServerType |= SV_TYPE_DOMAIN_CTRL; work->ServerType |= SV_TYPE_DOMAIN_MEMBER; } } /* update our server status */ add_server_entry(d,work,work->work_group,domain_type,0,myname,True); add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True); if (ismybcast(d->bcast_ip)) { /* ask all servers on our local net to announce to us */ announce_request(work, d->bcast_ip); } } /******************************************************************* unbecome the master browser ******************************************************************/ void become_nonmaster(struct domain_record *d, struct work_record *work) { DEBUG(2,("Becoming non-master for %s\n",work->work_group)); work->ServerType &= ~SV_TYPE_MASTER_BROWSER; work->ServerType &= ~SV_TYPE_DOMAIN_MASTER; work->ServerType |= SV_TYPE_POTENTIAL_BROWSER; work->ElectionCriterion &= ~0x4; remove_name_entry(work->work_group,0x1b); remove_name_entry(work->work_group,0x1d); remove_name_entry(MSBROWSE ,0x01); } /******************************************************************* run the election ******************************************************************/ void run_elections(void) { time_t t = time(NULL); static time_t lastime = 0; struct domain_record *d; /* send election packets once a second */ if (lastime && t-lastime <= 0) return; lastime = t; for (d = domainlist; d; d = d->next) { struct work_record *work; for (work = d->workgrouplist; work; work = work->next) { if (work->RunningElection) { send_election(d,work->work_group, work->ElectionCriterion, t-StartupTime,myname); if (work->ElectionCount++ >= 4) { /* I won! now what :-) */ DEBUG(2,(">>> Won election on %s %s <<<\n", work->work_group,inet_ntoa(d->bcast_ip))); work->RunningElection = False; become_master(d, work); } } } } } /******************************************************************* work out if I win an election ******************************************************************/ static BOOL win_election(struct work_record *work,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 = work->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. ******************************************************************/ void process_election(struct packet_struct *p,char *buf) { struct dgram_packet *dgram = &p->packet.dgram; struct in_addr ip = dgram->header.source_ip; struct domain_record *d = find_domain(ip); int version = CVAL(buf,0); uint32 criterion = IVAL(buf,1); int timeup = IVAL(buf,5)/1000; char *name = buf+13; struct work_record *work; if (!d) return; name[15] = 0; DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n", name,version,criterion,timeup)); if (same_context(dgram)) return; for (work = d->workgrouplist; work; work = work->next) { if (listening_name(work, &dgram->dest_name) && strequal(work->work_group, lp_workgroup()) && ismybcast(d->bcast_ip)) { if (win_election(work, version,criterion,timeup,name)) { if (!work->RunningElection) { work->needelection = True; work->ElectionCount=0; } } else { work->needelection = False; if (work->RunningElection) { work->RunningElection = False; DEBUG(3,(">>> Lost election on %s %s <<<\n", work->work_group,inet_ntoa(d->bcast_ip))); /* if we are the master then remove our masterly names */ if (AM_MASTER(work)) { become_nonmaster(d, work); } } } } } } /**************************************************************************** checks whether a browser election is to be run on any workgroup ***************************************************************************/ BOOL check_elections(void) { struct domain_record *d; BOOL run_any_election = False; for (d = domainlist; d; d = d->next) { struct work_record *work; for (work = d->workgrouplist; work; work = work->next) { run_any_election |= work->RunningElection; if (work->needelection && !work->RunningElection) { DEBUG(3,(">>> Starting election on %s %s <<<\n", work->work_group,inet_ntoa(d->bcast_ip))); work->ElectionCount = 0; work->RunningElection = True; work->needelection = False; } } } return run_any_election; }