Fixes for manpage from Bill Hawes <whawes@star.net>.
[Samba/gbeck.git] / source / nameannounce.c
blobb46436168c3996986cb5df5717d7cb14d6978a18
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
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.
21 Revision History:
23 14 jan 96: lkcl@pires.co.uk
24 added multiple workgroup domain master support
28 #include "includes.h"
30 #define TEST_CODE
32 extern int DEBUGLEVEL;
33 extern BOOL CanRecurse;
35 extern struct in_addr ipzero;
37 extern pstring myname;
39 extern int ClientDGRAM;
40 extern int ClientNMB;
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 wins_ip;
50 extern pstring scope;
52 /****************************************************************************
53 send a announce request to the local net
54 **************************************************************************/
55 void announce_request(struct work_record *work, struct in_addr ip)
57 pstring outbuf;
58 char *p;
60 if (!work) return;
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));
68 p = outbuf;
69 CVAL(p,0) = ANN_AnnouncementRequest;
70 p++;
72 CVAL(p,0) = work->token; /* (local) unique workgroup token id */
73 p++;
74 StrnCpy(p,myname,16);
75 strupper(p);
76 p = skip_string(p,1);
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(False, BROWSE_MAILSLOT,ClientDGRAM,
83 outbuf,PTR_DIFF(p,outbuf),
84 myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
88 /****************************************************************************
89 request an announcement
90 **************************************************************************/
91 void do_announce_request(char *info, char *to_name, int announce_type,
92 int from,
93 int to, struct in_addr dest_ip)
95 pstring outbuf;
96 char *p;
98 bzero(outbuf,sizeof(outbuf));
99 p = outbuf;
100 CVAL(p,0) = announce_type;
101 p++;
103 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
104 announce_type, info, inet_ntoa(dest_ip),to_name,to));
106 StrnCpy(p,info,16);
107 strupper(p);
108 p = skip_string(p,1);
110 send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
111 outbuf,PTR_DIFF(p,outbuf),
112 myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
116 /****************************************************************************
117 find a server responsible for a workgroup, and sync browse lists
118 control ends up back here via response_name_query.
119 **************************************************************************/
120 void sync_server(enum state_type state, char *serv_name, char *work_name,
121 int name_type,
122 struct subnet_record *d,
123 struct in_addr ip)
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, d, 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,
134 0x20, 0, ip);
139 /****************************************************************************
140 send a host announcement packet
141 **************************************************************************/
142 void do_announce_host(int command,
143 char *from_name, int from_type, struct in_addr from_ip,
144 char *to_name , int to_type , struct in_addr to_ip,
145 time_t announce_interval,
146 char *server_name, int server_type, char *server_comment)
148 pstring outbuf;
149 char *p;
151 bzero(outbuf,sizeof(outbuf));
152 p = outbuf+1;
154 /* command type */
155 CVAL(outbuf,0) = command;
157 /* announcement parameters */
158 CVAL(p,0) = updatecount;
159 SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
161 StrnCpy(p+5,server_name,16);
162 strupper(p+5);
164 CVAL(p,21) = MAJOR_VERSION; /* major version */
165 CVAL(p,22) = MINOR_VERSION; /* minor version */
167 SIVAL(p,23,server_type);
168 /* browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT)*/
169 SSVAL(p,27,BROWSER_ELECTION_VERSION);
170 SSVAL(p,29,BROWSER_CONSTANT); /* browse signature */
172 strcpy(p+31,server_comment);
173 p += 31;
174 p = skip_string(p,1);
176 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
178 /* send the announcement */
179 send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,outbuf,
180 PTR_DIFF(p,outbuf),
181 from_name, to_name,
182 from_type, to_type,
183 to_ip, from_ip);
187 /****************************************************************************
188 remove all samba's server entries
189 ****************************************************************************/
190 void remove_my_servers(void)
192 struct subnet_record *d;
193 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
195 struct work_record *work;
196 for (work = d->workgrouplist; work; work = work->next)
198 struct server_record *s;
199 for (s = work->serverlist; s; s = s->next)
201 if (!strequal(myname,s->serv.name)) continue;
202 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
209 /****************************************************************************
210 announce a server entry
211 ****************************************************************************/
212 void announce_server(struct subnet_record *d, struct work_record *work,
213 char *name, char *comment, time_t ttl, int server_type)
215 /* domain type cannot have anything in it that might confuse
216 a client into thinking that the domain is in fact a server.
217 (SV_TYPE_SERVER_UNIX, for example)
219 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
220 BOOL wins_iface = ip_equal(d->bcast_ip, wins_ip);
222 if (wins_iface && server_type != 0)
224 /* wins pseudo-ip interface */
225 if (!AM_MASTER(work))
227 /* non-master announce by unicast to the domain
228 master */
229 if (!lp_wins_support() && *lp_wins_server())
231 /* look up the domain master with the WINS server */
232 queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
233 NAME_QUERY_ANNOUNCE_HOST,
234 work->work_group,0x1b,0,ttl*1000,
235 server_type,name,comment,
236 False, False, ipzero, d->bcast_ip);
238 else
240 /* we are the WINS server, but not the domain master. */
241 /* XXXX we need to look up the domain master in our
242 WINS database list, and do_announce_host(). maybe
243 we could do a name query on the unsuspecting domain
244 master just to make sure it's awake. */
248 /* XXXX any other kinds of announcements we need to consider here?
249 e.g local master browsers... no. local master browsers do
250 local master announcements to their domain master. they even
251 use WINS lookup of the domain master if another wins server
252 is being used!
255 else
257 if (AM_MASTER(work))
259 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
260 inet_ntoa(d->bcast_ip),work->work_group));
262 do_announce_host(ANN_LocalMasterAnnouncement,
263 name , 0x00, d->myip,
264 work->work_group, 0x1e, d->bcast_ip,
265 ttl,
266 name, server_type, comment);
268 DEBUG(3,("sending domain announce to %s for %s\n",
269 inet_ntoa(d->bcast_ip),work->work_group));
271 /* XXXX should we do a domain-announce-kill? */
272 if (server_type != 0)
274 do_announce_host(ANN_DomainAnnouncement,
275 name , 0x00, d->myip,
276 MSBROWSE, 0x01, d->bcast_ip,
277 ttl,
278 work->work_group, server_type ? domain_type : 0,
279 name);
282 else
284 DEBUG(3,("sending host announce to %s for %s(1d)\n",
285 inet_ntoa(d->bcast_ip),work->work_group));
287 do_announce_host(ANN_HostAnnouncement,
288 name , 0x00, d->myip,
289 work->work_group, 0x1d, d->bcast_ip,
290 ttl,
291 name, server_type, comment);
296 /****************************************************************************
297 construct a host announcement unicast
298 **************************************************************************/
299 void announce_host(time_t t)
301 struct subnet_record *d;
302 pstring comment;
303 char *my_name;
305 StrnCpy(comment, lp_serverstring(), 43);
307 my_name = *myname ? myname : "NoName";
309 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
311 struct work_record *work;
313 for (work = d->workgrouplist; work; work = work->next)
315 uint32 stype = work->ServerType;
316 struct server_record *s;
317 BOOL announce = False;
319 /* must work on the code that does announcements at up to
320 30 seconds later if a master browser sends us a request
321 announce.
324 if (work->needannounce) {
325 /* drop back to a max 3 minute announce - this is to prevent a
326 single lost packet from stuffing things up for too long */
327 work->announce_interval = MIN(work->announce_interval,
328 CHECK_TIME_MIN_HOST_ANNCE*60);
329 work->lastannounce_time = t - (work->announce_interval+1);
332 /* announce every minute at first then progress to every 12 mins */
333 if (work->lastannounce_time &&
334 (t - work->lastannounce_time) < work->announce_interval)
335 continue;
337 if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60)
338 work->announce_interval += 60;
340 work->lastannounce_time = t;
342 for (s = work->serverlist; s; s = s->next) {
343 if (strequal(myname, s->serv.name)) {
344 announce = True;
345 break;
349 if (announce) {
350 announce_server(d,work,my_name,comment,
351 work->announce_interval,stype);
354 if (work->needannounce)
356 work->needannounce = False;
357 break;
358 /* sorry: can't do too many announces. do some more later */
364 /* Announce timer. Moved into global static so it can be reset
365 when a machine becomes a master browser. */
366 static time_t announce_timer_last=0;
368 /****************************************************************************
369 Reset the announce_timer so that a master browser announce will be done
370 immediately.
371 ****************************************************************************/
373 void reset_announce_timer()
375 announce_timer_last = 0;
378 /****************************************************************************
379 announce myself as a master to all other domain master browsers.
381 this actually gets done in search_and_sync_workgroups() via the
382 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
383 name query initiated here. see response_name_query()
384 **************************************************************************/
385 void announce_master(time_t t)
387 struct subnet_record *d;
388 struct work_record *work;
389 BOOL am_master = False; /* are we a master of some sort? :-) */
391 if (!announce_timer_last) announce_timer_last = t;
392 if (t-announce_timer_last < CHECK_TIME_MST_ANNOUNCE * 60)
394 DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
395 t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
396 return;
399 if(wins_subnet == 0)
401 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
402 return;
405 announce_timer_last = t;
407 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
409 struct work_record *work;
410 for (work = d->workgrouplist; work; work = work->next)
412 if (AM_MASTER(work))
414 am_master = True;
419 DEBUG(4,( "announce_master: am_master = %d for workgroup %s\n", am_master, lp_workgroup()));
421 if (!am_master) return; /* only proceed if we are a master browser */
423 /* Note that we don't do this if we are domain master browser
424 and that we *only* do this on the WINS subnet. */
426 /* Try and find our workgroup on the WINS subnet */
427 work = find_workgroupstruct(wins_subnet, lp_workgroup(), False);
429 if (work)
431 char *name;
432 int type;
434 if (*lp_domain_controller())
436 /* the domain controller option is used to manually specify
437 the domain master browser to sync with
440 /* XXXX i'm not sure we should be using the domain controller
441 option for this purpose.
444 name = lp_domain_controller();
445 type = 0x20;
447 else
449 /* assume that the domain master browser we want to sync
450 with is our own domain.
452 name = work->work_group;
453 type = 0x1b;
456 /* check the existence of a dmb for this workgroup, and if
457 one exists at the specified ip, sync with it and announce
458 ourselves as a master browser to it
461 if (!lp_wins_support() && *lp_wins_server() )
463 DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
464 name, type, lp_wins_server()));
466 queue_netbios_pkt_wins(ClientNMB,
467 NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
468 name, type, 0,0,0,
469 work->work_group,NULL,
470 False, False, ipzero, ipzero);
472 else if(lp_wins_support())
474 /* We are the WINS server - query ourselves for the dmb name. */
476 struct nmb_name netb_name;
477 struct subnet_record *d = 0;
478 struct name_record *nr = 0;
480 make_nmb_name(&netb_name, name, type, scope);
482 if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
484 DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
485 in our own WINS database.\n", work->work_group));
486 return;
489 /* Check that this isn't one of our addresses (ie. we are not domain master
490 ourselves) */
491 if(ismyip(nr->ip_flgs[0].ip))
493 DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
494 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
495 return;
498 /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
499 NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a
500 remote WINS server. */
502 DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
503 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
505 queue_netbios_packet(wins_subnet, ClientNMB,
506 NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
507 name, type, 0,0,0,
508 work->work_group,NULL,
509 False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip);
515 /****************************************************************************
516 do all the "remote" announcements. These are used to put ourselves
517 on a remote browse list. They are done blind, no checking is done to
518 see if there is actually a browse master at the other end.
519 **************************************************************************/
520 void announce_remote(time_t t)
522 char *s,*ptr;
523 static time_t last_time = 0;
524 pstring s2;
525 struct in_addr addr;
526 char *comment,*workgroup;
527 int stype = DFLT_SERVER_TYPE;
529 if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
530 return;
532 last_time = t;
534 s = lp_remote_announce();
535 if (!*s) return;
537 comment = lp_serverstring();
538 workgroup = lp_workgroup();
540 for (ptr=s; next_token(&ptr,s2,NULL); ) {
541 /* the entries are of the form a.b.c.d/WORKGROUP with
542 WORKGROUP being optional */
543 char *wgroup;
545 wgroup = strchr(s2,'/');
546 if (wgroup) *wgroup++ = 0;
547 if (!wgroup || !*wgroup)
548 wgroup = workgroup;
550 addr = *interpret_addr2(s2);
552 do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
553 wgroup,0x1e,addr,
554 REMOTE_ANNOUNCE_INTERVAL,
555 myname,stype,comment);