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