e115b0d88185d7ae138f0f9bb8d3df6a4bb53443
[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
32 */
33
34 #include "includes.h"
35
36 extern int ClientNMB;
37 extern int ClientDGRAM;
38
39 extern int DEBUGLEVEL;
40 extern pstring scope;
41
42 extern pstring myname;
43 extern struct in_addr ipzero;
44
45 /* machine comment for host announcements */
46 extern  pstring ServerComment;
47
48 /* here are my election parameters */
49
50 extern time_t StartupTime;
51
52 extern struct subnet_record *subnetlist;
53
54
55 /*******************************************************************
56   occasionally check to see if the master browser is around
57   ******************************************************************/
58 void check_master_browser(void)
59 {
60   static time_t lastrun=0;
61   time_t t = time(NULL);
62   struct subnet_record *d;
63
64   if (!lastrun) lastrun = t;
65   if (t < lastrun + CHECK_TIME_MST_BROWSE * 60)
66     return;
67   lastrun = t;
68
69   dump_workgroups();
70
71   for (d = subnetlist; d; d = d->next)
72     {
73       struct work_record *work;
74
75       for (work = d->workgrouplist; work; work = work->next)
76         {
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. */
79
80           if (!AM_MASTER(work))
81             {
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);
85             }
86         }
87     }
88 }
89
90
91 /*******************************************************************
92   what to do if a master browser DOESN't exist
93   ******************************************************************/
94 void browser_gone(char *work_name, struct in_addr ip)
95 {
96   struct subnet_record *d = find_subnet(ip);
97   struct work_record *work = find_workgroupstruct(d, work_name, False);
98
99   /* i don't know about this workgroup, therefore i don't care */
100   if (!work || !d) return;
101  
102   if (strequal(work->work_group, lp_workgroup()) && d->my_interface)
103   {
104
105       DEBUG(2,("Forcing election on %s %s\n",
106                work->work_group,inet_ntoa(d->bcast_ip)));
107
108       /* we can attempt to become master browser */
109       work->needelection = True;
110   }
111   else
112   {
113      /* local interfaces: force an election */
114      if (d->my_interface)
115        send_election(d, work->work_group, 0, 0, myname);
116
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).
122       */
123      remove_workgroup(d, work, d->my_interface);      
124   }
125 }
126
127
128 /****************************************************************************
129   send an election packet
130   **************************************************************************/
131 void send_election(struct subnet_record *d, char *group,uint32 criterion,
132                    int timeup,char *name)
133 {
134   pstring outbuf;
135   char *p;
136
137   if (!d) return;
138   
139   DEBUG(2,("Sending election to %s for workgroup %s\n",
140            inet_ntoa(d->bcast_ip),group));         
141
142   bzero(outbuf,sizeof(outbuf));
143   p = outbuf;
144   CVAL(p,0) = ANN_Election; /* election */
145   p++;
146
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);
154   
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));
157 }
158
159
160 /****************************************************************************
161   un-register a SELF name that got rejected.
162
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.
166
167   **************************************************************************/
168 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
169 {
170     struct work_record *work;
171
172     remove_netbios_name(d,name,name_type,SELF,ipzero);
173
174     if (!(work = find_workgroupstruct(d, name, False))) return;
175
176     if (ms_browser_name(name, name_type) ||
177         (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
178          (name_type == 0x1d || name_type == 0x1b)))
179     {
180       int remove_type = 0;
181
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;
188                         
189       become_nonmaster(d, work, remove_type);
190     }
191 }
192
193
194 /****************************************************************************
195   registers a name.
196
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.
199
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)
203 {
204   enum name_source source = ismyip(ip) ? SELF : REGISTER;
205
206   if (source == SELF)
207   {
208     struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
209
210     if (work && work->state != MST_NONE)
211     {
212       /* samba is in the process of working towards master browser-ness.
213          initiate the next stage.
214        */
215       become_master(d, work);
216     }
217   }
218   add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
219 }
220
221
222 /*******************************************************************
223   become the master browser.
224
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.
228
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.
234
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...)
239
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.
243
244   ******************************************************************/
245 void become_master(struct subnet_record *d, struct work_record *work)
246 {
247   uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX|0x00400000;
248
249   if (!work) return;
250   
251   DEBUG(2,("Becoming master for %s (currently at stage %d)\n",
252                                         work->work_group,work->state));
253   
254   switch (work->state)
255   {
256     case MST_NONE: /* while we were nothing but a server... */
257     {
258       work->state = MST_WON; /* ... an election win was successful */
259
260       work->ElectionCriterion |= 0x5;
261
262       /* update our server status */
263       work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
264       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
265
266       DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
267
268       /* add special browser name */
269       add_my_name_entry(d,MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
270
271       break;
272     }
273     case MST_WON: /* while nothing had happened except we won an election... */
274     {
275       work->state = MST_MSB; /* ... registering MSBROWSE was successful */
276
277       /* add server entry on successful registration of MSBROWSE */
278       add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
279
280       DEBUG(3,("go to second stage: register as master browser\n"));
281
282       /* add master name */
283       add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE         );
284   
285       break;
286     }
287     case MST_MSB: /* while we were still only registered MSBROWSE state... */
288     {
289       work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
290
291       /* update our server status */
292       work->ServerType |= SV_TYPE_MASTER_BROWSER;
293       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
294
295       if (d->my_interface && work->serverlist == NULL) /* no servers! */
296       {
297         /* ask all servers on our local net to announce to us */
298         announce_request(work, d->bcast_ip);
299       }
300
301       if (lp_domain_master())
302       {
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         );
306       }
307       else
308       {
309         DEBUG(3,("samba not configured as a domain master: no third stage.\n"));
310       }
311   
312       break;
313     }
314     case MST_BROWSER: /* while we were still a master browser... */
315     {
316       work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
317
318       /* update our server status */
319       if (lp_domain_master())
320       {
321         work->ServerType |= SV_TYPE_DOMAIN_MASTER;
322       
323         if (lp_domain_logons())
324             {
325               work->ServerType |= SV_TYPE_DOMAIN_CTRL;
326               work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
327             }
328         DEBUG(3,("fourth stage: samba is now a domain master.\n"));
329         add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
330       }
331   
332       break;
333     }
334     case MST_DOMAIN:
335     {
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;
339     }
340   }
341 }
342
343
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)
350 {
351   int new_server_type = work->ServerType;
352
353   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
354   
355   /* can only remove master or domain types with this function */
356   remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
357
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;
361
362   new_server_type &= ~remove_type;
363
364   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
365   {
366     /* no longer a master browser of any sort */
367
368         work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
369     work->ElectionCriterion &= ~0x4;
370     work->state = MST_NONE;
371
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);
375   }
376   
377   work->ServerType = new_server_type;
378
379   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
380   {
381     if (work->state == MST_DOMAIN)
382       work->state = MST_BROWSER;
383     remove_name_entry(d,work->work_group,0x1b);
384     
385   }
386
387   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
388   {
389     if (work->state >= MST_BROWSER)
390       work->state = MST_NONE;
391     remove_name_entry(d,work->work_group,0x1d);
392   }
393 }
394
395
396 /*******************************************************************
397   run the election
398   ******************************************************************/
399 void run_elections(void)
400 {
401   time_t t = time(NULL);
402   static time_t lastime = 0;
403   
404   struct subnet_record *d;
405   
406   /* send election packets once a second */
407   if (lastime && t-lastime <= 0) return;
408   
409   lastime = t;
410   
411   for (d = subnetlist; d; d = d->next)
412     {
413       struct work_record *work;
414       for (work = d->workgrouplist; work; work = work->next)
415         {
416           if (work->RunningElection)
417             {
418               send_election(d,work->work_group, work->ElectionCriterion,
419                             t-StartupTime,myname);
420               
421               if (work->ElectionCount++ >= 4)
422                 {
423                   /* I won! now what :-) */
424                   DEBUG(2,(">>> Won election on %s %s <<<\n",
425                            work->work_group,inet_ntoa(d->bcast_ip)));
426                   
427                   work->RunningElection = False;
428           work->state = MST_NONE;
429
430                   become_master(d, work);
431                 }
432             }
433         }
434     }
435 }
436
437
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)
443 {  
444   time_t t = time(NULL);
445   uint32 mycriterion;
446   if (version > ELECTION_VERSION) return(False);
447   if (version < ELECTION_VERSION) return(True);
448   
449   mycriterion = work->ElectionCriterion;
450
451   if (criterion > mycriterion) return(False);
452   if (criterion < mycriterion) return(True);
453
454   if (timeup > (t - StartupTime)) return(False);
455   if (timeup < (t - StartupTime)) return(True);
456
457   if (strcasecmp(myname,name) > 0) return(False);
458   
459   return(True);
460 }
461
462
463 /*******************************************************************
464   process a election packet
465
466   An election dynamically decides who will be the master. 
467   ******************************************************************/
468 void process_election(struct packet_struct *p,char *buf)
469 {
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;
478
479   if (!d) return;
480   
481   name[15] = 0;  
482
483   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
484            name,version,criterion,timeup));
485   
486   if (same_context(dgram)) return;
487   
488   for (work = d->workgrouplist; work; work = work->next)
489     {
490       if (listening_name(work, &dgram->dest_name) && 
491           strequal(work->work_group, lp_workgroup()) &&
492           d->my_interface)
493         {
494           if (win_election(work, version,criterion,timeup,name))
495             {
496               if (!work->RunningElection)
497                 {
498                   work->needelection = True;
499                   work->ElectionCount=0;
500           work->state = MST_NONE;
501                 }
502             }
503           else
504             {
505               work->needelection = False;
506               
507               if (work->RunningElection)
508                 {
509                   work->RunningElection = False;
510                   DEBUG(3,(">>> Lost election on %s %s <<<\n",
511                            work->work_group,inet_ntoa(d->bcast_ip)));
512                   
513                   /* if we are the master then remove our masterly names */
514                   if (AM_MASTER(work))
515                   {
516                       become_nonmaster(d, work,
517                                         SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
518                   }
519                 }
520             }
521         }
522     }
523 }
524
525
526 /****************************************************************************
527   checks whether a browser election is to be run on any workgroup
528
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.
532
533   ***************************************************************************/
534 BOOL check_elections(void)
535 {
536   struct subnet_record *d;
537   BOOL run_any_election = False;
538
539   for (d = subnetlist; d; d = d->next)
540     {
541       struct work_record *work;
542       for (work = d->workgrouplist; work; work = work->next)
543         {
544           run_any_election |= work->RunningElection;
545           
546           if (work->needelection && !work->RunningElection)
547             {
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;
553             }
554         }
555     }
556   return run_any_election;
557 }
558