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 /* this is our browse master/backup cache database */
44 struct browse_cache_record
*browserlist
= NULL
;
46 /* local interfaces structure */
47 extern struct interface
*local_interfaces
;
49 /* remote interfaces structure */
50 extern struct interface
*remote_interfaces
;
52 /* this is our domain/workgroup/server database */
53 struct subnet_record
*subnetlist
= NULL
;
55 static BOOL updatedlists
= True
;
58 int workgroup_count
= 0; /* unique index key: one for each workgroup */
60 /* what server type are we currently */
62 #define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
63 SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX | \
64 SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
67 /****************************************************************************
68 add a workgroup into the domain list
69 **************************************************************************/
70 static void add_workgroup(struct work_record
*work
, struct subnet_record
*d
)
72 struct work_record
*w2
;
74 if (!work
|| !d
) return;
76 if (!d
->workgrouplist
)
78 d
->workgrouplist
= work
;
84 for (w2
= d
->workgrouplist
; w2
->next
; w2
= w2
->next
);
92 /****************************************************************************
93 create a blank workgroup
94 **************************************************************************/
95 static struct work_record
*make_workgroup(char *name
)
97 struct work_record
*work
;
98 struct subnet_record
*d
;
101 if (!name
|| !name
[0]) return NULL
;
103 work
= (struct work_record
*)malloc(sizeof(*work
));
104 if (!work
) return(NULL
);
106 StrnCpy(work
->work_group
,name
,sizeof(work
->work_group
)-1);
107 work
->serverlist
= NULL
;
109 work
->ServerType
= DFLT_SERVER_TYPE
;
110 work
->RunningElection
= False
;
111 work
->ElectionCount
= 0;
112 work
->needelection
= False
;
113 work
->needannounce
= True
;
115 /* make sure all token representations of workgroups are unique */
117 for (d
= subnetlist
; d
&& t
== -1; d
= d
->next
)
119 struct work_record
*w
;
120 for (w
= d
->workgrouplist
; w
&& t
== -1; w
= w
->next
)
122 if (strequal(w
->work_group
, work
->work_group
)) t
= w
->token
;
128 work
->token
= ++workgroup_count
;
136 /* WfWg uses 01040b01 */
137 /* Win95 uses 01041501 */
138 /* NTAS uses ???????? */
139 work
->ElectionCriterion
= (MAINTAIN_LIST
<<1)|(ELECTION_VERSION
<<8);
140 work
->ElectionCriterion
|= (lp_os_level() << 24);
141 if (lp_domain_master()) {
142 work
->ElectionCriterion
|= 0x80;
149 /*******************************************************************
150 expire old servers in the serverlist
151 time of -1 indicates everybody dies
152 ******************************************************************/
153 static void remove_old_servers(struct work_record
*work
, time_t t
)
155 struct server_record
*s
;
156 struct server_record
*nexts
;
158 /* expire old entries in the serverlist */
159 for (s
= work
->serverlist
; s
; s
= nexts
)
161 if (t
== -1 || (s
->death_time
&& s
->death_time
< t
))
163 DEBUG(3,("Removing dead server %s\n",s
->serv
.name
));
167 if (s
->prev
) s
->prev
->next
= s
->next
;
168 if (s
->next
) s
->next
->prev
= s
->prev
;
170 if (work
->serverlist
== s
)
171 work
->serverlist
= s
->next
;
183 /*******************************************************************
185 ******************************************************************/
186 struct work_record
*remove_workgroup(struct subnet_record
*d
,
187 struct work_record
*work
)
189 struct work_record
*ret_work
= NULL
;
191 if (!d
|| !work
) return NULL
;
193 DEBUG(3,("Removing old workgroup %s\n", work
->work_group
));
195 remove_old_servers(work
, -1);
197 ret_work
= work
->next
;
199 if (work
->prev
) work
->prev
->next
= work
->next
;
200 if (work
->next
) work
->next
->prev
= work
->prev
;
202 if (d
->workgrouplist
== work
) d
->workgrouplist
= work
->next
;
210 /****************************************************************************
211 add a domain into the list
212 **************************************************************************/
213 static void add_subnet(struct subnet_record
*d
)
215 struct subnet_record
*d2
;
225 for (d2
= subnetlist
; d2
->next
; d2
= d2
->next
);
232 /***************************************************************************
233 add a browser into the list
234 **************************************************************************/
235 static void add_browse_cache(struct browse_cache_record
*b
)
237 struct browse_cache_record
*b2
;
247 for (b2
= browserlist
; b2
->next
; b2
= b2
->next
) ;
255 /***************************************************************************
256 add a server into the list
257 **************************************************************************/
258 static void add_server(struct work_record
*work
,struct server_record
*s
)
260 struct server_record
*s2
;
262 if (!work
->serverlist
) {
263 work
->serverlist
= s
;
269 for (s2
= work
->serverlist
; s2
->next
; s2
= s2
->next
) ;
277 /*******************************************************************
278 remove old browse entries
279 ******************************************************************/
280 void expire_browse_cache(time_t t
)
282 struct browse_cache_record
*b
;
283 struct browse_cache_record
*nextb
;
285 /* expire old entries in the serverlist */
286 for (b
= browserlist
; b
; b
= nextb
)
288 if (b
->synced
&& b
->sync_time
< t
)
290 DEBUG(3,("Removing dead cached browser %s\n",b
->name
));
293 if (b
->prev
) b
->prev
->next
= b
->next
;
294 if (b
->next
) b
->next
->prev
= b
->prev
;
296 if (browserlist
== b
) browserlist
= b
->next
;
308 /****************************************************************************
309 find a workgroup in the workgrouplist
310 only create it if the domain allows it, or the parameter 'add' insists
311 that it get created/added anyway. this allows us to force entries in
312 lmhosts file to be added.
313 **************************************************************************/
314 struct work_record
*find_workgroupstruct(struct subnet_record
*d
,
315 fstring name
, BOOL add
)
317 struct work_record
*ret
, *work
;
321 DEBUG(4, ("workgroup search for %s: ", name
));
323 if (strequal(name
, "*"))
325 DEBUG(2,("add any workgroups: initiating browser search on %s\n",
326 inet_ntoa(d
->bcast_ip
)));
327 queue_netbios_pkt_wins(d
,ClientNMB
,NMB_QUERY
, NAME_QUERY_FIND_MST
,
329 True
,False
, d
->bcast_ip
);
333 for (ret
= d
->workgrouplist
; ret
; ret
= ret
->next
) {
334 if (!strcmp(ret
->work_group
,name
)) {
335 DEBUG(4, ("found\n"));
341 DEBUG(4, ("not found\n"));
345 DEBUG(4,("not found: creating\n"));
347 if ((work
= make_workgroup(name
)))
349 if (lp_preferred_master() &&
350 strequal(lp_workgroup(), name
) &&
353 DEBUG(3, ("preferred master startup for %s\n", work
->work_group
));
354 work
->needelection
= True
;
355 work
->ElectionCriterion
|= (1<<3);
357 if (!d
->my_interface
)
359 work
->needelection
= False
;
361 add_workgroup(work
, d
);
367 /****************************************************************************
368 find a subnet in the subnetlist
369 **************************************************************************/
370 struct subnet_record
*find_subnet(struct in_addr bcast_ip
)
372 struct subnet_record
*d
;
373 struct in_addr wins_ip
= ipgrp
;
375 /* search through subnet list for broadcast/netmask that matches
376 the source ip address. a subnet 255.255.255.255 represents the
379 for (d
= subnetlist
; d
; d
= d
->next
)
381 if (ip_equal(bcast_ip
, wins_ip
))
383 if (ip_equal(bcast_ip
, d
->bcast_ip
))
388 else if (same_net(bcast_ip
, d
->bcast_ip
, d
->mask_ip
))
398 /****************************************************************************
399 dump a copy of the workgroup/domain database
400 **************************************************************************/
401 void dump_workgroups(void)
403 struct subnet_record
*d
;
405 for (d
= subnetlist
; d
; d
= d
->next
)
407 if (d
->workgrouplist
)
409 struct work_record
*work
;
411 DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d
->bcast_ip
)));
412 DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d
->mask_ip
)));
414 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
416 DEBUG(4,("\t%s(%d)\n", work
->work_group
, work
->token
));
417 if (work
->serverlist
)
419 struct server_record
*s
;
420 for (s
= work
->serverlist
; s
; s
= s
->next
)
422 DEBUG(4,("\t\t%s %8x (%s)\n",
423 s
->serv
.name
, s
->serv
.type
, s
->serv
.comment
));
431 /****************************************************************************
432 create a domain entry
433 ****************************************************************************/
434 static struct subnet_record
*make_subnet(struct in_addr bcast_ip
, struct in_addr mask_ip
)
436 struct subnet_record
*d
;
437 d
= (struct subnet_record
*)malloc(sizeof(*d
));
439 if (!d
) return(NULL
);
441 bzero((char *)d
,sizeof(*d
));
443 DEBUG(4, ("making domain %s ", inet_ntoa(bcast_ip
)));
444 DEBUG(4, ("%s\n", inet_ntoa(mask_ip
)));
446 d
->bcast_ip
= bcast_ip
;
447 d
->mask_ip
= mask_ip
;
448 d
->workgrouplist
= NULL
;
449 d
->my_interface
= False
; /* True iff the interface is on the samba host */
457 /****************************************************************************
458 add the remote interfaces from lp_remote_interfaces() and lp_interfaces()
459 to the netbios subnet database.
460 ****************************************************************************/
461 void add_subnet_interfaces(void)
465 /* loop on all local interfaces */
466 for (i
= local_interfaces
; i
; i
= i
->next
)
468 /* add the interface into our subnet database */
469 if (!find_subnet(i
->bcast
))
471 struct subnet_record
*d
= make_subnet(i
->bcast
,i
->nmask
);
474 /* short-cut method to identifying local interfaces */
475 d
->my_interface
= True
;
480 /* loop on all remote interfaces */
481 for (i
= remote_interfaces
; i
; i
= i
->next
)
483 /* add the interface into our subnet database */
484 if (!find_subnet(i
->bcast
))
486 make_subnet(i
->bcast
,i
->nmask
);
490 /* add the pseudo-ip interface for WINS: 255.255.255.255 */
491 if (lp_wins_support())
493 struct in_addr wins_bcast
= ipgrp
;
494 struct in_addr wins_nmask
= ipzero
;
495 make_subnet(wins_bcast
, wins_nmask
);
500 /****************************************************************************
501 add a domain entry. creates a workgroup, if necessary, and adds the domain
502 to the named a workgroup.
503 ****************************************************************************/
504 struct subnet_record
*add_subnet_entry(struct in_addr bcast_ip
,
505 struct in_addr mask_ip
,
506 char *name
, BOOL add
, BOOL lmhosts
)
508 struct subnet_record
*d
;
510 /* XXXX andrew: struct in_addr ip appears not to be referenced at all except
511 in the DEBUG comment. i assume that the DEBUG comment below actually
512 intends to refer to bcast_ip? i don't know.
514 struct in_addr ip = ipgrp;
518 if (zero_ip(bcast_ip
))
519 bcast_ip
= *iface_bcast(bcast_ip
);
521 /* add the domain into our domain database */
522 if ((d
= find_subnet(bcast_ip
)) ||
523 (d
= make_subnet(bcast_ip
, mask_ip
)))
525 struct work_record
*w
= find_workgroupstruct(d
, name
, add
);
526 extern pstring ServerComment
;
530 /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database
531 or register with WINS server, if it's our workgroup */
532 if (strequal(lp_workgroup(), name
) && d
->my_interface
)
534 add_my_name_entry(d
,name
,0x1e,NB_ACTIVE
|NB_GROUP
);
535 add_my_name_entry(d
,name
,0x0 ,NB_ACTIVE
|NB_GROUP
);
537 /* add samba server name to workgroup list */
538 if ((strequal(lp_workgroup(), name
) && d
->my_interface
) || lmhosts
)
540 add_server_entry(d
,w
,myname
,w
->ServerType
,0,ServerComment
,True
);
543 DEBUG(3,("Added domain name entry %s at %s\n", name
,inet_ntoa(bcast_ip
)));
549 /****************************************************************************
551 ****************************************************************************/
552 struct browse_cache_record
*add_browser_entry(char *name
, int type
, char *wg
,
553 time_t ttl
, struct in_addr ip
)
557 struct browse_cache_record
*b
;
559 /* search for the entry: if it's already in the cache, update that entry */
560 for (b
= browserlist
; b
; b
= b
->next
)
562 if (ip_equal(ip
,b
->ip
) && strequal(b
->group
, wg
)) break;
567 /* entries get left in the cache for a while. this stops sync'ing too
568 often if the network is large */
569 DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
570 b
->name
, b
->group
, inet_ntoa(b
->ip
), b
->sync_time
));
577 b
= (struct browse_cache_record
*)malloc(sizeof(*b
));
579 if (!b
) return(NULL
);
581 bzero((char *)b
,sizeof(*b
));
584 /* update the entry */
585 ttl
= time(NULL
)+ttl
;
587 StrnCpy(b
->name
,name
,sizeof(b
->name
)-1);
588 StrnCpy(b
->group
,wg
,sizeof(b
->group
)-1);
595 if (newentry
|| ttl
< b
->sync_time
)
603 DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n",
604 wg
, name
, type
, inet_ntoa(ip
),ttl
));
608 DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n",
609 wg
, name
, type
, inet_ntoa(ip
),ttl
));
616 /****************************************************************************
617 remove all samba's server entries
618 ****************************************************************************/
619 void remove_my_servers(void)
621 struct subnet_record
*d
;
622 for (d
= subnetlist
; d
; d
= d
->next
)
624 struct work_record
*work
;
625 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
627 struct server_record
*s
;
628 for (s
= work
->serverlist
; s
; s
= s
->next
)
630 if (!strequal(myname
,s
->serv
.name
)) continue;
631 announce_server(d
, work
, s
->serv
.name
, s
->serv
.comment
, 0, 0);
638 /****************************************************************************
640 ****************************************************************************/
641 struct server_record
*add_server_entry(struct subnet_record
*d
,
642 struct work_record
*work
,
643 char *name
,int servertype
,
644 int ttl
,char *comment
,
648 struct server_record
*s
;
655 for (s
= work
->serverlist
; s
; s
= s
->next
)
657 if (strequal(name
,s
->serv
.name
)) break;
662 DEBUG(4,("Not replacing %s\n",name
));
666 if (!s
|| s
->serv
.type
!= servertype
|| !strequal(s
->serv
.comment
, comment
))
672 s
= (struct server_record
*)malloc(sizeof(*s
));
674 if (!s
) return(NULL
);
676 bzero((char *)s
,sizeof(*s
));
680 if (d
->my_interface
&& strequal(lp_workgroup(),work
->work_group
))
683 servertype
|= SV_TYPE_LOCAL_LIST_ONLY
;
687 servertype
&= ~SV_TYPE_LOCAL_LIST_ONLY
;
690 /* update the entry */
691 StrnCpy(s
->serv
.name
,name
,sizeof(s
->serv
.name
)-1);
692 StrnCpy(s
->serv
.comment
,comment
,sizeof(s
->serv
.comment
)-1);
693 strupper(s
->serv
.name
);
694 s
->serv
.type
= servertype
;
695 s
->death_time
= servertype
? (ttl
?time(NULL
)+ttl
*3:0) : (time(NULL
)-1);
697 /* for a domain entry, the comment field refers to the server name */
699 if (s
->serv
.type
& SV_TYPE_DOMAIN_ENUM
) strupper(s
->serv
.comment
);
709 DEBUG(3,("Updated "));
712 DEBUG(3,("server entry %s of type %x (%s) to %s %s\n",
713 name
,servertype
,comment
,
714 work
->work_group
,inet_ntoa(d
->bcast_ip
)));
720 /*******************************************************************
722 ******************************************************************/
723 void write_browse_list(void)
725 struct subnet_record
*d
;
727 pstring fname
,fnamenew
;
730 if (!updatedlists
) return;
735 updatedlists
= False
;
738 strcpy(fname
,lp_lockdir());
739 trim_string(fname
,NULL
,"/");
741 strcat(fname
,SERVER_LIST
);
742 strcpy(fnamenew
,fname
);
743 strcat(fnamenew
,".");
745 f
= fopen(fnamenew
,"w");
749 DEBUG(4,("Can't open %s - %s\n",fnamenew
,strerror(errno
)));
753 for (d
= subnetlist
; d
; d
= d
->next
)
755 struct work_record
*work
;
756 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
758 struct server_record
*s
;
759 for (s
= work
->serverlist
; s
; s
= s
->next
)
763 /* don't list domains I don't have a master for */
764 if ((s
->serv
.type
& SV_TYPE_DOMAIN_ENUM
) && !s
->serv
.comment
[0])
769 /* output server details, plus what workgroup/domain
770 they're in. without the domain information, the
771 combined list of all servers in all workgroups gets
772 sent to anyone asking about any workgroup! */
774 sprintf(tmp
, "\"%s\"", s
->serv
.name
);
775 fprintf(f
, "%-25s ", tmp
);
776 fprintf(f
, "%08x ", s
->serv
.type
);
777 sprintf(tmp
, "\"%s\" ", s
->serv
.comment
);
778 fprintf(f
, "%-30s", tmp
);
779 fprintf(f
, "\"%s\"\n", work
->work_group
);
786 chmod(fnamenew
,0644);
787 rename(fnamenew
,fname
);
788 DEBUG(3,("Wrote browse list %s\n",fname
));
792 /*******************************************************************
793 expire old servers in the serverlist
794 ******************************************************************/
795 void expire_servers(time_t t
)
797 struct subnet_record
*d
;
799 for (d
= subnetlist
; d
; d
= d
->next
)
801 struct work_record
*work
;
803 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
805 remove_old_servers(work
, t
);