2 Unix SMB/Netbios implementation.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1995
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 14 jan 96: lkcl@pires.co.uk
24 added multiple workgroup domain master support
31 extern int ClientDGRAM;
33 extern int DEBUGLEVEL;
36 extern pstring myname;
38 /* machine comment for host announcements */
39 extern pstring ServerComment;
41 /* here are my election parameters */
43 extern time_t StartupTime;
45 extern struct subnet_record *subnetlist;
48 /*******************************************************************
49 occasionally check to see if the master browser is around
50 ******************************************************************/
51 void check_master_browser(void)
53 static time_t lastrun=0;
54 time_t t = time(NULL);
55 struct subnet_record *d;
57 if (!lastrun) lastrun = t;
58 if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
64 for (d = subnetlist; d; d = d->next)
66 struct work_record *work;
68 for (work = d->workgrouplist; work; work = work->next)
70 /* if we are not the browse master of a workgroup, and we can't
71 find a browser on the subnet, do something about it. */
75 queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
76 work->work_group,0x1d,0,0,
77 True,False,d->bcast_ip);
84 /*******************************************************************
85 what to do if a master browser DOESN't exist
86 ******************************************************************/
87 void browser_gone(char *work_name, struct in_addr ip)
89 struct subnet_record *d = find_subnet(ip);
90 struct work_record *work = find_workgroupstruct(d, work_name, False);
92 if (!work || !d) return;
94 if (strequal(work->work_group, lp_workgroup()) &&
95 ismybcast(d->bcast_ip))
98 DEBUG(2,("Forcing election on %s %s\n",
99 work->work_group,inet_ntoa(d->bcast_ip)));
101 /* we can attempt to become master browser */
102 work->needelection = True;
106 /* XXXX note: this will delete entries that have been added in by
107 lmhosts as well. a flag to ensure that these are not deleted may
110 /* workgroup with no master browser is not the default workgroup:
111 it's also not on our subnet. therefore delete it: it can be
112 recreated dynamically */
114 send_election(d, work->work_group, 0, 0, myname);
115 remove_workgroup(d, work);
120 /****************************************************************************
121 send an election packet
122 **************************************************************************/
123 void send_election(struct subnet_record *d, char *group,uint32 criterion,
124 int timeup,char *name)
131 DEBUG(2,("Sending election to %s for workgroup %s\n",
132 inet_ntoa(d->bcast_ip),group));
134 bzero(outbuf,sizeof(outbuf));
136 CVAL(p,0) = 8; /* election */
139 CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
140 SIVAL(p,1,criterion);
141 SIVAL(p,5,timeup*1000); /* ms - despite the spec */
145 p = skip_string(p,1);
147 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
148 name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
152 /*******************************************************************
153 become the master browser
154 ******************************************************************/
155 static void become_master(struct subnet_record *d, struct work_record *work)
157 uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000;
161 DEBUG(2,("Becoming master for %s\n",work->work_group));
163 work->ServerType |= SV_TYPE_MASTER_BROWSER;
164 work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
165 work->ElectionCriterion |= 0x5;
167 /* add browse, master and general names to database or register with WINS */
168 add_my_name_entry(d,MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP);
169 add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE );
171 if (lp_domain_master())
173 DEBUG(4,("Domain master: adding names...\n"));
175 /* add domain master and domain member names or register with WINS */
176 add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE );
178 work->ServerType |= SV_TYPE_DOMAIN_MASTER;
180 if (lp_domain_logons())
182 work->ServerType |= SV_TYPE_DOMAIN_CTRL;
183 work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
187 /* update our server status */
188 add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
189 add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
193 /* ask all servers on our local net to announce to us */
194 announce_request(work, d->bcast_ip);
199 /*******************************************************************
200 unbecome the master browser. initates removal of necessary netbios
201 names, and tells the world that we are no longer a master browser.
202 ******************************************************************/
203 void become_nonmaster(struct subnet_record *d, struct work_record *work,
206 int new_server_type = work->ServerType;
208 DEBUG(2,("Becoming non-master for %s\n",work->work_group));
210 /* can only remove master or domain types with this function */
211 remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
213 /* unbecome a master browser; unbecome a domain master, too :-( */
214 if (remove_type & SV_TYPE_MASTER_BROWSER)
215 remove_type |= SV_TYPE_DOMAIN_MASTER;
217 new_server_type &= ~remove_type;
219 if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
221 /* no longer a master browser of any sort */
223 work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
224 work->ElectionCriterion &= ~0x4;
226 /* announce ourselves as no longer active as a master browser. */
227 announce_server(d, work, work->work_group, myname, 0, 0);
228 remove_name_entry(d,MSBROWSE ,0x01);
231 work->ServerType = new_server_type;
233 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
234 remove_name_entry(d,work->work_group,0x1b);
236 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
237 remove_name_entry(d,work->work_group,0x1d);
241 /*******************************************************************
243 ******************************************************************/
244 void run_elections(void)
246 time_t t = time(NULL);
247 static time_t lastime = 0;
249 struct subnet_record *d;
251 /* send election packets once a second */
252 if (lastime && t-lastime <= 0) return;
256 for (d = subnetlist; d; d = d->next)
258 struct work_record *work;
259 for (work = d->workgrouplist; work; work = work->next)
261 if (work->RunningElection)
263 send_election(d,work->work_group, work->ElectionCriterion,
264 t-StartupTime,myname);
266 if (work->ElectionCount++ >= 4)
268 /* I won! now what :-) */
269 DEBUG(2,(">>> Won election on %s %s <<<\n",
270 work->work_group,inet_ntoa(d->bcast_ip)));
272 work->RunningElection = False;
273 become_master(d, work);
281 /*******************************************************************
282 work out if I win an election
283 ******************************************************************/
284 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
285 int timeup,char *name)
287 time_t t = time(NULL);
289 if (version > ELECTION_VERSION) return(False);
290 if (version < ELECTION_VERSION) return(True);
292 mycriterion = work->ElectionCriterion;
294 if (criterion > mycriterion) return(False);
295 if (criterion < mycriterion) return(True);
297 if (timeup > (t - StartupTime)) return(False);
298 if (timeup < (t - StartupTime)) return(True);
300 if (strcasecmp(myname,name) > 0) return(False);
306 /*******************************************************************
307 process a election packet
309 An election dynamically decides who will be the master.
310 ******************************************************************/
311 void process_election(struct packet_struct *p,char *buf)
313 struct dgram_packet *dgram = &p->packet.dgram;
314 struct in_addr ip = dgram->header.source_ip;
315 struct subnet_record *d = find_subnet(ip);
316 int version = CVAL(buf,0);
317 uint32 criterion = IVAL(buf,1);
318 int timeup = IVAL(buf,5)/1000;
320 struct work_record *work;
326 DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
327 name,version,criterion,timeup));
329 if (same_context(dgram)) return;
331 for (work = d->workgrouplist; work; work = work->next)
333 if (listening_name(work, &dgram->dest_name) &&
334 strequal(work->work_group, lp_workgroup()) &&
337 if (win_election(work, version,criterion,timeup,name))
339 if (!work->RunningElection)
341 work->needelection = True;
342 work->ElectionCount=0;
347 work->needelection = False;
349 if (work->RunningElection)
351 work->RunningElection = False;
352 DEBUG(3,(">>> Lost election on %s %s <<<\n",
353 work->work_group,inet_ntoa(d->bcast_ip)));
355 /* if we are the master then remove our masterly names */
358 become_nonmaster(d, work,
359 SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
368 /****************************************************************************
369 checks whether a browser election is to be run on any workgroup
370 ***************************************************************************/
371 BOOL check_elections(void)
373 struct subnet_record *d;
374 BOOL run_any_election = False;
376 for (d = subnetlist; d; d = d->next)
378 struct work_record *work;
379 for (work = d->workgrouplist; work; work = work->next)
381 run_any_election |= work->RunningElection;
383 if (work->needelection && !work->RunningElection)
385 DEBUG(3,(">>> Starting election on %s %s <<<\n",
386 work->work_group,inet_ntoa(d->bcast_ip)));
387 work->ElectionCount = 0;
388 work->RunningElection = True;
389 work->needelection = False;
393 return run_any_election;