2 Unix SMB/Netbios implementation.
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
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.
31 30 July 96: David.Chappell@mail.trincoll.edu
32 Expanded multiple workgroup domain master browser support.
39 extern int ClientDGRAM;
41 extern int DEBUGLEVEL;
44 extern struct in_addr ipzero;
45 extern struct in_addr ipgrp;
47 /* here are my election parameters */
49 extern time_t StartupTime;
51 extern struct subnet_record *subnetlist;
53 extern uint16 nb_type; /* samba's NetBIOS name type */
55 extern pstring myname;
58 /*******************************************************************
59 occasionally check to see if the master browser is around
60 ******************************************************************/
61 void check_master_browser(void)
63 static time_t lastrun=0;
64 time_t t = time(NULL);
65 struct subnet_record *d;
67 if (!lastrun) lastrun = t;
68 if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
74 for (d = subnetlist; d; d = d->next)
76 struct work_record *work;
78 for (work = d->workgrouplist; work; work = work->next)
80 /* if we are not the browse master of a workgroup, and we can't
81 find a browser on the subnet, do something about it. */
85 queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
86 work->token, work->work_group,0x1d,REGISTER,0,0,0,NULL,NULL,
87 True,False,d->bcast_ip,d->bcast_ip);
94 /*******************************************************************
95 what to do if a master browser DOESN't exist
96 ******************************************************************/
97 void browser_gone(char *work_name, struct in_addr ip)
99 struct subnet_record *d = find_subnet(ip);
100 struct work_record *work = find_workgroupstruct(d, work_name, False);
102 /* i don't know about this workgroup, therefore i don't care */
103 if (!work || !d) return;
105 /* don't do election stuff on the WINS subnet */
106 if (ip_equal(d->bcast_ip,ipgrp))
109 if (conf_should_local_master(work->token))
111 DEBUG(2,("Forcing election on %s %s\n",
112 work->work_group,inet_ntoa(d->bcast_ip)));
114 /* we can attempt to become master browser */
115 work->needelection = True;
119 /* local interfaces: force an election */
120 send_election(d, work->work_group, 0, 0, conf_browsing_alias(work->token));
122 /* only removes workgroup completely on a local interface
123 persistent lmhosts entries on a local interface _will_ be removed.
125 remove_workgroup(d, work,True);
130 /****************************************************************************
131 send an election packet
132 **************************************************************************/
133 void send_election(struct subnet_record *d, char *group,uint32 criterion,
134 int timeup,char *name)
141 DEBUG(2,("Sending election to %s for workgroup %s\n",
142 inet_ntoa(d->bcast_ip),group));
144 bzero(outbuf,sizeof(outbuf));
146 CVAL(p,0) = ANN_Election; /* election */
149 CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
150 SIVAL(p,1,criterion);
151 SIVAL(p,5,timeup*1000); /* ms - despite the spec */
155 p = skip_string(p,1);
157 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
158 name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
162 /****************************************************************************
163 un-register a SELF name that got rejected.
165 if this name happens to be rejected when samba is in the process
166 of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d)
167 or WORKGROUP(1b)) then we must stop being a master browser. sad.
169 **************************************************************************/
170 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
172 struct work_record *work;
174 remove_netbios_name(d,name,name_type,SELF,ipzero);
176 if (!(work = find_workgroupstruct(d, name, False))) return;
178 if (!AM_MASTER(work) && !AM_DMBRSE(work)) return;
180 if (ms_browser_name(name, name_type) ||
181 (conf_should_workgroup_member(work->token) &&
182 (name_type == 0x1d || name_type == 0x1b)))
186 if (ms_browser_name(name, name_type))
187 remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
188 if (name_type == 0x1d)
189 remove_type = SV_TYPE_MASTER_BROWSER;
190 if (name_type == 0x1b)
191 remove_type = SV_TYPE_DOMAIN_MASTER;
193 become_nonmaster(d, work, remove_type);
198 /****************************************************************************
201 if the name being added is a SELF name, we must additionally check
202 whether to proceed to the next stage in samba becoming a master browser.
204 **************************************************************************/
205 void name_register_work(struct subnet_record *d, int token,
206 char *name, int name_type, enum name_source source,
207 struct nmb_ip *data, time_t ttl, struct in_addr ip, BOOL bcast)
211 char *work_name = conf_workgroup_name(token);
212 struct work_record *work = find_workgroupstruct(d, work_name, False);
214 add_netbios_entry(d,name,name_type,data->nb_flags,
215 ttl,source,data->ip,True,!bcast);
219 if (work->state != MST_NONE)
221 /* samba is in the process of working towards master browser-ness.
222 initiate the next stage.
224 become_master(d, work);
232 /*******************************************************************
233 become the master browser.
235 this is done in stages. note that this could take a while,
236 particularly on a broadcast subnet, as we have to wait for
237 the implicit registration of each name to be accepted.
239 as each name is successfully registered, become_master() is
240 called again, in order to initiate the next stage. see
241 dead_netbios_entry() - deals with implicit name registration
242 and response_name_reg() - deals with explicit registration
245 stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
246 stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d)
247 stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b)
248 stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
250 XXXX note: this code still does not cope with the distinction
251 between different types of nodes, particularly between M and P
252 nodes. that comes later.
254 ******************************************************************/
255 void become_master(struct subnet_record *d, struct work_record *work)
259 /* domain type must be limited to domain enum + server type. it must
260 not have SV_TYPE_SERVER or anything else with SERVER in it, else
261 clients get confused and start thinking this entry is a server
264 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
271 my_name = conf_browsing_alias(work->token);
272 my_comment = conf_browsing_alias_comment(work->token);
274 StrnCpy(comment, my_comment, 43);
276 if (!conf_should_local_master(work->token))
278 DEBUG(1,("Should not become master for %s %s\n",
279 work->work_group,inet_ntoa(d->bcast_ip)));
280 work->state = MST_NONE;
284 DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
285 work->work_group,inet_ntoa(d->bcast_ip),work->state));
289 case MST_NONE: /* while we were nothing but a server... */
291 DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
292 work->state = MST_WON; /* ... an election win was successful */
294 work->ElectionCriterion |= 0x5;
296 /* update our server status */
297 work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
298 add_server_entry(d,work,my_name,work->ServerType,0,comment,True);
300 /* add special browser name */
301 add_my_name_entry(d,work->token,MSBROWSE,0x01,nb_type|NB_ACTIVE|NB_GROUP);
303 /* DON'T do anything else after calling add_my_name_entry() */
306 case MST_WON: /* while nothing had happened except we won an election... */
308 DEBUG(3,("go to second stage: register as master browser\n"));
309 work->state = MST_MSB; /* ... registering MSBROWSE was successful */
311 /* add server entry on successful registration of MSBROWSE */
312 add_server_entry(d,work,work->work_group,
313 domain_type,0,conf_browsing_alias(work->token),True);
315 /* add master name */
316 add_my_name_entry(d,work->token,work->work_group,0x1d,nb_type|NB_ACTIVE);
318 /* DON'T do anything else after calling add_my_name_entry() */
321 case MST_MSB: /* while we were still only registered MSBROWSE state... */
323 DEBUG(3,("2nd stage complete: registered as master browser\n"));
324 work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
326 /* update our server status */
327 work->ServerType |= SV_TYPE_MASTER_BROWSER;
328 add_server_entry(d,work,conf_browsing_alias(work->token),
329 work->ServerType,0,comment,True);
331 if (work->serverlist == NULL) /* no servers! */
333 /* ask all servers on our local net to announce to us */
334 announce_request(work, d->bcast_ip);
341 /* don't have to do anything: just report success */
342 DEBUG(3,("3rd stage: become master browser!\n"));
347 case MST_DOMAIN_NONE:
349 if (conf_should_domain_master(work->token))
351 work->state = MST_DOMAIN_MEM; /* ... become domain member */
352 DEBUG(3,("domain first stage: register as domain member\n"));
354 /* add domain member name */
355 add_my_name_entry(d,work->token,work->work_group,0x1e,
356 nb_type|NB_ACTIVE|NB_GROUP);
358 /* DON'T do anything else after calling add_my_name_entry() */
363 DEBUG(4,("samba not configured as a domain master.\n"));
371 if (conf_should_domain_master(work->token))
373 work->state = MST_DOMAIN_TST; /* ... possibly become domain master */
374 DEBUG(3,("domain second stage: register as domain master\n"));
376 if (conf_should_domain_logon(work->token))
378 work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
379 add_server_entry(d,work,my_name,work->ServerType,0,comment,True);
382 /* add domain master name */
383 add_my_name_entry(d,work->token,work->work_group,0x1b,
386 /* DON'T do anything else after calling add_my_name_entry() */
391 DEBUG(4,("samba not configured as a domain master.\n"));
397 case MST_DOMAIN_TST: /* while we were still a master browser... */
399 /* update our server status */
400 if (conf_should_domain_master(work->token))
402 struct subnet_record *d1;
403 uint32 update_type = 0;
405 DEBUG(3,("domain third stage: samba is now a domain master.\n"));
406 work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
408 update_type |= DFLT_SERVER_TYPE | SV_TYPE_DOMAIN_MASTER |
409 SV_TYPE_POTENTIAL_BROWSER;
411 work->ServerType |= update_type;
412 add_server_entry(d,work,my_name,work->ServerType,0,comment,True);
414 for (d1 = subnetlist; d1; d1 = d1->next)
416 struct work_record *w;
417 if (ip_equal(d1->bcast_ip, d->bcast_ip)) continue;
419 for (w = d1->workgrouplist; w; w = w->next)
421 struct server_record *s = find_server(w, my_name);
422 if (strequal(w->work_group, work->work_group))
424 w->ServerType |= update_type;
428 s->serv.type |= update_type;
429 DEBUG(4,("found server %s on %s: update to %8x\n",
430 s->serv.name, inet_ntoa(d1->bcast_ip),
442 /* don't have to do anything: just report success */
443 DEBUG(3,("fifth stage: there isn't one yet!\n"));
450 /*******************************************************************
451 unbecome the master browser. initates removal of necessary netbios
452 names, and tells the world that we are no longer a master browser.
453 ******************************************************************/
454 void become_nonmaster(struct subnet_record *d, struct work_record *work,
457 int new_server_type = work->ServerType;
463 BOOL wins = ip_equal(d->bcast_ip, ipgrp);
464 enum master_state state;
468 my_name = conf_browsing_alias (work->token);
469 my_comment = conf_browsing_alias_comment(work->token);
471 StrnCpy(comment, my_comment, 43);
473 DEBUG(2,("Becoming non-master for %s alias %s\n",work->work_group, my_name));
475 /* can only remove master or domain types with this function */
476 remove_type &= SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
478 /* unbecome a master browser; unbecome a domain master, too :-( */
479 if (remove_type & SV_TYPE_MASTER_BROWSER)
480 remove_type |= SV_TYPE_DOMAIN_MASTER;
482 new_server_type &= ~remove_type;
485 if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
487 /* no longer a master browser of any sort */
489 work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
490 work->ElectionCriterion &= ~0x4;
491 work->state = MST_NONE;
493 remove_name_entry(d,work->token,MSBROWSE,0x01);
496 work->ServerType = new_server_type;
498 if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
500 if (state == MST_DOMAIN)
503 work->state = MST_NONE;
505 work->state = MST_BROWSER;
507 remove_name_entry(d,work->token,work->work_group,0x1b);
510 if (!(work->ServerType & SV_TYPE_MASTER_BROWSER))
512 if (state >= MST_BROWSER)
513 work->state = MST_NONE;
514 remove_name_entry(d,work->token,work->work_group,0x1d);
517 /* update our internal records with our new server state */
518 add_server_entry(d, work, my_name, work->ServerType, 0, my_comment, True);
520 /* announce change in status on local interface */
523 /* XXXX do we need also to do an announce with a time of zero
524 if we are no longer a local master browser?
526 work->needannounce = True;
531 /*******************************************************************
533 ******************************************************************/
534 void run_elections(void)
536 time_t t = time(NULL);
537 static time_t lastime = 0;
539 struct subnet_record *d;
541 /* send election packets once a second */
542 if (lastime && t-lastime <= 0) return;
546 for (d = subnetlist; d; d = d->next)
548 struct work_record *work;
549 for (work = d->workgrouplist; work; work = work->next)
551 if (work->RunningElection)
553 char *alias = conf_browsing_alias(work->token);
555 send_election(d,work->work_group, work->ElectionCriterion,
556 t-StartupTime,alias);
558 if (work->ElectionCount++ >= 4)
560 /* I won! now what :-) */
561 DEBUG(2,(">>> Won election for %s as %s on %s <<<\n",
562 work->work_group,alias,inet_ntoa(d->bcast_ip)));
564 work->RunningElection = False;
565 work->state = MST_NONE;
567 become_master(d, work);
575 /*******************************************************************
576 work out if I win an election
577 ******************************************************************/
578 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
579 int timeup,char *name)
581 int mytimeup = time(NULL) - StartupTime;
582 uint32 mycriterion = work->ElectionCriterion;
584 DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
585 version,ELECTION_VERSION,
586 criterion,mycriterion,
590 if (version > ELECTION_VERSION) return(False);
591 if (version < ELECTION_VERSION) return(True);
593 if (criterion > mycriterion) return(False);
594 if (criterion < mycriterion) return(True);
596 if (timeup > mytimeup) return(False);
597 if (timeup < mytimeup) return(True);
599 if (strcasecmp(conf_browsing_alias(work->token),name) > 0) return(False);
605 /*******************************************************************
606 process a election packet
608 An election dynamically decides who will be the master.
609 ******************************************************************/
610 void process_election(struct packet_struct *p,char *buf)
612 struct dgram_packet *dgram = &p->packet.dgram;
613 struct in_addr ip = dgram->header.source_ip;
614 struct subnet_record *d = find_subnet(ip);
615 int version = CVAL(buf,0);
616 uint32 criterion = IVAL(buf,1);
617 int timeup = IVAL(buf,5)/1000;
619 struct work_record *work;
623 if (ip_equal(d->bcast_ip,ipgrp)) {
624 DEBUG(3,("Unexpected election request from %s %s on WINS net\n",
625 name, inet_ntoa(p->ip)));
631 DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
632 name,inet_ntoa(p->ip),version,criterion,timeup));
634 if (same_context(dgram)) return;
636 for (work = d->workgrouplist; work; work = work->next)
638 if (!conf_should_local_master(work->token)) continue;
640 if (win_election(work, version,criterion,timeup,name))
642 if (!work->RunningElection)
644 work->needelection = True;
645 work->ElectionCount=0;
646 work->state = MST_NONE;
651 work->needelection = False;
653 if (work->RunningElection || AM_MASTER(work))
655 work->RunningElection = False;
656 DEBUG(3,(">>> Lost election on %s %s <<<\n",
657 work->work_group,inet_ntoa(d->bcast_ip)));
659 /* if we are the master then remove our masterly names */
662 become_nonmaster(d, work,
663 SV_TYPE_MASTER_BROWSER|
664 SV_TYPE_DOMAIN_MASTER);
672 /****************************************************************************
673 checks whether a browser election is to be run on any workgroup
675 this function really ought to return the time between election
676 packets (which depends on whether samba intends to be a domain
677 master or a master browser) in milliseconds.
679 ***************************************************************************/
680 BOOL check_elections(void)
682 struct subnet_record *d;
683 BOOL run_any_election = False;
685 for (d = subnetlist; d; d = d->next)
687 struct work_record *work;
688 for (work = d->workgrouplist; work; work = work->next)
690 run_any_election |= work->RunningElection;
692 if (work->needelection && !work->RunningElection)
694 DEBUG(3,(">>> Starting election on %s %s <<<\n",
695 work->work_group,inet_ntoa(d->bcast_ip)));
696 work->ElectionCount = 0;
697 work->RunningElection = True;
698 work->needelection = False;
702 return run_any_election;