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 DEBUGLEVEL;
33 extern BOOL CanRecurse;
35 extern struct in_addr ipzero;
37 extern pstring myname;
39 extern int ClientDGRAM;
42 /* this is our domain/workgroup/server database */
43 extern struct subnet_record *subnetlist;
45 extern int updatecount;
46 extern int workgroup_count;
48 extern struct in_addr ipgrp;
52 /****************************************************************************
53 send a announce request to the local net
54 **************************************************************************/
55 void announce_request(struct work_record *work, struct in_addr ip)
62 work->needannounce = True;
64 DEBUG(2,("sending announce request to %s for workgroup %s\n",
65 inet_ntoa(ip),work->work_group));
67 bzero(outbuf,sizeof(outbuf));
69 CVAL(p,0) = ANN_AnnouncementRequest;
72 CVAL(p,0) = work->token; /* (local) unique workgroup token id */
78 /* XXXX note: if we sent the announcement request to 0x1d instead
79 of 0x1e, then we could get the master browser to announce to
80 us instead of the members of the workgroup. wha-hey! */
82 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
83 myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
87 /****************************************************************************
88 request an announcement
89 **************************************************************************/
90 void do_announce_request(char *info, char *to_name, int announce_type,
92 int to, struct in_addr dest_ip)
97 bzero(outbuf,sizeof(outbuf));
99 CVAL(p,0) = announce_type;
102 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
103 announce_type, info, inet_ntoa(dest_ip),to_name,to));
107 p = skip_string(p,1);
109 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
110 myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
114 /****************************************************************************
115 find a server responsible for a workgroup, and sync browse lists
116 control ends up back here via response_name_query.
117 **************************************************************************/
118 void sync_server(enum state_type state, char *serv_name, char *work_name,
122 /* with a domain master we can get the whole list (not local only list) */
123 BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
125 add_browser_entry(serv_name, name_type, work_name, 0, ip, local_only);
127 if (state == NAME_STATUS_DOM_SRV_CHK)
129 /* announce ourselves as a master browser to serv_name */
130 do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
136 /****************************************************************************
137 send a host announcement packet
138 **************************************************************************/
139 void do_announce_host(int command,
140 char *from_name, int from_type, struct in_addr from_ip,
141 char *to_name , int to_type , struct in_addr to_ip,
142 time_t announce_interval,
143 char *server_name, int server_type, char *server_comment)
148 bzero(outbuf,sizeof(outbuf));
152 CVAL(outbuf,0) = command;
154 /* announcement parameters */
155 CVAL(p,0) = updatecount;
156 SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
158 StrnCpy(p+5,server_name,16);
161 CVAL(p,21) = 0x02; /* major version */
162 CVAL(p,22) = 0x02; /* minor version */
164 SIVAL(p,23,server_type);
165 SSVAL(p,27,0x010f); /* browse version: got from NT/AS 4.00 */
166 SSVAL(p,29,0xaa55); /* browse signature */
168 strcpy(p+31,server_comment);
170 p = skip_string(p,1);
172 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
174 /* send the announcement */
175 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
183 /****************************************************************************
184 remove all samba's server entries
185 ****************************************************************************/
186 void remove_my_servers(void)
188 struct subnet_record *d;
189 for (d = subnetlist; d; d = d->next)
191 struct work_record *work;
192 for (work = d->workgrouplist; work; work = work->next)
194 struct server_record *s;
195 for (s = work->serverlist; s; s = s->next)
197 if (!strequal(myname,s->serv.name)) continue;
198 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
205 /****************************************************************************
206 announce a server entry
207 ****************************************************************************/
208 void announce_server(struct subnet_record *d, struct work_record *work,
209 char *name, char *comment, time_t ttl, int server_type)
211 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|DFLT_SERVER_TYPE;
212 BOOL wins_iface = ip_equal(d->bcast_ip, ipgrp);
214 if (wins_iface && server_type != 0)
216 /* wins pseudo-ip interface */
217 if (!AM_MASTER(work))
219 /* non-master announce by unicast to the domain
221 if (!lp_wins_support() && *lp_wins_server())
223 /* look up the domain master with the WINS server */
224 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
225 NAME_QUERY_ANNOUNCE_HOST,
226 work->work_group,0x1b,0,ttl*1000,
227 server_type,name,comment,
228 False, False, ipzero, d->bcast_ip);
232 /* we are the WINS server, but not the domain master. */
233 /* XXXX we need to look up the domain master in our
234 WINS database list, and do_announce_host(). maybe
235 we could do a name query on the unsuspecting domain
236 master just to make sure it's awake. */
240 /* XXXX any other kinds of announcements we need to consider here?
241 e.g local master browsers... no. local master browsers do
242 local master announcements to their domain master. they even
243 use WINS lookup of the domain master if another wins server
251 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
252 inet_ntoa(d->bcast_ip),work->work_group));
254 do_announce_host(ANN_LocalMasterAnnouncement,
255 name , 0x00, d->myip,
256 work->work_group, 0x1e, d->bcast_ip,
258 name, server_type, comment);
260 DEBUG(3,("sending domain announce to %s for %s\n",
261 inet_ntoa(d->bcast_ip),work->work_group));
263 /* XXXX should we do a domain-announce-kill? */
264 if (server_type != 0)
266 do_announce_host(ANN_DomainAnnouncement,
267 name , 0x00, d->myip,
268 MSBROWSE, 0x01, d->bcast_ip,
270 work->work_group, server_type ? domain_type : 0,
276 DEBUG(3,("sending host announce to %s for %s(1d)\n",
277 inet_ntoa(d->bcast_ip),work->work_group));
279 do_announce_host(ANN_HostAnnouncement,
280 name , 0x00, d->myip,
281 work->work_group, 0x1d, d->bcast_ip,
283 name, server_type, comment);
288 /****************************************************************************
289 construct a host announcement unicast
290 **************************************************************************/
291 void announce_host(void)
293 time_t t = time(NULL);
294 struct subnet_record *d;
298 StrnCpy(comment, lp_serverstring(), 43);
300 my_name = *myname ? myname : "NoName";
302 for (d = subnetlist; d; d = d->next)
304 struct work_record *work;
306 if (ip_equal(d->bcast_ip, ipgrp)) continue;
308 for (work = d->workgrouplist; work; work = work->next)
310 uint32 stype = work->ServerType;
311 struct server_record *s;
312 BOOL announce = False;
314 /* must work on the code that does announcements at up to
315 30 seconds later if a master browser sends us a request
319 if (work->needannounce) {
320 /* drop back to a max 3 minute announce - this is to prevent a
321 single lost packet from stuffing things up for too long */
322 work->announce_interval = MIN(work->announce_interval,
323 CHECK_TIME_MIN_HOST_ANNCE*60);
324 work->lastannounce_time = t - (work->announce_interval+1);
327 /* announce every minute at first then progress to every 12 mins */
328 if (work->lastannounce_time &&
329 (t - work->lastannounce_time) < work->announce_interval)
332 if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60)
333 work->announce_interval += 60;
335 work->lastannounce_time = t;
337 for (s = work->serverlist; s; s = s->next) {
338 if (strequal(myname, s->serv.name)) {
345 announce_server(d,work,my_name,comment,
346 work->announce_interval,stype);
349 if (work->needannounce)
351 work->needannounce = False;
353 /* sorry: can't do too many announces. do some more later */
360 /****************************************************************************
361 announce myself as a master to all other primary domain conrollers.
363 this actually gets done in search_and_sync_workgroups() via the
364 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
365 name query initiated here. see response_name_query()
366 **************************************************************************/
367 void announce_master(void)
369 struct subnet_record *d;
370 static time_t last=0;
371 time_t t = time(NULL);
372 BOOL am_master = False; /* are we a master of some sort? :-) */
375 if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
380 for (d = subnetlist; d; d = d->next)
382 struct work_record *work;
383 for (work = d->workgrouplist; work; work = work->next)
392 if (!am_master) return; /* only proceed if we are a master browser */
394 for (d = subnetlist; d; d = d->next)
396 struct work_record *work;
397 for (work = d->workgrouplist; work; work = work->next)
399 struct server_record *s;
400 for (s = work->serverlist; s; s = s->next)
402 if (strequal(s->serv.name, myname)) continue;
404 /* all DOMs (which should also be master browsers) */
405 if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
407 /* check the existence of a pdc for this workgroup, and if
408 one exists at the specified ip, sync with it and announce
409 ourselves as a master browser to it */
411 if (!*lp_domain_controller() ||
412 !strequal(lp_domain_controller(), s->serv.name))
414 if (!lp_wins_support() && *lp_wins_server())
416 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
417 NAME_QUERY_DOM_SRV_CHK,
418 work->work_group,0x1b,0,0,0,NULL,NULL,
419 False, False, ipzero, ipzero);
423 struct subnet_record *d2;
424 for (d2 = subnetlist; d2; d2 = d2->next)
426 queue_netbios_packet(d,ClientNMB,NMB_QUERY,
427 NAME_QUERY_DOM_SRV_CHK,
428 work->work_group,0x1b,0,0,0,NULL,NULL,
429 True, False, d2->bcast_ip, d2->bcast_ip);
436 /* now do primary domain controller - the one that's not
437 necessarily in our browse lists, although it ought to be
438 this pdc is the one that we get TOLD about through smb.conf.
439 basically, if it's on a subnet that we know about, it may end
440 up in our browse lists (which is why it's explicitly excluded
441 in the code above) */
443 if (*lp_domain_controller())
448 ip = *interpret_addr2(lp_domain_controller());
455 DEBUG(2, ("Searching for DOM %s at %s\n",
456 lp_domain_controller(), inet_ntoa(ip)));
458 /* check the existence of a pdc for this workgroup, and if
459 one exists at the specified ip, sync with it and announce
460 ourselves as a master browser to it */
461 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
462 work->work_group,0x1b,0,0,0,NULL,NULL,
463 bcast, False, ip, ip);
471 /****************************************************************************
472 do all the "remote" announcements. These are used to put ourselves
473 on a remote browse list. They are done blind, no checking is done to
474 see if there is actually a browse master at the other end.
475 **************************************************************************/
476 void announce_remote(void)
479 static time_t last_time = 0;
480 time_t t = time(NULL);
483 char *comment,*workgroup;
484 int stype = DFLT_SERVER_TYPE;
486 if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
491 s = lp_remote_announce();
494 comment = lp_serverstring();
495 workgroup = lp_workgroup();
497 for (ptr=s; next_token(&ptr,s2,NULL); ) {
498 /* the entries are of the form a.b.c.d/WORKGROUP with
499 WORKGROUP being optional */
502 wgroup = strchr(s2,'/');
503 if (wgroup) *wgroup++ = 0;
504 if (!wgroup || !*wgroup)
507 addr = *interpret_addr2(s2);
509 do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
511 REMOTE_ANNOUNCE_INTERVAL,
512 myname,stype,comment);