namedbname.c: Added 'S' or 'R' flags to wins.dat to aid debugging. Forced all type 1e
[samba.git] / source / 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-1997
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 wins_ip;
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, int search)
144 {
145         struct name_record *ret;
146   
147         for (ret = n; ret; ret = ret->next)
148         {
149                 if (name_equal(&ret->name,name))
150                 {
151                         /* self search: self names only */
152                         if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF)
153                         {
154                                 continue;
155                         }
156                         DEBUG(9,("find_name: found name %s(%02x)\n", 
157                                   name->name, name->name_type));
158                         return ret;
159                 }
160         }
161     DEBUG(9,("find_name: name %s(%02x) NOT FOUND\n", name->name, 
162               name->name_type));
163     return NULL;
164 }
165
166
167 /****************************************************************************
168   find a name in the domain database namelist 
169   search can be any of:
170   FIND_SELF - look exclusively for names the samba server has added for itself
171   FIND_LOCAL - look for names in the local subnet record.
172   FIND_WINS - look for names in the WINS record
173   **************************************************************************/
174 struct name_record *find_name_search(struct subnet_record **d,
175                         struct nmb_name *name,
176                         int search, struct in_addr ip)
177 {
178   if (d == NULL) return NULL; /* bad error! */
179         
180   if (search & FIND_LOCAL) {
181     if (*d != NULL) {
182       struct name_record *n = find_name((*d)->namelist, name, search);
183       DEBUG(4,("find_name on local: %s %s search %x\n",
184                namestr(name),inet_ntoa(ip), search));
185       if (n) return n;
186     }
187   }
188
189   if (!(search & FIND_WINS)) return NULL;
190
191   /* find WINS subnet record. */
192   *d = wins_subnet;
193   
194   if (*d == NULL) return NULL;
195   
196   DEBUG(4,("find_name on WINS: %s %s search %x\n",
197            namestr(name),inet_ntoa(ip), search));
198   return find_name((*d)->namelist, name, search);
199 }
200
201
202 /****************************************************************************
203   dump a copy of the name table
204   **************************************************************************/
205 void dump_names(void)
206 {
207   struct name_record *n;
208   fstring fname, fnamenew;
209   time_t t = time(NULL);
210   
211   FILE *f;
212  
213   if(lp_wins_support() == False || wins_subnet == 0)
214     return;
215  
216   strcpy(fname,lp_lockdir());
217   trim_string(fname,NULL,"/");
218   strcat(fname,"/");
219   strcat(fname,WINS_LIST);
220   strcpy(fnamenew,fname);
221   strcat(fnamenew,".");
222   
223   f = fopen(fnamenew,"w");
224   
225   if (!f)
226   {
227     DEBUG(3,("Can't open %s - %s\n",fnamenew,strerror(errno)));
228     return;
229   }
230   
231   DEBUG(4,("Dump of WINS name table:\n"));
232   
233   for (n = wins_subnet->namelist; n; n = n->next)
234    {
235      int i;
236
237      DEBUG(4,("%15s ", inet_ntoa(wins_subnet->bcast_ip)));
238      DEBUG(4,("%15s ", inet_ntoa(wins_subnet->mask_ip)));
239      DEBUG(4,("%-19s TTL=%ld ",
240                namestr(&n->name),
241                n->death_time?n->death_time-t:0));
242
243      for (i = 0; i < n->num_ips; i++)
244       {
245         DEBUG(4,("%15s NB=%2x source=%d",
246                  inet_ntoa(n->ip_flgs[i].ip),
247                     n->ip_flgs[i].nb_flags,n->source));
248
249       }
250      DEBUG(4,("\n"));
251
252      if (f && ((n->source == REGISTER) || (n->source == SELF)))
253       {
254       /* XXXX i have little imagination as to how to output nb_flags as
255          anything other than as a hexadecimal number :-) */
256
257         fprintf(f, "%s#%02x %ld ",
258                n->name.name,n->name.name_type, /* XXXX ignore scope for now */
259                n->death_time);
260
261         for (i = 0; i < n->num_ips; i++)
262         {
263            fprintf(f, "%s %2x%c ",
264                 inet_ntoa(n->ip_flgs[i].ip),
265                 n->ip_flgs[i].nb_flags, (n->source == REGISTER ? 'R' : 'S'));
266         }
267         fprintf(f, "\n");
268       }
269
270   }
271
272   fclose(f);
273   unlink(fname);
274   chmod(fnamenew,0644);
275   rename(fnamenew,fname);   
276
277   DEBUG(3,("Wrote wins database %s\n",fname));
278 }
279
280
281 /****************************************************************************
282   load a netbios name database file
283
284   XXXX we cannot cope with loading Internet Group names, yet
285   ****************************************************************************/
286 void load_netbios_names(void)
287 {
288   struct subnet_record *d = wins_subnet;
289   fstring fname;
290
291   FILE *f;
292   pstring line;
293
294   if (!d) return;
295
296   strcpy(fname,lp_lockdir());
297   trim_string(fname,NULL,"/");
298   strcat(fname,"/");
299   strcat(fname,WINS_LIST);
300
301   f = fopen(fname,"r");
302
303   if (!f) {
304     DEBUG(2,("Can't open wins database file %s\n",fname));
305     return;
306   }
307
308   while (!feof(f))
309     {
310       pstring name_str, ip_str, ttd_str, nb_flags_str;
311
312       pstring name;
313       int type = 0;
314       unsigned int nb_flags;
315       time_t ttd;
316           struct in_addr ipaddr;
317
318           enum name_source source;
319
320       char *ptr;
321           int count = 0;
322
323       char *p;
324
325       if (!fgets_slash(line,sizeof(pstring),f)) continue;
326
327       if (*line == '#') continue;
328
329         ptr = line;
330
331         if (next_token(&ptr,name_str    ,NULL)) ++count;
332         if (next_token(&ptr,ttd_str     ,NULL)) ++count;
333         if (next_token(&ptr,ip_str      ,NULL)) ++count;
334         if (next_token(&ptr,nb_flags_str,NULL)) ++count;
335
336         if (count <= 0) continue;
337
338         if (count != 4) {
339           DEBUG(0,("Ill formed wins line"));
340           DEBUG(0,("[%s]: name#type abs_time ip nb_flags\n",line));
341           continue;
342         }
343
344       /* Deal with SELF or REGISTER name encoding. Default is REGISTER 
345          for compatibility with old nmbds. */
346       if(nb_flags_str[strlen(nb_flags_str)-1] == 'S')
347       {
348         DEBUG(5,("Ignoring SELF name %s\n", line));
349         continue;
350       }
351
352       if(nb_flags_str[strlen(nb_flags_str)-1] == 'R')
353         nb_flags_str[strlen(nb_flags_str)-1] = '\0';
354
355       /* netbios name. # divides the name from the type (hex): netbios#xx */
356       strcpy(name,name_str);
357
358       p = strchr(name,'#');
359
360       if (p) {
361             *p = 0;
362             sscanf(p+1,"%x",&type);
363       }
364
365       /* decode the netbios flags (hex) and the time-to-die (seconds) */
366           sscanf(nb_flags_str,"%x",&nb_flags);
367           sscanf(ttd_str,"%ld",&ttd);
368
369           ipaddr = *interpret_addr2(ip_str);
370
371       if (ip_equal(ipaddr,ipzero)) {
372          source = SELF;
373       }
374       else
375       {
376          source = REGISTER;
377       }
378
379       DEBUG(4, ("add WINS line: %s#%02x %ld %s %2x\n",
380                name,type, ttd, inet_ntoa(ipaddr), nb_flags));
381
382       /* add all entries that have 60 seconds or more to live */
383       if (ttd - 60 > time(NULL) || ttd == 0)
384       {
385         time_t t = (ttd?ttd-time(NULL):0) / 3;
386
387         /* add netbios entry read from the wins.dat file. IF it's ok */
388         add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True,True);
389       }
390     }
391
392   fclose(f);
393 }
394
395
396 /****************************************************************************
397   remove an entry from the name list
398   ****************************************************************************/
399 void remove_netbios_name(struct subnet_record *d,
400                         char *name,int type, enum name_source source,
401                          struct in_addr ip)
402 {
403   struct nmb_name nn;
404   struct name_record *n;
405
406   make_nmb_name(&nn, name, type, scope);
407   n = find_name_search(&d, &nn, FIND_LOCAL, ip);
408   
409   if (n && n->source == source) remove_name(d,n);
410 }
411
412
413 /****************************************************************************
414   add an entry to the name list.
415
416   this is a multi-purpose function.
417
418   it adds samba's own names in to its records on each interface, keeping a
419   record of whether it is a master browser, domain master, or WINS server.
420
421   it also keeps a record of WINS entries.
422
423   ****************************************************************************/
424 struct name_record *add_netbios_entry(struct subnet_record *d,
425                 char *name, int type, int nb_flags, 
426                 int ttl, enum name_source source, struct in_addr ip,
427                 BOOL new_only,BOOL wins)
428 {
429   struct name_record *n;
430   struct name_record *n2=NULL;
431   struct subnet_record *found_subnet = 0;
432   int search = 0;
433   BOOL self = (source == SELF);
434
435   /* add the name to the WINS list if the name comes from a directed query */
436   search |= wins ? FIND_WINS : FIND_LOCAL;
437
438   /* If it's a local search then we need to set the subnet
439      we are looking at. */
440   if(search & FIND_LOCAL)
441     found_subnet = d;
442
443   /* search for SELF names only */
444   search |= self ? FIND_SELF : 0;
445
446   if (!self)
447   {
448     if (!wins && (type != 0x1b))
449     {
450        /* the only broadcast (non-WINS) names we are adding are ours
451           (SELF) and Domain Master type names */
452        return NULL;
453     }
454     if(wins && (type == 0x1d))
455     {
456       /* Do not allow any 0x1d names to be registered in a WINS,
457          database although we return success for them.
458        */
459       return NULL;
460     }
461   }
462
463   if(type == 0x1e)      
464   {  
465     /* Add all 1e names as address 255.255.255.255 */  
466     ip = *interpret_addr2("255.255.255.255");  
467   }  
468
469   n = (struct name_record *)malloc(sizeof(*n));
470   if (!n) return(NULL);
471
472   bzero((char *)n,sizeof(*n));
473
474   n->num_ips = 1; /* XXXX ONLY USE THIS FUNCTION FOR ONE ENTRY */
475   n->ip_flgs = (struct nmb_ip*)malloc(sizeof(*n->ip_flgs) * n->num_ips);
476   if (!n->ip_flgs)
477   {
478      free(n);
479      return NULL;
480   }
481
482   make_nmb_name(&n->name,name,type,scope);
483
484   if ((n2 = find_name_search(&found_subnet, &n->name, search, new_only?ipzero:ip)))
485   {
486     free(n->ip_flgs);
487     free(n);
488     if (new_only || (n2->source==SELF && source!=SELF)) return n2;
489     n = n2;
490     d = found_subnet;
491   }
492
493   if (ttl)
494      n->death_time = time(NULL)+ttl*3;
495   n->refresh_time = time(NULL)+GET_TTL(ttl);
496
497   /* XXXX only one entry expected with this function */
498   n->ip_flgs[0].ip = ip;
499   n->ip_flgs[0].nb_flags = nb_flags;
500
501   n->source = source;
502   
503   if (!n2) add_name(d,n);
504
505   DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x to interface %s\n",
506            namestr(&n->name),inet_ntoa(ip),ttl,nb_flags,
507            ip_equal(d->bcast_ip, wins_ip) ? "WINS" : (char *)inet_ntoa(d->bcast_ip)));
508
509   return(n);
510 }
511
512
513 /*******************************************************************
514   expires old names in the namelist
515   ******************************************************************/
516 void expire_names(time_t t)
517 {
518         struct name_record *n;
519         struct name_record *next;
520         struct subnet_record *d;
521
522         /* expire old names */
523         for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d))
524         {
525           for (n = d->namelist; n; n = next)
526             {
527               next = n->next;
528               if (n->death_time && n->death_time < t)
529                 {
530                   if (n->source == SELF) {
531                     DEBUG(3,("not expiring SELF name %s\n", namestr(&n->name)));
532                     n->death_time += 300;
533                     continue;
534                   }
535                   DEBUG(3,("Removing dead name %s\n", namestr(&n->name)));
536                   
537                   if (n->prev) n->prev->next = n->next;
538                   if (n->next) n->next->prev = n->prev;
539                   
540                   if (d->namelist == n) d->namelist = n->next; 
541                   
542                   free(n->ip_flgs);
543                   free(n);
544                 }
545             }
546         }
547 }
548
549
550 /***************************************************************************
551   assume a WINS name is a dns name, and do a gethostbyname() on it.
552   ****************************************************************************/
553 struct name_record *dns_name_search(struct nmb_name *question, int Time)
554 {
555         int name_type = question->name_type;
556         char *qname = question->name;
557         BOOL dns_type = (name_type == 0x20 || name_type == 0);
558         struct in_addr dns_ip;
559
560         if (wins_subnet == NULL) 
561           return NULL;
562
563         DEBUG(3,("Search for %s - ", namestr(question)));
564
565         /* only do DNS lookups if the query is for type 0x20 or type 0x0 */
566         if (!dns_type)
567         {
568                 DEBUG(3,("types 0x20 0x0 only: name not found\n"));
569                 return NULL;
570         }
571
572         /* look it up with DNS */      
573         dns_ip.s_addr = interpret_addr(qname);
574
575         if (!dns_ip.s_addr)
576         {
577                 /* no luck with DNS. We could possibly recurse here XXXX */
578                 DEBUG(3,("not found. no recursion.\n"));
579                 /* add the fail to WINS cache of names. give it 1 hour in the cache */
580                 add_netbios_entry(wins_subnet,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip,
581                                   True, True);
582                 return NULL;
583         }
584
585         DEBUG(3,("found with DNS: %s\n", inet_ntoa(dns_ip)));
586
587         /* add it to our WINS cache of names. give it 2 hours in the cache */
588         return add_netbios_entry(wins_subnet,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip,
589                                  True,True);
590 }