c841d9b7a60a9dddd67900335cf1e8581190fd47
[kai/samba.git] / source3 / 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-1995
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    Revision History:
22
23    14 jan 96: lkcl@pires.co.uk
24    added multiple workgroup domain master support
25
26 */
27
28 #include "includes.h"
29
30 extern int ClientNMB;
31 extern int ClientDGRAM;
32
33 extern int DEBUGLEVEL;
34 extern pstring scope;
35
36 extern pstring myname;
37
38 /* machine comment for host announcements */
39 extern  pstring ServerComment;
40
41 /* here are my election parameters */
42
43 extern time_t StartupTime;
44
45 #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
46
47 extern struct subnet_record *subnetlist;
48
49
50 /*******************************************************************
51   occasionally check to see if the master browser is around
52   ******************************************************************/
53 void check_master_browser(void)
54 {
55   static time_t lastrun=0;
56   time_t t = time(NULL);
57   struct subnet_record *d;
58
59   if (!lastrun) lastrun = t;
60   if (t < lastrun + CHECK_TIME_MST_BROWSE * 60) 
61     return;
62   lastrun = t;
63
64   dump_workgroups();
65
66   for (d = subnetlist; d; d = d->next)
67     {
68       struct work_record *work;
69
70       for (work = d->workgrouplist; work; work = work->next)
71         {
72           /* if we are not the browse master of a workgroup, and we can't
73              find a browser on the subnet, do something about it. */
74
75           if (!AM_MASTER(work))
76             {
77               queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK,
78                                    work->work_group,0x1d,0,0,
79                                    True,False,d->bcast_ip);
80             }
81         }
82     }
83 }
84
85
86 /*******************************************************************
87   what to do if a master browser DOESN't exist
88   ******************************************************************/
89 void browser_gone(char *work_name, struct in_addr ip)
90 {
91   struct subnet_record *d = find_subnet(ip);
92   struct work_record *work = find_workgroupstruct(d, work_name, False);
93
94   if (!work || !d) return;
95
96   if (strequal(work->work_group, lp_workgroup()) &&
97       ismybcast(d->bcast_ip))
98     {
99
100       DEBUG(2,("Forcing election on %s %s\n",
101                work->work_group,inet_ntoa(d->bcast_ip)));
102
103       /* we can attempt to become master browser */
104       work->needelection = True;
105     }
106   else
107     {
108       /* XXXX note: this will delete entries that have been added in by
109          lmhosts as well. a flag to ensure that these are not deleted may
110          be considered */
111       
112       /* workgroup with no master browser is not the default workgroup:
113          it's also not on our subnet. therefore delete it: it can be
114          recreated dynamically */
115       
116       send_election(d, work->work_group, 0, 0, myname);
117       remove_workgroup(d, work);      
118     }
119 }
120
121
122 /****************************************************************************
123   send an election packet
124   **************************************************************************/
125 void send_election(struct subnet_record *d, char *group,uint32 criterion,
126                    int timeup,char *name)
127 {
128   pstring outbuf;
129   char *p;
130
131   if (!d) return;
132   
133   DEBUG(2,("Sending election to %s for workgroup %s\n",
134            inet_ntoa(d->bcast_ip),group));         
135
136   bzero(outbuf,sizeof(outbuf));
137   p = outbuf;
138   CVAL(p,0) = 8; /* election */
139   p++;
140
141   CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION;
142   SIVAL(p,1,criterion);
143   SIVAL(p,5,timeup*1000); /* ms - despite the spec */
144   p += 13;
145   strcpy(p,name);
146   strupper(p);
147   p = skip_string(p,1);
148   
149   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
150                       name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip));
151 }
152
153
154 /*******************************************************************
155   become the master browser
156   ******************************************************************/
157 static void become_master(struct subnet_record *d, struct work_record *work)
158 {
159   uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX | 0x00400000;
160
161   if (!work) return;
162   
163   DEBUG(2,("Becoming master for %s\n",work->work_group));
164   
165   work->ServerType |= SV_TYPE_MASTER_BROWSER;
166   work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER;
167   work->ElectionCriterion |= 0x5;
168   
169   /* add browse, master and general names to database or register with WINS */
170   add_my_name_entry(d,MSBROWSE        ,0x01,NB_ACTIVE|NB_GROUP);
171   add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE         );
172   
173   if (lp_domain_master())
174     {
175       DEBUG(4,("Domain master: adding names...\n"));
176       
177       /* add domain master and domain member names or register with WINS */
178       add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE         );
179       
180       work->ServerType |= SV_TYPE_DOMAIN_MASTER;
181       
182       if (lp_domain_logons())
183         {
184           work->ServerType |= SV_TYPE_DOMAIN_CTRL;
185           work->ServerType |= SV_TYPE_DOMAIN_MEMBER;
186         }
187     }
188   
189   /* update our server status */
190   add_server_entry(d,work,work->work_group,domain_type,0,myname,True);
191   add_server_entry(d,work,myname,work->ServerType,0,ServerComment,True);
192   
193   if (d->my_interface)
194     {
195       /* ask all servers on our local net to announce to us */
196       announce_request(work, d->bcast_ip);
197     }
198 }
199
200
201 /*******************************************************************
202   unbecome the master browser. initates removal of necessary netbios 
203   names, and tells the world that we are no longer a master browser.
204   ******************************************************************/
205 void become_nonmaster(struct subnet_record *d, struct work_record *work,
206                                 int remove_type)
207 {
208   int new_server_type = work->ServerType;
209
210   DEBUG(2,("Becoming non-master for %s\n",work->work_group));
211   
212   /* can only remove master or domain types with this function */
213   remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
214
215   /* unbecome a master browser; unbecome a domain master, too :-( */
216   if (remove_type & SV_TYPE_MASTER_BROWSER)
217     remove_type |= SV_TYPE_DOMAIN_MASTER;
218   
219   new_server_type &= ~remove_type;
220
221   if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER)))
222   {
223     /* no longer a master browser of any sort */
224
225         work->ServerType |= SV_TYPE_POTENTIAL_BROWSER;
226   work->ElectionCriterion &= ~0x4;
227   
228         /* announce ourselves as no longer active as a master browser. */
229     announce_server(d, work, work->work_group, myname, 0, 0);
230     remove_name_entry(d,MSBROWSE        ,0x01);
231   }
232   
233   work->ServerType = new_server_type;
234
235   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
236     remove_name_entry(d,work->work_group,0x1b);
237
238   if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER))
239     remove_name_entry(d,work->work_group,0x1d);
240 }
241
242
243 /*******************************************************************
244   run the election
245   ******************************************************************/
246 void run_elections(void)
247 {
248   time_t t = time(NULL);
249   static time_t lastime = 0;
250   
251   struct subnet_record *d;
252   
253   /* send election packets once a second */
254   if (lastime && t-lastime <= 0) return;
255   
256   lastime = t;
257   
258   for (d = subnetlist; d; d = d->next)
259     {
260       struct work_record *work;
261       for (work = d->workgrouplist; work; work = work->next)
262         {
263           if (work->RunningElection)
264             {
265               send_election(d,work->work_group, work->ElectionCriterion,
266                             t-StartupTime,myname);
267               
268               if (work->ElectionCount++ >= 4)
269                 {
270                   /* I won! now what :-) */
271                   DEBUG(2,(">>> Won election on %s %s <<<\n",
272                            work->work_group,inet_ntoa(d->bcast_ip)));
273                   
274                   work->RunningElection = False;
275                   become_master(d, work);
276                 }
277             }
278         }
279     }
280 }
281
282
283 /*******************************************************************
284   work out if I win an election
285   ******************************************************************/
286 static BOOL win_election(struct work_record *work,int version,uint32 criterion,
287                          int timeup,char *name)
288 {  
289   time_t t = time(NULL);
290   uint32 mycriterion;
291   if (version > ELECTION_VERSION) return(False);
292   if (version < ELECTION_VERSION) return(True);
293   
294   mycriterion = work->ElectionCriterion;
295
296   if (criterion > mycriterion) return(False);
297   if (criterion < mycriterion) return(True);
298
299   if (timeup > (t - StartupTime)) return(False);
300   if (timeup < (t - StartupTime)) return(True);
301
302   if (strcasecmp(myname,name) > 0) return(False);
303   
304   return(True);
305 }
306
307
308 /*******************************************************************
309   process a election packet
310
311   An election dynamically decides who will be the master. 
312   ******************************************************************/
313 void process_election(struct packet_struct *p,char *buf)
314 {
315   struct dgram_packet *dgram = &p->packet.dgram;
316   struct in_addr ip = dgram->header.source_ip;
317   struct subnet_record *d = find_subnet(ip);
318   int version = CVAL(buf,0);
319   uint32 criterion = IVAL(buf,1);
320   int timeup = IVAL(buf,5)/1000;
321   char *name = buf+13;
322   struct work_record *work;
323
324   if (!d) return;
325   
326   name[15] = 0;  
327
328   DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
329            name,version,criterion,timeup));
330   
331   if (same_context(dgram)) return;
332   
333   for (work = d->workgrouplist; work; work = work->next)
334     {
335       if (listening_name(work, &dgram->dest_name) && 
336           strequal(work->work_group, lp_workgroup()) &&
337           d->my_interface)
338         {
339           if (win_election(work, version,criterion,timeup,name))
340             {
341               if (!work->RunningElection)
342                 {
343                   work->needelection = True;
344                   work->ElectionCount=0;
345                 }
346             }
347           else
348             {
349               work->needelection = False;
350               
351               if (work->RunningElection)
352                 {
353                   work->RunningElection = False;
354                   DEBUG(3,(">>> Lost election on %s %s <<<\n",
355                            work->work_group,inet_ntoa(d->bcast_ip)));
356                   
357                   /* if we are the master then remove our masterly names */
358                   if (AM_MASTER(work))
359                     {
360                       become_nonmaster(d, work,
361                                         SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER);
362                     }
363                 }
364             }
365         }
366     }
367 }
368
369
370 /****************************************************************************
371   checks whether a browser election is to be run on any workgroup
372   ***************************************************************************/
373 BOOL check_elections(void)
374 {
375   struct subnet_record *d;
376   BOOL run_any_election = False;
377
378   for (d = subnetlist; d; d = d->next)
379     {
380       struct work_record *work;
381       for (work = d->workgrouplist; work; work = work->next)
382         {
383           run_any_election |= work->RunningElection;
384           
385           if (work->needelection && !work->RunningElection)
386             {
387               DEBUG(3,(">>> Starting election on %s %s <<<\n",
388                        work->work_group,inet_ntoa(d->bcast_ip)));
389               work->ElectionCount = 0;
390               work->RunningElection = True;
391               work->needelection = False;
392             }
393         }
394     }
395   return run_any_election;
396 }
397