sorted out various timer delay bugs: nameannounce.c nameserv.c
[Samba.git] / source / nameelect.c
blobe115b0d88185d7ae138f0f9bb8d3df6a4bb53443
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1996
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.
21 Module name: nameelect.c
23 Revision History:
25 14 jan 96: lkcl@pires.co.uk
26 added multiple workgroup domain master support
28 04 jul 96: lkcl@pires.co.uk
29 added system to become a master browser by stages.
34 #include "includes.h"
36 extern int ClientNMB;
37 extern int ClientDGRAM;
39 extern int DEBUGLEVEL;
40 extern pstring scope;
42 extern pstring myname;
43 extern struct in_addr ipzero;
45 /* machine comment for host announcements */
46 extern pstring ServerComment;
48 /* here are my election parameters */
50 extern time_t StartupTime;
52 extern struct subnet_record *subnetlist;
55 /*******************************************************************
56 occasionally check to see if the master browser is around
57 ******************************************************************/
58 void check_master_browser(void)
60 static time_t lastrun=0;
61 time_t t = time(NULL);
62 struct subnet_record *d;
64 if (!lastrun) lastrun = t;
65 if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
66 return;
67 lastrun = t;
69 dump_workgroups();
71 for (d = subnetlist; d; d = d->next)
73 struct work_record *work;
75 for (work = d->workgrouplist; work; work = work->next)
77 /* if we are not the browse master of a workgroup, and we can't
78 find a browser on the subnet, do something about it. */
80 if (!AM_MASTER(work))
82 queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
83 work->work_group,0x1d,0,0,
84 True,False,d->bcast_ip,d->bcast_ip);
91 /*******************************************************************
92 what to do if a master browser DOESN't exist
93 ******************************************************************/
94 void browser_gone(char *work_name, struct in_addr ip)
96 struct subnet_record *d = find_subnet(ip);
97 struct work_record *work = find_workgroupstruct(d, work_name, False);
99 /* i don't know about this workgroup, therefore i don't care */
100 if (!work || !d) return;
102 if (strequal(work->work_group, lp_workgroup()) && d->my_interface)
105 DEBUG(2,("Forcing election on %s %s\n",
106 work->work_group,inet_ntoa(d->bcast_ip)));
108 /* we can attempt to become master browser */
109 work->needelection = True;
111 else
113 /* local interfaces: force an election */
114 if (d->my_interface)
115 send_election(d, work->work_group, 0, 0, myname);
117 /* only removes workgroup completely on a local interface or
118 if there are no server entries on the remote interface.
119 (persistent lmhost entries on a remote interface will stop
120 the workgroup being removed. persistent lmhosts entries on
121 a local interface _will_ be removed).
123 remove_workgroup(d, work, d->my_interface);
128 /****************************************************************************
129 send an election packet
130 **************************************************************************/
131 void send_election(struct subnet_record *d, char *group,uint32 criterion,
132 int timeup,char *name)
134 pstring outbuf;
135 char *p;
137 if (!d) return;
139 DEBUG(2,("Sending election to %s for workgroup %s\n",
140 inet_ntoa(d->bcast_ip),group));
142 bzero(outbuf,sizeof(outbuf));
143 p = outbuf;
144 CVAL(p,0) = ANN_Election; /* election */
145 p++;
147 CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
148 SIVAL(p,1,criterion);
149 SIVAL(p,5,timeup*1000); /* ms - despite the spec */
150 p += 13;
151 strcpy(p,name);
152 strupper(p);
153 p = skip_string(p,1);
155 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
156 name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
160 /****************************************************************************
161 un-register a SELF name that got rejected.
163 if this name happens to be rejected when samba is in the process
164 of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
165 or WORKGROUP(1b)) then we must stop being a master browser. sad.
167 **************************************************************************/
168 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
170 struct work_record *work;
172 remove_netbios_name(d,name,name_type,SELF,ipzero);
174 if (!(work = find_workgroupstruct(d, name, False))) return;
176 if (ms_browser_name(name, name_type) ||
177 (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
178 (name_type == 0x1d || name_type == 0x1b)))
180 int remove_type = 0;
182 if (ms_browser_name(name, name_type))
183 remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
184 if (name_type == 0x1d)
185 remove_type = SV_TYPE_MASTER_BROWSER;
186 if (name_type == 0x1b)
187 remove_type = SV_TYPE_DOMAIN_MASTER;
189 become_nonmaster(d, work, remove_type);
194 /****************************************************************************
195 registers a name.
197 if the name being added is a SELF name, we must additionally check
198 whether to proceed to the next stage in samba becoming a master browser.
200 **************************************************************************/
201 void name_register_work(struct subnet_record *d, char *name, int name_type,
202 int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast)
204 enum name_source source = ismyip(ip) ? SELF : REGISTER;
206 if (source == SELF)
208 struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
210 if (work && work->state != MST_NONE)
212 /* samba is in the process of working towards master browser-ness.
213 initiate the next stage.
215 become_master(d, work);
218 add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
222 /*******************************************************************
223 become the master browser.
225 this is done in stages. note that this could take a while,
226 particularly on a broadcast subnet, as we have to wait for
227 the implicit registration of each name to be accepted.
229 as each name is successfully registered, become_master() is
230 called again, in order to initiate the next stage. see
231 dead_netbios_entry() - deals with implicit name registration
232 and response_name_reg() - deals with explicit registration
233 with a WINS server.
235 stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
236 stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d)
237 stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b)
238 stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
240 XXXX note: this code still does not cope with the distinction
241 between different types of nodes, particularly between M and P
242 nodes. that comes later.
244 ******************************************************************/
245 void become_master(struct subnet_record *d, struct work_record *work)
247 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX|0x00400000;
249 if (!work) return;
251 DEBUG(2,("Becoming master for %s (currently at stage %d)\n",
252 work->work_group,work->state));
254 switch (work->state)
256 case MST_NONE: /* while we were nothing but a server... */
258 work->state = MST_WON; /* ... an election win was successful */
260 work->ElectionCriterion |= 0x5;
262 /* update our server status */
263 work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
264 add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
266 DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
268 /* add special browser name */
269 add_my_name_entry(d,MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP);
271 break;
273 case MST_WON: /* while nothing had happened except we won an election... */
275 work->state = MST_MSB; /* ... registering MSBROWSE was successful */
277 /* add server entry on successful registration of MSBROWSE */
278 add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
280 DEBUG(3,("go to second stage: register as master browser\n"));
282 /* add master name */
283 add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE );
285 break;
287 case MST_MSB: /* while we were still only registered MSBROWSE state... */
289 work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
291 /* update our server status */
292 work->ServerType |= SV_TYPE_MASTER_BROWSER;
293 add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
295 if (d->my_interface && work->serverlist == NULL) /* no servers! */
297 /* ask all servers on our local net to announce to us */
298 announce_request(work, d->bcast_ip);
301 if (lp_domain_master())
303 DEBUG(3,("third stage: register as domain master\n"));
304 /* add domain master name */
305 add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE );
307 else
309 DEBUG(3,("samba not configured as a domain master: no third stage.\n"));
312 break;
314 case MST_BROWSER: /* while we were still a master browser... */
316 work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
318 /* update our server status */
319 if (lp_domain_master())
321 work->ServerType |= SV_TYPE_DOMAIN_MASTER;
323 if (lp_domain_logons())
325 work->ServerType |= SV_TYPE_DOMAIN_CTRL;
326 work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
328 DEBUG(3,("fourth stage: samba is now a domain master.\n"));
329 add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
332 break;
334 case MST_DOMAIN:
336 /* nothing else to become, at the moment: we are top-dog. */
337 DEBUG(3,("fifth stage: there isn't one yet!\n"));
338 break;
344 /*******************************************************************
345 unbecome the master browser. initates removal of necessary netbios
346 names, and tells the world that we are no longer a master browser.
347 ******************************************************************/
348 void become_nonmaster(struct subnet_record *d, struct work_record *work,
349 int remove_type)
351 int new_server_type = work->ServerType;
353 DEBUG(2,("Becoming non-master for %s\n",work->work_group));
355 /* can only remove master or domain types with this function */
356 remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
358 /* unbecome a master browser; unbecome a domain master, too :-( */
359 if (remove_type & SV_TYPE_MASTER_BROWSER)
360 remove_type |= SV_TYPE_DOMAIN_MASTER;
362 new_server_type &= ~remove_type;
364 if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
366 /* no longer a master browser of any sort */
368 work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
369 work->ElectionCriterion &= ~0x4;
370 work->state = MST_NONE;
372 /* announce ourselves as no longer active as a master browser. */
373 announce_server(d, work, work->work_group, myname, 0, 0);
374 remove_name_entry(d,MSBROWSE ,0x01);
377 work->ServerType = new_server_type;
379 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
381 if (work->state == MST_DOMAIN)
382 work->state = MST_BROWSER;
383 remove_name_entry(d,work->work_group,0x1b);
387 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
389 if (work->state >= MST_BROWSER)
390 work->state = MST_NONE;
391 remove_name_entry(d,work->work_group,0x1d);
396 /*******************************************************************
397 run the election
398 ******************************************************************/
399 void run_elections(void)
401 time_t t = time(NULL);
402 static time_t lastime = 0;
404 struct subnet_record *d;
406 /* send election packets once a second */
407 if (lastime && t-lastime <= 0) return;
409 lastime = t;
411 for (d = subnetlist; d; d = d->next)
413 struct work_record *work;
414 for (work = d->workgrouplist; work; work = work->next)
416 if (work->RunningElection)
418 send_election(d,work->work_group, work->ElectionCriterion,
419 t-StartupTime,myname);
421 if (work->ElectionCount++ >= 4)
423 /* I won! now what :-) */
424 DEBUG(2,(">>> Won election on %s %s <<<\n",
425 work->work_group,inet_ntoa(d->bcast_ip)));
427 work->RunningElection = False;
428 work->state = MST_NONE;
430 become_master(d, work);
438 /*******************************************************************
439 work out if I win an election
440 ******************************************************************/
441 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
442 int timeup,char *name)
444 time_t t = time(NULL);
445 uint32 mycriterion;
446 if (version > ELECTION_VERSION) return(False);
447 if (version < ELECTION_VERSION) return(True);
449 mycriterion = work->ElectionCriterion;
451 if (criterion > mycriterion) return(False);
452 if (criterion < mycriterion) return(True);
454 if (timeup > (t - StartupTime)) return(False);
455 if (timeup < (t - StartupTime)) return(True);
457 if (strcasecmp(myname,name) > 0) return(False);
459 return(True);
463 /*******************************************************************
464 process a election packet
466 An election dynamically decides who will be the master.
467 ******************************************************************/
468 void process_election(struct packet_struct *p,char *buf)
470 struct dgram_packet *dgram = &p->packet.dgram;
471 struct in_addr ip = dgram->header.source_ip;
472 struct subnet_record *d = find_subnet(ip);
473 int version = CVAL(buf,0);
474 uint32 criterion = IVAL(buf,1);
475 int timeup = IVAL(buf,5)/1000;
476 char *name = buf+13;
477 struct work_record *work;
479 if (!d) return;
481 name[15] = 0;
483 DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
484 name,version,criterion,timeup));
486 if (same_context(dgram)) return;
488 for (work = d->workgrouplist; work; work = work->next)
490 if (listening_name(work, &dgram->dest_name) &&
491 strequal(work->work_group, lp_workgroup()) &&
492 d->my_interface)
494 if (win_election(work, version,criterion,timeup,name))
496 if (!work->RunningElection)
498 work->needelection = True;
499 work->ElectionCount=0;
500 work->state = MST_NONE;
503 else
505 work->needelection = False;
507 if (work->RunningElection)
509 work->RunningElection = False;
510 DEBUG(3,(">>> Lost election on %s %s <<<\n",
511 work->work_group,inet_ntoa(d->bcast_ip)));
513 /* if we are the master then remove our masterly names */
514 if (AM_MASTER(work))
516 become_nonmaster(d, work,
517 SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
526 /****************************************************************************
527 checks whether a browser election is to be run on any workgroup
529 this function really ought to return the time between election
530 packets (which depends on whether samba intends to be a domain
531 master or a master browser) in milliseconds.
533 ***************************************************************************/
534 BOOL check_elections(void)
536 struct subnet_record *d;
537 BOOL run_any_election = False;
539 for (d = subnetlist; d; d = d->next)
541 struct work_record *work;
542 for (work = d->workgrouplist; work; work = work->next)
544 run_any_election |= work->RunningElection;
546 if (work->needelection && !work->RunningElection)
548 DEBUG(3,(">>> Starting election on %s %s <<<\n",
549 work->work_group,inet_ntoa(d->bcast_ip)));
550 work->ElectionCount = 0;
551 work->RunningElection = True;
552 work->needelection = False;
556 return run_any_election;