s3:param: add a utility function lp_idmap_range() to get the configured range for...
[Samba/gbeck.git] / source3 / nmbd / nmbd_browsesync.c
blob8bc28ad301bb93665a3ef61c179d4354377caa34
1 /*
2 Unix SMB/CIFS implementation.
3 NBT netbios routines and daemon - version 2
4 Copyright (C) Andrew Tridgell 1994-1998
5 Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6 Copyright (C) Jeremy Allison 1994-2003
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "nmbd/nmbd.h"
26 /* This is our local master browser list database. */
27 extern struct browse_cache_record *lmb_browserlist;
29 /****************************************************************************
30 As a domain master browser, do a sync with a local master browser.
31 **************************************************************************/
33 static void sync_with_lmb(struct browse_cache_record *browc)
35 struct work_record *work;
37 if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) ) {
38 if( DEBUGLVL( 0 ) ) {
39 dbgtext( "sync_with_lmb:\n" );
40 dbgtext( "Failed to get a workgroup for a local master browser " );
41 dbgtext( "cache entry workgroup " );
42 dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
44 return;
47 /* We should only be doing this if we are a domain master browser for
48 the given workgroup. Ensure this is so. */
50 if(!AM_DOMAIN_MASTER_BROWSER(work)) {
51 if( DEBUGLVL( 0 ) ) {
52 dbgtext( "sync_with_lmb:\n" );
53 dbgtext( "We are trying to sync with a local master browser " );
54 dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
55 dbgtext( "and we are not a domain master browser on this workgroup.\n" );
56 dbgtext( "Error!\n" );
58 return;
61 if( DEBUGLVL( 2 ) ) {
62 dbgtext( "sync_with_lmb:\n" );
63 dbgtext( "Initiating sync with local master browser " );
64 dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
65 dbgtext( "for workgroup %s\n", browc->work_group );
68 sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
70 browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
73 /****************************************************************************
74 Sync or expire any local master browsers.
75 **************************************************************************/
77 void dmb_expire_and_sync_browser_lists(time_t t)
79 static time_t last_run = 0;
80 struct browse_cache_record *browc;
82 /* Only do this every 20 seconds. */
83 if (t - last_run < 20)
84 return;
86 last_run = t;
88 expire_lmb_browsers(t);
90 for( browc = lmb_browserlist; browc; browc = browc->next ) {
91 if (browc->sync_time < t)
92 sync_with_lmb(browc);
96 /****************************************************************************
97 As a local master browser, send an announce packet to the domain master browser.
98 **************************************************************************/
100 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
102 char outbuf[1024];
103 unstring myname;
104 unstring dmb_name;
105 char *p;
107 if(ismyip_v4(work->dmb_addr)) {
108 if( DEBUGLVL( 2 ) ) {
109 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
110 dbgtext( "We are both a domain and a local master browser for " );
111 dbgtext( "workgroup %s. ", work->work_group );
112 dbgtext( "Do not announce to ourselves.\n" );
114 return;
117 memset(outbuf,'\0',sizeof(outbuf));
118 p = outbuf;
119 SCVAL(p,0,ANN_MasterAnnouncement);
120 p++;
122 unstrcpy(myname, lp_netbios_name());
123 if (!strupper_m(myname)) {
124 DEBUG(2,("strupper_m %s failed\n", myname));
125 return;
127 myname[15]='\0';
128 /* The call below does CH_UNIX -> CH_DOS conversion. JRA */
129 push_ascii(p, myname, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
131 p = skip_string(outbuf,sizeof(outbuf),p);
133 if( DEBUGLVL( 4 ) ) {
134 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
135 dbgtext( "Sending local master announce to " );
136 dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
137 work->work_group );
140 /* Target name for send_mailslot must be in UNIX charset. */
141 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
142 send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
143 lp_netbios_name(), 0x0, dmb_name, 0x0,
144 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
147 /****************************************************************************
148 As a local master browser, do a sync with a domain master browser.
149 **************************************************************************/
151 static void sync_with_dmb(struct work_record *work)
153 unstring dmb_name;
155 if( DEBUGLVL( 2 ) ) {
156 dbgtext( "sync_with_dmb:\n" );
157 dbgtext( "Initiating sync with domain master browser " );
158 dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
159 dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
160 dbgtext( "for workgroup %s\n", work->work_group );
163 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
164 sync_browse_lists(work, dmb_name, work->dmb_name.name_type,
165 work->dmb_addr, False, True);
168 /****************************************************************************
169 Function called when a node status query to a domain master browser IP succeeds.
170 ****************************************************************************/
172 static void domain_master_node_status_success(struct subnet_record *subrec,
173 struct userdata_struct *userdata,
174 struct res_rec *answers,
175 struct in_addr from_ip)
177 struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
179 if( work == NULL ) {
180 if( DEBUGLVL( 0 ) ) {
181 dbgtext( "domain_master_node_status_success:\n" );
182 dbgtext( "Unable to find workgroup " );
183 dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
185 return;
188 if( DEBUGLVL( 3 ) ) {
189 dbgtext( "domain_master_node_status_success:\n" );
190 dbgtext( "Success in node status for workgroup " );
191 dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
194 /* Go through the list of names found at answers->rdata and look for
195 the first SERVER<0x20> name. */
197 if(answers->rdata != NULL) {
198 char *p = answers->rdata;
199 int numnames = CVAL(p, 0);
201 p += 1;
203 while (numnames--) {
204 unstring qname;
205 uint16 nb_flags;
206 int name_type;
208 pull_ascii_nstring(qname, sizeof(qname), p);
209 name_type = CVAL(p,15);
210 nb_flags = get_nb_flags(&p[16]);
211 trim_char(qname,'\0',' ');
213 p += 18;
215 if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) {
216 struct nmb_name nmbname;
218 make_nmb_name(&nmbname, qname, name_type);
220 /* Copy the dmb name and IP address
221 into the workgroup struct. */
223 work->dmb_name = nmbname;
224 putip((char *)&work->dmb_addr, &from_ip);
226 /* Do the local master browser announcement to the domain
227 master browser name and IP. */
228 announce_local_master_browser_to_domain_master_browser( work );
230 /* Now synchronise lists with the domain master browser. */
231 sync_with_dmb(work);
232 break;
235 } else if( DEBUGLVL( 0 ) ) {
236 dbgtext( "domain_master_node_status_success:\n" );
237 dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
238 dbgtext( "%s.\n", inet_ntoa(from_ip) );
242 /****************************************************************************
243 Function called when a node status query to a domain master browser IP fails.
244 ****************************************************************************/
246 static void domain_master_node_status_fail(struct subnet_record *subrec,
247 struct response_record *rrec)
249 struct userdata_struct *userdata = rrec->userdata;
251 if( DEBUGLVL( 0 ) ) {
252 dbgtext( "domain_master_node_status_fail:\n" );
253 dbgtext( "Doing a node status request to the domain master browser\n" );
254 dbgtext( "for workgroup %s ", userdata ? userdata->data : "NULL" );
255 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
256 dbgtext( "Cannot sync browser lists.\n" );
260 /****************************************************************************
261 Function called when a query for a WORKGROUP<1b> name succeeds.
262 ****************************************************************************/
264 static void find_domain_master_name_query_success(struct subnet_record *subrec,
265 struct userdata_struct *userdata_in,
266 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
269 * Unfortunately, finding the IP address of the Domain Master Browser,
270 * as we have here, is not enough. We need to now do a sync to the
271 * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
272 * respond to the SMBSERVER name. To get this name from IP
273 * address we do a Node status request, and look for the first
274 * NAME<0x20> in the response, and take that as the server name.
275 * We also keep a cache of the Domain Master Browser name for this
276 * workgroup in the Workgroup struct, so that if the same IP addess
277 * is returned every time, we don't need to do the node status
278 * request.
281 struct work_record *work;
282 struct nmb_name nmbname;
283 struct userdata_struct *userdata;
284 size_t size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
285 unstring qname;
287 pull_ascii_nstring(qname, sizeof(qname), q_name->name);
288 if( !(work = find_workgroup_on_subnet(subrec, qname)) ) {
289 if( DEBUGLVL( 0 ) ) {
290 dbgtext( "find_domain_master_name_query_success:\n" );
291 dbgtext( "Failed to find workgroup %s\n", qname);
293 return;
296 /* First check if we already have a dmb for this workgroup. */
298 if(!is_zero_ip_v4(work->dmb_addr) && ip_equal_v4(work->dmb_addr, answer_ip)) {
299 /* Do the local master browser announcement to the domain
300 master browser name and IP. */
301 announce_local_master_browser_to_domain_master_browser( work );
303 /* Now synchronise lists with the domain master browser. */
304 sync_with_dmb(work);
305 return;
306 } else {
307 zero_ip_v4(&work->dmb_addr);
310 /* Now initiate the node status request. */
312 /* We used to use the name "*",0x0 here, but some Windows
313 * servers don't answer that name. However we *know* they
314 * have the name workgroup#1b (as we just looked it up).
315 * So do the node status request on this name instead.
316 * Found at LBL labs. JRA.
319 make_nmb_name(&nmbname,work->work_group,0x1b);
321 /* Put the workgroup name into the userdata so we know
322 what workgroup we're talking to when the reply comes
323 back. */
325 /* Setup the userdata_struct - this is copied so we can use
326 a stack variable for this. */
328 if((userdata = (struct userdata_struct *)SMB_MALLOC(size)) == NULL) {
329 DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
330 return;
333 userdata->copy_fn = NULL;
334 userdata->free_fn = NULL;
335 userdata->userdata_len = strlen(work->work_group)+1;
336 strlcpy(userdata->data, work->work_group, size - sizeof(*userdata));
338 node_status( subrec, &nmbname, answer_ip,
339 domain_master_node_status_success,
340 domain_master_node_status_fail,
341 userdata);
343 zero_free(userdata, size);
346 /****************************************************************************
347 Function called when a query for a WORKGROUP<1b> name fails.
348 ****************************************************************************/
350 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
351 struct response_record *rrec,
352 struct nmb_name *question_name, int fail_code)
354 if( DEBUGLVL( 0 ) ) {
355 dbgtext( "find_domain_master_name_query_fail:\n" );
356 dbgtext( "Unable to find the Domain Master Browser name " );
357 dbgtext( "%s for the workgroup %s.\n",
358 nmb_namestr(question_name), question_name->name );
359 dbgtext( "Unable to sync browse lists in this workgroup.\n" );
363 /****************************************************************************
364 As a local master browser for a workgroup find the domain master browser
365 name, announce ourselves as local master browser to it and then pull the
366 full domain browse lists from it onto the given subnet.
367 **************************************************************************/
369 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
370 struct work_record *work)
372 /* Only do this if we are using a WINS server. */
373 if(we_are_a_wins_client() == False) {
374 if( DEBUGLVL( 10 ) ) {
375 dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
376 dbgtext( "Ignoring, as we are not a WINS client.\n" );
378 return;
381 /* First, query for the WORKGROUP<1b> name from the WINS server. */
382 query_name(unicast_subnet, work->work_group, 0x1b,
383 find_domain_master_name_query_success,
384 find_domain_master_name_query_fail,
385 NULL);
388 /****************************************************************************
389 Function called when a node status query to a domain master browser IP succeeds.
390 This function is only called on query to a Samba 1.9.18 or above WINS server.
392 Note that adding the workgroup name is enough for this workgroup to be
393 browsable by clients, as clients query the WINS server or broadcast
394 nets for the WORKGROUP<1b> name when they want to browse a workgroup
395 they are not in. We do not need to do a sync with this Domain Master
396 Browser in order for our browse clients to see machines in this workgroup.
397 JRA.
398 ****************************************************************************/
400 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
401 struct userdata_struct *userdata,
402 struct res_rec *answers,
403 struct in_addr from_ip)
405 unstring server_name;
407 server_name[0] = 0;
409 if( DEBUGLVL( 3 ) ) {
410 dbgtext( "get_domain_master_name_node_status_success:\n" );
411 dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
415 * Go through the list of names found at answers->rdata and look for
416 * the first WORKGROUP<0x1b> name.
419 if(answers->rdata != NULL) {
420 char *p = answers->rdata;
421 int numnames = CVAL(p, 0);
423 p += 1;
425 while (numnames--) {
426 unstring qname;
427 uint16 nb_flags;
428 int name_type;
430 pull_ascii_nstring(qname, sizeof(qname), p);
431 name_type = CVAL(p,15);
432 nb_flags = get_nb_flags(&p[16]);
433 trim_char(qname,'\0',' ');
435 p += 18;
437 if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
438 server_name[0] == 0) {
439 /* this is almost certainly the server netbios name */
440 strlcpy(server_name, qname, sizeof(server_name));
441 continue;
444 if(!(nb_flags & NB_GROUP) && (name_type == 0x1b)) {
445 struct work_record *work;
447 if( DEBUGLVL( 5 ) ) {
448 dbgtext( "get_domain_master_name_node_status_success:\n" );
449 dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
450 dbgtext( "is a domain master browser for workgroup " );
451 dbgtext( "%s. Adding this name.\n", qname );
455 * If we don't already know about this workgroup, add it
456 * to the workgroup list on the unicast_subnet.
459 work = find_workgroup_on_subnet( subrec, qname);
460 if (work == NULL) {
461 struct nmb_name nmbname;
463 * Add it - with an hour in the cache.
465 work = create_workgroup_on_subnet(subrec, qname, 60*60);
466 if (work == NULL) {
467 return;
470 /* remember who the master is */
471 strlcpy(work->local_master_browser_name,
472 server_name,
473 sizeof(work->local_master_browser_name));
474 make_nmb_name(&nmbname, server_name, 0x20);
475 work->dmb_name = nmbname;
476 work->dmb_addr = from_ip;
478 break;
481 } else if( DEBUGLVL( 1 ) ) {
482 dbgtext( "get_domain_master_name_node_status_success:\n" );
483 dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
484 dbgtext( "%s.\n", inet_ntoa(from_ip) );
488 /****************************************************************************
489 Function called when a node status query to a domain master browser IP fails.
490 ****************************************************************************/
492 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
493 struct response_record *rrec)
495 if( DEBUGLVL( 2 ) ) {
496 dbgtext( "get_domain_master_name_node_status_fail:\n" );
497 dbgtext( "Doing a node status request to the domain master browser " );
498 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
499 dbgtext( "Cannot get workgroup name.\n" );
503 /****************************************************************************
504 Function called when a query for *<1b> name succeeds.
505 ****************************************************************************/
507 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
508 struct userdata_struct *userdata_in,
509 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
512 * We now have a list of all the domain master browsers for all workgroups
513 * that have registered with the WINS server. Now do a node status request
514 * to each one and look for the first 1b name in the reply. This will be
515 * the workgroup name that we will add to the unicast subnet as a 'non-local'
516 * workgroup.
519 struct nmb_name nmbname;
520 struct in_addr send_ip;
521 int i;
523 if( DEBUGLVL( 5 ) ) {
524 dbgtext( "find_all_domain_master_names_query_succes:\n" );
525 dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
526 dbgtext( "IP addresses for Domain Master Browsers.\n" );
529 for(i = 0; i < rrec->rdlength / 6; i++) {
530 /* Initiate the node status requests. */
531 make_nmb_name(&nmbname, "*", 0);
533 putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
536 * Don't send node status requests to ourself.
539 if(ismyip_v4( send_ip )) {
540 if( DEBUGLVL( 5 ) ) {
541 dbgtext( "find_all_domain_master_names_query_succes:\n" );
542 dbgtext( "Not sending node status to our own IP " );
543 dbgtext( "%s.\n", inet_ntoa(send_ip) );
545 continue;
548 if( DEBUGLVL( 5 ) ) {
549 dbgtext( "find_all_domain_master_names_query_success:\n" );
550 dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
553 node_status( subrec, &nmbname, send_ip,
554 get_domain_master_name_node_status_success,
555 get_domain_master_name_node_status_fail,
556 NULL);
560 /****************************************************************************
561 Function called when a query for *<1b> name fails.
562 ****************************************************************************/
563 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
564 struct response_record *rrec,
565 struct nmb_name *question_name, int fail_code)
567 if( DEBUGLVL( 10 ) ) {
568 dbgtext( "find_domain_master_name_query_fail:\n" );
569 dbgtext( "WINS server did not reply to a query for name " );
570 dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
571 dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
575 /****************************************************************************
576 If we are a domain master browser on the unicast subnet, do a query to the
577 WINS server for the *<1b> name. This will only work to a Samba WINS server,
578 so ignore it if we fail. If we succeed, contact each of the IP addresses in
579 turn and do a node status request to them. If this succeeds then look for a
580 <1b> name in the reply - this is the workgroup name. Add this to the unicast
581 subnet. This is expensive, so we only do this every 15 minutes.
582 **************************************************************************/
584 void collect_all_workgroup_names_from_wins_server(time_t t)
586 static time_t lastrun = 0;
587 struct work_record *work;
589 /* Only do this if we are using a WINS server. */
590 if(we_are_a_wins_client() == False)
591 return;
593 /* Check to see if we are a domain master browser on the unicast subnet. */
594 if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL) {
595 if( DEBUGLVL( 0 ) ) {
596 dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
597 dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
598 dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
600 return;
603 if(!AM_DOMAIN_MASTER_BROWSER(work))
604 return;
606 if ((lastrun != 0) && (t < lastrun + (15 * 60)))
607 return;
609 lastrun = t;
611 /* First, query for the *<1b> name from the WINS server. */
612 query_name(unicast_subnet, "*", 0x1b,
613 find_all_domain_master_names_query_success,
614 find_all_domain_master_names_query_fail,
615 NULL);
619 /****************************************************************************
620 If we are a domain master browser on the unicast subnet, do a regular sync
621 with all other DMBs that we know of on that subnet.
623 To prevent exponential network traffic with large numbers of workgroups
624 we use a randomised system where sync probability is inversely proportional
625 to the number of known workgroups
626 **************************************************************************/
628 void sync_all_dmbs(time_t t)
630 static time_t lastrun = 0;
631 struct work_record *work;
632 int count=0;
634 /* Only do this if we are using a WINS server. */
635 if(we_are_a_wins_client() == False)
636 return;
638 /* Check to see if we are a domain master browser on the
639 unicast subnet. */
640 work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
641 if (!work)
642 return;
644 if (!AM_DOMAIN_MASTER_BROWSER(work))
645 return;
647 if ((lastrun != 0) && (t < lastrun + (5 * 60)))
648 return;
650 /* count how many syncs we might need to do */
651 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
652 if (strcmp(lp_workgroup(), work->work_group)) {
653 count++;
657 /* leave if we don't have to do any syncs */
658 if (count == 0) {
659 return;
662 /* sync with a probability of 1/count */
663 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
664 if (strcmp(lp_workgroup(), work->work_group)) {
665 unstring dmb_name;
667 if (((unsigned)sys_random()) % count != 0)
668 continue;
670 lastrun = t;
672 if (!work->dmb_name.name[0]) {
673 /* we don't know the DMB - assume it is
674 the same as the unicast local master */
675 make_nmb_name(&work->dmb_name,
676 work->local_master_browser_name,
677 0x20);
680 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
682 DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
683 dmb_name, inet_ntoa(work->dmb_addr)));
685 sync_browse_lists(work,
686 dmb_name,
687 work->dmb_name.name_type,
688 work->dmb_addr, False, False);