2 Unix SMB/Netbios implementation.
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.
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 wins_ip
;
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(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
,
93 int to
, struct in_addr dest_ip
)
98 bzero(outbuf
,sizeof(outbuf
));
100 CVAL(p
,0) = announce_type
;
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
));
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
,
122 struct subnet_record
*d
,
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
,
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
)
151 bzero(outbuf
,sizeof(outbuf
));
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);
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
);
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
,
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
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
);
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
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
,
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
,
278 work
->work_group
, server_type
? domain_type
: 0,
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
,
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
;
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
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
)
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
)) {
350 announce_server(d
,work
,my_name
,comment
,
351 work
->announce_interval
,stype
);
354 if (work
->needannounce
)
356 work
->needannounce
= False
;
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
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 ));
401 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
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
)
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
);
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();
449 /* assume that the domain master browser we want to sync
450 with is our own domain.
452 name
= work
->work_group
;
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
,
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
));
489 /* Check that this isn't one of our addresses (ie. we are not domain master
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
) ));
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
,
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
)
523 static time_t last_time
= 0;
526 char *comment
,*workgroup
;
527 int stype
= DFLT_SERVER_TYPE
;
529 if (last_time
&& t
< last_time
+ REMOTE_ANNOUNCE_INTERVAL
)
534 s
= lp_remote_announce();
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 */
545 wgroup
= strchr(s2
,'/');
546 if (wgroup
) *wgroup
++ = 0;
547 if (!wgroup
|| !*wgroup
)
550 addr
= *interpret_addr2(s2
);
552 do_announce_host(ANN_HostAnnouncement
,myname
,0x20,*iface_ip(addr
),
554 REMOTE_ANNOUNCE_INTERVAL
,
555 myname
,stype
,comment
);