Line data Source code
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 3 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, see <http://www.gnu.org/licenses/>.
20 :
21 : */
22 :
23 : #include "includes.h"
24 : #include "system/filesys.h"
25 : #include "../librpc/gen_ndr/svcctl.h"
26 : #include "nmbd/nmbd.h"
27 : #include "lib/util/string_wrappers.h"
28 :
29 : int updatecount = 0;
30 :
31 : /*******************************************************************
32 : Remove all the servers in a work group.
33 : ******************************************************************/
34 :
35 0 : void remove_all_servers(struct work_record *work)
36 : {
37 : struct server_record *servrec;
38 : struct server_record *nexts;
39 :
40 0 : for (servrec = work->serverlist; servrec; servrec = nexts) {
41 0 : DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
42 0 : nexts = servrec->next;
43 0 : DLIST_REMOVE(work->serverlist, servrec);
44 0 : ZERO_STRUCTP(servrec);
45 0 : SAFE_FREE(servrec);
46 : }
47 :
48 0 : work->subnet->work_changed = True;
49 0 : }
50 :
51 : /***************************************************************************
52 : Add a server into a workgroup serverlist.
53 : **************************************************************************/
54 :
55 77 : static void add_server_to_workgroup(struct work_record *work,
56 : struct server_record *servrec)
57 : {
58 77 : DLIST_ADD_END(work->serverlist, servrec);
59 77 : work->subnet->work_changed = True;
60 77 : }
61 :
62 : /****************************************************************************
63 : Find a server in a server list.
64 : **************************************************************************/
65 :
66 748 : struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
67 : {
68 : struct server_record *ret;
69 :
70 1079 : for (ret = work->serverlist; ret; ret = ret->next) {
71 970 : if (strequal(ret->serv.name,name))
72 639 : return ret;
73 : }
74 109 : return NULL;
75 : }
76 :
77 :
78 : /****************************************************************************
79 : Remove a server entry from this workgroup.
80 : ****************************************************************************/
81 :
82 14 : void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
83 : {
84 14 : DLIST_REMOVE(work->serverlist, servrec);
85 14 : ZERO_STRUCTP(servrec);
86 14 : SAFE_FREE(servrec);
87 14 : work->subnet->work_changed = True;
88 14 : }
89 :
90 : /****************************************************************************
91 : Create a server entry on this workgroup.
92 : ****************************************************************************/
93 :
94 77 : struct server_record *create_server_on_workgroup(struct work_record *work,
95 : const char *name,int servertype,
96 : int ttl, const char *comment)
97 : {
98 : struct server_record *servrec;
99 :
100 77 : if (name[0] == '*') {
101 0 : DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
102 : name));
103 0 : return (NULL);
104 : }
105 :
106 77 : if(find_server_in_workgroup(work, name) != NULL) {
107 0 : DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
108 : workgroup %s. This is a bug.\n", name, work->work_group));
109 0 : return NULL;
110 : }
111 :
112 77 : if((servrec = SMB_MALLOC_P(struct server_record)) == NULL) {
113 0 : DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
114 0 : return NULL;
115 : }
116 :
117 77 : memset((char *)servrec,'\0',sizeof(*servrec));
118 :
119 77 : servrec->subnet = work->subnet;
120 :
121 77 : fstrcpy(servrec->serv.name,name);
122 77 : fstrcpy(servrec->serv.comment,comment);
123 77 : if (!strupper_m(servrec->serv.name)) {
124 0 : DEBUG(2,("strupper_m %s failed\n", servrec->serv.name));
125 0 : SAFE_FREE(servrec);
126 0 : return NULL;
127 : }
128 77 : servrec->serv.type = servertype;
129 :
130 77 : update_server_ttl(servrec, ttl);
131 :
132 77 : add_server_to_workgroup(work, servrec);
133 :
134 77 : DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
135 : workgroup %s.\n", name,servertype,comment, work->work_group));
136 :
137 77 : return(servrec);
138 : }
139 :
140 : /*******************************************************************
141 : Update the ttl field of a server record.
142 : *******************************************************************/
143 :
144 138 : void update_server_ttl(struct server_record *servrec, int ttl)
145 : {
146 138 : if(ttl > lp_max_ttl())
147 0 : ttl = lp_max_ttl();
148 :
149 138 : if(is_myname(servrec->serv.name))
150 47 : servrec->death_time = PERMANENT_TTL;
151 : else
152 91 : servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
153 138 : }
154 :
155 : /*******************************************************************
156 : Expire old servers in the serverlist. A time of -1 indicates
157 : everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
158 : This should only be called from expire_workgroups_and_servers().
159 : ******************************************************************/
160 :
161 8470 : void expire_servers(struct work_record *work, time_t t)
162 : {
163 : struct server_record *servrec;
164 : struct server_record *nexts;
165 :
166 14009 : for (servrec = work->serverlist; servrec; servrec = nexts) {
167 5539 : nexts = servrec->next;
168 :
169 5539 : if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) {
170 2 : DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
171 2 : remove_server_from_workgroup(work, servrec);
172 : }
173 : }
174 8470 : }
175 :
176 : /*******************************************************************
177 : Decide if we should write out a server record for this server.
178 : We return zero if we should not. Check if we've already written
179 : out this server record from an earlier subnet.
180 : ******************************************************************/
181 :
182 225 : static uint32_t write_this_server_name( struct subnet_record *subrec,
183 : struct work_record *work,
184 : struct server_record *servrec)
185 : {
186 : struct subnet_record *ssub;
187 : struct work_record *iwork;
188 :
189 : /* Go through all the subnets we have already seen. */
190 225 : for (ssub = FIRST_SUBNET; ssub && (ssub != subrec); ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) {
191 0 : for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) {
192 0 : if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL) {
193 : /*
194 : * We have already written out this server record, don't
195 : * do it again. This gives precedence to servers we have seen
196 : * on the broadcast subnets over servers that may have been
197 : * added via a sync on the unicast_subet.
198 : *
199 : * The correct way to do this is to have a serverlist file
200 : * per subnet - this means changes to smbd as well. I may
201 : * add this at a later date (JRA).
202 : */
203 :
204 0 : return 0;
205 : }
206 : }
207 : }
208 :
209 225 : return servrec->serv.type;
210 : }
211 :
212 : /*******************************************************************
213 : Decide if we should write out a workgroup record for this workgroup.
214 : We return zero if we should not. Don't write out lp_workgroup() (we've
215 : already done it) and also don't write out a second workgroup record
216 : on the unicast subnet that we've already written out on one of the
217 : broadcast subnets.
218 : ******************************************************************/
219 :
220 1146 : static uint32_t write_this_workgroup_name( struct subnet_record *subrec,
221 : struct work_record *work)
222 : {
223 : struct subnet_record *ssub;
224 :
225 1146 : if(strequal(lp_workgroup(), work->work_group))
226 418 : return 0;
227 :
228 : /* This is a workgroup we have seen on a broadcast subnet. All
229 : these have the same type. */
230 :
231 728 : if(subrec != unicast_subnet)
232 728 : return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
233 :
234 0 : for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) {
235 : /* This is the unicast subnet so check if we've already written out
236 : this subnet when we passed over the broadcast subnets. */
237 :
238 0 : if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
239 0 : return 0;
240 : }
241 :
242 : /* All workgroups on the unicast subnet (except our own, which we
243 : have already written out) cannot be local. */
244 :
245 0 : return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
246 : }
247 :
248 : /*******************************************************************
249 : Write out the browse.dat file.
250 : ******************************************************************/
251 :
252 1847 : void write_browse_list_entry(FILE *fp, const char *name, uint32_t rec_type,
253 : const char *local_master_browser_name, const char *description)
254 : {
255 : fstring tmp;
256 :
257 1847 : slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
258 1847 : fprintf(fp, "%-25s ", tmp);
259 1847 : fprintf(fp, "%08x ", rec_type);
260 1847 : slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
261 1847 : fprintf(fp, "%-30s", tmp);
262 1847 : fprintf(fp, "\"%s\"\n", description);
263 1847 : }
264 :
265 17228 : void write_browse_list(time_t t, bool force_write)
266 : {
267 : struct subnet_record *subrec;
268 : struct work_record *work;
269 : struct server_record *servrec;
270 : char *fname;
271 : char *fnamenew;
272 : uint32_t stype;
273 : int i;
274 : int fd;
275 : FILE *fp;
276 17228 : bool list_changed = force_write;
277 : static time_t lasttime = 0;
278 17228 : TALLOC_CTX *ctx = talloc_tos();
279 : const struct loadparm_substitution *lp_sub =
280 17228 : loadparm_s3_global_substitution();
281 :
282 : /* Always dump if we're being told to by a signal. */
283 17228 : if(force_write == False) {
284 17145 : if (!lasttime)
285 126 : lasttime = t;
286 17145 : if (t - lasttime < 5)
287 13661 : return;
288 : }
289 :
290 3567 : lasttime = t;
291 :
292 3567 : dump_workgroups(force_write);
293 :
294 6798 : for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
295 3567 : if(subrec->work_changed) {
296 336 : list_changed = True;
297 336 : break;
298 : }
299 : }
300 :
301 3567 : if(!list_changed)
302 3149 : return;
303 :
304 418 : updatecount++;
305 :
306 418 : fname = cache_path(talloc_tos(), SERVER_LIST);
307 418 : if (!fname) {
308 0 : return;
309 : }
310 418 : fnamenew = talloc_asprintf(ctx, "%s.",
311 : fname);
312 418 : if (!fnamenew) {
313 0 : talloc_free(fname);
314 0 : return;
315 : }
316 :
317 418 : fd = open(fnamenew, O_WRONLY|O_CREAT|O_TRUNC, 0644);
318 418 : if (fd == -1) {
319 0 : DBG_ERR("Can't open file %s: %s\n", fnamenew,
320 : strerror(errno));
321 0 : talloc_free(fnamenew);
322 0 : talloc_free(fname);
323 0 : return;
324 : }
325 :
326 418 : fp = fdopen(fd, "w");
327 418 : if (!fp) {
328 0 : DBG_ERR("fdopen failed: %s\n", strerror(errno));
329 0 : close(fd);
330 0 : talloc_free(fnamenew);
331 0 : talloc_free(fname);
332 0 : return;
333 : }
334 418 : fd = -1;
335 :
336 : /*
337 : * Write out a record for our workgroup. Use the record from the first
338 : * subnet.
339 : */
340 :
341 418 : if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) {
342 0 : DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
343 : lp_workgroup()));
344 0 : fclose(fp);
345 0 : talloc_free(fnamenew);
346 0 : talloc_free(fname);
347 0 : return;
348 : }
349 :
350 418 : write_browse_list_entry(fp, work->work_group,
351 : SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
352 418 : work->local_master_browser_name, work->work_group);
353 :
354 : /*
355 : * We need to do something special for our own names.
356 : * This is due to the fact that we may be a local master browser on
357 : * one of our broadcast subnets, and a domain master on the unicast
358 : * subnet. We iterate over the subnets and only write out the name
359 : * once.
360 : */
361 :
362 894 : for (i=0; my_netbios_names(i); i++) {
363 476 : stype = 0;
364 952 : for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
365 476 : if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
366 0 : continue;
367 476 : if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
368 0 : continue;
369 :
370 476 : stype |= servrec->serv.type;
371 : }
372 :
373 : /* Output server details, plus what workgroup they're in. */
374 952 : write_browse_list_entry(fp, my_netbios_names(i), stype,
375 476 : string_truncate(lp_server_string(talloc_tos(), lp_sub), MAX_SERVER_STRING_LENGTH), lp_workgroup());
376 : }
377 :
378 836 : for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
379 418 : subrec->work_changed = False;
380 :
381 1564 : for (work = subrec->workgrouplist; work ; work = work->next) {
382 : /* Write out a workgroup record for a workgroup. */
383 1146 : uint32_t wg_type = write_this_workgroup_name( subrec, work);
384 :
385 1146 : if(wg_type) {
386 728 : write_browse_list_entry(fp, work->work_group, wg_type,
387 728 : work->local_master_browser_name,
388 728 : work->work_group);
389 : }
390 :
391 : /* Now write out any server records a workgroup may have. */
392 :
393 1847 : for (servrec = work->serverlist; servrec ; servrec = servrec->next) {
394 : uint32_t serv_type;
395 :
396 : /* We have already written our names here. */
397 701 : if(is_myname(servrec->serv.name))
398 476 : continue;
399 :
400 225 : serv_type = write_this_server_name(subrec, work, servrec);
401 225 : if(serv_type) {
402 : /* Output server details, plus what workgroup they're in. */
403 225 : write_browse_list_entry(fp, servrec->serv.name, serv_type,
404 225 : servrec->serv.comment, work->work_group);
405 : }
406 : }
407 : }
408 : }
409 :
410 418 : fclose(fp);
411 418 : unlink(fname);
412 418 : chmod(fnamenew,0644);
413 418 : rename(fnamenew,fname);
414 418 : DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
415 418 : talloc_free(fnamenew);
416 418 : talloc_free(fname);
417 : }
|