modified become_master() to a state-based system. becoming a master
[Samba.git] / source / namedb.c
blob305dfb4476643a16bb6540800914f0e3c4161a02
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1995
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.
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.
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.
21 Revision History:
23 14 jan 96: lkcl@pires.co.uk
24 added multiple workgroup domain master support
28 #include "includes.h"
29 #include "smb.h"
31 extern int ClientNMB;
32 extern int ClientDGRAM;
34 extern int DEBUGLEVEL;
36 extern time_t StartupTime;
37 extern pstring myname;
38 extern pstring scope;
40 extern struct in_addr ipgrp;
41 extern struct in_addr ipzero;
43 /* local interfaces structure */
44 extern struct interface *local_interfaces;
46 /* remote interfaces structure */
47 extern struct interface *remote_interfaces;
49 /* this is our domain/workgroup/server database */
50 struct subnet_record *subnetlist = NULL;
52 static BOOL updatedlists = True;
53 int updatecount=0;
55 int workgroup_count = 0; /* unique index key: one for each workgroup */
57 /* what server type are we currently */
59 #define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
60 SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \
61 SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
64 /****************************************************************************
65 add a workgroup into the domain list
66 **************************************************************************/
67 static void add_workgroup(struct work_record *work, struct subnet_record *d)
69 struct work_record *w2;
71 if (!work || !d) return;
73 if (!d->workgrouplist)
75 d->workgrouplist = work;
76 work->prev = NULL;
77 work->next = NULL;
78 return;
81 for (w2 = d->workgrouplist; w2->next; w2 = w2->next);
83 w2->next = work;
84 work->next = NULL;
85 work->prev = w2;
89 /****************************************************************************
90 create a blank workgroup
91 **************************************************************************/
92 static struct work_record *make_workgroup(char *name)
94 struct work_record *work;
95 struct subnet_record *d;
96 int t = -1;
98 if (!name || !name[0]) return NULL;
100 work = (struct work_record *)malloc(sizeof(*work));
101 if (!work) return(NULL);
103 StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
104 work->serverlist = NULL;
106 work->ServerType = DFLT_SERVER_TYPE;
107 work->RunningElection = False;
108 work->ElectionCount = 0;
109 work->needelection = False;
110 work->needannounce = True;
111 work->state = MST_NONE;
113 /* make sure all token representations of workgroups are unique */
115 for (d = subnetlist; d && t == -1; d = d->next)
117 struct work_record *w;
118 for (w = d->workgrouplist; w && t == -1; w = w->next)
120 if (strequal(w->work_group, work->work_group)) t = w->token;
124 if (t == -1)
126 work->token = ++workgroup_count;
128 else
130 work->token = t;
134 /* WfWg uses 01040b01 */
135 /* Win95 uses 01041501 */
136 /* NTAS uses ???????? */
137 work->ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
138 work->ElectionCriterion |= (lp_os_level() << 24);
139 if (lp_domain_master()) {
140 work->ElectionCriterion |= 0x80;
143 return work;
147 /*******************************************************************
148 expire old servers in the serverlist
149 time of -1 indicates everybody dies
150 ******************************************************************/
151 static void remove_old_servers(struct work_record *work, time_t t)
153 struct server_record *s;
154 struct server_record *nexts;
156 /* expire old entries in the serverlist */
157 for (s = work->serverlist; s; s = nexts)
159 if (t == -1 || (s->death_time && s->death_time < t))
161 DEBUG(3,("Removing dead server %s\n",s->serv.name));
162 updatedlists = True;
163 nexts = s->next;
165 if (s->prev) s->prev->next = s->next;
166 if (s->next) s->next->prev = s->prev;
168 if (work->serverlist == s)
169 work->serverlist = s->next;
171 free(s);
173 else
175 nexts = s->next;
181 /*******************************************************************
182 remove workgroups
183 ******************************************************************/
184 struct work_record *remove_workgroup(struct subnet_record *d,
185 struct work_record *work)
187 struct work_record *ret_work = NULL;
189 if (!d || !work) return NULL;
191 DEBUG(3,("Removing old workgroup %s\n", work->work_group));
193 remove_old_servers(work, -1);
195 ret_work = work->next;
197 if (work->prev) work->prev->next = work->next;
198 if (work->next) work->next->prev = work->prev;
200 if (d->workgrouplist == work) d->workgrouplist = work->next;
202 free(work);
204 return ret_work;
208 /****************************************************************************
209 add a domain into the list
210 **************************************************************************/
211 static void add_subnet(struct subnet_record *d)
213 struct subnet_record *d2;
215 if (!subnetlist)
217 subnetlist = d;
218 d->prev = NULL;
219 d->next = NULL;
220 return;
223 for (d2 = subnetlist; d2->next; d2 = d2->next);
225 d2->next = d;
226 d->next = NULL;
227 d->prev = d2;
230 /***************************************************************************
231 add a server into the list
232 **************************************************************************/
233 static void add_server(struct work_record *work,struct server_record *s)
235 struct server_record *s2;
237 if (!work->serverlist) {
238 work->serverlist = s;
239 s->prev = NULL;
240 s->next = NULL;
241 return;
244 for (s2 = work->serverlist; s2->next; s2 = s2->next) ;
246 s2->next = s;
247 s->next = NULL;
248 s->prev = s2;
252 /****************************************************************************
253 find a workgroup in the workgrouplist
254 only create it if the domain allows it, or the parameter 'add' insists
255 that it get created/added anyway. this allows us to force entries in
256 lmhosts file to be added.
257 **************************************************************************/
258 struct work_record *find_workgroupstruct(struct subnet_record *d,
259 fstring name, BOOL add)
261 struct work_record *ret, *work;
263 if (!d) return NULL;
265 DEBUG(4, ("workgroup search for %s: ", name));
267 if (strequal(name, "*"))
269 DEBUG(2,("add any workgroups: initiating browser search on %s\n",
270 inet_ntoa(d->bcast_ip)));
271 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, NAME_QUERY_FIND_MST,
272 MSBROWSE,0x1,0,0,
273 True,False, d->bcast_ip);
274 return NULL;
277 for (ret = d->workgrouplist; ret; ret = ret->next) {
278 if (!strcmp(ret->work_group,name)) {
279 DEBUG(4, ("found\n"));
280 return(ret);
284 if (!add) {
285 DEBUG(4, ("not found\n"));
286 return NULL;
289 DEBUG(4,("not found: creating\n"));
291 if ((work = make_workgroup(name)))
293 if (lp_preferred_master() &&
294 strequal(lp_workgroup(), name) &&
295 d->my_interface)
297 DEBUG(3, ("preferred master startup for %s\n", work->work_group));
298 work->needelection = True;
299 work->ElectionCriterion |= (1<<3);
301 if (!d->my_interface)
303 work->needelection = False;
305 add_workgroup(work, d);
306 return(work);
308 return NULL;
311 /****************************************************************************
312 find a subnet in the subnetlist
313 **************************************************************************/
314 struct subnet_record *find_subnet(struct in_addr bcast_ip)
316 struct subnet_record *d;
317 struct in_addr wins_ip = ipgrp;
319 /* search through subnet list for broadcast/netmask that matches
320 the source ip address. a subnet 255.255.255.255 represents the
321 WINS list. */
323 for (d = subnetlist; d; d = d->next)
325 if (ip_equal(bcast_ip, wins_ip))
327 if (ip_equal(bcast_ip, d->bcast_ip))
329 return d;
332 else if (same_net(bcast_ip, d->bcast_ip, d->mask_ip))
334 return(d);
338 return (NULL);
342 /****************************************************************************
343 dump a copy of the workgroup/domain database
344 **************************************************************************/
345 void dump_workgroups(void)
347 struct subnet_record *d;
349 for (d = subnetlist; d; d = d->next)
351 if (d->workgrouplist)
353 struct work_record *work;
355 DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip)));
356 DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip)));
358 for (work = d->workgrouplist; work; work = work->next)
360 DEBUG(4,("\t%s(%d)\n", work->work_group, work->token));
361 if (work->serverlist)
363 struct server_record *s;
364 for (s = work->serverlist; s; s = s->next)
366 DEBUG(4,("\t\t%s %8x (%s)\n",
367 s->serv.name, s->serv.type, s->serv.comment));
375 /****************************************************************************
376 create a domain entry
377 ****************************************************************************/
378 static struct subnet_record *make_subnet(struct in_addr bcast_ip, struct in_addr mask_ip)
380 struct subnet_record *d;
381 d = (struct subnet_record *)malloc(sizeof(*d));
383 if (!d) return(NULL);
385 bzero((char *)d,sizeof(*d));
387 DEBUG(4, ("making domain %s ", inet_ntoa(bcast_ip)));
388 DEBUG(4, ("%s\n", inet_ntoa(mask_ip)));
390 d->bcast_ip = bcast_ip;
391 d->mask_ip = mask_ip;
392 d->workgrouplist = NULL;
393 d->my_interface = False; /* True iff the interface is on the samba host */
395 add_subnet(d);
397 return d;
401 /****************************************************************************
402 add the remote interfaces from lp_remote_interfaces() and lp_interfaces()
403 to the netbios subnet database.
404 ****************************************************************************/
405 void add_subnet_interfaces(void)
407 struct interface *i;
409 /* loop on all local interfaces */
410 for (i = local_interfaces; i; i = i->next)
412 /* add the interface into our subnet database */
413 if (!find_subnet(i->bcast))
415 struct subnet_record *d = make_subnet(i->bcast,i->nmask);
416 if (d)
418 /* short-cut method to identifying local interfaces */
419 d->my_interface = True;
424 /* loop on all remote interfaces */
425 for (i = remote_interfaces; i; i = i->next)
427 /* add the interface into our subnet database */
428 if (!find_subnet(i->bcast))
430 make_subnet(i->bcast,i->nmask);
434 /* add the pseudo-ip interface for WINS: 255.255.255.255 */
435 if (lp_wins_support())
437 struct in_addr wins_bcast = ipgrp;
438 struct in_addr wins_nmask = ipzero;
439 make_subnet(wins_bcast, wins_nmask);
444 /****************************************************************************
445 add a domain entry. creates a workgroup, if necessary, and adds the domain
446 to the named a workgroup.
447 ****************************************************************************/
448 struct subnet_record *add_subnet_entry(struct in_addr bcast_ip,
449 struct in_addr mask_ip,
450 char *name, BOOL add, BOOL lmhosts)
452 struct subnet_record *d;
454 /* XXXX andrew: struct in_addr ip appears not to be referenced at all except
455 in the DEBUG comment. i assume that the DEBUG comment below actually
456 intends to refer to bcast_ip? i don't know.
458 struct in_addr ip = ipgrp;
462 if (zero_ip(bcast_ip))
463 bcast_ip = *iface_bcast(bcast_ip);
465 /* add the domain into our domain database */
466 if ((d = find_subnet(bcast_ip)) ||
467 (d = make_subnet(bcast_ip, mask_ip)))
469 struct work_record *w = find_workgroupstruct(d, name, add);
470 extern pstring ServerComment;
472 if (!w) return NULL;
474 /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
475 or register with WINS server, if it's our workgroup */
476 if (strequal(lp_workgroup(), name) && d->my_interface)
478 add_my_name_entry(d,name,0x1e,NB_ACTIVE|NB_GROUP);
479 add_my_name_entry(d,name,0x0 ,NB_ACTIVE|NB_GROUP);
481 /* add samba server name to workgroup list */
482 if ((strequal(lp_workgroup(), name) && d->my_interface) || lmhosts)
484 add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True);
487 DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(bcast_ip)));
488 return d;
490 return NULL;
493 /****************************************************************************
494 remove all samba's server entries
495 ****************************************************************************/
496 void remove_my_servers(void)
498 struct subnet_record *d;
499 for (d = subnetlist; d; d = d->next)
501 struct work_record *work;
502 for (work = d->workgrouplist; work; work = work->next)
504 struct server_record *s;
505 for (s = work->serverlist; s; s = s->next)
507 if (!strequal(myname,s->serv.name)) continue;
508 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
515 /****************************************************************************
516 add a server entry
517 ****************************************************************************/
518 struct server_record *add_server_entry(struct subnet_record *d,
519 struct work_record *work,
520 char *name,int servertype,
521 int ttl,char *comment,
522 BOOL replace)
524 BOOL newentry=False;
525 struct server_record *s;
527 if (name[0] == '*')
529 return (NULL);
532 for (s = work->serverlist; s; s = s->next)
534 if (strequal(name,s->serv.name)) break;
537 if (s && !replace)
539 DEBUG(4,("Not replacing %s\n",name));
540 return(s);
543 if (!s || s->serv.type != servertype || !strequal(s->serv.comment, comment))
544 updatedlists=True;
546 if (!s)
548 newentry = True;
549 s = (struct server_record *)malloc(sizeof(*s));
551 if (!s) return(NULL);
553 bzero((char *)s,sizeof(*s));
557 if (d->my_interface && strequal(lp_workgroup(),work->work_group))
559 if (servertype)
560 servertype |= SV_TYPE_LOCAL_LIST_ONLY;
562 else
564 servertype &= ~SV_TYPE_LOCAL_LIST_ONLY;
567 /* update the entry */
568 StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1);
569 StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1);
570 strupper(s->serv.name);
571 s->serv.type = servertype;
572 s->death_time = servertype ? (ttl?time(NULL)+ttl*3:0) : (time(NULL)-1);
574 /* for a domain entry, the comment field refers to the server name */
576 if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment);
578 if (newentry)
580 add_server(work, s);
582 DEBUG(3,("Added "));
584 else
586 DEBUG(3,("Updated "));
589 DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
590 name,servertype,comment,
591 work->work_group,inet_ntoa(d->bcast_ip)));
593 return(s);
597 /****************************************************************************
598 add the default workgroup into my domain
599 **************************************************************************/
600 void add_my_subnets(char *group)
602 struct interface *i;
604 /* add or find domain on our local subnet, in the default workgroup */
606 if (*group == '*') return;
608 /* the coding choice is up to you, andrew: i can see why you don't want
609 global access to the local_interfaces structure: so it can't get
610 messed up! */
611 for (i = local_interfaces; i; i = i->next)
613 add_subnet_entry(i->bcast,i->nmask,group, True, False);
618 /*******************************************************************
619 write out browse.dat
620 ******************************************************************/
621 void write_browse_list(void)
623 struct subnet_record *d;
625 pstring fname,fnamenew;
626 FILE *f;
628 if (!updatedlists) return;
630 dump_names();
631 dump_workgroups();
633 updatedlists = False;
634 updatecount++;
636 strcpy(fname,lp_lockdir());
637 trim_string(fname,NULL,"/");
638 strcat(fname,"/");
639 strcat(fname,SERVER_LIST);
640 strcpy(fnamenew,fname);
641 strcat(fnamenew,".");
643 f = fopen(fnamenew,"w");
645 if (!f)
647 DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
648 return;
651 for (d = subnetlist; d ; d = d->next)
653 struct work_record *work;
654 for (work = d->workgrouplist; work ; work = work->next)
656 struct server_record *s;
657 for (s = work->serverlist; s ; s = s->next)
659 fstring tmp;
661 /* don't list domains I don't have a master for */
662 if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) && !s->serv.comment[0])
664 continue;
667 /* output server details, plus what workgroup/domain
668 they're in. without the domain information, the
669 combined list of all servers in all workgroups gets
670 sent to anyone asking about any workgroup! */
672 sprintf(tmp, "\"%s\"", s->serv.name);
673 fprintf(f, "%-25s ", tmp);
674 fprintf(f, "%08x ", s->serv.type);
675 sprintf(tmp, "\"%s\" ", s->serv.comment);
676 fprintf(f, "%-30s", tmp);
677 fprintf(f, "\"%s\"\n", work->work_group);
682 fclose(f);
683 unlink(fname);
684 chmod(fnamenew,0644);
685 rename(fnamenew,fname);
686 DEBUG(3,("Wrote browse list %s\n",fname));
690 /*******************************************************************
691 expire old servers in the serverlist
692 ******************************************************************/
693 void expire_servers(time_t t)
695 struct subnet_record *d;
697 for (d = subnetlist ; d ; d = d->next)
699 struct work_record *work;
701 for (work = d->workgrouplist; work; work = work->next)
703 remove_old_servers(work, t);