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