updated docs to match code mods from last two or three updates. done
[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       DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n"));
259       work->state = MST_WON; /* ... an election win was successful */
260
261       work->ElectionCriterion |= 0x5;
262
263       /* update our server status */
264       work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
265       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
266
267       /* add special browser name */
268       add_my_name_entry(d,MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
269
270       /* DON'T do anything else after calling add_my_name_entry() */
271       return;
272     }
273     case MST_WON: /* while nothing had happened except we won an election... */
274     {
275       DEBUG(3,("go to second stage: register as master browser\n"));
276       work->state = MST_MSB; /* ... registering MSBROWSE was successful */
277
278       /* add server entry on successful registration of MSBROWSE */
279       add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
280
281       /* add master name */
282       add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE         );
283   
284       /* DON'T do anything else after calling add_my_name_entry() */
285       return;
286     }
287     case MST_MSB: /* while we were still only registered MSBROWSE state... */
288     {
289       DEBUG(3,("2nd stage complete: registered as master browser\n"));
290       work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */
291
292       /* update our server status */
293       work->ServerType |= SV_TYPE_MASTER_BROWSER;
294       add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
295
296       if (d->my_interface && work->serverlist == NULL) /* no servers! */
297       {
298         /* ask all servers on our local net to announce to us */
299         announce_request(work, d->bcast_ip);
300       }
301
302       if (lp_domain_master())
303       {
304         DEBUG(3,("third stage: register as domain master\n"));
305
306         /* add domain master name */
307         add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE         );
308
309         /* DON'T do anything else after calling add_my_name_entry() */
310         return;
311       }
312       else
313       {
314         DEBUG(3,("samba not configured as a domain master: no third stage.\n"));
315       }
316   
317       break;
318     }
319     case MST_BROWSER: /* while we were still a master browser... */
320     {
321       /* update our server status */
322       if (lp_domain_master())
323       {
324         DEBUG(3,("fourth stage: samba is now a domain master.\n"));
325         work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */
326
327         work->ServerType |= SV_TYPE_DOMAIN_MASTER;
328       
329         if (lp_domain_logons())
330             {
331               work->ServerType |= SV_TYPE_DOMAIN_CTRL;
332               work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
333             }
334         add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
335       }
336   
337       break;
338     }
339     case MST_DOMAIN:
340     {
341       /* nothing else to become, at the moment: we are top-dog. */
342       DEBUG(3,("fifth stage: there isn't one yet!\n"));
343       break;
344     }
345   }
346 }
347
348
349 /*******************************************************************
350   unbecome the master browser. initates removal of necessary netbios 
351   names, and tells the world that we are no longer a master browser.
352   ******************************************************************/
353 void become_nonmaster(struct subnet_record *d, struct work_record *work,
354                                 int remove_type)
355 {
356   int new_server_type = work->ServerType;
357
358   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
359   
360   /* can only remove master or domain types with this function */
361   remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
362
363   /* unbecome a master browser; unbecome a domain master, too :-( */
364   if (remove_type & SV_TYPE_MASTER_BROWSER)
365     remove_type |= SV_TYPE_DOMAIN_MASTER;
366
367   new_server_type &= ~remove_type;
368
369   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
370   {
371     /* no longer a master browser of any sort */
372
373         work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
374     work->ElectionCriterion &= ~0x4;
375     work->state = MST_NONE;
376
377         /* announce ourselves as no longer active as a master browser. */
378     announce_server(d, work, work->work_group, myname, 0, 0);
379     remove_name_entry(d,MSBROWSE        ,0x01);
380   }
381   
382   work->ServerType = new_server_type;
383
384   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
385   {
386     if (work->state == MST_DOMAIN)
387       work->state = MST_BROWSER;
388     remove_name_entry(d,work->work_group,0x1b);
389     
390   }
391
392   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
393   {
394     if (work->state >= MST_BROWSER)
395       work->state = MST_NONE;
396     remove_name_entry(d,work->work_group,0x1d);
397   }
398 }
399
400
401 /*******************************************************************
402   run the election
403   ******************************************************************/
404 void run_elections(void)
405 {
406   time_t t = time(NULL);
407   static time_t lastime = 0;
408   
409   struct subnet_record *d;
410   
411   /* send election packets once a second */
412   if (lastime && t-lastime <= 0) return;
413   
414   lastime = t;
415   
416   for (d = subnetlist; d; d = d->next)
417     {
418       struct work_record *work;
419       for (work = d->workgrouplist; work; work = work->next)
420         {
421           if (work->RunningElection)
422             {
423               send_election(d,work->work_group, work->ElectionCriterion,
424                             t-StartupTime,myname);
425               
426               if (work->ElectionCount++ >= 4)
427                 {
428                   /* I won! now what :-) */
429                   DEBUG(2,(">>> Won election on %s %s <<<\n",
430                            work->work_group,inet_ntoa(d->bcast_ip)));
431                   
432                   work->RunningElection = False;
433           work->state = MST_NONE;
434
435                   become_master(d, work);
436                 }
437             }
438         }
439     }
440 }
441
442
443 /*******************************************************************
444   work out if I win an election
445   ******************************************************************/
446 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
447                          int timeup,char *name)
448 {  
449   time_t t = time(NULL);
450   uint32 mycriterion;
451   if (version > ELECTION_VERSION) return(False);
452   if (version < ELECTION_VERSION) return(True);
453   
454   mycriterion = work->ElectionCriterion;
455
456   if (criterion > mycriterion) return(False);
457   if (criterion < mycriterion) return(True);
458
459   if (timeup > (t - StartupTime)) return(False);
460   if (timeup < (t - StartupTime)) return(True);
461
462   if (strcasecmp(myname,name) > 0) return(False);
463   
464   return(True);
465 }
466
467
468 /*******************************************************************
469   process a election packet
470
471   An election dynamically decides who will be the master. 
472   ******************************************************************/
473 void process_election(struct packet_struct *p,char *buf)
474 {
475   struct dgram_packet *dgram = &p->packet.dgram;
476   struct in_addr ip = dgram->header.source_ip;
477   struct subnet_record *d = find_subnet(ip);
478   int version = CVAL(buf,0);
479   uint32 criterion = IVAL(buf,1);
480   int timeup = IVAL(buf,5)/1000;
481   char *name = buf+13;
482   struct work_record *work;
483
484   if (!d) return;
485   
486   name[15] = 0;  
487
488   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
489            name,version,criterion,timeup));
490   
491   if (same_context(dgram)) return;
492   
493   for (work = d->workgrouplist; work; work = work->next)
494     {
495       if (listening_name(work, &dgram->dest_name) && 
496           strequal(work->work_group, lp_workgroup()) &&
497           d->my_interface)
498         {
499           if (win_election(work, version,criterion,timeup,name))
500             {
501               if (!work->RunningElection)
502                 {
503                   work->needelection = True;
504                   work->ElectionCount=0;
505           work->state = MST_NONE;
506                 }
507             }
508           else
509             {
510               work->needelection = False;
511               
512               if (work->RunningElection)
513                 {
514                   work->RunningElection = False;
515                   DEBUG(3,(">>> Lost election on %s %s <<<\n",
516                            work->work_group,inet_ntoa(d->bcast_ip)));
517                   
518                   /* if we are the master then remove our masterly names */
519                   if (AM_MASTER(work))
520                   {
521                       become_nonmaster(d, work,
522                                         SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
523                   }
524                 }
525             }
526         }
527     }
528 }
529
530
531 /****************************************************************************
532   checks whether a browser election is to be run on any workgroup
533
534   this function really ought to return the time between election
535   packets (which depends on whether samba intends to be a domain
536   master or a master browser) in milliseconds.
537
538   ***************************************************************************/
539 BOOL check_elections(void)
540 {
541   struct subnet_record *d;
542   BOOL run_any_election = False;
543
544   for (d = subnetlist; d; d = d->next)
545     {
546       struct work_record *work;
547       for (work = d->workgrouplist; work; work = work->next)
548         {
549           run_any_election |= work->RunningElection;
550           
551           if (work->needelection && !work->RunningElection)
552             {
553               DEBUG(3,(">>> Starting election on %s %s <<<\n",
554                        work->work_group,inet_ntoa(d->bcast_ip)));
555               work->ElectionCount = 0;
556               work->RunningElection = True;
557               work->needelection = False;
558             }
559         }
560     }
561   return run_any_election;
562 }
563