a simple SMB torture tester. This will allow us to evaluate locking
[Samba.git] / source / nameannounce.c
blob03fab914debdc2f2efc95a5d0bae4e62dfb14d2f
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 SMB Version handling
8 Copyright (C) John H Terpstra 1995-1997
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 Revision History:
26 14 jan 96: lkcl@pires.co.uk
27 added multiple workgroup domain master support
31 #include "includes.h"
33 #define TEST_CODE
35 extern int DEBUGLEVEL;
36 extern BOOL CanRecurse;
38 extern struct in_addr ipzero;
40 extern pstring myname;
41 extern fstring myworkgroup;
42 extern char **my_netbios_names;
44 extern int ClientDGRAM;
45 extern int ClientNMB;
47 /* this is our domain/workgroup/server database */
48 extern struct subnet_record *subnetlist;
50 extern int updatecount;
51 extern int workgroup_count;
53 extern struct in_addr wins_ip;
55 extern pstring scope;
57 /****************************************************************************
58 send a announce request to the local net
59 **************************************************************************/
60 void announce_request(struct work_record *work, struct in_addr ip)
62 pstring outbuf;
63 char *p;
65 if (!work) return;
67 work->needannounce = True;
69 DEBUG(2,("sending announce request to %s for workgroup %s\n",
70 inet_ntoa(ip),work->work_group));
72 bzero(outbuf,sizeof(outbuf));
73 p = outbuf;
74 CVAL(p,0) = ANN_AnnouncementRequest;
75 p++;
77 CVAL(p,0) = work->token; /* (local) unique workgroup token id */
78 p++;
79 StrnCpy(p,myname,16);
80 strupper(p);
81 p = skip_string(p,1);
83 /* XXXX note: if we sent the announcement request to 0x1d instead
84 of 0x1e, then we could get the master browser to announce to
85 us instead of the members of the workgroup. wha-hey! */
87 send_mailslot_reply(False, BROWSE_MAILSLOT, ClientDGRAM,
88 outbuf,PTR_DIFF(p,outbuf),
89 myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
93 /****************************************************************************
94 request an announcement
95 **************************************************************************/
96 void do_announce_request(char *info, char *to_name, int announce_type,
97 int from,
98 int to, struct in_addr dest_ip)
100 pstring outbuf;
101 char *p;
103 bzero(outbuf,sizeof(outbuf));
104 p = outbuf;
105 CVAL(p,0) = announce_type;
106 p++;
108 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
109 announce_type, info, inet_ntoa(dest_ip),to_name,to));
111 StrnCpy(p,info,16);
112 strupper(p);
113 p = skip_string(p,1);
115 send_mailslot_reply(False,BROWSE_MAILSLOT, ClientDGRAM,
116 outbuf,PTR_DIFF(p,outbuf),
117 myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
121 /****************************************************************************
122 find a server responsible for a workgroup, and sync browse lists
123 control ends up back here via response_name_query.
124 **************************************************************************/
125 void sync_server(enum state_type state, char *serv_name, char *work_name,
126 int name_type,
127 struct subnet_record *d,
128 struct in_addr ip)
130 /* with a domain master we can get the whole list (not local only list) */
131 BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
133 add_browser_entry(serv_name, name_type, work_name, 0, d, ip, local_only);
135 if (state == NAME_STATUS_DOM_SRV_CHK)
137 /* announce ourselves as a master browser to serv_name */
138 do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
139 0x20, 0, ip);
144 /****************************************************************************
145 send a host announcement packet
146 **************************************************************************/
147 static void do_announce_host(int command,
148 char *from_name, int from_type, struct in_addr from_ip,
149 char *to_name , int to_type , struct in_addr to_ip,
150 time_t announce_interval,
151 char *server_name, int server_type, char *server_comment)
153 pstring outbuf;
154 char *p;
156 bzero(outbuf,sizeof(outbuf));
157 p = outbuf+1;
159 /* command type */
160 CVAL(outbuf,0) = command;
162 /* announcement parameters */
163 CVAL(p,0) = updatecount;
164 SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
166 StrnCpy(p+5,server_name,16);
167 strupper(p+5);
169 CVAL(p,21) = lp_major_announce_version(); /* major version */
170 CVAL(p,22) = lp_minor_announce_version(); /* minor version */
172 SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
173 /* browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT)*/
174 SSVAL(p,27,BROWSER_ELECTION_VERSION);
175 SSVAL(p,29,BROWSER_CONSTANT); /* browse signature */
177 pstrcpy(p+31,server_comment);
178 p += 31;
179 p = skip_string(p,1);
181 debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
183 /* send the announcement */
184 send_mailslot_reply(False,BROWSE_MAILSLOT, ClientDGRAM, outbuf,
185 PTR_DIFF(p,outbuf),
186 from_name, to_name,
187 from_type, to_type,
188 to_ip, from_ip);
192 /****************************************************************************
193 announce all samba's server entries as 'gone'.
194 ****************************************************************************/
195 void announce_my_servers_removed(void)
197 struct subnet_record *d;
198 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
200 struct work_record *work;
201 for (work = d->workgrouplist; work; work = work->next)
203 struct server_record *s;
204 for (s = work->serverlist; s; s = s->next)
206 if (!is_myname(s->serv.name)) continue;
207 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
214 /****************************************************************************
215 announce a server entry
216 ****************************************************************************/
217 void announce_server(struct subnet_record *d, struct work_record *work,
218 char *name, char *comment, time_t ttl, int server_type)
220 /* domain type cannot have anything in it that might confuse
221 a client into thinking that the domain is in fact a server.
222 (SV_TYPE_SERVER_UNIX, for example)
224 uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
225 BOOL wins_iface = ip_equal(d->bcast_ip, wins_ip);
227 if(wins_iface)
229 DEBUG(0,("announce_server: error - announcement requested on WINS \
230 interface for workgroup %s, name %s\n", work->work_group, name));
231 return;
234 /* Only do domain announcements if we are a master and it's
235 our name we're being asked to announce. */
236 if (AM_MASTER(work) && strequal(myname,name))
238 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
239 inet_ntoa(d->bcast_ip),work->work_group));
241 do_announce_host(ANN_LocalMasterAnnouncement,
242 name , 0x00, d->myip,
243 work->work_group, 0x1e, d->bcast_ip,
244 ttl,
245 name, server_type, comment);
247 DEBUG(3,("sending domain announce to %s for %s\n",
248 inet_ntoa(d->bcast_ip),work->work_group));
250 /* XXXX should we do a domain-announce-kill? */
251 if (server_type != 0)
253 do_announce_host(ANN_DomainAnnouncement,
254 name , 0x00, d->myip,
255 MSBROWSE, 0x01, d->bcast_ip,
256 ttl,
257 work->work_group, server_type ? domain_type : 0,
258 name);
261 else
263 DEBUG(3,("sending host announce to %s for %s(1d)\n",
264 inet_ntoa(d->bcast_ip),work->work_group));
266 do_announce_host(ANN_HostAnnouncement,
267 name , 0x00, d->myip,
268 work->work_group, 0x1d, d->bcast_ip,
269 ttl,
270 name, server_type, comment);
274 /****************************************************************************
275 construct a host announcement unicast
276 **************************************************************************/
277 void announce_host(time_t t)
279 struct subnet_record *d;
280 pstring comment;
281 char *my_name;
283 StrnCpy(comment, lp_serverstring(), 43);
285 my_name = *myname ? myname : "NoName";
287 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
289 struct work_record *work;
291 for (work = d->workgrouplist; work; work = work->next)
293 uint32 stype = work->ServerType;
294 struct server_record *s;
296 /* must work on the code that does announcements at up to
297 30 seconds later if a master browser sends us a request
298 announce.
301 if (work->needannounce) {
302 /* drop back to a max 3 minute announce - this is to prevent a
303 single lost packet from stuffing things up for too long */
304 work->announce_interval = MIN(work->announce_interval,
305 CHECK_TIME_MIN_HOST_ANNCE*60);
306 work->lastannounce_time = t - (work->announce_interval+1);
309 /* announce every minute at first then progress to every 12 mins */
310 if (work->lastannounce_time &&
311 (t - work->lastannounce_time) < work->announce_interval)
312 continue;
314 if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60)
315 work->announce_interval += 60;
317 work->lastannounce_time = t;
319 for (s = work->serverlist; s; s = s->next) {
320 if (is_myname(s->serv.name)) {
321 /* If we are any kind of browser or logon server, only
322 announce it for our primary name, not our aliases. */
323 if(!strequal(myname, s->serv.name))
324 stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|
325 SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
326 announce_server(d,work,s->serv.name,comment,
327 work->announce_interval,stype);
331 if (work->needannounce)
333 work->needannounce = False;
334 break;
335 /* sorry: can't do too many announces. do some more later */
341 /* Announce timer. Moved into global static so it can be reset
342 when a machine becomes a master browser. */
343 static time_t announce_timer_last=0;
345 /****************************************************************************
346 Reset the announce_timer so that a master browser announce will be done
347 immediately.
348 ****************************************************************************/
350 void reset_announce_timer()
352 announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
355 /****************************************************************************
356 announce myself as a master to all other domain master browsers.
358 this actually gets done in search_and_sync_workgroups() via the
359 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
360 name query initiated here. see response_name_query()
361 **************************************************************************/
362 void announce_master(time_t t)
364 struct subnet_record *d;
365 struct work_record *work;
366 BOOL am_master = False; /* are we a master of some sort? :-) */
368 if (!announce_timer_last) announce_timer_last = t;
369 if (t-announce_timer_last < CHECK_TIME_MST_ANNOUNCE * 60)
371 DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
372 t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
373 return;
376 if(wins_client_subnet == NULL)
378 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
379 return;
382 announce_timer_last = t;
384 for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
386 for (work = d->workgrouplist; work; work = work->next)
388 if (AM_MASTER(work))
390 am_master = True;
391 DEBUG(4,( "announce_master: am_master = %d for \
392 workgroup %s\n", am_master, work->work_group));
397 if (!am_master) return; /* only proceed if we are a master browser */
399 /* Note that we don't do this if we are domain master browser
400 and that we *only* do this on the WINS subnet. */
402 /* Try and find our workgroup on the WINS subnet */
403 work = find_workgroupstruct(wins_client_subnet, myworkgroup, False);
405 if (work)
407 /* assume that the domain master browser we want to sync
408 with is our own domain.
410 char *name = work->work_group;
411 int type = 0x1b;
413 /* check the existence of a dmb for this workgroup, and if
414 one exists at the specified ip, sync with it and announce
415 ourselves as a master browser to it
418 if (!lp_wins_support() && *lp_wins_server() )
420 DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
421 name, type, lp_wins_server()));
423 queue_netbios_pkt_wins(ClientNMB,
424 NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
425 name, type, 0,0,0,
426 work->work_group,NULL,
427 ipzero, ipzero);
429 else if(lp_wins_support())
431 /* We are the WINS server - query ourselves for the dmb name. */
433 struct nmb_name netb_name;
434 struct name_record *nr = 0;
436 make_nmb_name(&netb_name, name, type, scope);
438 if ((nr = find_name_on_subnet(wins_client_subnet, &netb_name, FIND_ANY_NAME)) == 0)
440 DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
441 in our own WINS database.\n", work->work_group));
442 return;
445 /* Check that this isn't one of our addresses (ie. we are not domain master
446 ourselves) */
447 if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
449 DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
450 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
451 return;
454 /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
455 NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a
456 remote WINS server. */
458 DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
459 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
461 queue_netbios_packet(wins_client_subnet, ClientNMB,
462 NMB_STATUS,NAME_STATUS_DOM_SRV_CHK,
463 name, type, 0,0,0,
464 work->work_group,NULL,
465 False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip, 0);
470 /****************************************************************************
471 do all the "remote" announcements. These are used to put ourselves
472 on a remote browse list. They are done blind, no checking is done to
473 see if there is actually a browse master at the other end.
474 **************************************************************************/
475 void announce_remote(time_t t)
477 char *s,*ptr;
478 static time_t last_time = 0;
479 pstring s2;
480 struct in_addr addr;
481 char *comment,*workgroup;
482 int stype = lp_default_server_announce();
484 if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
485 return;
487 last_time = t;
489 s = lp_remote_announce();
490 if (!*s) return;
492 comment = lp_serverstring();
493 workgroup = myworkgroup;
495 for (ptr=s; next_token(&ptr,s2,NULL); )
497 /* the entries are of the form a.b.c.d/WORKGROUP with
498 WORKGROUP being optional */
499 char *wgroup;
500 int n;
502 wgroup = strchr(s2,'/');
503 if (wgroup) *wgroup++ = 0;
504 if (!wgroup || !*wgroup)
505 wgroup = workgroup;
507 addr = *interpret_addr2(s2);
509 /* Announce all our names including aliases */
510 for (n=0; my_netbios_names[n]; n++)
512 char *name = my_netbios_names[n];
513 do_announce_host(ANN_HostAnnouncement,name,0x20,*iface_ip(addr),
514 wgroup,0x1e,addr,
515 REMOTE_ANNOUNCE_INTERVAL,
516 name,stype,comment);