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