checks against file handle in api_fd_reply(). i don't know what error
[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-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_client_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_client_subnet) && lp_wins_support())
148     updatedlists = True;
149 }
150
151
152 /****************************************************************************
153   find a name in a subnet.
154   **************************************************************************/
155 struct name_record *find_name_on_subnet(struct subnet_record *d,
156                         struct nmb_name *name, BOOL self_only)
157 {
158   struct name_record *n = d->namelist;
159   struct name_record *ret;
160   
161   for (ret = n; ret; ret = ret->next)
162   {
163     if (name_equal(&ret->name,name))
164     {
165       /* self search: self names only */
166       if (self_only && (ret->source != SELF))
167       {
168         continue;
169       }
170       DEBUG(9,("find_name_on_subnet: on subnet %s - found name %s(%02x) source=%d\n", 
171                 inet_ntoa(d->bcast_ip), name->name, name->name_type, ret->source));
172       return ret;
173     }
174   }
175   DEBUG(9,("find_name_on_subnet: on subnet %s - name %s(%02x) NOT FOUND\n", 
176             inet_ntoa(d->bcast_ip), name->name, name->name_type));
177   return NULL;
178 }
179
180 /****************************************************************************
181   dump a copy of the name table
182   **************************************************************************/
183 void dump_names(void)
184 {
185   struct name_record *n;
186   fstring fname, fnamenew;
187   time_t t = time(NULL);
188   
189   FILE *f;
190  
191   if(lp_wins_support() == False || wins_client_subnet == NULL)
192     return;
193  
194   fstrcpy(fname,lp_lockdir());
195   trim_string(fname,NULL,"/");
196   strcat(fname,"/");
197   strcat(fname,WINS_LIST);
198   fstrcpy(fnamenew,fname);
199   strcat(fnamenew,".");
200   
201   f = fopen(fnamenew,"w");
202   
203   if (!f)
204   {
205     DEBUG(3,("Can't open %s - %s\n",fnamenew,strerror(errno)));
206     return;
207   }
208   
209   DEBUG(4,("Dump of WINS name table:\n"));
210   
211   for (n = wins_client_subnet->namelist; n; n = n->next)
212    {
213      int i;
214
215      DEBUG(4,("%15s ", inet_ntoa(wins_client_subnet->bcast_ip)));
216      DEBUG(4,("%15s ", inet_ntoa(wins_client_subnet->mask_ip)));
217      DEBUG(4,("%-19s TTL=%ld ",
218                namestr(&n->name),
219                n->death_time?n->death_time-t:0));
220
221      for (i = 0; i < n->num_ips; i++)
222       {
223         DEBUG(4,("%15s NB=%2x source=%d",
224                  inet_ntoa(n->ip_flgs[i].ip),
225                     n->ip_flgs[i].nb_flags,n->source));
226
227       }
228      DEBUG(4,("\n"));
229
230      if (f && ((n->source == REGISTER) || (n->source == SELF)))
231       {
232       /* XXXX i have little imagination as to how to output nb_flags as
233          anything other than as a hexadecimal number :-) */
234
235         fprintf(f, "%s#%02x %ld ",
236                n->name.name,n->name.name_type, /* XXXX ignore scope for now */
237                n->death_time);
238
239         for (i = 0; i < n->num_ips; i++)
240         {
241            fprintf(f, "%s %2x%c ",
242                 inet_ntoa(n->ip_flgs[i].ip),
243                 n->ip_flgs[i].nb_flags, (n->source == REGISTER ? 'R' : 'S'));
244         }
245         fprintf(f, "\n");
246       }
247
248   }
249
250   fclose(f);
251   unlink(fname);
252   chmod(fnamenew,0644);
253   rename(fnamenew,fname);   
254
255   DEBUG(3,("Wrote wins database %s\n",fname));
256 }
257
258
259 /****************************************************************************
260   load a netbios name database file
261
262   XXXX we cannot cope with loading Internet Group names, yet
263   ****************************************************************************/
264 void load_netbios_names(void)
265 {
266   struct subnet_record *d = wins_client_subnet;
267   fstring fname;
268
269   FILE *f;
270   pstring line;
271
272   if (!d) return;
273
274   fstrcpy(fname,lp_lockdir());
275   trim_string(fname,NULL,"/");
276   strcat(fname,"/");
277   strcat(fname,WINS_LIST);
278
279   f = fopen(fname,"r");
280
281   if (!f) {
282     DEBUG(2,("Can't open wins database file %s\n",fname));
283     return;
284   }
285
286   while (!feof(f))
287     {
288       pstring name_str, ip_str, ttd_str, nb_flags_str;
289
290       pstring name;
291       int type = 0;
292       unsigned int nb_flags;
293       time_t ttd;
294           struct in_addr ipaddr;
295
296           enum name_source source;
297
298       char *ptr;
299           int count = 0;
300
301       char *p;
302
303       if (!fgets_slash(line,sizeof(pstring),f)) continue;
304
305       if (*line == '#') continue;
306
307         ptr = line;
308
309         if (next_token(&ptr,name_str    ,NULL)) ++count;
310         if (next_token(&ptr,ttd_str     ,NULL)) ++count;
311         if (next_token(&ptr,ip_str      ,NULL)) ++count;
312         if (next_token(&ptr,nb_flags_str,NULL)) ++count;
313
314         if (count <= 0) continue;
315
316         if (count != 4) {
317           DEBUG(0,("Ill formed wins line"));
318           DEBUG(0,("[%s]: name#type abs_time ip nb_flags\n",line));
319           continue;
320         }
321
322       /* Deal with SELF or REGISTER name encoding. Default is REGISTER 
323          for compatibility with old nmbds. */
324       if(nb_flags_str[strlen(nb_flags_str)-1] == 'S')
325       {
326         DEBUG(5,("Ignoring SELF name %s\n", line));
327         continue;
328       }
329
330       if(nb_flags_str[strlen(nb_flags_str)-1] == 'R')
331         nb_flags_str[strlen(nb_flags_str)-1] = '\0';
332
333       /* netbios name. # divides the name from the type (hex): netbios#xx */
334       pstrcpy(name,name_str);
335
336       p = strchr(name,'#');
337
338       if (p) {
339             *p = 0;
340             sscanf(p+1,"%x",&type);
341       }
342
343       /* decode the netbios flags (hex) and the time-to-die (seconds) */
344           sscanf(nb_flags_str,"%x",&nb_flags);
345           sscanf(ttd_str,"%ld",&ttd);
346
347           ipaddr = *interpret_addr2(ip_str);
348
349       if (ip_equal(ipaddr,ipzero)) {
350          source = SELF;
351       }
352       else
353       {
354          source = REGISTER;
355       }
356
357       DEBUG(4, ("add WINS line: %s#%02x %ld %s %2x\n",
358                name,type, ttd, inet_ntoa(ipaddr), nb_flags));
359
360       /* add all entries that have 60 seconds or more to live */
361       if (ttd - 60 > time(NULL) || ttd == 0)
362       {
363         time_t t = (ttd?ttd-time(NULL):0) / 3;
364
365         /* add netbios entry read from the wins.dat file. IF it's ok */
366         add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True);
367       }
368     }
369
370   fclose(f);
371 }
372
373
374 /****************************************************************************
375   remove an entry from the name list
376   ****************************************************************************/
377 void remove_netbios_name(struct subnet_record *d,
378                         char *name,int type, enum name_source source)
379 {
380   struct nmb_name nn;
381   struct name_record *n;
382
383   make_nmb_name(&nn, name, type, scope);
384   n = find_name_on_subnet(d, &nn, FIND_ANY_NAME);
385   
386   if (n && n->source == source) remove_name(d,n);
387 }
388
389
390 /****************************************************************************
391   add an entry to the name list.
392
393   this is a multi-purpose function.
394
395   it adds samba's own names in to its records on each interface, keeping a
396   record of whether it is a master browser, domain master, or WINS server.
397
398   it also keeps a record of WINS entries.
399
400   ****************************************************************************/
401 struct name_record *add_netbios_entry(struct subnet_record *d,
402                 char *name, int type, int nb_flags, int ttl, 
403                 enum name_source source, struct in_addr ip, BOOL new_only)
404 {
405   struct name_record *n;
406   struct name_record *n2=NULL;
407   BOOL self = (source == SELF) ? FIND_SELF_NAME : FIND_ANY_NAME;
408   /* It's a WINS add if we're adding to the wins_client_subnet. */
409   BOOL wins = ( wins_client_subnet && (d == wins_client_subnet));
410
411   if(d == NULL)
412   {
413     DEBUG(0,("add_netbios_entry: called with NULL subnet record. This is a bug - \
414 please report this.!\n"));
415     return NULL;
416   }
417
418   if (!self)
419   {
420     if (!wins && (type != 0x1b))
421     {
422        /* the only broadcast (non-WINS) names we are adding are ours
423           (SELF) and Domain Master type names */
424        return NULL;
425     }
426     if(wins && (type == 0x1d))
427     {
428       /* Do not allow any 0x1d names to be registered in a WINS,
429          database although we return success for them.
430        */
431       return NULL;
432     }
433   }
434
435   n = (struct name_record *)malloc(sizeof(*n));
436   if (!n) return(NULL);
437
438   bzero((char *)n,sizeof(*n));
439
440   n->num_ips = 1; /* XXXX ONLY USE THIS FUNCTION FOR ONE ENTRY */
441   n->ip_flgs = (struct nmb_ip*)malloc(sizeof(*n->ip_flgs) * n->num_ips);
442   if (!n->ip_flgs)
443   {
444      free(n);
445      return NULL;
446   }
447
448   bzero((char *)n->ip_flgs, sizeof(*n->ip_flgs) * n->num_ips);
449
450   make_nmb_name(&n->name,name,type,scope);
451
452   if ((n2 = find_name_on_subnet(d, &n->name, self)))
453   {
454     free(n->ip_flgs);
455     free(n);
456     if (new_only || (n2->source==SELF && source!=SELF)) return n2;
457     n = n2;
458   }
459
460   if (ttl)
461      n->death_time = time(NULL)+ttl*3;
462   n->refresh_time = time(NULL)+GET_TTL(ttl);
463
464   /* XXXX only one entry expected with this function */
465   n->ip_flgs[0].ip = ip;
466   n->ip_flgs[0].nb_flags = nb_flags;
467
468   n->source = source;
469   
470   if (!n2) add_name(d,n);
471
472   DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x to interface %s\n",
473            namestr(&n->name),inet_ntoa(ip),ttl,nb_flags,
474            wins ? "WINS" : (char *)inet_ntoa(d->bcast_ip)));
475
476   return(n);
477 }
478
479
480 /*******************************************************************
481   expires old names in the namelist
482   ******************************************************************/
483 void expire_names(time_t t)
484 {
485   struct name_record *n;
486   struct name_record *next;
487   struct subnet_record *d;
488
489   /* expire old names */
490   for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d))
491   {
492     for (n = d->namelist; n; n = next)
493     {
494       next = n->next;
495       if (n->death_time && n->death_time < t)
496       {
497         if (n->source == SELF) 
498         {
499           DEBUG(3,("not expiring SELF name %s\n", namestr(&n->name)));
500                     n->death_time += 300;
501           continue;
502         }
503         DEBUG(3,("Removing dead name %s\n", namestr(&n->name)));
504                   
505         if (n->prev) n->prev->next = n->next;
506         if (n->next) n->next->prev = n->prev;
507                   
508         if (d->namelist == n) d->namelist = n->next; 
509                   
510         if(n->ip_flgs != NULL)
511           free(n->ip_flgs);
512         free(n);
513       }
514     }
515   }
516 }
517
518