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