2 Unix SMB/Netbios implementation.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1997
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.
26 14 jan 96: lkcl@pires.co.uk
27 added multiple workgroup domain master support
35 extern int DEBUGLEVEL
;
36 extern BOOL CanRecurse
;
38 extern struct in_addr ipzero
;
40 extern pstring myname
;
41 extern fstring myworkgroup
;
43 extern int ClientDGRAM
;
46 /* this is our domain/workgroup/server database */
47 extern struct subnet_record
*subnetlist
;
49 extern int updatecount
;
50 extern int workgroup_count
;
52 extern struct in_addr wins_ip
;
56 /****************************************************************************
57 send a announce request to the local net
58 **************************************************************************/
59 void announce_request(struct work_record
*work
, struct in_addr ip
)
66 work
->needannounce
= True
;
68 DEBUG(2,("sending announce request to %s for workgroup %s\n",
69 inet_ntoa(ip
),work
->work_group
));
71 bzero(outbuf
,sizeof(outbuf
));
73 CVAL(p
,0) = ANN_AnnouncementRequest
;
76 CVAL(p
,0) = work
->token
; /* (local) unique workgroup token id */
82 /* XXXX note: if we sent the announcement request to 0x1d instead
83 of 0x1e, then we could get the master browser to announce to
84 us instead of the members of the workgroup. wha-hey! */
86 send_mailslot_reply(False
, BROWSE_MAILSLOT
,ClientDGRAM
,
87 outbuf
,PTR_DIFF(p
,outbuf
),
88 myname
,work
->work_group
,0x20,0x1e,ip
,*iface_ip(ip
));
92 /****************************************************************************
93 request an announcement
94 **************************************************************************/
95 void do_announce_request(char *info
, char *to_name
, int announce_type
,
97 int to
, struct in_addr dest_ip
)
102 bzero(outbuf
,sizeof(outbuf
));
104 CVAL(p
,0) = announce_type
;
107 DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
108 announce_type
, info
, inet_ntoa(dest_ip
),to_name
,to
));
112 p
= skip_string(p
,1);
114 send_mailslot_reply(False
,BROWSE_MAILSLOT
,ClientDGRAM
,
115 outbuf
,PTR_DIFF(p
,outbuf
),
116 myname
,to_name
,from
,to
,dest_ip
,*iface_ip(dest_ip
));
120 /****************************************************************************
121 find a server responsible for a workgroup, and sync browse lists
122 control ends up back here via response_name_query.
123 **************************************************************************/
124 void sync_server(enum state_type state
, char *serv_name
, char *work_name
,
126 struct subnet_record
*d
,
129 /* with a domain master we can get the whole list (not local only list) */
130 BOOL local_only
= (state
!= NAME_STATUS_DOM_SRV_CHK
);
132 add_browser_entry(serv_name
, name_type
, work_name
, 0, d
, ip
, local_only
);
134 if (state
== NAME_STATUS_DOM_SRV_CHK
)
136 /* announce ourselves as a master browser to serv_name */
137 do_announce_request(myname
, serv_name
, ANN_MasterAnnouncement
,
143 /****************************************************************************
144 send a host announcement packet
145 **************************************************************************/
146 void do_announce_host(int command
,
147 char *from_name
, int from_type
, struct in_addr from_ip
,
148 char *to_name
, int to_type
, struct in_addr to_ip
,
149 time_t announce_interval
,
150 char *server_name
, int server_type
, char *server_comment
)
155 bzero(outbuf
,sizeof(outbuf
));
159 CVAL(outbuf
,0) = command
;
161 /* announcement parameters */
162 CVAL(p
,0) = updatecount
;
163 SIVAL(p
,1,announce_interval
*1000); /* ms - despite the spec */
165 StrnCpy(p
+5,server_name
,16);
168 CVAL(p
,21) = MAJOR_VERSION
; /* major version */
169 CVAL(p
,22) = MINOR_VERSION
; /* minor version */
171 SIVAL(p
,23,server_type
& ~SV_TYPE_LOCAL_LIST_ONLY
);
172 /* browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT)*/
173 SSVAL(p
,27,BROWSER_ELECTION_VERSION
);
174 SSVAL(p
,29,BROWSER_CONSTANT
); /* browse signature */
176 strcpy(p
+31,server_comment
);
178 p
= skip_string(p
,1);
180 debug_browse_data(outbuf
, PTR_DIFF(p
,outbuf
));
182 /* send the announcement */
183 send_mailslot_reply(False
,BROWSE_MAILSLOT
,ClientDGRAM
,outbuf
,
191 /****************************************************************************
192 announce all samba's server entries as 'gone'.
193 ****************************************************************************/
194 void announce_my_servers_removed(void)
196 struct subnet_record
*d
;
197 for (d
= FIRST_SUBNET
; d
; d
= NEXT_SUBNET_EXCLUDING_WINS(d
))
199 struct work_record
*work
;
200 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
202 struct server_record
*s
;
203 for (s
= work
->serverlist
; s
; s
= s
->next
)
205 if (!strequal(myname
,s
->serv
.name
)) continue;
206 announce_server(d
, work
, s
->serv
.name
, s
->serv
.comment
, 0, 0);
213 /****************************************************************************
214 announce a server entry
215 ****************************************************************************/
216 void announce_server(struct subnet_record
*d
, struct work_record
*work
,
217 char *name
, char *comment
, time_t ttl
, int server_type
)
219 /* domain type cannot have anything in it that might confuse
220 a client into thinking that the domain is in fact a server.
221 (SV_TYPE_SERVER_UNIX, for example)
223 uint32 domain_type
= SV_TYPE_DOMAIN_ENUM
|SV_TYPE_NT
;
224 BOOL wins_iface
= ip_equal(d
->bcast_ip
, wins_ip
);
228 DEBUG(0,("announce_server: error - announcement requested on WINS \
229 interface for workgroup %s, name %s\n", work
->work_group
, name
));
235 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
236 inet_ntoa(d
->bcast_ip
),work
->work_group
));
238 do_announce_host(ANN_LocalMasterAnnouncement
,
239 name
, 0x00, d
->myip
,
240 work
->work_group
, 0x1e, d
->bcast_ip
,
242 name
, server_type
, comment
);
244 DEBUG(3,("sending domain announce to %s for %s\n",
245 inet_ntoa(d
->bcast_ip
),work
->work_group
));
247 /* XXXX should we do a domain-announce-kill? */
248 if (server_type
!= 0)
250 do_announce_host(ANN_DomainAnnouncement
,
251 name
, 0x00, d
->myip
,
252 MSBROWSE
, 0x01, d
->bcast_ip
,
254 work
->work_group
, server_type
? domain_type
: 0,
260 DEBUG(3,("sending host announce to %s for %s(1d)\n",
261 inet_ntoa(d
->bcast_ip
),work
->work_group
));
263 do_announce_host(ANN_HostAnnouncement
,
264 name
, 0x00, d
->myip
,
265 work
->work_group
, 0x1d, d
->bcast_ip
,
267 name
, server_type
, comment
);
271 /****************************************************************************
272 construct a host announcement unicast
273 **************************************************************************/
274 void announce_host(time_t t
)
276 struct subnet_record
*d
;
280 StrnCpy(comment
, lp_serverstring(), 43);
282 my_name
= *myname
? myname
: "NoName";
284 for (d
= FIRST_SUBNET
; d
; d
= NEXT_SUBNET_EXCLUDING_WINS(d
))
286 struct work_record
*work
;
288 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
290 uint32 stype
= work
->ServerType
;
291 struct server_record
*s
;
292 BOOL announce
= False
;
294 /* must work on the code that does announcements at up to
295 30 seconds later if a master browser sends us a request
299 if (work
->needannounce
) {
300 /* drop back to a max 3 minute announce - this is to prevent a
301 single lost packet from stuffing things up for too long */
302 work
->announce_interval
= MIN(work
->announce_interval
,
303 CHECK_TIME_MIN_HOST_ANNCE
*60);
304 work
->lastannounce_time
= t
- (work
->announce_interval
+1);
307 /* announce every minute at first then progress to every 12 mins */
308 if (work
->lastannounce_time
&&
309 (t
- work
->lastannounce_time
) < work
->announce_interval
)
312 if (work
->announce_interval
< CHECK_TIME_MAX_HOST_ANNCE
* 60)
313 work
->announce_interval
+= 60;
315 work
->lastannounce_time
= t
;
317 for (s
= work
->serverlist
; s
; s
= s
->next
) {
318 if (strequal(myname
, s
->serv
.name
)) {
325 announce_server(d
,work
,my_name
,comment
,
326 work
->announce_interval
,stype
);
329 if (work
->needannounce
)
331 work
->needannounce
= False
;
333 /* sorry: can't do too many announces. do some more later */
339 /* Announce timer. Moved into global static so it can be reset
340 when a machine becomes a master browser. */
341 static time_t announce_timer_last
=0;
343 /****************************************************************************
344 Reset the announce_timer so that a master browser announce will be done
346 ****************************************************************************/
348 void reset_announce_timer()
350 announce_timer_last
= time(NULL
) - (CHECK_TIME_MST_ANNOUNCE
* 60);
353 /****************************************************************************
354 announce myself as a master to all other domain master browsers.
356 this actually gets done in search_and_sync_workgroups() via the
357 NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
358 name query initiated here. see response_name_query()
359 **************************************************************************/
360 void announce_master(time_t t
)
362 struct subnet_record
*d
;
363 struct work_record
*work
;
364 BOOL am_master
= False
; /* are we a master of some sort? :-) */
366 if (!announce_timer_last
) announce_timer_last
= t
;
367 if (t
-announce_timer_last
< CHECK_TIME_MST_ANNOUNCE
* 60)
369 DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
370 t
, announce_timer_last
, CHECK_TIME_MST_ANNOUNCE
* 60 ));
374 if(wins_subnet
== NULL
)
376 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
380 announce_timer_last
= t
;
382 for (d
= FIRST_SUBNET
; d
; d
= NEXT_SUBNET_EXCLUDING_WINS(d
))
384 struct work_record
*work
;
385 for (work
= d
->workgrouplist
; work
; work
= work
->next
)
390 DEBUG(4,( "announce_master: am_master = %d for \
391 workgroup %s\n", am_master
, work
->work_group
));
396 if (!am_master
) return; /* only proceed if we are a master browser */
398 /* Note that we don't do this if we are domain master browser
399 and that we *only* do this on the WINS subnet. */
401 /* Try and find our workgroup on the WINS subnet */
402 work
= find_workgroupstruct(wins_subnet
, myworkgroup
, False
);
410 /* assume that the domain master browser we want to sync
411 with is our own domain.
413 name
= work
->work_group
;
417 /* check the existence of a dmb for this workgroup, and if
418 one exists at the specified ip, sync with it and announce
419 ourselves as a master browser to it
422 if (!lp_wins_support() && *lp_wins_server() )
424 DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
425 name
, type
, lp_wins_server()));
427 queue_netbios_pkt_wins(ClientNMB
,
428 NMB_QUERY
,NAME_QUERY_DOM_SRV_CHK
,
430 work
->work_group
,NULL
,
431 False
, True
, ipzero
, ipzero
);
433 else if(lp_wins_support())
435 /* We are the WINS server - query ourselves for the dmb name. */
437 struct nmb_name netb_name
;
438 struct subnet_record
*d
= 0;
439 struct name_record
*nr
= 0;
441 make_nmb_name(&netb_name
, name
, type
, scope
);
443 if ((nr
= find_name_search(&d
, &netb_name
, FIND_WINS
, ipzero
)) == 0)
445 DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
446 in our own WINS database.\n", work
->work_group
));
450 /* Check that this isn't one of our addresses (ie. we are not domain master
452 if(ismyip(nr
->ip_flgs
[0].ip
) || ip_equal(nr
->ip_flgs
[0].ip
, ipzero
))
454 DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
455 is one of our interfaces.\n", work
->work_group
, inet_ntoa(nr
->ip_flgs
[0].ip
) ));
459 /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
460 NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a
461 remote WINS server. */
463 DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
464 for workgroup %s\n", name
, type
, inet_ntoa(nr
->ip_flgs
[0].ip
), work
->work_group
));
466 queue_netbios_packet(wins_subnet
, ClientNMB
,
467 NMB_QUERY
,NAME_STATUS_DOM_SRV_CHK
,
469 work
->work_group
,NULL
,
470 False
, False
, nr
->ip_flgs
[0].ip
, nr
->ip_flgs
[0].ip
);
476 /****************************************************************************
477 do all the "remote" announcements. These are used to put ourselves
478 on a remote browse list. They are done blind, no checking is done to
479 see if there is actually a browse master at the other end.
480 **************************************************************************/
481 void announce_remote(time_t t
)
484 static time_t last_time
= 0;
487 char *comment
,*workgroup
;
488 int stype
= DFLT_SERVER_TYPE
;
490 if (last_time
&& t
< last_time
+ REMOTE_ANNOUNCE_INTERVAL
)
495 s
= lp_remote_announce();
498 comment
= lp_serverstring();
499 workgroup
= myworkgroup
;
501 for (ptr
=s
; next_token(&ptr
,s2
,NULL
); ) {
502 /* the entries are of the form a.b.c.d/WORKGROUP with
503 WORKGROUP being optional */
506 wgroup
= strchr(s2
,'/');
507 if (wgroup
) *wgroup
++ = 0;
508 if (!wgroup
|| !*wgroup
)
511 addr
= *interpret_addr2(s2
);
513 do_announce_host(ANN_HostAnnouncement
,myname
,0x20,*iface_ip(addr
),
515 REMOTE_ANNOUNCE_INTERVAL
,
516 myname
,stype
,comment
);