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
;
42 extern char **my_netbios_names
;
44 extern int ClientDGRAM
;
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
;
57 /****************************************************************************
58 send a announce request to the local net
59 **************************************************************************/
60 void announce_request(struct work_record
*work
, struct in_addr ip
)
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
));
74 CVAL(p
,0) = ANN_AnnouncementRequest
;
77 CVAL(p
,0) = work
->token
; /* (local) unique workgroup token id */
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
,
98 int to
, struct in_addr dest_ip
)
103 bzero(outbuf
,sizeof(outbuf
));
105 CVAL(p
,0) = announce_type
;
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
));
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
,
127 struct subnet_record
*d
,
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
,
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
)
156 bzero(outbuf
,sizeof(outbuf
));
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);
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
);
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
,
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
);
229 DEBUG(0,("announce_server: error - announcement requested on WINS \
230 interface for workgroup %s, name %s\n", work
->work_group
, name
));
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
,
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
,
257 work
->work_group
, server_type
? domain_type
: 0,
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
,
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
;
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
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
)
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
;
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
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 ));
376 if(wins_client_subnet
== NULL
)
378 DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
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
)
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
);
407 /* assume that the domain master browser we want to sync
408 with is our own domain.
410 char *name
= work
->work_group
;
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
,
426 work
->work_group
,NULL
,
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
));
445 /* Check that this isn't one of our addresses (ie. we are not domain master
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
) ));
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
,
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
)
478 static time_t last_time
= 0;
481 char *comment
,*workgroup
;
482 int stype
= lp_default_server_announce();
484 if (last_time
&& t
< last_time
+ REMOTE_ANNOUNCE_INTERVAL
)
489 s
= lp_remote_announce();
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 */
502 wgroup
= strchr(s2
,'/');
503 if (wgroup
) *wgroup
++ = 0;
504 if (!wgroup
|| !*wgroup
)
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
),
515 REMOTE_ANNOUNCE_INTERVAL
,