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