6c8a4751f43fd8a2888960a51ff2d72cb883c25a
[samba.git] / source / nameelect.c
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
6    
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.
11    
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.
16    
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.
20    
21    Module name: nameelect.c
22
23    Revision History:
24
25    14 jan 96: lkcl@pires.co.uk
26    added multiple workgroup domain master support
27
28    04 jul 96: lkcl@pires.co.uk
29    added system to become a master browser by stages.
30
31    30 July 96: David.Chappell@mail.trincoll.edu
32    Expanded multiple workgroup domain master browser support.
33
34 */
35
36 #include "includes.h"          
37
38 extern int ClientNMB;
39 extern int ClientDGRAM;
40
41 extern int DEBUGLEVEL;
42 extern pstring scope;
43
44 extern struct in_addr ipzero;
45 extern struct in_addr ipgrp;
46
47 /* here are my election parameters */
48
49 extern time_t StartupTime;
50
51 extern struct subnet_record *subnetlist;
52
53 extern uint16 nb_type; /* samba's NetBIOS name type */
54
55 extern pstring myname;
56
57
58 /*******************************************************************
59   occasionally check to see if the master browser is around
60   ******************************************************************/
61 void check_master_browser(void)
62 {
63   static time_t lastrun=0;
64   time_t t = time(NULL);
65   struct subnet_record *d;
66
67   if (!lastrun) lastrun = t;
68   if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
69     return;
70   lastrun = t;
71
72   dump_workgroups();
73
74   for (d = subnetlist; d; d = d->next)
75   {
76     struct work_record *work;
77
78     for (work = d->workgrouplist; work; work = work->next)
79     {
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. */
82
83       if (!AM_MASTER(work))
84       {
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);
88       }
89     }
90   }
91 }
92
93
94 /*******************************************************************
95   what to do if a master browser DOESN't exist
96   ******************************************************************/
97 void browser_gone(char *work_name, struct in_addr ip)
98 {
99   struct subnet_record *d = find_subnet(ip);
100   struct work_record *work = find_workgroupstruct(d, work_name, False);
101
102   /* i don't know about this workgroup, therefore i don't care */
103   if (!work || !d) return;
104
105   /* don't do election stuff on the WINS subnet */
106   if (ip_equal(d->bcast_ip,ipgrp)) 
107     return;
108
109   if (conf_should_local_master(work->token))
110   {
111       DEBUG(2,("Forcing election on %s %s\n",
112            work->work_group,inet_ntoa(d->bcast_ip)));
113
114       /* we can attempt to become master browser */
115       work->needelection = True;
116   }
117   else
118   {
119      /* local interfaces: force an election */
120     send_election(d, work->work_group, 0, 0, conf_browsing_alias(work->token));
121
122      /* only removes workgroup completely on a local interface 
123         persistent lmhosts entries on a local interface _will_ be removed.
124       */
125      remove_workgroup(d, work,True);
126   }
127 }
128
129
130 /****************************************************************************
131   send an election packet
132   **************************************************************************/
133 void send_election(struct subnet_record *d, char *group,uint32 criterion,
134            int timeup,char *name)
135 {
136   pstring outbuf;
137   char *p;
138
139   if (!d) return;
140   
141   DEBUG(2,("Sending election to %s for workgroup %s\n",
142        inet_ntoa(d->bcast_ip),group));     
143
144   bzero(outbuf,sizeof(outbuf));
145   p = outbuf;
146   CVAL(p,0) = ANN_Election; /* election */
147   p++;
148
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 */
152   p += 13;
153   strcpy(p,name);
154   strupper(p);
155   p = skip_string(p,1);
156   
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));
159 }
160
161
162 /****************************************************************************
163   un-register a SELF name that got rejected.
164
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.
168
169   **************************************************************************/
170 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
171 {
172     struct work_record *work;
173
174     remove_netbios_name(d,name,name_type,SELF,ipzero);
175
176     if (!(work = find_workgroupstruct(d, name, False))) return;
177
178     if (!AM_MASTER(work) && !AM_DMBRSE(work)) return;
179
180     if (ms_browser_name(name, name_type) ||
181         (conf_should_workgroup_member(work->token) &&
182          (name_type == 0x1d || name_type == 0x1b)))
183     {
184       int remove_type = 0;
185
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;
192             
193       become_nonmaster(d, work, remove_type);
194     }
195 }
196
197
198 /****************************************************************************
199   registers a name.
200
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.
203
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)
208 {
209   if (source == SELF)
210   {
211     char *work_name = conf_workgroup_name(token);
212     struct work_record *work = find_workgroupstruct(d, work_name, False);
213
214     add_netbios_entry(d,name,name_type,data->nb_flags,
215                       ttl,source,data->ip,True,!bcast);
216
217     if (work)
218     {
219       if (work->state != MST_NONE)
220       {
221         /* samba is in the process of working towards master browser-ness.
222            initiate the next stage.
223          */
224         become_master(d, work);
225         return;
226       }
227     }
228   }
229 }
230
231
232 /*******************************************************************
233   become the master browser.
234
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.
238
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
243   with a WINS server.
244
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...)
249
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.
253
254   ******************************************************************/
255 void become_master(struct subnet_record *d, struct work_record *work)
256 {
257   pstring comment;
258
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
262      not a workgroup
263    */
264   uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
265
266   char *my_name   ;
267   char *my_comment;
268
269   if (!work) return;
270   
271   my_name    = conf_browsing_alias(work->token);
272   my_comment = conf_browsing_alias_comment(work->token);
273
274   StrnCpy(comment, my_comment, 43);
275   
276   if (!conf_should_local_master(work->token))
277   {
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;
281     return;
282   }
283
284   DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n",
285                     work->work_group,inet_ntoa(d->bcast_ip),work->state));
286   
287   switch (work->state)
288   {
289     case MST_NONE: /* while we were nothing but a server... */
290     {
291       DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
292       work->state = MST_WON; /* ... an election win was successful */
293
294       work->ElectionCriterion |= 0x5;
295
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);
299
300       /* add special browser name */
301       add_my_name_entry(d,work->token,MSBROWSE,0x01,nb_type|NB_ACTIVE|NB_GROUP);
302
303       /* DON'T do anything else after calling add_my_name_entry() */
304       return;
305     }
306     case MST_WON: /* while nothing had happened except we won an election... */
307     {
308       DEBUG(3,("go to second stage: register as master browser\n"));
309       work->state = MST_MSB; /* ... registering MSBROWSE was successful */
310
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);
314
315       /* add master name */
316       add_my_name_entry(d,work->token,work->work_group,0x1d,nb_type|NB_ACTIVE);
317   
318       /* DON'T do anything else after calling add_my_name_entry() */
319       return;
320     }
321     case MST_MSB: /* while we were still only registered MSBROWSE state... */
322     {
323       DEBUG(3,("2nd stage complete: registered as master browser\n"));
324       work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
325
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);
330
331       if (work->serverlist == NULL) /* no servers! */
332       {
333         /* ask all servers on our local net to announce to us */
334         announce_request(work, d->bcast_ip);
335       }
336       break;
337    }
338
339    case MST_BROWSER:
340    {
341       /* don't have to do anything: just report success */
342       DEBUG(3,("3rd stage: become master browser!\n"));
343
344       break;
345    }
346
347    case MST_DOMAIN_NONE:
348    {
349       if (conf_should_domain_master(work->token))
350       {
351         work->state = MST_DOMAIN_MEM; /* ... become domain member */
352         DEBUG(3,("domain first stage: register as domain member\n"));
353
354         /* add domain member name */
355         add_my_name_entry(d,work->token,work->work_group,0x1e,
356                           nb_type|NB_ACTIVE|NB_GROUP);
357
358         /* DON'T do anything else after calling add_my_name_entry() */
359         return;
360       }
361       else
362       {
363         DEBUG(4,("samba not configured as a domain master.\n"));
364       }
365   
366       break;
367    }
368
369    case MST_DOMAIN_MEM:
370    {
371       if (conf_should_domain_master(work->token))
372       {
373         work->state = MST_DOMAIN_TST; /* ... possibly become domain master */
374         DEBUG(3,("domain second stage: register as domain master\n"));
375
376         if (conf_should_domain_logon(work->token))
377         {
378           work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
379           add_server_entry(d,work,my_name,work->ServerType,0,comment,True);
380         }
381
382         /* add domain master name */
383         add_my_name_entry(d,work->token,work->work_group,0x1b,
384                           nb_type|NB_ACTIVE         );
385
386         /* DON'T do anything else after calling add_my_name_entry() */
387         return;
388       }
389       else
390       {
391         DEBUG(4,("samba not configured as a domain master.\n"));
392       }
393   
394       break;
395     }
396
397     case MST_DOMAIN_TST: /* while we were still a master browser... */
398     {
399       /* update our server status */
400       if (conf_should_domain_master(work->token))
401       {
402         struct subnet_record *d1;
403         uint32 update_type = 0;
404
405         DEBUG(3,("domain third stage: samba is now a domain master.\n"));
406         work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
407
408         update_type |= DFLT_SERVER_TYPE | SV_TYPE_DOMAIN_MASTER | 
409           SV_TYPE_POTENTIAL_BROWSER;
410
411         work->ServerType |= update_type;
412         add_server_entry(d,work,my_name,work->ServerType,0,comment,True);
413
414         for (d1 = subnetlist; d1; d1 = d1->next)
415         {
416             struct work_record *w;
417             if (ip_equal(d1->bcast_ip, d->bcast_ip)) continue;
418
419             for (w = d1->workgrouplist; w; w = w->next)
420             {
421                 struct server_record *s = find_server(w, my_name);
422                 if (strequal(w->work_group, work->work_group))
423                 {
424                     w->ServerType |= update_type;
425                 }
426                 if (s)
427                 {
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),
431                                     s->serv.type));
432                 }
433             }
434         }
435       }
436   
437       break;
438     }
439
440     case MST_DOMAIN:
441     {
442       /* don't have to do anything: just report success */
443       DEBUG(3,("fifth stage: there isn't one yet!\n"));
444       break;
445     }
446   }
447 }
448
449
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,
455                 int remove_type)
456 {
457   int new_server_type = work->ServerType;
458
459   pstring comment;
460
461   char *my_name   ;
462   char *my_comment;
463   BOOL wins = ip_equal(d->bcast_ip, ipgrp);
464   enum master_state state;
465
466   if (!work) return;
467   
468   my_name    = conf_browsing_alias        (work->token);
469   my_comment = conf_browsing_alias_comment(work->token);
470
471   StrnCpy(comment, my_comment, 43);
472   
473   DEBUG(2,("Becoming non-master for %s alias %s\n",work->work_group, my_name));
474   
475   /* can only remove master or domain types with this function */
476   remove_type &= SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER;
477
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;
481
482   new_server_type &= ~remove_type;
483   state = work->state;
484
485   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
486   {
487     /* no longer a master browser of any sort */
488
489     work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
490     work->ElectionCriterion &= ~0x4;
491     work->state = MST_NONE;
492
493     remove_name_entry(d,work->token,MSBROWSE,0x01);
494   }
495   
496   work->ServerType = new_server_type;
497
498   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
499   {
500     if (state == MST_DOMAIN)
501     {
502       if (wins)
503         work->state = MST_NONE;
504       else
505         work->state = MST_BROWSER;
506     }
507     remove_name_entry(d,work->token,work->work_group,0x1b);
508   }
509
510   if (!(work->ServerType & SV_TYPE_MASTER_BROWSER))
511   {
512     if (state >= MST_BROWSER)
513       work->state = MST_NONE;
514     remove_name_entry(d,work->token,work->work_group,0x1d);
515   }
516
517   /* update our internal records with our new server state */
518   add_server_entry(d, work, my_name, work->ServerType, 0, my_comment, True);
519
520   /* announce change in status on local interface */
521   if (!wins)
522   {
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?
525      */
526     work->needannounce = True;
527   }
528 }
529
530
531 /*******************************************************************
532   run the election
533   ******************************************************************/
534 void run_elections(void)
535 {
536   time_t t = time(NULL);
537   static time_t lastime = 0;
538   
539   struct subnet_record *d;
540   
541   /* send election packets once a second */
542   if (lastime && t-lastime <= 0) return;
543   
544   lastime = t;
545   
546   for (d = subnetlist; d; d = d->next)
547   {
548     struct work_record *work;
549     for (work = d->workgrouplist; work; work = work->next)
550     {
551       if (work->RunningElection)
552       {
553         char *alias = conf_browsing_alias(work->token);
554
555         send_election(d,work->work_group, work->ElectionCriterion,
556                 t-StartupTime,alias);
557           
558         if (work->ElectionCount++ >= 4)
559         {
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)));
563           
564           work->RunningElection = False;
565           work->state = MST_NONE;
566
567           become_master(d, work);
568         }
569       }
570     }
571   }
572 }
573
574
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)
580 {  
581   int mytimeup = time(NULL) - StartupTime;
582   uint32 mycriterion = work->ElectionCriterion;
583
584   DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n",
585            version,ELECTION_VERSION,
586            criterion,mycriterion,
587            timeup,mytimeup,
588            name,myname));
589
590   if (version > ELECTION_VERSION) return(False);
591   if (version < ELECTION_VERSION) return(True);
592   
593   if (criterion > mycriterion) return(False);
594   if (criterion < mycriterion) return(True);
595
596   if (timeup > mytimeup) return(False);
597   if (timeup < mytimeup) return(True);
598
599   if (strcasecmp(conf_browsing_alias(work->token),name) > 0) return(False);
600   
601   return(True);
602 }
603
604
605 /*******************************************************************
606   process a election packet
607
608   An election dynamically decides who will be the master. 
609   ******************************************************************/
610 void process_election(struct packet_struct *p,char *buf)
611 {
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;
618   char *name = buf+13;
619   struct work_record *work;
620
621   if (!d) return;
622
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)));
626     return;
627   }
628   
629   name[15] = 0;  
630
631   DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n",
632            name,inet_ntoa(p->ip),version,criterion,timeup));
633   
634   if (same_context(dgram)) return;
635   
636   for (work = d->workgrouplist; work; work = work->next)
637   {
638     if (!conf_should_local_master(work->token)) continue;
639
640     if (win_election(work, version,criterion,timeup,name))
641     {
642         if (!work->RunningElection)
643         {
644           work->needelection = True;
645           work->ElectionCount=0;
646           work->state = MST_NONE;
647         }
648     }
649     else
650     {
651         work->needelection = False;
652           
653         if (work->RunningElection || AM_MASTER(work))
654         {
655           work->RunningElection = False;
656           DEBUG(3,(">>> Lost election on %s %s <<<\n",
657                work->work_group,inet_ntoa(d->bcast_ip)));
658           
659           /* if we are the master then remove our masterly names */
660           if (AM_MASTER(work))
661           {
662               become_nonmaster(d, work,
663                     SV_TYPE_MASTER_BROWSER|
664                     SV_TYPE_DOMAIN_MASTER);
665           }
666         }
667     }
668   }
669 }
670
671
672 /****************************************************************************
673   checks whether a browser election is to be run on any workgroup
674
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.
678
679   ***************************************************************************/
680 BOOL check_elections(void)
681 {
682   struct subnet_record *d;
683   BOOL run_any_election = False;
684
685   for (d = subnetlist; d; d = d->next)
686     {
687       struct work_record *work;
688       for (work = d->workgrouplist; work; work = work->next)
689     {
690       run_any_election |= work->RunningElection;
691       
692       if (work->needelection && !work->RunningElection)
693         {
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;
699         }
700     }
701     }
702   return run_any_election;
703 }
704