removed all of lukes recent changes. I need to do a p2 release but
[Samba.git] / source / nameelect.c
blob07429013e0db5380d9beef1bb9b1d4507a607172
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;
44 extern struct in_addr ipgrp;
46 /* here are my election parameters */
48 extern time_t StartupTime;
50 extern struct subnet_record *subnetlist;
52 extern uint16 nb_type; /* samba's NetBIOS name type */
54 /*******************************************************************
55 occasionally check to see if the master browser is around
56 ******************************************************************/
57 void check_master_browser(void)
59 static time_t lastrun=0;
60 time_t t = time(NULL);
61 struct subnet_record *d;
63 if (!lastrun) lastrun = t;
64 if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
65 return;
66 lastrun = t;
68 dump_workgroups();
70 for (d = subnetlist; d; d = d->next)
72 struct work_record *work;
74 for (work = d->workgrouplist; work; work = work->next)
76 /* if we are not the browse master of a workgroup, and we can't
77 find a browser on the subnet, do something about it. */
79 if (!AM_MASTER(work))
81 queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
82 work->work_group,0x1d,0,0,0,NULL,NULL,
83 True,False,d->bcast_ip,d->bcast_ip);
90 /*******************************************************************
91 what to do if a master browser DOESN't exist
92 ******************************************************************/
93 void browser_gone(char *work_name, struct in_addr ip)
95 struct subnet_record *d = find_subnet(ip);
96 struct work_record *work = find_workgroupstruct(d, work_name, False);
98 /* i don't know about this workgroup, therefore i don't care */
99 if (!work || !d) return;
101 /* don't do election stuff on the WINS subnet */
102 if (ip_equal(d->bcast_ip,ipgrp))
103 return;
105 if (strequal(work->work_group, lp_workgroup()))
108 DEBUG(2,("Forcing election on %s %s\n",
109 work->work_group,inet_ntoa(d->bcast_ip)));
111 /* we can attempt to become master browser */
112 work->needelection = True;
114 else
116 /* local interfaces: force an election */
117 send_election(d, work->work_group, 0, 0, myname);
119 /* only removes workgroup completely on a local interface
120 persistent lmhosts entries on a local interface _will_ be removed).
122 remove_workgroup(d, work,True);
127 /****************************************************************************
128 send an election packet
129 **************************************************************************/
130 void send_election(struct subnet_record *d, char *group,uint32 criterion,
131 int timeup,char *name)
133 pstring outbuf;
134 char *p;
136 if (!d) return;
138 DEBUG(2,("Sending election to %s for workgroup %s\n",
139 inet_ntoa(d->bcast_ip),group));
141 bzero(outbuf,sizeof(outbuf));
142 p = outbuf;
143 CVAL(p,0) = ANN_Election; /* election */
144 p++;
146 CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
147 SIVAL(p,1,criterion);
148 SIVAL(p,5,timeup*1000); /* ms - despite the spec */
149 p += 13;
150 strcpy(p,name);
151 strupper(p);
152 p = skip_string(p,1);
154 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
155 name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
159 /****************************************************************************
160 un-register a SELF name that got rejected.
162 if this name happens to be rejected when samba is in the process
163 of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
164 or WORKGROUP(1b)) then we must stop being a master browser. sad.
166 **************************************************************************/
167 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
169 struct work_record *work;
171 remove_netbios_name(d,name,name_type,SELF,ipzero);
173 if (!(work = find_workgroupstruct(d, name, False))) return;
175 if (ms_browser_name(name, name_type) ||
176 (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
177 (name_type == 0x1d || name_type == 0x1b)))
179 int remove_type = 0;
181 if (ms_browser_name(name, name_type))
182 remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
183 if (name_type == 0x1d)
184 remove_type = SV_TYPE_MASTER_BROWSER;
185 if (name_type == 0x1b)
186 remove_type = SV_TYPE_DOMAIN_MASTER;
188 become_nonmaster(d, work, remove_type);
193 /****************************************************************************
194 registers a name.
196 if the name being added is a SELF name, we must additionally check
197 whether to proceed to the next stage in samba becoming a master browser.
199 **************************************************************************/
200 void name_register_work(struct subnet_record *d, char *name, int name_type,
201 int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast)
203 enum name_source source = (ismyip(ip) || ip_equal(ip, ipzero)) ?
204 SELF : REGISTER;
206 if (source == SELF)
208 struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
210 add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
212 if (work)
214 if (work->state != MST_NONE)
216 /* samba is in the process of working towards master browser-ness.
217 initiate the next stage.
219 become_master(d, work);
220 return;
227 /*******************************************************************
228 become the master browser.
230 this is done in stages. note that this could take a while,
231 particularly on a broadcast subnet, as we have to wait for
232 the implicit registration of each name to be accepted.
234 as each name is successfully registered, become_master() is
235 called again, in order to initiate the next stage. see
236 dead_netbios_entry() - deals with implicit name registration
237 and response_name_reg() - deals with explicit registration
238 with a WINS server.
240 stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
241 stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d)
242 stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b)
243 stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
245 XXXX note: this code still does not cope with the distinction
246 between different types of nodes, particularly between M and P
247 nodes. that comes later.
249 ******************************************************************/
250 void become_master(struct subnet_record *d, struct work_record *work)
252 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|DFLT_SERVER_TYPE|
253 SV_TYPE_POTENTIAL_BROWSER;
255 if (!work) return;
257 DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
258 work->work_group,inet_ntoa(d->bcast_ip),work->state));
260 switch (work->state)
262 case MST_NONE: /* while we were nothing but a server... */
264 DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
265 work->state = MST_WON; /* ... an election win was successful */
267 work->ElectionCriterion |= 0x5;
269 /* update our server status */
270 work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
271 add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
273 /* add special browser name */
274 add_my_name_entry(d,MSBROWSE ,0x01,nb_type|NB_ACTIVE|NB_GROUP);
276 /* DON'T do anything else after calling add_my_name_entry() */
277 return;
279 case MST_WON: /* while nothing had happened except we won an election... */
281 DEBUG(3,("go to second stage: register as master browser\n"));
282 work->state = MST_MSB; /* ... registering MSBROWSE was successful */
284 /* add server entry on successful registration of MSBROWSE */
285 add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
287 /* add master name */
288 add_my_name_entry(d,work->work_group,0x1d,nb_type|NB_ACTIVE);
290 /* DON'T do anything else after calling add_my_name_entry() */
291 return;
293 case MST_MSB: /* while we were still only registered MSBROWSE state... */
295 DEBUG(3,("2nd stage complete: registered as master browser\n"));
296 work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
298 /* update our server status */
299 work->ServerType |= SV_TYPE_MASTER_BROWSER;
300 add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
302 if (work->serverlist == NULL) /* no servers! */
304 /* ask all servers on our local net to announce to us */
305 announce_request(work, d->bcast_ip);
307 break;
310 case MST_BROWSER:
312 /* don't have to do anything: just report success */
313 DEBUG(3,("3rd stage: become master browser!\n"));
315 break;
318 case MST_DOMAIN_NONE:
320 if (lp_domain_master())
322 work->state = MST_DOMAIN_MEM; /* ... become domain member */
323 DEBUG(3,("domain first stage: register as domain member\n"));
325 /* add domain member name */
326 add_my_name_entry(d,work->work_group,0x1e,nb_type|NB_ACTIVE|NB_GROUP);
328 /* DON'T do anything else after calling add_my_name_entry() */
329 return;
331 else
333 DEBUG(4,("samba not configured as a domain master.\n"));
336 break;
339 case MST_DOMAIN_MEM:
341 if (lp_domain_master())
343 work->state = MST_DOMAIN_TST; /* ... possibly become domain master */
344 DEBUG(3,("domain second stage: register as domain master\n"));
346 if (lp_domain_logons())
348 work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
349 add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
352 /* add domain master name */
353 add_my_name_entry(d,work->work_group,0x1b,nb_type|NB_ACTIVE );
355 /* DON'T do anything else after calling add_my_name_entry() */
356 return;
358 else
360 DEBUG(4,("samba not configured as a domain master.\n"));
363 break;
366 case MST_DOMAIN_TST: /* while we were still a master browser... */
368 /* update our server status */
369 if (lp_domain_master())
371 struct subnet_record *d1;
372 uint32 update_type = 0;
374 DEBUG(3,("domain third stage: samba is now a domain master.\n"));
375 work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
377 update_type |= DFLT_SERVER_TYPE | SV_TYPE_DOMAIN_MASTER |
378 SV_TYPE_POTENTIAL_BROWSER;
380 work->ServerType |= update_type;
381 add_server_entry(d,work,myname,work->ServerType,0,lp_serverstring(),True);
383 for (d1 = subnetlist; d1; d1 = d1->next)
385 struct work_record *w;
386 if (ip_equal(d1->bcast_ip, d->bcast_ip)) continue;
388 for (w = d1->workgrouplist; w; w = w->next)
390 struct server_record *s = find_server(w, myname);
391 if (strequal(w->work_group, work->work_group))
393 w->ServerType |= update_type;
395 if (s)
397 s->serv.type |= update_type;
398 DEBUG(4,("found server %s on %s: update to %8x\n",
399 s->serv.name, inet_ntoa(d1->bcast_ip),
400 s->serv.type));
406 break;
409 case MST_DOMAIN:
411 /* don't have to do anything: just report success */
412 DEBUG(3,("fifth stage: there isn't one yet!\n"));
413 break;
419 /*******************************************************************
420 unbecome the master browser. initates removal of necessary netbios
421 names, and tells the world that we are no longer a master browser.
422 ******************************************************************/
423 void become_nonmaster(struct subnet_record *d, struct work_record *work,
424 int remove_type)
426 int new_server_type = work->ServerType;
428 DEBUG(2,("Becoming non-master for %s\n",work->work_group));
430 /* can only remove master or domain types with this function */
431 remove_type &= SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
433 /* unbecome a master browser; unbecome a domain master, too :-( */
434 if (remove_type & SV_TYPE_MASTER_BROWSER)
435 remove_type |= SV_TYPE_DOMAIN_MASTER;
437 new_server_type &= ~remove_type;
439 if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
441 /* no longer a master browser of any sort */
443 work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
444 work->ElectionCriterion &= ~0x4;
445 work->state = MST_NONE;
447 /* announce ourselves as no longer active as a master browser. */
448 announce_server(d, work, work->work_group, myname, 0, 0);
449 remove_name_entry(d,MSBROWSE ,0x01);
452 work->ServerType = new_server_type;
454 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
456 if (work->state == MST_DOMAIN)
457 work->state = MST_BROWSER;
458 remove_name_entry(d,work->work_group,0x1b);
461 if (!(work->ServerType & SV_TYPE_MASTER_BROWSER))
463 if (work->state >= MST_BROWSER)
464 work->state = MST_NONE;
465 remove_name_entry(d,work->work_group,0x1d);
470 /*******************************************************************
471 run the election
472 ******************************************************************/
473 void run_elections(void)
475 time_t t = time(NULL);
476 static time_t lastime = 0;
478 struct subnet_record *d;
480 /* send election packets once a second */
481 if (lastime && t-lastime <= 0) return;
483 lastime = t;
485 for (d = subnetlist; d; d = d->next)
487 struct work_record *work;
488 for (work = d->workgrouplist; work; work = work->next)
490 if (work->RunningElection)
492 send_election(d,work->work_group, work->ElectionCriterion,
493 t-StartupTime,myname);
495 if (work->ElectionCount++ >= 4)
497 /* I won! now what :-) */
498 DEBUG(2,(">>> Won election on %s %s <<<\n",
499 work->work_group,inet_ntoa(d->bcast_ip)));
501 work->RunningElection = False;
502 work->state = MST_NONE;
504 become_master(d, work);
512 /*******************************************************************
513 work out if I win an election
514 ******************************************************************/
515 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
516 int timeup,char *name)
518 int mytimeup = time(NULL) - StartupTime;
519 uint32 mycriterion = work->ElectionCriterion;
521 DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
522 version,ELECTION_VERSION,
523 criterion,mycriterion,
524 timeup,mytimeup,
525 name,myname));
527 if (version > ELECTION_VERSION) return(False);
528 if (version < ELECTION_VERSION) return(True);
530 if (criterion > mycriterion) return(False);
531 if (criterion < mycriterion) return(True);
533 if (timeup > mytimeup) return(False);
534 if (timeup < mytimeup) return(True);
536 if (strcasecmp(myname,name) > 0) return(False);
538 return(True);
542 /*******************************************************************
543 process a election packet
545 An election dynamically decides who will be the master.
546 ******************************************************************/
547 void process_election(struct packet_struct *p,char *buf)
549 struct dgram_packet *dgram = &p->packet.dgram;
550 struct in_addr ip = dgram->header.source_ip;
551 struct subnet_record *d = find_subnet(ip);
552 int version = CVAL(buf,0);
553 uint32 criterion = IVAL(buf,1);
554 int timeup = IVAL(buf,5)/1000;
555 char *name = buf+13;
556 struct work_record *work;
558 if (!d) return;
560 if (ip_equal(d->bcast_ip,ipgrp)) {
561 DEBUG(3,("Unexpected election request from %s %s on WINS net\n",
562 name, inet_ntoa(p->ip)));
563 return;
566 name[15] = 0;
568 DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
569 name,inet_ntoa(p->ip),version,criterion,timeup));
571 if (same_context(dgram)) return;
573 for (work = d->workgrouplist; work; work = work->next)
575 if (!strequal(work->work_group, lp_workgroup()))
576 continue;
578 if (win_election(work, version,criterion,timeup,name)) {
579 if (!work->RunningElection) {
580 work->needelection = True;
581 work->ElectionCount=0;
582 work->state = MST_NONE;
584 } else {
585 work->needelection = False;
587 if (work->RunningElection || AM_MASTER(work)) {
588 work->RunningElection = False;
589 DEBUG(3,(">>> Lost election on %s %s <<<\n",
590 work->work_group,inet_ntoa(d->bcast_ip)));
591 if (AM_MASTER(work))
592 become_nonmaster(d, work,
593 SV_TYPE_MASTER_BROWSER|
594 SV_TYPE_DOMAIN_MASTER);
601 /****************************************************************************
602 checks whether a browser election is to be run on any workgroup
604 this function really ought to return the time between election
605 packets (which depends on whether samba intends to be a domain
606 master or a master browser) in milliseconds.
608 ***************************************************************************/
609 BOOL check_elections(void)
611 struct subnet_record *d;
612 BOOL run_any_election = False;
614 for (d = subnetlist; d; d = d->next)
616 struct work_record *work;
617 for (work = d->workgrouplist; work; work = work->next)
619 run_any_election |= work->RunningElection;
621 if (work->needelection && !work->RunningElection)
623 DEBUG(3,(">>> Starting election on %s %s <<<\n",
624 work->work_group,inet_ntoa(d->bcast_ip)));
625 work->ElectionCount = 0;
626 work->RunningElection = True;
627 work->needelection = False;
631 return run_any_election;