1/*
2   Unix SMB/CIFS implementation.
3   NBT netbios routines and daemon - version 2
4   Copyright (C) Andrew Tridgell 1994-1998
5   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6   Copyright (C) Jeremy Allison 1994-1998
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23
24#include "includes.h"
25
26extern int ClientNMB;
27
28extern uint16 samba_nb_type;
29
30int workgroup_count = 0; /* unique index key: one for each workgroup */
31
32/****************************************************************************
33  Add a workgroup into the list.
34**************************************************************************/
35
36static void add_workgroup(struct subnet_record *subrec, struct work_record *work)
37{
38	work->subnet = subrec;
39	DLIST_ADD(subrec->workgrouplist, work);
40	subrec->work_changed = True;
41}
42
43/****************************************************************************
44 Copy name to unstring. Used by create_workgroup() and find_workgroup_on_subnet().
45**************************************************************************/
46
47static void name_to_unstring(unstring unname, const char *name)
48{
49        nstring nname;
50
51	errno = 0;
52	push_ascii_nstring(nname, name);
53	if (errno == E2BIG) {
54		unstring tname;
55		pull_ascii_nstring(tname, sizeof(tname), nname);
56		unstrcpy(unname, tname);
57		DEBUG(0,("name_to_nstring: workgroup name %s is too long. Truncating to %s\n",
58			name, tname));
59	} else {
60		unstrcpy(unname, name);
61	}
62}
63
64/****************************************************************************
65  Create an empty workgroup.
66**************************************************************************/
67
68static struct work_record *create_workgroup(const char *name, int ttl)
69{
70	struct work_record *work;
71	struct subnet_record *subrec;
72	int t = -1;
73
74	if((work = SMB_MALLOC_P(struct work_record)) == NULL) {
75		DEBUG(0,("create_workgroup: malloc fail !\n"));
76		return NULL;
77	}
78	memset((char *)work, '\0', sizeof(*work));
79
80	name_to_unstring(work->work_group, name);
81
82	work->serverlist = NULL;
83
84	work->RunningElection = False;
85	work->ElectionCount = 0;
86	work->announce_interval = 0;
87	work->needelection = False;
88	work->needannounce = True;
89	work->lastannounce_time = time(NULL);
90	work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
91	work->dom_state = DOMAIN_NONE;
92	work->log_state = LOGON_NONE;
93
94	work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
95
96	/* Make sure all token representations of workgroups are unique. */
97
98	for (subrec = FIRST_SUBNET; subrec && (t == -1); subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
99		struct work_record *w;
100		for (w = subrec->workgrouplist; w && t == -1; w = w->next) {
101			if (strequal(w->work_group, work->work_group))
102				t = w->token;
103		}
104	}
105
106	if (t == -1)
107		work->token = ++workgroup_count;
108	else
109		work->token = t;
110
111	/* No known local master browser as yet. */
112	*work->local_master_browser_name = '\0';
113
114	/* No known domain master browser as yet. */
115	*work->dmb_name.name = '\0';
116	zero_ip(&work->dmb_addr);
117
118	/* WfWg  uses 01040b01 */
119	/* Win95 uses 01041501 */
120	/* NTAS  uses ???????? */
121	work->ElectionCriterion  = (MAINTAIN_LIST)|(BROWSER_ELECTION_VERSION<<8);
122	work->ElectionCriterion |= (lp_os_level() << 24);
123	if (lp_domain_master())
124		work->ElectionCriterion |= 0x80;
125
126	return work;
127}
128
129/*******************************************************************
130  Remove a workgroup.
131******************************************************************/
132
133static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec,
134                                     struct work_record *work)
135{
136	struct work_record *ret_work = NULL;
137
138	DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group));
139
140	ret_work = work->next;
141
142	remove_all_servers(work);
143
144	if (!work->serverlist) {
145		if (work->prev)
146			work->prev->next = work->next;
147		if (work->next)
148			work->next->prev = work->prev;
149
150		if (subrec->workgrouplist == work)
151			subrec->workgrouplist = work->next;
152
153		ZERO_STRUCTP(work);
154		SAFE_FREE(work);
155	}
156
157	subrec->work_changed = True;
158
159	return ret_work;
160}
161
162/****************************************************************************
163  Find a workgroup in the workgroup list of a subnet.
164**************************************************************************/
165
166struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec,
167                                             const char *name)
168{
169	struct work_record *ret;
170 	unstring un_name;
171
172	DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ",
173		name, subrec->subnet_name));
174
175	name_to_unstring(un_name, name);
176
177	for (ret = subrec->workgrouplist; ret; ret = ret->next) {
178		if (strequal(ret->work_group,un_name)) {
179			DEBUGADD(4, ("found.\n"));
180			return(ret);
181		}
182	}
183	DEBUGADD(4, ("not found.\n"));
184	return NULL;
185}
186
187/****************************************************************************
188  Create a workgroup in the workgroup list of the subnet.
189**************************************************************************/
190
191struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec,
192                                               const char *name, int ttl)
193{
194	struct work_record *work = NULL;
195
196	DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n",
197		name, subrec->subnet_name));
198
199	if ((work = create_workgroup(name, ttl))) {
200		add_workgroup(subrec, work);
201		subrec->work_changed = True;
202		return(work);
203	}
204
205	return NULL;
206}
207
208/****************************************************************************
209  Update a workgroup ttl.
210**************************************************************************/
211
212void update_workgroup_ttl(struct work_record *work, int ttl)
213{
214	if(work->death_time != PERMANENT_TTL)
215		work->death_time = time(NULL)+(ttl*3);
216	work->subnet->work_changed = True;
217}
218
219/****************************************************************************
220 Fail function called if we cannot register the WORKGROUP<0> and
221 WORKGROUP<1e> names on the net.
222**************************************************************************/
223
224static void fail_register(struct subnet_record *subrec, struct response_record *rrec,
225                          struct nmb_name *nmbname)
226{
227	DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n",
228		nmb_namestr(nmbname), subrec->subnet_name));
229}
230
231/****************************************************************************
232 If the workgroup is our primary workgroup, add the required names to it.
233**************************************************************************/
234
235void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work)
236{
237	int i;
238
239	if(!strequal(lp_workgroup(), work->work_group))
240		return;
241
242	/* If this is a broadcast subnet then start elections on it if we are so configured. */
243
244	if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) &&
245			(subrec != wins_server_subnet) && lp_preferred_master() && lp_local_master()) {
246		DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \
247workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
248		work->needelection = True;
249		work->ElectionCriterion |= (1<<3);
250	}
251
252	/* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */
253
254	register_name(subrec,lp_workgroup(),0x0,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
255	register_name(subrec,lp_workgroup(),0x1e,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
256
257	for( i = 0; my_netbios_names(i); i++) {
258		const char *name = my_netbios_names(i);
259		int stype = lp_default_server_announce() | (lp_local_master() ?  SV_TYPE_POTENTIAL_BROWSER : 0 );
260
261		if(!strequal(global_myname(), name))
262			stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
263
264		create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY, PERMANENT_TTL,
265				string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
266		DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \
267on subnet %s\n", name, subrec->subnet_name));
268	}
269}
270
271/****************************************************************************
272  Dump a copy of the workgroup database into the log file.
273  **************************************************************************/
274
275void dump_workgroups(BOOL force_write)
276{
277	struct subnet_record *subrec;
278	int debuglevel = force_write ? 0 : 4;
279
280	for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
281		if (subrec->workgrouplist) {
282			struct work_record *work;
283
284			if( DEBUGLVL( debuglevel ) ) {
285				dbgtext( "dump_workgroups()\n " );
286				dbgtext( "dump workgroup on subnet %15s: ", subrec->subnet_name );
287				dbgtext( "netmask=%15s:\n", inet_ntoa(subrec->mask_ip) );
288			}
289
290			for (work = subrec->workgrouplist; work; work = work->next) {
291				DEBUGADD( debuglevel, ( "\t%s(%d) current master browser = %s\n", work->work_group,
292					work->token, *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" ) );
293				if (work->serverlist) {
294					struct server_record *servrec;
295					for (servrec = work->serverlist; servrec; servrec = servrec->next) {
296						DEBUGADD( debuglevel, ( "\t\t%s %8x (%s)\n",
297							servrec->serv.name,
298							servrec->serv.type,
299							servrec->serv.comment ) );
300					}
301				}
302			}
303		}
304	}
305}
306
307/****************************************************************************
308  Expire any dead servers on all workgroups. If the workgroup has expired
309  remove it.
310  **************************************************************************/
311
312void expire_workgroups_and_servers(time_t t)
313{
314	struct subnet_record *subrec;
315
316	for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
317		struct work_record *work;
318		struct work_record *nextwork;
319
320		for (work = subrec->workgrouplist; work; work = nextwork) {
321			nextwork = work->next;
322			expire_servers(work, t);
323
324			if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) &&
325					((t == -1) || (work->death_time < t))) {
326				DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n",
327						work->work_group));
328				remove_workgroup_from_subnet(subrec, work);
329			}
330		}
331	}
332}
333