fixed an obvious bug that meant that DosPrintQEnum could never
[kai/samba.git] / source3 / namedbname.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-1996
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    Module name: namedbname.c
22
23    Revision History:
24
25    14 jan 96: lkcl@pires.co.uk
26    added multiple workgroup domain master support
27
28    04 jul 96: lkcl@pires.co.uk
29    created module namedbname containing name database functions
30 */
31
32 #include "includes.h"
33
34 extern int DEBUGLEVEL;
35
36 extern pstring scope;
37 extern struct in_addr ipzero;
38 extern struct in_addr ipgrp;
39
40 extern struct subnet_record *subnetlist;
41
42 #define WINS_LIST "wins.dat"
43
44 uint16 nb_type = 0; /* samba's NetBIOS name type */
45
46
47 /****************************************************************************
48   samba's NetBIOS name type
49
50   XXXX maybe functionality could be set: B, M, P or H name registration
51   and resolution could be set through nb_type. just a thought.  
52   ****************************************************************************/
53 void set_samba_nb_type(void)
54 {
55         if (lp_wins_support() || (*lp_wins_server()))
56         {
57                 nb_type = NB_MFLAG; /* samba is a 'hybrid' node type */
58         }
59         else
60         {
61                 nb_type = NB_BFLAG; /* samba is broadcast-only node type */
62         }
63 }
64
65
66 /****************************************************************************
67   true if two netbios names are equal
68 ****************************************************************************/
69 BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2)
70 {
71   return n1->name_type == n2->name_type &&
72                  strequal(n1->name ,n2->name ) &&
73          strequal(n1->scope,n2->scope);
74 }
75
76
77 /****************************************************************************
78   true if the netbios name is ^1^2__MSBROWSE__^2^1
79
80   note: this name is registered if as a master browser or backup browser
81   you are responsible for a workgroup (when you announce a domain by
82   broadcasting on your local subnet, you announce it as coming from this
83   name: see announce_host()).
84
85   **************************************************************************/
86 BOOL ms_browser_name(char *name, int type)
87 {
88   return strequal(name,MSBROWSE) && type == 0x01;
89 }
90
91
92 /****************************************************************************
93   add a netbios name into the namelist
94   **************************************************************************/
95 static void add_name(struct subnet_record *d, struct name_record *n)
96 {
97   struct name_record *n2;
98
99   if (!d) return;
100
101   if (!d->namelist)
102   {
103     d->namelist = n;
104     n->prev = NULL;
105     n->next = NULL;
106     return;
107   }
108
109   for (n2 = d->namelist; n2->next; n2 = n2->next) ;
110
111   n2->next = n;
112   n->next = NULL;
113   n->prev = n2;
114 }
115
116
117 /****************************************************************************
118   remove a name from the namelist. The pointer must be an element just 
119   retrieved
120   **************************************************************************/
121 void remove_name(struct subnet_record *d, struct name_record *n)
122 {
123   struct name_record *nlist;
124   if (!d) return;
125
126   nlist = d->namelist;
127
128   while (nlist && nlist != n) nlist = nlist->next;
129
130   if (nlist)
131   {
132     if (nlist->next) nlist->next->prev = nlist->prev;
133     if (nlist->prev) nlist->prev->next = nlist->next;
134     free(nlist);
135   }
136 }
137
138
139 /****************************************************************************
140   find a name in a namelist.
141   **************************************************************************/
142 struct name_record *find_name(struct name_record *n,
143                         struct nmb_name *name,
144                         int search)
145 {
146         struct name_record *ret;
147   
148         for (ret = n; ret; ret = ret->next)
149         {
150                 if (name_equal(&ret->name,name))
151                 {
152                         /* self search: self names only */
153                         if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF)
154                                 continue;
155           
156                         return ret;
157                 }
158         }
159     return NULL;
160 }
161
162
163 /****************************************************************************
164   find a name in the domain database namelist 
165   search can be any of:
166   FIND_SELF - look exclusively for names the samba server has added for itself
167   FIND_LOCAL - look for names in the local subnet record.
168   FIND_WINS - look for names in the WINS record
169   **************************************************************************/
170 struct name_record *find_name_search(struct subnet_record **d,
171                         struct nmb_name *name,
172                         int search, struct in_addr ip)
173 {
174         if (d == NULL) return NULL; /* bad error! */
175         
176     if ((search & FIND_LOCAL) == FIND_LOCAL)
177         {
178                 if (*d != NULL)
179         {
180                         struct name_record *n = find_name((*d)->namelist, name, search);
181                         DEBUG(4,("find_name on local: %s %s search %x\n",
182                                                 namestr(name),inet_ntoa(ip), search));
183                         if (n) return n;
184                 }
185         }
186
187         if ((search & FIND_WINS) != FIND_WINS) return NULL;
188
189         /* find WINS subnet record. */
190         *d = find_subnet(ipgrp);
191
192         if (*d == NULL) return NULL;
193
194         DEBUG(4,("find_name on WINS: %s %s search %x\n",
195                                                 namestr(name),inet_ntoa(ip), search));
196         return find_name((*d)->namelist, name, search);
197 }
198
199
200 /****************************************************************************
201   dump a copy of the name table
202   **************************************************************************/
203 void dump_names(void)
204 {
205   struct name_record *n;
206   struct subnet_record *d;
207   fstring fname, fnamenew;
208   time_t t = time(NULL);
209   
210   FILE *f;
211   
212   strcpy(fname,lp_lockdir());
213   trim_string(fname,NULL,"/");
214   strcat(fname,"/");
215   strcat(fname,WINS_LIST);
216   strcpy(fnamenew,fname);
217   strcat(fnamenew,".");
218   
219   f = fopen(fnamenew,"w");
220   
221   if (!f)
222   {
223     DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
224   }
225   
226   DEBUG(3,("Dump of local name table:\n"));
227   
228   for (d = subnetlist; d; d = d->next)
229    for (n = d->namelist; n; n = n->next)
230     {
231       if (f && ip_equal(d->bcast_ip, ipgrp) && n->source == REGISTER)
232       {
233         fstring data;
234
235       /* XXXX i have little imagination as to how to output nb_flags as
236          anything other than as a hexadecimal number :-) */
237
238         sprintf(data, "%s#%02x %s %2x %ld",
239                n->name.name,n->name.name_type, /* XXXX ignore scope for now */
240                inet_ntoa(n->ip),
241                n->nb_flags,
242                n->death_time);
243             fprintf(f, "%s\n", data);
244       }
245
246           DEBUG(3,("%15s ", inet_ntoa(d->bcast_ip)));
247           DEBUG(3,("%15s ", inet_ntoa(d->mask_ip)));
248       DEBUG(3,("%-19s %15s NB=%2x TTL=%ld \n",
249                namestr(&n->name),
250                inet_ntoa(n->ip),
251                n->nb_flags,
252            n->death_time?n->death_time-t:0));
253     }
254
255   fclose(f);
256   unlink(fname);
257   chmod(fnamenew,0644);
258   rename(fnamenew,fname);   
259
260   DEBUG(3,("Wrote wins database %s\n",fname));
261 }
262
263
264 /****************************************************************************
265 load a netbios name database file
266 ****************************************************************************/
267 void load_netbios_names(void)
268 {
269   struct subnet_record *d = find_subnet(ipgrp);
270   fstring fname;
271
272   FILE *f;
273   pstring line;
274
275   if (!d) return;
276
277   strcpy(fname,lp_lockdir());
278   trim_string(fname,NULL,"/");
279   strcat(fname,"/");
280   strcat(fname,WINS_LIST);
281
282   f = fopen(fname,"r");
283
284   if (!f) {
285     DEBUG(2,("Can't open wins database file %s\n",fname));
286     return;
287   }
288
289   while (!feof(f))
290     {
291       pstring name_str, ip_str, ttd_str, nb_flags_str;
292
293       pstring name;
294       int type = 0;
295       int nb_flags;
296       time_t ttd;
297           struct in_addr ipaddr;
298
299           enum name_source source;
300
301       char *ptr;
302           int count = 0;
303
304       char *p;
305
306       if (!fgets_slash(line,sizeof(pstring),f)) continue;
307
308       if (*line == '#') continue;
309
310         ptr = line;
311
312         if (next_token(&ptr,name_str    ,NULL)) ++count;
313         if (next_token(&ptr,ip_str      ,NULL)) ++count;
314         if (next_token(&ptr,ttd_str     ,NULL)) ++count;
315         if (next_token(&ptr,nb_flags_str,NULL)) ++count;
316
317         if (count <= 0) continue;
318
319         if (count != 4) {
320           DEBUG(0,("Ill formed wins line"));
321           DEBUG(0,("[%s]: name#type ip nb_flags abs_time\n",line));
322           continue;
323         }
324
325       /* netbios name. # divides the name from the type (hex): netbios#xx */
326       strcpy(name,name_str);
327
328       p = strchr(name,'#');
329
330       if (p) {
331             *p = 0;
332             sscanf(p+1,"%x",&type);
333       }
334
335       /* decode the netbios flags (hex) and the time-to-die (seconds) */
336           sscanf(nb_flags_str,"%x",&nb_flags);
337           sscanf(ttd_str,"%ld",&ttd);
338
339           ipaddr = *interpret_addr2(ip_str);
340
341       if (ip_equal(ipaddr,ipzero)) {
342          source = SELF;
343       }
344       else
345       {
346          source = REGISTER;
347       }
348
349       DEBUG(4, ("add WINS line: %s#%02x %s %ld %2x\n",
350                name,type, inet_ntoa(ipaddr), ttd, nb_flags));
351
352       /* add all entries that have 60 seconds or more to live */
353       if (ttd - 60 < time(NULL) || ttd == 0)
354       {
355         time_t t = (ttd?ttd-time(NULL):0) / 3;
356
357         /* add netbios entry read from the wins.dat file. IF it's ok */
358         add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True,True);
359       }
360     }
361
362   fclose(f);
363 }
364
365
366 /****************************************************************************
367   remove an entry from the name list
368   ****************************************************************************/
369 void remove_netbios_name(struct subnet_record *d,
370                         char *name,int type, enum name_source source,
371                          struct in_addr ip)
372 {
373   struct nmb_name nn;
374   struct name_record *n;
375
376   make_nmb_name(&nn, name, type, scope);
377   n = find_name_search(&d, &nn, FIND_LOCAL, ip);
378   
379   if (n && n->source == source) remove_name(d,n);
380 }
381
382
383 /****************************************************************************
384   add an entry to the name list.
385
386   this is a multi-purpose function.
387
388   it adds samba's own names in to its records on each interface, keeping a
389   record of whether it is a master browser, domain master, or WINS server.
390
391   it also keeps a record of WINS entries.
392
393   ****************************************************************************/
394 struct name_record *add_netbios_entry(struct subnet_record *d,
395                 char *name, int type, int nb_flags, 
396                 int ttl, enum name_source source, struct in_addr ip,
397                 BOOL new_only,BOOL wins)
398 {
399   struct name_record *n;
400   struct name_record *n2=NULL;
401   int search = 0;
402   BOOL self = source == SELF;
403
404   /* add the name to the WINS list if the name comes from a directed query */
405   search |= wins ? FIND_WINS : FIND_LOCAL;
406   /* search for SELF names only */
407   search |= self ? FIND_SELF : 0;
408
409   if (!self)
410   {
411     if (!wins && type != 0x1b)
412     {
413        /* the only broadcast (non-WINS) names we are adding are ours
414           (SELF) and Domain Master type names */
415        return NULL;
416     }
417   }
418
419   n = (struct name_record *)malloc(sizeof(*n));
420   if (!n) return(NULL);
421
422   bzero((char *)n,sizeof(*n));
423
424   make_nmb_name(&n->name,name,type,scope);
425
426   if ((n2 = find_name_search(&d, &n->name, search, new_only?ipzero:ip)))
427   {
428     free(n);
429     if (new_only || (n2->source==SELF && source!=SELF)) return n2;
430     n = n2;
431   }
432
433   if (ttl)
434      n->death_time = time(NULL)+ttl*3;
435   n->refresh_time = time(NULL)+GET_TTL(ttl);
436
437   n->ip = ip;
438   n->nb_flags = nb_flags;
439   n->source = source;
440   
441   if (!n2) add_name(d,n);
442
443   DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n",
444                 namestr(&n->name),inet_ntoa(ip),ttl,nb_flags));
445
446   return(n);
447 }
448
449
450 /*******************************************************************
451   expires old names in the namelist
452   ******************************************************************/
453 void expire_names(time_t t)
454 {
455         struct name_record *n;
456         struct name_record *next;
457         struct subnet_record *d;
458
459         /* expire old names */
460         for (d = subnetlist; d; d = d->next)
461         {
462           for (n = d->namelist; n; n = next)
463                 {
464                   if (n->death_time && n->death_time < t)
465                 {
466                   DEBUG(3,("Removing dead name %s\n", namestr(&n->name)));
467                   
468                   next = n->next;
469                   
470                   if (n->prev) n->prev->next = n->next;
471                   if (n->next) n->next->prev = n->prev;
472                   
473                   if (d->namelist == n) d->namelist = n->next; 
474                   
475                   free(n);
476                 }
477                   else
478                 {
479                   next = n->next;
480                 }
481                 }
482         }
483 }
484
485
486 /***************************************************************************
487   reply to a name query
488   ****************************************************************************/
489 struct name_record *search_for_name(struct subnet_record **d,
490                                         struct nmb_name *question,
491                                     struct in_addr ip, int Time, int search)
492 {
493   int name_type = question->name_type;
494   char *qname = question->name;
495   BOOL dns_type = name_type == 0x20 || name_type == 0;
496   
497   struct name_record *n;
498   
499   DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip)));
500   
501   /* first look up name in cache */
502   n = find_name_search(d,question,search,ip);
503   
504   if (*d == NULL) return NULL;
505
506   DEBUG(4,("subnet %s ", inet_ntoa((*d)->bcast_ip)));
507
508   /* now try DNS lookup. */
509   if (!n)
510     {
511       struct in_addr dns_ip;
512       unsigned long a;
513       
514       /* only do DNS lookups if the query is for type 0x20 or type 0x0 */
515       if (!dns_type && name_type != 0x1b)
516         {
517           DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n"));
518           return NULL;
519         }
520       
521       /* look it up with DNS */      
522       a = interpret_addr(qname);
523       
524       putip((char *)&dns_ip,(char *)&a);
525       
526       if (!a)
527         {
528           /* no luck with DNS. We could possibly recurse here XXXX */
529           DEBUG(3,("no recursion.\n"));
530       /* add the fail to our WINS cache of names. give it 1 hour in the cache */
531           add_netbios_entry(*d,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip,
532                                                 True, True);
533           return NULL;
534         }
535       
536       /* add it to our WINS cache of names. give it 2 hours in the cache */
537       n = add_netbios_entry(*d,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip,
538                                                 True,True);
539       
540       /* failed to add it? yikes! */
541       if (!n) return NULL;
542     }
543   
544   /* is our entry already dead? */
545   if (n->death_time)
546     {
547       if (n->death_time < Time) return False;
548     }
549   
550   /* it may have been an earlier failure */
551   if (n->source == DNSFAIL)
552     {
553       DEBUG(3,("DNSFAIL\n"));
554       return NULL;
555     }
556   
557   DEBUG(3,("OK %s\n",inet_ntoa(n->ip)));      
558   
559   return n;
560 }
561
562