386e7b0bab3c75d381e038273149cff0d7e9ca01
[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   if (!work || !d) return;
100
101   if (strequal(work->work_group, lp_workgroup()) &&
102       ismybcast(d->bcast_ip))
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       /* XXXX note: this will delete entries that have been added in by
114          lmhosts as well. a flag to ensure that these are not deleted may
115          be considered */
116       
117       /* workgroup with no master browser is not the default workgroup:
118          it's also not on our subnet. therefore delete it: it can be
119          recreated dynamically */
120       
121       send_election(d, work->work_group, 0, 0, myname);
122       remove_workgroup(d, work);      
123     }
124 }
125
126
127 /****************************************************************************
128   send an election packet
129   **************************************************************************/
130 void send_election(struct subnet_record *d, char *group,uint32 criterion,
131                    int timeup,char *name)
132 {
133   pstring outbuf;
134   char *p;
135
136   if (!d) return;
137   
138   DEBUG(2,("Sending election to %s for workgroup %s\n",
139            inet_ntoa(d->bcast_ip),group));         
140
141   bzero(outbuf,sizeof(outbuf));
142   p = outbuf;
143   CVAL(p,0) = ANN_Election; /* election */
144   p++;
145
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);
153   
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));
156 }
157
158
159 /****************************************************************************
160   un-register a SELF name that got rejected.
161
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.
165
166   **************************************************************************/
167 void name_unregister_work(struct subnet_record *d, char *name, int name_type)
168 {
169     struct work_record *work;
170
171     remove_netbios_name(d,name,name_type,SELF,ipzero);
172
173     if (!(work = find_workgroupstruct(d, name, False))) return;
174
175     if (ms_browser_name(name, name_type) ||
176         (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 &&
177          (name_type == 0x1d || name_type == 0x1b)))
178     {
179       int remove_type = 0;
180
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;
187                         
188       become_nonmaster(d, work, remove_type);
189     }
190 }
191
192
193 /****************************************************************************
194   registers a name.
195
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.
198
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)
202 {
203   enum name_source source = ismyip(ip) ? SELF : REGISTER;
204
205   if (source == SELF)
206   {
207     struct work_record *work = find_workgroupstruct(d, lp_workgroup(), False);
208
209     if (work && work->state != MST_NONE)
210     {
211       /* samba is in the process of working towards master browser-ness.
212          initiate the next stage.
213        */
214       become_master(d, work);
215     }
216   }
217   add_netbios_entry(d,name,name_type,nb_flags,ttl,source,ip,True,!bcast);
218 }
219
220
221 /*******************************************************************
222   become the master browser.
223
224   this is done in stages. note that this could take a while, 
225   particularly on a broadcast subnet, as we have to wait for
226   the implicit registration of each name to be accepted.
227
228   as each name is successfully registered, become_master() is
229   called again, in order to initiate the next stage. see
230   dead_netbios_entry() - deals with implicit name registration
231   and response_name_reg() - deals with explicit registration
232   with a WINS server.
233
234   stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1.
235   stage 2: was MST_WON  - go to MST_MSB  and register WORKGROUP(0x1d)
236   stage 3: was MST_MSB  - go to MST_BROWSER and register WORKGROUP(0x1b)
237   stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...)
238
239   XXXX note: this code still does not cope with the distinction
240   between different types of nodes, particularly between M and P
241   nodes. that comes later.
242
243   ******************************************************************/
244 void become_master(struct subnet_record *d, struct work_record *work)
245 {
246   uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX|0x00400000;
247
248   if (!work) return;
249   
250   DEBUG(2,("Becoming master for %s (stage %d)",work->work_group,work->state));
251   
252   switch (work->state)
253   {
254     case MST_NONE: /* while we were nothing but a server... */
255     {
256       work->state = MST_WON; /* ... an election win was successful */
257
258       work->ElectionCriterion |= 0x5;
259
260       /* update our server status */
261       work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
262       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
263
264       DEBUG(2,("first stage: register ^1^2__MSBROWSE__^2^1\n"));
265
266       /* add special browser name */
267       add_my_name_entry(d,MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
268
269       break;
270     }
271     case MST_WON: /* while nothing had happened except we won an election... */
272     {
273       work->state = MST_MSB; /* ... registering MSBROWSE was successful */
274
275       /* add server entry on successful registration of MSBROWSE */
276       add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
277
278       DEBUG(2,("second stage: register as master browser\n"));
279
280       /* add master name */
281       add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE         );
282   
283       break;
284     }
285     case MST_MSB: /* while we were still only registered MSBROWSE state... */
286     {
287       work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
288
289       /* update our server status */
290       work->ServerType |= SV_TYPE_MASTER_BROWSER;
291       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
292
293       if (d->my_interface && work->serverlist == NULL) /* no servers! */
294       {
295         /* ask all servers on our local net to announce to us */
296         announce_request(work, d->bcast_ip);
297       }
298
299       if (lp_domain_master())
300       {
301         DEBUG(2,("third stage: register as domain master\n"));
302         /* add domain master name */
303         add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE         );
304       }
305       else
306       {
307         DEBUG(2,("samba not configured as a domain master: no third stage.\n"));
308       }
309   
310       break;
311     }
312     case MST_BROWSER: /* while we were still a master browser... */
313     {
314       work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
315
316       /* update our server status */
317       if (lp_domain_master())
318       {
319         work->ServerType |= SV_TYPE_DOMAIN_MASTER;
320       
321         if (lp_domain_logons())
322             {
323               work->ServerType |= SV_TYPE_DOMAIN_CTRL;
324               work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
325             }
326         DEBUG(2,("fourth stage: samba is now a domain master.\n"));
327         add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
328       }
329   
330       break;
331     }
332     case MST_DOMAIN:
333     {
334       /* nothing else to become, at the moment: we are top-dog. */
335       DEBUG(2,("fifth stage: there isn't one yet!\n"));
336       break;
337     }
338   }
339 }
340
341
342 /*******************************************************************
343   unbecome the master browser. initates removal of necessary netbios 
344   names, and tells the world that we are no longer a master browser.
345   ******************************************************************/
346 void become_nonmaster(struct subnet_record *d, struct work_record *work,
347                                 int remove_type)
348 {
349   int new_server_type = work->ServerType;
350
351   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
352   
353   /* can only remove master or domain types with this function */
354   remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
355
356   /* unbecome a master browser; unbecome a domain master, too :-( */
357   if (remove_type & SV_TYPE_MASTER_BROWSER)
358     remove_type |= SV_TYPE_DOMAIN_MASTER;
359
360   new_server_type &= ~remove_type;
361
362   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
363   {
364     /* no longer a master browser of any sort */
365
366         work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
367     work->ElectionCriterion &= ~0x4;
368     work->state = MST_NONE;
369
370         /* announce ourselves as no longer active as a master browser. */
371     announce_server(d, work, work->work_group, myname, 0, 0);
372     remove_name_entry(d,MSBROWSE        ,0x01);
373   }
374   
375   work->ServerType = new_server_type;
376
377   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
378   {
379     if (work->state == MST_DOMAIN)
380       work->state = MST_BROWSER;
381     remove_name_entry(d,work->work_group,0x1b);
382     
383   }
384
385   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
386   {
387     if (work->state >= MST_BROWSER)
388       work->state = MST_NONE;
389     remove_name_entry(d,work->work_group,0x1d);
390   }
391 }
392
393
394 /*******************************************************************
395   run the election
396   ******************************************************************/
397 void run_elections(void)
398 {
399   time_t t = time(NULL);
400   static time_t lastime = 0;
401   
402   struct subnet_record *d;
403   
404   /* send election packets once a second */
405   if (lastime && t-lastime <= 0) return;
406   
407   lastime = t;
408   
409   for (d = subnetlist; d; d = d->next)
410     {
411       struct work_record *work;
412       for (work = d->workgrouplist; work; work = work->next)
413         {
414           if (work->RunningElection)
415             {
416               send_election(d,work->work_group, work->ElectionCriterion,
417                             t-StartupTime,myname);
418               
419               if (work->ElectionCount++ >= 4)
420                 {
421                   /* I won! now what :-) */
422                   DEBUG(2,(">>> Won election on %s %s <<<\n",
423                            work->work_group,inet_ntoa(d->bcast_ip)));
424                   
425                   work->RunningElection = False;
426           work->state = MST_NONE;
427
428                   become_master(d, work);
429                 }
430             }
431         }
432     }
433 }
434
435
436 /*******************************************************************
437   work out if I win an election
438   ******************************************************************/
439 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
440                          int timeup,char *name)
441 {  
442   time_t t = time(NULL);
443   uint32 mycriterion;
444   if (version > ELECTION_VERSION) return(False);
445   if (version < ELECTION_VERSION) return(True);
446   
447   mycriterion = work->ElectionCriterion;
448
449   if (criterion > mycriterion) return(False);
450   if (criterion < mycriterion) return(True);
451
452   if (timeup > (t - StartupTime)) return(False);
453   if (timeup < (t - StartupTime)) return(True);
454
455   if (strcasecmp(myname,name) > 0) return(False);
456   
457   return(True);
458 }
459
460
461 /*******************************************************************
462   process a election packet
463
464   An election dynamically decides who will be the master. 
465   ******************************************************************/
466 void process_election(struct packet_struct *p,char *buf)
467 {
468   struct dgram_packet *dgram = &p->packet.dgram;
469   struct in_addr ip = dgram->header.source_ip;
470   struct subnet_record *d = find_subnet(ip);
471   int version = CVAL(buf,0);
472   uint32 criterion = IVAL(buf,1);
473   int timeup = IVAL(buf,5)/1000;
474   char *name = buf+13;
475   struct work_record *work;
476
477   if (!d) return;
478   
479   name[15] = 0;  
480
481   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
482            name,version,criterion,timeup));
483   
484   if (same_context(dgram)) return;
485   
486   for (work = d->workgrouplist; work; work = work->next)
487     {
488       if (listening_name(work, &dgram->dest_name) && 
489           strequal(work->work_group, lp_workgroup()) &&
490           d->my_interface)
491         {
492           if (win_election(work, version,criterion,timeup,name))
493             {
494               if (!work->RunningElection)
495                 {
496                   work->needelection = True;
497                   work->ElectionCount=0;
498           work->state = MST_NONE;
499                 }
500             }
501           else
502             {
503               work->needelection = False;
504               
505               if (work->RunningElection)
506                 {
507                   work->RunningElection = False;
508                   DEBUG(3,(">>> Lost election on %s %s <<<\n",
509                            work->work_group,inet_ntoa(d->bcast_ip)));
510                   
511                   /* if we are the master then remove our masterly names */
512                   if (AM_MASTER(work))
513                   {
514                       become_nonmaster(d, work,
515                                         SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
516                   }
517                 }
518             }
519         }
520     }
521 }
522
523
524 /****************************************************************************
525   checks whether a browser election is to be run on any workgroup
526
527   this function really ought to return the time between election
528   packets (which depends on whether samba intends to be a domain
529   master or a master browser) in milliseconds.
530
531   ***************************************************************************/
532 BOOL check_elections(void)
533 {
534   struct subnet_record *d;
535   BOOL run_any_election = False;
536
537   for (d = subnetlist; d; d = d->next)
538     {
539       struct work_record *work;
540       for (work = d->workgrouplist; work; work = work->next)
541         {
542           run_any_election |= work->RunningElection;
543           
544           if (work->needelection && !work->RunningElection)
545             {
546               DEBUG(3,(">>> Starting election on %s %s <<<\n",
547                        work->work_group,inet_ntoa(d->bcast_ip)));
548               work->ElectionCount = 0;
549               work->RunningElection = True;
550               work->needelection = False;
551             }
552         }
553     }
554   return run_any_election;
555 }
556