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