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