2 Unix SMB/Netbios implementation.
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.
23 14 jan 96: lkcl@pires.co.uk
24 added multiple workgroup domain master support
32 extern int ClientDGRAM;
34 extern int DEBUGLEVEL;
36 extern time_t StartupTime;
37 extern pstring myname;
40 extern struct in_addr ipgrp;
42 /* this is our browse master/backup cache database */
43 struct browse_cache_record *browserlist = NULL;
45 /* this is our domain/workgroup/server database */
46 struct subnet_record *subnetlist = NULL;
48 static BOOL updatedlists = True;
51 int workgroup_count = 0; /* unique index key: one for each workgroup */
53 /* what server type are we currently */
55 #define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
56 SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \
57 SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
60 /****************************************************************************
61 add a workgroup into the domain list
62 **************************************************************************/
63 static void add_workgroup(struct work_record *work, struct subnet_record *d)
65 struct work_record *w2;
67 if (!work || !d) return;
69 if (!d->workgrouplist)
71 d->workgrouplist = work;
77 for (w2 = d->workgrouplist; w2->next; w2 = w2->next);
85 /****************************************************************************
86 create a blank workgroup
87 **************************************************************************/
88 static struct work_record *make_workgroup(char *name)
90 struct work_record *work;
91 struct subnet_record *d;
94 if (!name || !name[0]) return NULL;
96 work = (struct work_record *)malloc(sizeof(*work));
97 if (!work) return(NULL);
99 StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
100 work->serverlist = NULL;
102 work->ServerType = DFLT_SERVER_TYPE;
103 work->RunningElection = False;
104 work->ElectionCount = 0;
105 work->needelection = False;
106 work->needannounce = True;
108 /* make sure all token representations of workgroups are unique */
110 for (d = subnetlist; d && t == -1; d = d->next)
112 struct work_record *w;
113 for (w = d->workgrouplist; w && t == -1; w = w->next)
115 if (strequal(w->work_group, work->work_group)) t = w->token;
121 work->token = ++workgroup_count;
129 /* WfWg uses 01040b01 */
130 /* Win95 uses 01041501 */
131 /* NTAS uses ???????? */
132 work->ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
133 work->ElectionCriterion |= (lp_os_level() << 24);
134 if (lp_domain_master()) {
135 work->ElectionCriterion |= 0x80;
142 /*******************************************************************
143 expire old servers in the serverlist
144 time of -1 indicates everybody dies
145 ******************************************************************/
146 static void remove_old_servers(struct work_record *work, time_t t)
148 struct server_record *s;
149 struct server_record *nexts;
151 /* expire old entries in the serverlist */
152 for (s = work->serverlist; s; s = nexts)
154 if (t == -1 || (s->death_time && s->death_time < t))
156 DEBUG(3,("Removing dead server %s\n",s->serv.name));
160 if (s->prev) s->prev->next = s->next;
161 if (s->next) s->next->prev = s->prev;
163 if (work->serverlist == s)
164 work->serverlist = s->next;
176 /*******************************************************************
178 ******************************************************************/
179 struct work_record *remove_workgroup(struct subnet_record *d,
180 struct work_record *work)
182 struct work_record *ret_work = NULL;
184 if (!d || !work) return NULL;
186 DEBUG(3,("Removing old workgroup %s\n", work->work_group));
188 remove_old_servers(work, -1);
190 ret_work = work->next;
192 if (work->prev) work->prev->next = work->next;
193 if (work->next) work->next->prev = work->prev;
195 if (d->workgrouplist == work) d->workgrouplist = work->next;
203 /****************************************************************************
204 add a domain into the list
205 **************************************************************************/
206 static void add_subnet(struct subnet_record *d)
208 struct subnet_record *d2;
218 for (d2 = subnetlist; d2->next; d2 = d2->next);
225 /***************************************************************************
226 add a browser into the list
227 **************************************************************************/
228 static void add_browse_cache(struct browse_cache_record *b)
230 struct browse_cache_record *b2;
240 for (b2 = browserlist; b2->next; b2 = b2->next) ;
248 /***************************************************************************
249 add a server into the list
250 **************************************************************************/
251 static void add_server(struct work_record *work,struct server_record *s)
253 struct server_record *s2;
255 if (!work->serverlist) {
256 work->serverlist = s;
262 for (s2 = work->serverlist; s2->next; s2 = s2->next) ;
270 /*******************************************************************
271 remove old browse entries
272 ******************************************************************/
273 void expire_browse_cache(time_t t)
275 struct browse_cache_record *b;
276 struct browse_cache_record *nextb;
278 /* expire old entries in the serverlist */
279 for (b = browserlist; b; b = nextb)
281 if (b->synced && b->sync_time < t)
283 DEBUG(3,("Removing dead cached browser %s\n",b->name));
286 if (b->prev) b->prev->next = b->next;
287 if (b->next) b->next->prev = b->prev;
289 if (browserlist == b) browserlist = b->next;
301 /****************************************************************************
302 find a workgroup in the workgrouplist
303 only create it if the domain allows it, or the parameter 'add' insists
304 that it get created/added anyway. this allows us to force entries in
305 lmhosts file to be added.
306 **************************************************************************/
307 struct work_record *find_workgroupstruct(struct subnet_record *d,
308 fstring name, BOOL add)
310 struct work_record *ret, *work;
314 DEBUG(4, ("workgroup search for %s: ", name));
316 if (strequal(name, "*"))
318 DEBUG(2,("add any workgroups: initiating browser search on %s\n",
319 inet_ntoa(d->bcast_ip)));
320 queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, FIND_MASTER,
322 True,False, d->bcast_ip);
326 for (ret = d->workgrouplist; ret; ret = ret->next) {
327 if (!strcmp(ret->work_group,name)) {
328 DEBUG(4, ("found\n"));
334 DEBUG(4, ("not found\n"));
338 DEBUG(4,("not found: creating\n"));
340 if ((work = make_workgroup(name)))
342 work->needelection = False;
344 if (lp_preferred_master() &&
345 strequal(lp_workgroup(), name) &&
348 DEBUG(3, ("preferred master startup for %s\n", work->work_group));
349 work->needelection = True;
350 work->ElectionCriterion |= (1<<3);
352 add_workgroup(work, d);
358 /****************************************************************************
359 find a domain in the subnetlist
360 **************************************************************************/
361 struct subnet_record *find_domain(struct in_addr ip)
363 struct subnet_record *d;
365 /* search through domain list for broadcast/netmask that matches
366 the source ip address */
368 for (d = subnetlist; d; d = d->next)
370 if (same_net(ip, d->bcast_ip, d->mask_ip))
378 /****************************************************************************
379 dump a copy of the workgroup/domain database
380 **************************************************************************/
381 void dump_workgroups(void)
383 struct subnet_record *d;
385 for (d = subnetlist; d; d = d->next)
387 if (d->workgrouplist)
389 struct work_record *work;
391 DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip)));
392 DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip)));
394 for (work = d->workgrouplist; work; work = work->next)
396 DEBUG(4,("\t%s(%d)\n", work->work_group, work->token));
397 if (work->serverlist)
399 struct server_record *s;
400 for (s = work->serverlist; s; s = s->next)
402 DEBUG(4,("\t\t%s %8x (%s)\n",
403 s->serv.name, s->serv.type, s->serv.comment));
411 /****************************************************************************
412 create a domain entry
413 ****************************************************************************/
414 static struct subnet_record *make_subnet(struct in_addr bcast_ip,
417 struct subnet_record *d;
418 d = (struct subnet_record *)malloc(sizeof(*d));
420 if (!d) return(NULL);
422 bzero((char *)d,sizeof(*d));
424 DEBUG(4,("making subnet %s ", inet_ntoa(bcast_ip)));
425 DEBUG(4,("%s\n", inet_ntoa(mask)));
427 d->bcast_ip = bcast_ip;
429 d->workgrouplist = NULL;
430 d->my_interface = ismybcast(d->bcast_ip);
437 /****************************************************************************
438 add a domain entry. creates a workgroup, if necessary, and adds the domain
439 to the named a workgroup.
440 ****************************************************************************/
441 struct subnet_record *add_subnet_entry(struct in_addr source_ip,
442 struct in_addr source_mask,
443 char *name, BOOL add)
445 struct subnet_record *d;
450 if (zero_ip(source_ip))
451 source_ip = *iface_bcast(source_ip);
453 /* add the domain into our domain database */
454 if ((d = find_domain(source_ip)) ||
455 (d = make_subnet(source_ip, source_mask)))
457 struct work_record *w = find_workgroupstruct(d, name, add);
461 /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
462 or register with WINS server, if it's our workgroup */
463 if (strequal(lp_workgroup(), name))
465 extern pstring ServerComment;
466 add_name_entry(name,0x1e,NB_ACTIVE|NB_GROUP);
467 add_name_entry(name,0x0 ,NB_ACTIVE|NB_GROUP);
468 add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True);
471 DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(ip)));
477 /****************************************************************************
479 ****************************************************************************/
480 struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
481 time_t ttl, struct in_addr ip)
485 struct browse_cache_record *b;
487 /* search for the entry: if it's already in the cache, update that entry */
488 for (b = browserlist; b; b = b->next)
490 if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break;
495 /* entries get left in the cache for a while. this stops sync'ing too
496 often if the network is large */
497 DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
498 b->name, b->group, inet_ntoa(b->ip), b->sync_time));
505 b = (struct browse_cache_record *)malloc(sizeof(*b));
507 if (!b) return(NULL);
509 bzero((char *)b,sizeof(*b));
512 /* update the entry */
513 ttl = time(NULL)+ttl;
515 StrnCpy(b->name ,name,sizeof(b->name )-1);
516 StrnCpy(b->group,wg ,sizeof(b->group)-1);
523 if (newentry || ttl < b->sync_time)
531 DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n",
532 wg, name, type, inet_ntoa(ip),ttl));
536 DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n",
537 wg, name, type, inet_ntoa(ip),ttl));
544 /****************************************************************************
546 ****************************************************************************/
547 struct server_record *add_server_entry(struct subnet_record *d,
548 struct work_record *work,
549 char *name,int servertype,
550 int ttl,char *comment,
554 struct server_record *s;
561 for (s = work->serverlist; s; s = s->next)
563 if (strequal(name,s->serv.name)) break;
568 DEBUG(4,("Not replacing %s\n",name));
577 s = (struct server_record *)malloc(sizeof(*s));
579 if (!s) return(NULL);
581 bzero((char *)s,sizeof(*s));
584 if (d->my_interface &&
585 strequal(lp_workgroup(),work->work_group))
588 servertype |= SV_TYPE_LOCAL_LIST_ONLY;
592 servertype &= ~SV_TYPE_LOCAL_LIST_ONLY;
595 /* update the entry */
596 StrnCpy(s->serv.name,name,sizeof(s->serv.name)-1);
597 StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1);
598 strupper(s->serv.name);
599 s->serv.type = servertype;
600 s->death_time = ttl?time(NULL)+ttl*3:0;
603 s->death_time = time(NULL)-1;
605 /* for a domain entry, the comment field refers to the server name */
607 if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment);
617 DEBUG(3,("Updated "));
620 DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
621 name,servertype,comment,
622 work->work_group,inet_ntoa(d->bcast_ip)));
628 /*******************************************************************
630 ******************************************************************/
631 void write_browse_list(void)
633 struct subnet_record *d;
635 pstring fname,fnamenew;
638 if (!updatedlists) return;
643 updatedlists = False;
646 strcpy(fname,lp_lockdir());
647 trim_string(fname,NULL,"/");
649 strcat(fname,SERVER_LIST);
650 strcpy(fnamenew,fname);
651 strcat(fnamenew,".");
653 f = fopen(fnamenew,"w");
657 DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
661 for (d = subnetlist; d ; d = d->next)
663 struct work_record *work;
664 for (work = d->workgrouplist; work ; work = work->next)
666 struct server_record *s;
667 for (s = work->serverlist; s ; s = s->next)
671 /* don't list domains I don't have a master for */
672 if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) &&
678 /* output server details, plus what workgroup/domain
679 they're in. without the domain information, the
680 combined list of all servers in all workgroups gets
681 sent to anyone asking about any workgroup! */
683 sprintf(tmp, "\"%s\"", s->serv.name);
684 fprintf(f, "%-25s ", tmp);
685 fprintf(f, "%08x ", s->serv.type);
686 sprintf(tmp, "\"%s\" ", s->serv.comment);
687 fprintf(f, "%-30s", tmp);
688 fprintf(f, "\"%s\"\n", work->work_group);
695 chmod(fnamenew,0644);
696 rename(fnamenew,fname);
697 DEBUG(3,("Wrote browse list %s\n",fname));
701 /*******************************************************************
702 expire old servers in the serverlist
703 ******************************************************************/
704 void expire_servers(time_t t)
706 struct subnet_record *d;
708 for (d = subnetlist ; d ; d = d->next)
710 struct work_record *work;
712 for (work = d->workgrouplist; work; work = work->next)
714 remove_old_servers(work, t);