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 /* machine comment for host announcements */
46 extern pstring ServerComment;
48 extern int updatecount;
49 extern int workgroup_count;
51 extern struct in_addr ipgrp;
55 /****************************************************************************
56 send a announce request to the local net
57 **************************************************************************/
58 void announce_request(struct work_record *work, struct in_addr ip)
65 work->needannounce = True;
67 DEBUG(2,("sending announce request to %s for workgroup %s\n",
68 inet_ntoa(ip),work->work_group));
70 bzero(outbuf,sizeof(outbuf));
72 CVAL(p,0) = ANN_AnnouncementRequest;
75 CVAL(p,0) = work->token; /* (local) unique workgroup token id */
81 /* XXXX note: if we sent the announcement request to 0x1d instead
82 of 0x1e, then we could get the master browser to announce to
83 us instead of the members of the workgroup. wha-hey! */
85 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
86 myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
90 /****************************************************************************
91 request an announcement
92 **************************************************************************/
93 void do_announce_request(char *info, char *to_name, int announce_type,
95 int to, struct in_addr dest_ip)
100 bzero(outbuf,sizeof(outbuf));
102 CVAL(p,0) = announce_type;
105 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
106 announce_type, info, inet_ntoa(dest_ip),to_name,to));
110 p = skip_string(p,1);
112 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,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,
125 /* with a domain master we can get the whole list (not local only list) */
126 BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
128 add_browser_entry(serv_name, name_type, work_name, 0, ip, local_only);
130 if (state == NAME_STATUS_DOM_SRV_CHK)
132 /* announce ourselves as a master browser to serv_name */
133 do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
139 /****************************************************************************
140 construct a host announcement unicast
142 this function should not be used heavily, and only when we are _not_
143 a master browser and _not_ a primary domain controller.
145 **************************************************************************/
146 void announce_backup(void)
148 static time_t lastrun = 0;
149 time_t t = time(NULL);
152 struct subnet_record *d1;
154 static uint32 id_count = 0;
156 if (!lastrun) lastrun = t;
158 if (t < lastrun + 1 * 60)
160 if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60)
165 DEBUG(4,("checking backups...\n"));
167 for (tok = 0; tok <= workgroup_count; tok++)
169 for (d1 = subnetlist; d1; d1 = d1->next)
171 struct work_record *work;
172 struct subnet_record *d;
174 /* search for unique workgroup: only the name matters */
175 for (work = d1->workgrouplist;
176 work && (tok != work->token);
181 if (AM_MASTER(work) && AM_DOMCTL(work)) continue;
183 /* found one: announce it across all domains */
184 for (d = subnetlist; d; d = d->next)
187 DEBUG(2,("sending announce backup %s workgroup %s(%d)\n",
188 inet_ntoa(d->bcast_ip),work->work_group,
191 bzero(outbuf,sizeof(outbuf));
194 CVAL(p,0) = ANN_GetBackupListReq;
195 CVAL(p,1) = work->token; /* workgroup unique key index */
196 SIVAL(p,2,++id_count); /* unique count. not that we use it! */
200 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
202 if (!AM_DOMCTL(work))
204 /* only ask for a list of backup domain controllers
205 if we are not a domain controller ourselves */
207 send_mailslot_reply(BROWSE_MAILSLOT,
210 myname, work->work_group,
211 0x0,0x1b,d->bcast_ip,
212 *iface_ip(d->bcast_ip));
215 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
217 if (!AM_MASTER(work))
219 /* only ask for a list of master browsers if we
220 are not a master browser ourselves */
222 send_mailslot_reply(BROWSE_MAILSLOT,
225 myname, work->work_group,
226 0x0,0x1d,d->bcast_ip,
227 *iface_ip(d->bcast_ip));
235 /****************************************************************************
236 send a host announcement packet
237 **************************************************************************/
238 void do_announce_host(int command,
239 char *from_name, int from_type, struct in_addr from_ip,
240 char *to_name , int to_type , struct in_addr to_ip,
241 time_t announce_interval,
242 char *server_name, int server_type, char *server_comment)
247 bzero(outbuf,sizeof(outbuf));
251 CVAL(outbuf,0) = command;
253 /* announcement parameters */
254 CVAL(p,0) = updatecount;
255 SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
257 StrnCpy(p+5,server_name,16);
260 CVAL(p,21) = 0x02; /* major version */
261 CVAL(p,22) = 0x02; /* minor version */
263 SIVAL(p,23,server_type);
264 SSVAL(p,27,0x010f); /* browse version: got from NT/AS 4.00 */
265 SSVAL(p,29,0xaa55); /* browse signature */
267 strcpy(p+31,server_comment);
269 p = skip_string(p,1);
271 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
273 /* send the announcement */
274 send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
282 /****************************************************************************
283 remove all samba's server entries
284 ****************************************************************************/
285 void remove_my_servers(void)
287 struct subnet_record *d;
288 for (d = subnetlist; d; d = d->next)
290 struct work_record *work;
291 for (work = d->workgrouplist; work; work = work->next)
293 struct server_record *s;
294 for (s = work->serverlist; s; s = s->next)
296 if (!strequal(myname,s->serv.name)) continue;
297 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
304 /****************************************************************************
305 announce a server entry
306 ****************************************************************************/
307 void announce_server(struct subnet_record *d, struct work_record *work,
308 char *name, char *comment, time_t ttl, int server_type)
310 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX;
311 BOOL wins_iface = ip_equal(d->bcast_ip, ipgrp);
313 if (wins_iface && server_type != 0)
315 /* wins pseudo-ip interface */
316 if (!AM_MASTER(work))
318 /* non-master announce by unicast to the domain master */
319 if (!lp_wins_support() && *lp_wins_server())
321 /* look up the domain master with the WINS server */
322 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
323 NAME_QUERY_ANNOUNCE_HOST,
324 work->work_group,0x1b,0,ttl*1000,
325 server_type,name,comment,
326 False, False, ipzero, d->bcast_ip);
330 /* we are the WINS server, but not the domain master. */
331 /* XXXX we need to look up the domain master in our
332 WINS database list, and do_announce_host(). maybe
333 we could do a name query on the unsuspecting domain
334 master just to make sure it's awake. */
340 /* XXXX announce to backup domain masters? */
343 /* XXXX any other kinds of announcements we need to consider here?
344 e.g local master browsers... no. local master browsers do
345 local master announcements to their domain master. they even
346 use WINS lookup of the domain master if another wins server
354 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
355 inet_ntoa(d->bcast_ip),work->work_group));
357 do_announce_host(ANN_LocalMasterAnnouncement,
358 name , 0x00, d->myip,
359 work->work_group, 0x1e, d->bcast_ip,
361 name, server_type, comment);
363 DEBUG(3,("sending domain announce to %s for %s\n",
364 inet_ntoa(d->bcast_ip),work->work_group));
366 /* XXXX should we do a domain-announce-kill? */
367 if (server_type != 0)
371 domain_type |= SV_TYPE_DOMAIN_CTRL;
373 do_announce_host(ANN_DomainAnnouncement,
374 name , 0x00, d->myip,
375 MSBROWSE, 0x01, d->bcast_ip,
377 work->work_group, server_type ? domain_type : 0,
383 DEBUG(3,("sending host announce to %s for %s(1d)\n",
384 inet_ntoa(d->bcast_ip),work->work_group));
386 do_announce_host(ANN_HostAnnouncement,
387 name , 0x00, d->myip,
388 work->work_group, 0x1d, d->bcast_ip,
390 name, server_type, comment);
395 /****************************************************************************
396 construct a host announcement unicast
397 **************************************************************************/
398 void announce_host(void)
400 time_t t = time(NULL);
401 struct subnet_record *d;
405 StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
407 my_name = *myname ? myname : "NoName";
409 for (d = subnetlist; d; d = d->next)
411 struct work_record *work;
413 if (ip_equal(d->bcast_ip, ipgrp)) continue;
415 for (work = d->workgrouplist; work; work = work->next)
417 uint32 stype = work->ServerType;
418 struct server_record *s;
419 BOOL announce = False;
421 /* must work on the code that does announcements at up to
422 30 seconds later if a master browser sends us a request
426 if (work->needannounce) {
427 /* drop back to a max 3 minute announce - this is to prevent a
428 single lost packet from stuffing things up for too long */
429 work->announce_interval = MIN(work->announce_interval,
430 CHECK_TIME_MIN_HOST_ANNCE*60);
431 work->lastannounce_time = t - (work->announce_interval+1);
434 /* announce every minute at first then progress to every 12 mins */
435 if (work->lastannounce_time &&
436 (t - work->lastannounce_time) < work->announce_interval)
439 if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60)
440 work->announce_interval += 60;
442 work->lastannounce_time = t;
445 if (!d->my_interface) {
446 stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
447 SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
448 SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
452 for (s = work->serverlist; s; s = s->next) {
453 if (strequal(myname, s->serv.name)) {
461 announce_server(d,work,my_name,comment,work->announce_interval,stype);
464 if (work->needannounce)
466 work->needannounce = False;
468 /* sorry: can't do too many announces. do some more later */
475 /****************************************************************************
476 announce myself as a master to all other primary domain conrollers.
478 this actually gets done in search_and_sync_workgroups() via the
479 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
480 name query initiated here. see response_name_query()
481 **************************************************************************/
482 void announce_master(void)
484 struct subnet_record *d;
485 static time_t last=0;
486 time_t t = time(NULL);
487 BOOL am_master = False; /* are we a master of some sort? :-) */
490 if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
495 for (d = subnetlist; d; d = d->next)
497 struct work_record *work;
498 for (work = d->workgrouplist; work; work = work->next)
507 if (!am_master) return; /* only proceed if we are a master browser */
509 for (d = subnetlist; d; d = d->next)
511 struct work_record *work;
512 for (work = d->workgrouplist; work; work = work->next)
514 struct server_record *s;
515 for (s = work->serverlist; s; s = s->next)
517 if (strequal(s->serv.name, myname)) continue;
519 /* all DOMs (which should also be master browsers) */
520 if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
522 /* check the existence of a pdc for this workgroup, and if
523 one exists at the specified ip, sync with it and announce
524 ourselves as a master browser to it */
526 if (!*lp_domain_controller() ||
527 !strequal(lp_domain_controller(), s->serv.name))
529 if (!lp_wins_support() && *lp_wins_server())
531 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
532 NAME_QUERY_DOM_SRV_CHK,
533 work->work_group,0x1b,0,0,0,NULL,NULL,
534 False, False, ipzero, ipzero);
538 struct subnet_record *d2;
539 for (d2 = subnetlist; d2; d2 = d2->next)
541 queue_netbios_packet(d,ClientNMB,NMB_QUERY,
542 NAME_QUERY_DOM_SRV_CHK,
543 work->work_group,0x1b,0,0,0,NULL,NULL,
544 True, False, d2->bcast_ip, d2->bcast_ip);
551 /* now do primary domain controller - the one that's not
552 necessarily in our browse lists, although it ought to be
553 this pdc is the one that we get TOLD about through smb.conf.
554 basically, if it's on a subnet that we know about, it may end
555 up in our browse lists (which is why it's explicitly excluded
556 in the code above) */
558 if (*lp_domain_controller())
563 ip = *interpret_addr2(lp_domain_controller());
570 DEBUG(2, ("Searching for DOM %s at %s\n",
571 lp_domain_controller(), inet_ntoa(ip)));
573 /* check the existence of a pdc for this workgroup, and if
574 one exists at the specified ip, sync with it and announce
575 ourselves as a master browser to it */
576 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
577 work->work_group,0x1b,0,0,0,NULL,NULL,
578 bcast, False, ip, ip);
586 /****************************************************************************
587 do all the "remote" announcements. These are used to put ourselves
588 on a remote browse list. They are done blind, no checking is done to
589 see if there is actually a browse master at the other end.
590 **************************************************************************/
591 void announce_remote(void)
594 static time_t last_time = 0;
595 time_t t = time(NULL);
598 char *comment,*workgroup;
599 int stype = SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_PRINTQ_SERVER |
602 if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
607 s = lp_remote_announce();
610 comment = lp_serverstring();
611 workgroup = lp_workgroup();
613 for (ptr=s; next_token(&ptr,s2,NULL); ) {
614 /* the entries are of the form a.b.c.d/WORKGROUP with
615 WORKGROUP being optional */
618 wgroup = strchr(s2,'/');
619 if (wgroup) *wgroup++ = 0;
620 if (!wgroup || !*wgroup)
623 addr = *interpret_addr2(s2);
625 do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
627 REMOTE_ANNOUNCE_INTERVAL,
628 myname,stype,comment);