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
;
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
;
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
;
81 for (w2
= d
->workgrouplist
; w2
->next
; w2
= w2
->next
);
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
;
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
;
126 work
->token
= ++workgroup_count
;
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;
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
));
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
;
181 /*******************************************************************
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
;
208 /****************************************************************************
209 add a domain into the list
210 **************************************************************************/
211 static void add_subnet(struct subnet_record
*d
)
213 struct subnet_record
*d2
;
223 for (d2
= subnetlist
; d2
->next
; d2
= d2
->next
);
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
;
244 for (s2
= work
->serverlist
; s2
->next
; s2
= s2
->next
) ;
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
;
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
,
273 True
,False
, d
->bcast_ip
);
277 for (ret
= d
->workgrouplist
; ret
; ret
= ret
->next
) {
278 if (!strcmp(ret
->work_group
,name
)) {
279 DEBUG(4, ("found\n"));
285 DEBUG(4, ("not found\n"));
289 DEBUG(4,("not found: creating\n"));
291 if ((work
= make_workgroup(name
)))
293 if (lp_preferred_master() &&
294 strequal(lp_workgroup(), name
) &&
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
);
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
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
))
332 else if (same_net(bcast_ip
, d
->bcast_ip
, d
->mask_ip
))
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 */
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)
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
);
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
;
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
)));
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 /****************************************************************************
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
,
525 struct server_record
*s
;
532 for (s
= work
->serverlist
; s
; s
= s
->next
)
534 if (strequal(name
,s
->serv
.name
)) break;
539 DEBUG(4,("Not replacing %s\n",name
));
543 if (!s
|| s
->serv
.type
!= servertype
|| !strequal(s
->serv
.comment
, comment
))
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
))
560 servertype
|= SV_TYPE_LOCAL_LIST_ONLY
;
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
);
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
)));
597 /****************************************************************************
598 add the default workgroup into my domain
599 **************************************************************************/
600 void add_my_subnets(char *group
)
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
611 for (i
= local_interfaces
; i
; i
= i
->next
)
613 add_subnet_entry(i
->bcast
,i
->nmask
,group
, True
, False
);
618 /*******************************************************************
620 ******************************************************************/
621 void write_browse_list(void)
623 struct subnet_record
*d
;
625 pstring fname
,fnamenew
;
628 if (!updatedlists
) return;
633 updatedlists
= False
;
636 strcpy(fname
,lp_lockdir());
637 trim_string(fname
,NULL
,"/");
639 strcat(fname
,SERVER_LIST
);
640 strcpy(fnamenew
,fname
);
641 strcat(fnamenew
,".");
643 f
= fopen(fnamenew
,"w");
647 DEBUG(4,("Can't open %s - %s\n",fnamenew
,strerror(errno
)));
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
)
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])
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
);
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
);