2 Unix SMB/Netbios implementation.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1997
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 DEBUGLEVEL;
33 extern BOOL CanRecurse;
35 extern struct in_addr ipzero;
37 extern pstring myname;
38 extern fstring myworkgroup;
40 extern int ClientDGRAM;
43 /* this is our domain/workgroup/server database */
44 extern struct subnet_record *subnetlist;
46 extern int updatecount;
47 extern int workgroup_count;
49 extern struct in_addr wins_ip;
53 /****************************************************************************
54 send a announce request to the local net
55 **************************************************************************/
56 void announce_request(struct work_record *work, struct in_addr ip)
63 work->needannounce = True;
65 DEBUG(2,("sending announce request to %s for workgroup %s\n",
66 inet_ntoa(ip),work->work_group));
68 bzero(outbuf,sizeof(outbuf));
70 CVAL(p,0) = ANN_AnnouncementRequest;
73 CVAL(p,0) = work->token; /* (local) unique workgroup token id */
79 /* XXXX note: if we sent the announcement request to 0x1d instead
80 of 0x1e, then we could get the master browser to announce to
81 us instead of the members of the workgroup. wha-hey! */
83 send_mailslot_reply(False, BROWSE_MAILSLOT,ClientDGRAM,
84 outbuf,PTR_DIFF(p,outbuf),
85 myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
89 /****************************************************************************
90 request an announcement
91 **************************************************************************/
92 void do_announce_request(char *info, char *to_name, int announce_type,
94 int to, struct in_addr dest_ip)
99 bzero(outbuf,sizeof(outbuf));
101 CVAL(p,0) = announce_type;
104 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
105 announce_type, info, inet_ntoa(dest_ip),to_name,to));
109 p = skip_string(p,1);
111 send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
112 outbuf,PTR_DIFF(p,outbuf),
113 myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
117 /****************************************************************************
118 find a server responsible for a workgroup, and sync browse lists
119 control ends up back here via response_name_query.
120 **************************************************************************/
121 void sync_server(enum state_type state, char *serv_name, char *work_name,
123 struct subnet_record *d,
126 /* with a domain master we can get the whole list (not local only list) */
127 BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
129 add_browser_entry(serv_name, name_type, work_name, 0, d, ip, local_only);
131 if (state == NAME_STATUS_DOM_SRV_CHK)
133 /* announce ourselves as a master browser to serv_name */
134 do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
140 /****************************************************************************
141 send a host announcement packet
142 **************************************************************************/
143 void do_announce_host(int command,
144 char *from_name, int from_type, struct in_addr from_ip,
145 char *to_name , int to_type , struct in_addr to_ip,
146 time_t announce_interval,
147 char *server_name, int server_type, char *server_comment)
152 bzero(outbuf,sizeof(outbuf));
156 CVAL(outbuf,0) = command;
158 /* announcement parameters */
159 CVAL(p,0) = updatecount;
160 SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
162 StrnCpy(p+5,server_name,16);
165 CVAL(p,21) = MAJOR_VERSION; /* major version */
166 CVAL(p,22) = MINOR_VERSION; /* minor version */
168 SIVAL(p,23,server_type);
169 /* browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT)*/
170 SSVAL(p,27,BROWSER_ELECTION_VERSION);
171 SSVAL(p,29,BROWSER_CONSTANT); /* browse signature */
173 strcpy(p+31,server_comment);
175 p = skip_string(p,1);
177 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
179 /* send the announcement */
180 send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,outbuf,
188 /****************************************************************************
189 announce all samba's server entries as 'gone'.
190 ****************************************************************************/
191 void announce_my_servers_removed(void)
193 struct subnet_record *d;
194 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
196 struct work_record *work;
197 for (work = d->workgrouplist; work; work = work->next)
199 struct server_record *s;
200 for (s = work->serverlist; s; s = s->next)
202 if (!strequal(myname,s->serv.name)) continue;
203 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
210 /****************************************************************************
211 announce a server entry
212 ****************************************************************************/
213 void announce_server(struct subnet_record *d, struct work_record *work,
214 char *name, char *comment, time_t ttl, int server_type)
216 /* domain type cannot have anything in it that might confuse
217 a client into thinking that the domain is in fact a server.
218 (SV_TYPE_SERVER_UNIX, for example)
220 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
221 BOOL wins_iface = ip_equal(d->bcast_ip, wins_ip);
223 if (wins_iface && server_type != 0)
225 /* wins pseudo-ip interface */
226 if (!AM_MASTER(work))
228 /* non-master announce by unicast to the domain
230 if (!lp_wins_support() && *lp_wins_server())
232 /* look up the domain master with the WINS server */
233 queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
234 NAME_QUERY_ANNOUNCE_HOST,
235 work->work_group,0x1b,0,ttl*1000,
236 server_type,name,comment,
237 False, False, ipzero, d->bcast_ip);
241 /* we are the WINS server, but not the domain master. */
242 /* XXXX we need to look up the domain master in our
243 WINS database list, and do_announce_host(). maybe
244 we could do a name query on the unsuspecting domain
245 master just to make sure it's awake. */
249 /* XXXX any other kinds of announcements we need to consider here?
250 e.g local master browsers... no. local master browsers do
251 local master announcements to their domain master. they even
252 use WINS lookup of the domain master if another wins server
260 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
261 inet_ntoa(d->bcast_ip),work->work_group));
263 do_announce_host(ANN_LocalMasterAnnouncement,
264 name , 0x00, d->myip,
265 work->work_group, 0x1e, d->bcast_ip,
267 name, server_type, comment);
269 DEBUG(3,("sending domain announce to %s for %s\n",
270 inet_ntoa(d->bcast_ip),work->work_group));
272 /* XXXX should we do a domain-announce-kill? */
273 if (server_type != 0)
275 do_announce_host(ANN_DomainAnnouncement,
276 name , 0x00, d->myip,
277 MSBROWSE, 0x01, d->bcast_ip,
279 work->work_group, server_type ? domain_type : 0,
285 DEBUG(3,("sending host announce to %s for %s(1d)\n",
286 inet_ntoa(d->bcast_ip),work->work_group));
288 do_announce_host(ANN_HostAnnouncement,
289 name , 0x00, d->myip,
290 work->work_group, 0x1d, d->bcast_ip,
292 name, server_type, comment);
297 /****************************************************************************
298 construct a host announcement unicast
299 **************************************************************************/
300 void announce_host(time_t t)
302 struct subnet_record *d;
306 StrnCpy(comment, lp_serverstring(), 43);
308 my_name = *myname ? myname : "NoName";
310 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
312 struct work_record *work;
314 for (work = d->workgrouplist; work; work = work->next)
316 uint32 stype = work->ServerType;
317 struct server_record *s;
318 BOOL announce = False;
320 /* must work on the code that does announcements at up to
321 30 seconds later if a master browser sends us a request
325 if (work->needannounce) {
326 /* drop back to a max 3 minute announce - this is to prevent a
327 single lost packet from stuffing things up for too long */
328 work->announce_interval = MIN(work->announce_interval,
329 CHECK_TIME_MIN_HOST_ANNCE*60);
330 work->lastannounce_time = t - (work->announce_interval+1);
333 /* announce every minute at first then progress to every 12 mins */
334 if (work->lastannounce_time &&
335 (t - work->lastannounce_time) < work->announce_interval)
338 if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60)
339 work->announce_interval += 60;
341 work->lastannounce_time = t;
343 for (s = work->serverlist; s; s = s->next) {
344 if (strequal(myname, s->serv.name)) {
351 announce_server(d,work,my_name,comment,
352 work->announce_interval,stype);
355 if (work->needannounce)
357 work->needannounce = False;
359 /* sorry: can't do too many announces. do some more later */
365 /* Announce timer. Moved into global static so it can be reset
366 when a machine becomes a master browser. */
367 static time_t announce_timer_last=0;
369 /****************************************************************************
370 Reset the announce_timer so that a master browser announce will be done
372 ****************************************************************************/
374 void reset_announce_timer()
376 announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
379 /****************************************************************************
380 announce myself as a master to all other domain master browsers.
382 this actually gets done in search_and_sync_workgroups() via the
383 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
384 name query initiated here. see response_name_query()
385 **************************************************************************/
386 void announce_master(time_t t)
388 struct subnet_record *d;
389 struct work_record *work;
390 BOOL am_master = False; /* are we a master of some sort? :-) */
392 if (!announce_timer_last) announce_timer_last = t;
393 if (t-announce_timer_last < CHECK_TIME_MST_ANNOUNCE * 60)
395 DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
396 t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
400 if(wins_subnet == NULL)
402 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
406 announce_timer_last = t;
408 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
410 struct work_record *work;
411 for (work = d->workgrouplist; work; work = work->next)
416 DEBUG(4,( "announce_master: am_master = %d for \
417 workgroup %s\n", am_master, work->work_group));
422 if (!am_master) return; /* only proceed if we are a master browser */
424 /* Note that we don't do this if we are domain master browser
425 and that we *only* do this on the WINS subnet. */
427 /* Try and find our workgroup on the WINS subnet */
428 work = find_workgroupstruct(wins_subnet, myworkgroup, False);
435 if (*lp_domain_controller())
437 /* the domain controller option is used to manually specify
438 the domain master browser to sync with
441 /* XXXX i'm not sure we should be using the domain controller
442 option for this purpose.
445 name = lp_domain_controller();
450 /* assume that the domain master browser we want to sync
451 with is our own domain.
453 name = work->work_group;
457 /* check the existence of a dmb for this workgroup, and if
458 one exists at the specified ip, sync with it and announce
459 ourselves as a master browser to it
462 if (!lp_wins_support() && *lp_wins_server() )
464 DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
465 name, type, lp_wins_server()));
467 queue_netbios_pkt_wins(ClientNMB,
468 NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
470 work->work_group,NULL,
471 False, False, ipzero, ipzero);
473 else if(lp_wins_support())
475 /* We are the WINS server - query ourselves for the dmb name. */
477 struct nmb_name netb_name;
478 struct subnet_record *d = 0;
479 struct name_record *nr = 0;
481 make_nmb_name(&netb_name, name, type, scope);
483 if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
485 DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
486 in our own WINS database.\n", work->work_group));
490 /* Check that this isn't one of our addresses (ie. we are not domain master
492 if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
494 DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
495 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
499 /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
500 NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a
501 remote WINS server. */
503 DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
504 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
506 queue_netbios_packet(wins_subnet, ClientNMB,
507 NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
509 work->work_group,NULL,
510 False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip);
516 /****************************************************************************
517 do all the "remote" announcements. These are used to put ourselves
518 on a remote browse list. They are done blind, no checking is done to
519 see if there is actually a browse master at the other end.
520 **************************************************************************/
521 void announce_remote(time_t t)
524 static time_t last_time = 0;
527 char *comment,*workgroup;
528 int stype = DFLT_SERVER_TYPE;
530 if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
535 s = lp_remote_announce();
538 comment = lp_serverstring();
539 workgroup = myworkgroup;
541 for (ptr=s; next_token(&ptr,s2,NULL); ) {
542 /* the entries are of the form a.b.c.d/WORKGROUP with
543 WORKGROUP being optional */
546 wgroup = strchr(s2,'/');
547 if (wgroup) *wgroup++ = 0;
548 if (!wgroup || !*wgroup)
551 addr = *interpret_addr2(s2);
553 do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
555 REMOTE_ANNOUNCE_INTERVAL,
556 myname,stype,comment);