fix a bug that meant alpha6 couldn't compile.
[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 #include "localnet.h"
32
33 extern int DEBUGLEVEL;
34
35 extern time_t StartupTime;
36 extern pstring myname;
37 extern pstring scope;
38 extern struct in_addr bcast_ip;
39
40 /* this is our browse master/backup cache database */
41 struct browse_cache_record *browserlist = NULL;
42
43 /* this is our domain/workgroup/server database */
44 struct domain_record *domainlist = NULL;
45
46 static BOOL updatedlists = True;
47 int  updatecount=0;
48
49 int workgroup_count = 0; /* unique index key: one for each workgroup */
50
51 /* what server type are we currently */
52
53 #define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
54                         SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \
55                         SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
56
57 /* here are my election parameters */
58 #define MSBROWSE "\001\002__MSBROWSE__\002"
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, fstring name, BOOL add)
309 {
310   struct work_record *ret, *work;
311   
312   if (!d) return NULL;
313   
314   DEBUG(4, ("workgroup search for %s: ", name));
315   
316   if (strequal(name, "*"))
317     {
318       DEBUG(2,("add any workgroups: initiating browser search on %s\n",
319                inet_ntoa(d->bcast_ip)));
320       queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, FIND_MASTER,
321                              MSBROWSE,0x1,0,
322                              True,False, d->bcast_ip);
323       return NULL;
324     }
325   
326   for (ret = d->workgrouplist; ret; ret = ret->next)
327     {
328       if (!strcmp(ret->work_group,name))
329         {
330           DEBUG(4, ("found\n"));
331           return(ret);
332         }
333     }
334   
335   DEBUG(4, ("not found: creating\n"));
336   
337   if ((work = make_workgroup(name)))
338     {
339       if (lp_preferred_master() &&
340           strequal(lp_workgroup(), name) &&
341           ip_equal(d->bcast_ip, bcast_ip))
342         {
343           DEBUG(3, ("preferred master startup for %s\n", work->work_group));
344           work->needelection = True;
345           work->ElectionCriterion |= (1<<3);
346         }
347       if (!ip_equal(bcast_ip, d->bcast_ip))
348         {
349           work->needelection = False;
350         }
351       add_workgroup(work, d);
352       return(work);
353     }
354   return NULL;
355 }
356
357 /****************************************************************************
358   find a domain in the domainlist 
359   **************************************************************************/
360 struct domain_record *find_domain(struct in_addr source_ip)
361 {   
362   struct domain_record *d;
363   
364   /* search through domain list for broadcast/netmask that matches
365      the source ip address */
366   
367   for (d = domainlist; d; d = d->next)
368     {
369       if (same_net(source_ip, d->bcast_ip, d->mask_ip))
370         {
371           return(d);
372         }
373     }
374   
375   return (NULL);
376 }
377
378
379 /****************************************************************************
380   dump a copy of the workgroup/domain database
381   **************************************************************************/
382 void dump_workgroups(void)
383 {
384   struct domain_record *d;
385   
386   for (d = domainlist; d; d = d->next)
387     {
388       if (d->workgrouplist)
389         {
390           struct work_record *work;
391           
392           DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip)));
393           DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip)));
394           
395           for (work = d->workgrouplist; work; work = work->next)
396             {
397               DEBUG(4,("\t%s(%d)\n", work->work_group, work->token));
398               if (work->serverlist)
399                 {
400                   struct server_record *s;                
401                   for (s = work->serverlist; s; s = s->next)
402                     {
403                       DEBUG(4,("\t\t%s %8x (%s)\n",
404                                s->serv.name, s->serv.type, s->serv.comment));
405                     }
406                 }
407             }
408         }
409     }
410 }
411
412 /****************************************************************************
413   create a domain entry
414   ****************************************************************************/
415 static struct domain_record *make_domain(struct in_addr ip, struct in_addr mask)
416 {
417   struct domain_record *d;
418   d = (struct domain_record *)malloc(sizeof(*d));
419   
420   if (!d) return(NULL);
421   
422   bzero((char *)d,sizeof(*d));
423   
424   DEBUG(4, ("making domain %s ", inet_ntoa(ip)));
425   DEBUG(4, ("%s\n", inet_ntoa(mask)));
426   
427   d->bcast_ip = ip;
428   d->mask_ip  = mask;
429   d->workgrouplist = NULL;
430   
431   add_domain(d);
432   
433   return d;
434 }
435
436 /****************************************************************************
437   add a domain entry. creates a workgroup, if necessary, and adds the domain
438   to the named a workgroup.
439   ****************************************************************************/
440 struct domain_record *add_domain_entry(struct in_addr source_ip, 
441                                        struct in_addr source_mask,
442                                        char *name, BOOL add)
443 {
444   struct domain_record *d;
445   struct in_addr ip;
446   
447   ip = *interpret_addr2("255.255.255.255");
448   
449   if (zero_ip(source_ip)) source_ip = bcast_ip;
450   
451   /* add the domain into our domain database */
452   if ((d = find_domain(source_ip)) ||
453       (d = make_domain(source_ip, source_mask)))
454     {
455       struct work_record *w = find_workgroupstruct(d, name, add);
456       
457       /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
458          or register with WINS server, if it's our workgroup */
459       if (strequal(lp_workgroup(), name))
460         {
461           extern pstring ServerComment;
462           add_name_entry(name,0x1e,NB_ACTIVE|NB_GROUP);
463           add_name_entry(name,0x0 ,NB_ACTIVE|NB_GROUP);
464           add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True);
465         }
466       
467       DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(ip)));
468       return d;
469     }
470   return NULL;
471 }
472
473 /****************************************************************************
474   add a browser entry
475   ****************************************************************************/
476 struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
477                                               time_t ttl, struct in_addr ip)
478 {
479   BOOL newentry=False;
480   
481   struct browse_cache_record *b;
482
483   /* search for the entry: if it's already in the cache, update that entry */
484   for (b = browserlist; b; b = b->next)
485     {
486       if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break;
487     }
488   
489   if (b && b->synced)
490     {
491       /* entries get left in the cache for a while. this stops sync'ing too
492          often if the network is large */
493       DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
494                 b->name, b->group, inet_ntoa(b->ip), b->sync_time));
495       return NULL;
496     }
497   
498   if (!b)
499     {
500       newentry = True;
501       b = (struct browse_cache_record *)malloc(sizeof(*b));
502       
503       if (!b) return(NULL);
504       
505       bzero((char *)b,sizeof(*b));
506     }
507   
508   /* update the entry */
509   ttl = time(NULL)+ttl;
510   
511   StrnCpy(b->name ,name,sizeof(b->name )-1);
512   StrnCpy(b->group,wg  ,sizeof(b->group)-1);
513   strupper(b->name);
514   strupper(b->group);
515   
516   b->ip     = ip;
517   b->type   = type;
518   
519   if (newentry || ttl < b->sync_time) b->sync_time = ttl;
520   
521   if (newentry)
522     {
523       b->synced = False;
524       add_browse_cache(b);
525       
526       DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n",
527                wg, name, type, inet_ntoa(ip),ttl));
528     }
529   else
530     {
531       DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n",
532                wg, name, type, inet_ntoa(ip),ttl));
533     }
534   
535   return(b);
536 }
537
538
539 /****************************************************************************
540   add a server entry
541   ****************************************************************************/
542 struct server_record *add_server_entry(struct domain_record *d, 
543                                        struct work_record *work,
544                                        char *name,int servertype, 
545                                        int ttl,char *comment,
546                                        BOOL replace)
547 {
548   BOOL newentry=False;
549   struct server_record *s;
550   
551   if (name[0] == '*')
552     {
553       return (NULL);
554     }
555   
556   for (s = work->serverlist; s; s = s->next)
557     {
558       if (strequal(name,s->serv.name)) break;
559     }
560   
561   if (s && !replace)
562     {
563       DEBUG(4,("Not replacing %s\n",name));
564       return(s);
565     }
566   
567   updatedlists=True;
568   
569   if (!s)
570     {
571       newentry = True;
572       s = (struct server_record *)malloc(sizeof(*s));
573       
574       if (!s) return(NULL);
575       
576       bzero((char *)s,sizeof(*s));
577     }
578   
579   if (ip_equal(bcast_ip, d->bcast_ip) &&
580       strequal(lp_workgroup(),work->work_group))
581     {
582       servertype |= SV_TYPE_LOCAL_LIST_ONLY;
583     }
584   else
585     {
586       servertype &= ~SV_TYPE_LOCAL_LIST_ONLY;
587     }
588   
589   /* update the entry */
590   StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1);
591   StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1);
592   strupper(s->serv.name);
593   s->serv.type  = servertype;
594   s->death_time = ttl?time(NULL)+ttl*3:0;
595   
596   /* for a domain entry, the comment field refers to the server name */
597   
598   if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment);
599   
600   if (newentry)
601     {
602       add_server(work, s);
603       
604       DEBUG(3,("Added "));
605     }
606   else
607     {
608       DEBUG(3,("Updated "));
609     }
610   
611   DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
612            name,servertype,comment,
613            work->work_group,inet_ntoa(d->bcast_ip)));
614   
615   return(s);
616 }
617
618
619 /*******************************************************************
620   write out browse.dat
621   ******************************************************************/
622 void write_browse_list(void)
623 {
624   struct domain_record *d;
625   
626   pstring fname,fnamenew;
627   FILE *f;
628   
629   if (!updatedlists) return;
630   
631   dump_names();
632   dump_workgroups();
633   
634   updatedlists = False;
635   updatecount++;
636   
637   strcpy(fname,lp_lockdir());
638   trim_string(fname,NULL,"/");
639   strcat(fname,"/");
640   strcat(fname,SERVER_LIST);
641   strcpy(fnamenew,fname);
642   strcat(fnamenew,".");
643   
644   f = fopen(fnamenew,"w");
645   
646   if (!f)
647     {
648       DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
649       return;
650     }
651   
652   for (d = domainlist; d ; d = d->next)
653     {
654       struct work_record *work;
655       for (work = d->workgrouplist; work ; work = work->next)
656         {
657           struct server_record *s;
658           for (s = work->serverlist; s ; s = s->next)
659             {
660               fstring tmp;
661               
662               /* don't list domains I don't have a master for */
663               if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) &&
664                   !s->serv.comment[0])
665                 {
666                   continue;
667                 }
668               
669               /* output server details, plus what workgroup/domain
670                  they're in. without the domain information, the
671                  combined list of all servers in all workgroups gets
672                  sent to anyone asking about any workgroup! */
673               
674               sprintf(tmp, "\"%s\"", s->serv.name);
675               fprintf(f, "%-25s ", tmp);
676               fprintf(f, "%08x ", s->serv.type);
677               sprintf(tmp, "\"%s\" ", s->serv.comment);
678               fprintf(f, "%-30s", tmp);
679               fprintf(f, "\"%s\"\n", work->work_group);
680             }
681         }
682     }
683   
684   fclose(f);
685   unlink(fname);
686   chmod(fnamenew,0644);
687   rename(fnamenew,fname);   
688   DEBUG(3,("Wrote browse list %s\n",fname));
689 }
690
691
692 /*******************************************************************
693   expire old servers in the serverlist
694   ******************************************************************/
695 void expire_servers(time_t t)
696 {
697   struct domain_record *d;
698   
699   for (d = domainlist ; d ; d = d->next)
700     {
701       struct work_record *work;
702       
703       for (work = d->workgrouplist; work; work = work->next)
704         {
705           remove_old_servers(work, t);
706         }
707     }
708 }
709