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