modified become_master() to a state-based system. becoming a master
[samba.git] / source / namedb.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 "smb.h"
30
31 extern int ClientNMB;
32 extern int ClientDGRAM;
33
34 extern int DEBUGLEVEL;
35
36 extern time_t StartupTime;
37 extern pstring myname;
38 extern pstring scope;
39
40 extern struct in_addr ipgrp;
41 extern struct in_addr ipzero;
42
43 /* local interfaces structure */
44 extern struct interface *local_interfaces;
45
46 /* remote interfaces structure */
47 extern struct interface *remote_interfaces;
48
49 /* this is our domain/workgroup/server database */
50 struct subnet_record *subnetlist = NULL;
51
52 static BOOL updatedlists = True;
53 int  updatecount=0;
54
55 int workgroup_count = 0; /* unique index key: one for each workgroup */
56
57 /* what server type are we currently */
58
59 #define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
60                         SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \
61                         SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
62
63
64 /****************************************************************************
65   add a workgroup into the domain list
66   **************************************************************************/
67 static void add_workgroup(struct work_record *work, struct subnet_record *d)
68 {
69   struct work_record *w2;
70
71   if (!work || !d) return;
72
73   if (!d->workgrouplist)
74     {
75       d->workgrouplist = work;
76       work->prev = NULL;
77       work->next = NULL;
78       return;
79     }
80   
81   for (w2 = d->workgrouplist; w2->next; w2 = w2->next);
82   
83   w2->next = work;
84   work->next = NULL;
85   work->prev = w2;
86 }
87
88
89 /****************************************************************************
90   create a blank workgroup 
91   **************************************************************************/
92 static struct work_record *make_workgroup(char *name)
93 {
94   struct work_record *work;
95   struct subnet_record *d;
96   int t = -1;
97   
98   if (!name || !name[0]) return NULL;
99   
100   work = (struct work_record *)malloc(sizeof(*work));
101   if (!work) return(NULL);
102   
103   StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
104   work->serverlist = NULL;
105   
106   work->ServerType = DFLT_SERVER_TYPE;
107   work->RunningElection = False;
108   work->ElectionCount = 0;
109   work->needelection = False;
110   work->needannounce = True;
111   work->state = MST_NONE;
112   
113   /* make sure all token representations of workgroups are unique */
114   
115   for (d = subnetlist; d && t == -1; d = d->next)
116     {
117       struct work_record *w;
118       for (w = d->workgrouplist; w && t == -1; w = w->next)
119         {
120           if (strequal(w->work_group, work->work_group)) t = w->token;
121         }
122     }
123   
124   if (t == -1)
125     {
126       work->token = ++workgroup_count;
127     }
128   else
129     {
130       work->token = t;
131     }
132   
133   
134   /* WfWg  uses 01040b01 */
135   /* Win95 uses 01041501 */
136   /* NTAS  uses ???????? */
137   work->ElectionCriterion  = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8); 
138   work->ElectionCriterion |= (lp_os_level() << 24);
139   if (lp_domain_master()) {
140     work->ElectionCriterion |= 0x80;
141   }
142   
143   return work;
144 }
145
146
147 /*******************************************************************
148   expire old servers in the serverlist
149   time of -1 indicates everybody dies
150   ******************************************************************/
151 static void remove_old_servers(struct work_record *work, time_t t)
152 {
153   struct server_record *s;
154   struct server_record *nexts;
155   
156   /* expire old entries in the serverlist */
157   for (s = work->serverlist; s; s = nexts)
158     {
159       if (t == -1 || (s->death_time && s->death_time < t))
160         {
161           DEBUG(3,("Removing dead server %s\n",s->serv.name));
162           updatedlists = True;
163           nexts = s->next;
164           
165           if (s->prev) s->prev->next = s->next;
166           if (s->next) s->next->prev = s->prev;
167           
168           if (work->serverlist == s) 
169             work->serverlist = s->next; 
170
171           free(s);
172         }
173       else
174         {
175           nexts = s->next;
176         }
177     }
178 }
179
180
181 /*******************************************************************
182   remove workgroups
183   ******************************************************************/
184 struct work_record *remove_workgroup(struct subnet_record *d, 
185                                      struct work_record *work)
186 {
187   struct work_record *ret_work = NULL;
188   
189   if (!d || !work) return NULL;
190   
191   DEBUG(3,("Removing old workgroup %s\n", work->work_group));
192   
193   remove_old_servers(work, -1);
194   
195   ret_work = work->next;
196   
197   if (work->prev) work->prev->next = work->next;
198   if (work->next) work->next->prev = work->prev;
199   
200   if (d->workgrouplist == work) d->workgrouplist = work->next; 
201   
202   free(work);
203   
204   return ret_work;
205 }
206
207
208 /****************************************************************************
209   add a domain into the list
210   **************************************************************************/
211 static void add_subnet(struct subnet_record *d)
212 {
213   struct subnet_record *d2;
214
215   if (!subnetlist)
216   {
217     subnetlist = d;
218     d->prev = NULL;
219     d->next = NULL;
220     return;
221   }
222
223   for (d2 = subnetlist; d2->next; d2 = d2->next);
224
225   d2->next = d;
226   d->next = NULL;
227   d->prev = d2;
228 }
229
230 /***************************************************************************
231   add a server into the list
232   **************************************************************************/
233 static void add_server(struct work_record *work,struct server_record *s)
234 {
235   struct server_record *s2;
236
237   if (!work->serverlist) {
238     work->serverlist = s;
239     s->prev = NULL;
240     s->next = NULL;
241     return;
242   }
243
244   for (s2 = work->serverlist; s2->next; s2 = s2->next) ;
245
246   s2->next = s;
247   s->next = NULL;
248   s->prev = s2;
249 }
250
251
252 /****************************************************************************
253   find a workgroup in the workgrouplist 
254   only create it if the domain allows it, or the parameter 'add' insists
255   that it get created/added anyway. this allows us to force entries in
256   lmhosts file to be added.
257   **************************************************************************/
258 struct work_record *find_workgroupstruct(struct subnet_record *d, 
259                                          fstring name, BOOL add)
260 {
261   struct work_record *ret, *work;
262   
263   if (!d) return NULL;
264   
265   DEBUG(4, ("workgroup search for %s: ", name));
266   
267   if (strequal(name, "*"))
268     {
269       DEBUG(2,("add any workgroups: initiating browser search on %s\n",
270                inet_ntoa(d->bcast_ip)));
271       queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, NAME_QUERY_FIND_MST,
272                              MSBROWSE,0x1,0,0,
273                              True,False, d->bcast_ip);
274       return NULL;
275     }
276   
277   for (ret = d->workgrouplist; ret; ret = ret->next) {
278     if (!strcmp(ret->work_group,name)) {
279       DEBUG(4, ("found\n"));
280       return(ret);
281     }
282   }
283
284   if (!add) {
285     DEBUG(4, ("not found\n"));
286     return NULL;
287   }
288
289   DEBUG(4,("not found: creating\n"));
290   
291   if ((work = make_workgroup(name)))
292     {
293       if (lp_preferred_master() &&
294           strequal(lp_workgroup(), name) &&
295           d->my_interface)
296         {
297           DEBUG(3, ("preferred master startup for %s\n", work->work_group));
298           work->needelection = True;
299           work->ElectionCriterion |= (1<<3);
300         }
301       if (!d->my_interface)
302         {
303           work->needelection = False;
304         }
305       add_workgroup(work, d);
306       return(work);
307     }
308   return NULL;
309 }
310
311 /****************************************************************************
312   find a subnet in the subnetlist 
313   **************************************************************************/
314 struct subnet_record *find_subnet(struct in_addr bcast_ip)
315 {   
316   struct subnet_record *d;
317   struct in_addr wins_ip = ipgrp;
318   
319   /* search through subnet list for broadcast/netmask that matches
320      the source ip address. a subnet 255.255.255.255 represents the
321      WINS list. */
322   
323   for (d = subnetlist; d; d = d->next)
324     {
325         if (ip_equal(bcast_ip, wins_ip))
326             {
327            if (ip_equal(bcast_ip, d->bcast_ip))
328            {
329                return d;
330            }
331         }
332         else if (same_net(bcast_ip, d->bcast_ip, d->mask_ip))
333             {
334               return(d);
335             }
336     }
337   
338   return (NULL);
339 }
340
341
342 /****************************************************************************
343   dump a copy of the workgroup/domain database
344   **************************************************************************/
345 void dump_workgroups(void)
346 {
347   struct subnet_record *d;
348   
349   for (d = subnetlist; d; d = d->next)
350     {
351       if (d->workgrouplist)
352         {
353           struct work_record *work;
354           
355           DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip)));
356           DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip)));
357           
358           for (work = d->workgrouplist; work; work = work->next)
359             {
360               DEBUG(4,("\t%s(%d)\n", work->work_group, work->token));
361               if (work->serverlist)
362                 {
363                   struct server_record *s;                
364                   for (s = work->serverlist; s; s = s->next)
365                     {
366                       DEBUG(4,("\t\t%s %8x (%s)\n",
367                                s->serv.name, s->serv.type, s->serv.comment));
368                     }
369                 }
370             }
371         }
372     }
373 }
374
375 /****************************************************************************
376   create a domain entry
377   ****************************************************************************/
378 static struct subnet_record *make_subnet(struct in_addr bcast_ip, struct in_addr mask_ip)
379 {
380   struct subnet_record *d;
381   d = (struct subnet_record *)malloc(sizeof(*d));
382   
383   if (!d) return(NULL);
384   
385   bzero((char *)d,sizeof(*d));
386   
387   DEBUG(4, ("making domain %s ", inet_ntoa(bcast_ip)));
388   DEBUG(4, ("%s\n", inet_ntoa(mask_ip)));
389   
390   d->bcast_ip = bcast_ip;
391   d->mask_ip  = mask_ip;
392   d->workgrouplist = NULL;
393   d->my_interface = False; /* True iff the interface is on the samba host */
394   
395   add_subnet(d);
396   
397   return d;
398 }
399
400
401 /****************************************************************************
402   add the remote interfaces from lp_remote_interfaces() and lp_interfaces()
403   to the netbios subnet database.
404   ****************************************************************************/
405 void add_subnet_interfaces(void)
406 {
407         struct interface *i;
408
409         /* loop on all local interfaces */
410         for (i = local_interfaces; i; i = i->next)
411         {
412                 /* add the interface into our subnet database */
413                 if (!find_subnet(i->bcast))
414                 {
415                     struct subnet_record *d = make_subnet(i->bcast,i->nmask);
416                         if (d)
417                         {
418                                 /* short-cut method to identifying local interfaces */
419                                 d->my_interface = True;
420                         }
421                 }
422         }
423
424         /* loop on all remote interfaces */
425         for (i = remote_interfaces; i; i = i->next)
426         {
427                 /* add the interface into our subnet database */
428                 if (!find_subnet(i->bcast))
429                 {
430                     make_subnet(i->bcast,i->nmask);
431                 }
432         }
433
434         /* add the pseudo-ip interface for WINS: 255.255.255.255 */
435         if (lp_wins_support())
436     {
437                 struct in_addr wins_bcast = ipgrp;
438                 struct in_addr wins_nmask = ipzero;
439                 make_subnet(wins_bcast, wins_nmask);
440     }
441 }
442
443
444 /****************************************************************************
445   add a domain entry. creates a workgroup, if necessary, and adds the domain
446   to the named a workgroup.
447   ****************************************************************************/
448 struct subnet_record *add_subnet_entry(struct in_addr bcast_ip, 
449                                        struct in_addr mask_ip,
450                                        char *name, BOOL add, BOOL lmhosts)
451 {
452   struct subnet_record *d;
453
454   /* XXXX andrew: struct in_addr ip appears not to be referenced at all except
455      in the DEBUG comment. i assume that the DEBUG comment below actually
456      intends to refer to bcast_ip? i don't know.
457
458   struct in_addr ip = ipgrp;
459
460   */
461
462   if (zero_ip(bcast_ip)) 
463     bcast_ip = *iface_bcast(bcast_ip);
464   
465   /* add the domain into our domain database */
466   if ((d = find_subnet(bcast_ip)) ||
467       (d = make_subnet(bcast_ip, mask_ip)))
468     {
469       struct work_record *w = find_workgroupstruct(d, name, add);
470           extern pstring ServerComment;
471       
472       if (!w) return NULL;
473
474       /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
475          or register with WINS server, if it's our workgroup */
476       if (strequal(lp_workgroup(), name) && d->my_interface)
477         {
478           add_my_name_entry(d,name,0x1e,NB_ACTIVE|NB_GROUP);
479           add_my_name_entry(d,name,0x0 ,NB_ACTIVE|NB_GROUP);
480         }
481       /* add samba server name to workgroup list */
482       if ((strequal(lp_workgroup(), name) && d->my_interface) || lmhosts)
483       {
484             add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True);
485       }
486       
487       DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(bcast_ip)));
488       return d;
489     }
490   return NULL;
491 }
492
493 /****************************************************************************
494   remove all samba's server entries
495   ****************************************************************************/
496 void remove_my_servers(void)
497 {
498         struct subnet_record *d; 
499         for (d = subnetlist; d; d = d->next)
500         {
501                 struct work_record *work;
502                 for (work = d->workgrouplist; work; work = work->next)
503                 {
504                         struct server_record *s;
505                         for (s = work->serverlist; s; s = s->next)
506                         {
507                                 if (!strequal(myname,s->serv.name)) continue;
508                                 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
509                         }
510                 }
511         }
512 }
513
514
515 /****************************************************************************
516   add a server entry
517   ****************************************************************************/
518 struct server_record *add_server_entry(struct subnet_record *d, 
519                                        struct work_record *work,
520                                        char *name,int servertype, 
521                                        int ttl,char *comment,
522                                        BOOL replace)
523 {
524   BOOL newentry=False;
525   struct server_record *s;
526   
527   if (name[0] == '*')
528     {
529       return (NULL);
530     }
531   
532   for (s = work->serverlist; s; s = s->next)
533     {
534       if (strequal(name,s->serv.name)) break;
535     }
536   
537   if (s && !replace)
538     {
539       DEBUG(4,("Not replacing %s\n",name));
540       return(s);
541     }
542   
543   if (!s || s->serv.type != servertype || !strequal(s->serv.comment, comment))
544     updatedlists=True;
545
546   if (!s)
547     {
548       newentry = True;
549       s = (struct server_record *)malloc(sizeof(*s));
550       
551       if (!s) return(NULL);
552       
553       bzero((char *)s,sizeof(*s));
554     }
555   
556   
557   if (d->my_interface && strequal(lp_workgroup(),work->work_group))
558     {
559           if (servertype)
560         servertype |= SV_TYPE_LOCAL_LIST_ONLY;
561     }
562   else
563     {
564       servertype &= ~SV_TYPE_LOCAL_LIST_ONLY;
565     }
566   
567   /* update the entry */
568   StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1);
569   StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1);
570   strupper(s->serv.name);
571   s->serv.type  = servertype;
572   s->death_time = servertype ? (ttl?time(NULL)+ttl*3:0) : (time(NULL)-1);
573   
574   /* for a domain entry, the comment field refers to the server name */
575   
576   if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment);
577   
578   if (newentry)
579     {
580       add_server(work, s);
581       
582       DEBUG(3,("Added "));
583     }
584   else
585     {
586       DEBUG(3,("Updated "));
587     }
588   
589   DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
590            name,servertype,comment,
591            work->work_group,inet_ntoa(d->bcast_ip)));
592   
593   return(s);
594 }
595
596
597 /****************************************************************************
598   add the default workgroup into my domain
599   **************************************************************************/
600 void add_my_subnets(char *group)
601 {
602   struct interface *i;
603
604   /* add or find domain on our local subnet, in the default workgroup */
605   
606   if (*group == '*') return;
607
608         /* the coding choice is up to you, andrew: i can see why you don't want
609        global access to the local_interfaces structure: so it can't get
610        messed up! */
611     for (i = local_interfaces; i; i = i->next)
612     {
613       add_subnet_entry(i->bcast,i->nmask,group, True, False);
614     }
615 }
616
617
618 /*******************************************************************
619   write out browse.dat
620   ******************************************************************/
621 void write_browse_list(void)
622 {
623   struct subnet_record *d;
624   
625   pstring fname,fnamenew;
626   FILE *f;
627   
628   if (!updatedlists) return;
629   
630   dump_names();
631   dump_workgroups();
632   
633   updatedlists = False;
634   updatecount++;
635   
636   strcpy(fname,lp_lockdir());
637   trim_string(fname,NULL,"/");
638   strcat(fname,"/");
639   strcat(fname,SERVER_LIST);
640   strcpy(fnamenew,fname);
641   strcat(fnamenew,".");
642   
643   f = fopen(fnamenew,"w");
644   
645   if (!f)
646     {
647       DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
648       return;
649     }
650   
651   for (d = subnetlist; d ; d = d->next)
652     {
653       struct work_record *work;
654       for (work = d->workgrouplist; work ; work = work->next)
655         {
656           struct server_record *s;
657           for (s = work->serverlist; s ; s = s->next)
658             {
659               fstring tmp;
660               
661               /* don't list domains I don't have a master for */
662               if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) && !s->serv.comment[0])
663                 {
664                   continue;
665                 }
666               
667               /* output server details, plus what workgroup/domain
668                  they're in. without the domain information, the
669                  combined list of all servers in all workgroups gets
670                  sent to anyone asking about any workgroup! */
671               
672               sprintf(tmp, "\"%s\"", s->serv.name);
673               fprintf(f, "%-25s ", tmp);
674               fprintf(f, "%08x ", s->serv.type);
675               sprintf(tmp, "\"%s\" ", s->serv.comment);
676               fprintf(f, "%-30s", tmp);
677               fprintf(f, "\"%s\"\n", work->work_group);
678             }
679         }
680     }
681   
682   fclose(f);
683   unlink(fname);
684   chmod(fnamenew,0644);
685   rename(fnamenew,fname);   
686   DEBUG(3,("Wrote browse list %s\n",fname));
687 }
688
689
690 /*******************************************************************
691   expire old servers in the serverlist
692   ******************************************************************/
693 void expire_servers(time_t t)
694 {
695   struct subnet_record *d;
696   
697   for (d = subnetlist ; d ; d = d->next)
698     {
699       struct work_record *work;
700       
701       for (work = d->workgrouplist; work; work = work->next)
702         {
703           remove_old_servers(work, t);
704         }
705     }
706 }
707