removed two unneeded files after Richard backed out these changes.
[Samba.git] / source / nmbd / nmbd_browsesync.c
blob40e04e7ecb7b37a324fe0b9422b22dd9ec481081
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 NBT netbios routines and daemon - version 2
5 Copyright (C) Andrew Tridgell 1994-1998
6 Copyright (C) Luke Kenneth Casson Leighton 1994-1998
7 Copyright (C) Jeremy Allison 1994-1998
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "smb.h"
28 extern pstring global_myname;
29 extern fstring global_myworkgroup;
31 /* This is our local master browser list database. */
32 extern ubi_dlList lmb_browserlist[];
34 /****************************************************************************
35 As a domain master browser, do a sync with a local master browser.
36 **************************************************************************/
37 static void sync_with_lmb(struct browse_cache_record *browc)
39 struct work_record *work;
41 if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) )
43 if( DEBUGLVL( 0 ) )
45 dbgtext( "sync_with_lmb:\n" );
46 dbgtext( "Failed to get a workgroup for a local master browser " );
47 dbgtext( "cache entry workgroup " );
48 dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
50 return;
53 /* We should only be doing this if we are a domain master browser for
54 the given workgroup. Ensure this is so. */
56 if(!AM_DOMAIN_MASTER_BROWSER(work))
58 if( DEBUGLVL( 0 ) )
60 dbgtext( "sync_with_lmb:\n" );
61 dbgtext( "We are trying to sync with a local master browser " );
62 dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
63 dbgtext( "and we are not a domain master browser on this workgroup.\n" );
64 dbgtext( "Error!\n" );
66 return;
69 if( DEBUGLVL( 2 ) )
71 dbgtext( "sync_with_lmb:\n" );
72 dbgtext( "Initiating sync with local master browser " );
73 dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
74 dbgtext( "for workgroup %s\n", browc->work_group );
77 sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
79 browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
82 /****************************************************************************
83 Sync or expire any local master browsers.
84 **************************************************************************/
85 void dmb_expire_and_sync_browser_lists(time_t t)
87 static time_t last_run = 0;
88 struct browse_cache_record *browc;
90 /* Only do this every 20 seconds. */
91 if (t - last_run < 20)
92 return;
94 last_run = t;
96 expire_lmb_browsers(t);
98 for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
99 browc;
100 browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
102 if (browc->sync_time < t)
103 sync_with_lmb(browc);
107 /****************************************************************************
108 As a local master browser, send an announce packet to the domain master browser.
109 **************************************************************************/
111 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
113 pstring outbuf;
114 char *p;
116 if(ismyip(work->dmb_addr))
118 if( DEBUGLVL( 2 ) )
120 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
121 dbgtext( "We are both a domain and a local master browser for " );
122 dbgtext( "workgroup %s. ", work->work_group );
123 dbgtext( "Do not announce to ourselves.\n" );
125 return;
128 memset(outbuf,'\0',sizeof(outbuf));
129 p = outbuf;
130 SCVAL(p,0,ANN_MasterAnnouncement);
131 p++;
133 StrnCpy(p,global_myname,15);
134 strupper(p);
135 p = skip_string(p,1);
137 if( DEBUGLVL( 4 ) )
139 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
140 dbgtext( "Sending local master announce to " );
141 dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
142 work->work_group );
145 send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
146 global_myname, 0x0, work->dmb_name.name, 0x0,
147 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
151 /****************************************************************************
152 As a local master browser, do a sync with a domain master browser.
153 **************************************************************************/
155 static void sync_with_dmb(struct work_record *work)
157 if( DEBUGLVL( 2 ) )
159 dbgtext( "sync_with_dmb:\n" );
160 dbgtext( "Initiating sync with domain master browser " );
161 dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
162 dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
163 dbgtext( "for workgroup %s\n", work->work_group );
166 sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type,
167 work->dmb_addr, False, True);
170 /****************************************************************************
171 Function called when a node status query to a domain master browser IP succeeds.
172 ****************************************************************************/
174 static void domain_master_node_status_success(struct subnet_record *subrec,
175 struct userdata_struct *userdata,
176 struct res_rec *answers,
177 struct in_addr from_ip)
179 struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
181 if( work == NULL )
183 if( DEBUGLVL( 0 ) )
185 dbgtext( "domain_master_node_status_success:\n" );
186 dbgtext( "Unable to find workgroup " );
187 dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
189 return;
192 if( DEBUGLVL( 3 ) )
194 dbgtext( "domain_master_node_status_success:\n" );
195 dbgtext( "Success in node status for workgroup " );
196 dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
199 /* Go through the list of names found at answers->rdata and look for
200 the first SERVER<0x20> name. */
202 if(answers->rdata != NULL)
204 char *p = answers->rdata;
205 int numnames = CVAL(p, 0);
207 p += 1;
209 while (numnames--)
211 char qname[17];
212 uint16 nb_flags;
213 int name_type;
215 StrnCpy(qname,p,15);
216 name_type = CVAL(p,15);
217 nb_flags = get_nb_flags(&p[16]);
218 trim_string(qname,NULL," ");
220 p += 18;
222 if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
224 struct nmb_name nmbname;
226 make_nmb_name(&nmbname, qname, name_type);
228 /* Copy the dmb name and IP address
229 into the workgroup struct. */
231 work->dmb_name = nmbname;
232 putip((char *)&work->dmb_addr, &from_ip);
234 /* Do the local master browser announcement to the domain
235 master browser name and IP. */
236 announce_local_master_browser_to_domain_master_browser( work );
238 /* Now synchronise lists with the domain master browser. */
239 sync_with_dmb(work);
240 break;
244 else
245 if( DEBUGLVL( 0 ) )
247 dbgtext( "domain_master_node_status_success:\n" );
248 dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
249 dbgtext( "%s.\n", inet_ntoa(from_ip) );
253 /****************************************************************************
254 Function called when a node status query to a domain master browser IP fails.
255 ****************************************************************************/
257 static void domain_master_node_status_fail(struct subnet_record *subrec,
258 struct response_record *rrec)
260 struct userdata_struct *userdata = rrec->userdata;
262 if( DEBUGLVL( 0 ) )
264 dbgtext( "domain_master_node_status_fail:\n" );
265 dbgtext( "Doing a node status request to the domain master browser\n" );
266 dbgtext( "for workgroup %s ", userdata->data );
267 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
268 dbgtext( "Cannot sync browser lists.\n" );
272 /****************************************************************************
273 Function called when a query for a WORKGROUP<1b> name succeeds.
274 ****************************************************************************/
276 static void find_domain_master_name_query_success(struct subnet_record *subrec,
277 struct userdata_struct *userdata_in,
278 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
281 * Unfortunately, finding the IP address of the Domain Master Browser,
282 * as we have here, is not enough. We need to now do a sync to the
283 * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
284 * respond to the SMBSERVER name. To get this name from IP
285 * address we do a Node status request, and look for the first
286 * NAME<0x20> in the response, and take that as the server name.
287 * We also keep a cache of the Domain Master Browser name for this
288 * workgroup in the Workgroup struct, so that if the same IP addess
289 * is returned every time, we don't need to do the node status
290 * request.
293 struct work_record *work;
294 struct nmb_name nmbname;
295 struct userdata_struct *userdata;
296 int size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
298 if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) )
300 if( DEBUGLVL( 0 ) )
302 dbgtext( "find_domain_master_name_query_success:\n" );
303 dbgtext( "Failed to find workgroup %s\n", q_name->name );
305 return;
308 /* First check if we already have a dmb for this workgroup. */
310 if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip))
312 /* Do the local master browser announcement to the domain
313 master browser name and IP. */
314 announce_local_master_browser_to_domain_master_browser( work );
316 /* Now synchronise lists with the domain master browser. */
317 sync_with_dmb(work);
318 return;
320 else
321 zero_ip(&work->dmb_addr);
323 /* Now initiate the node status request. */
324 make_nmb_name(&nmbname,"*",0x0);
326 /* Put the workgroup name into the userdata so we know
327 what workgroup we're talking to when the reply comes
328 back. */
330 /* Setup the userdata_struct - this is copied so we can use
331 a stack variable for this. */
332 if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
334 DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
335 return;
338 userdata->copy_fn = NULL;
339 userdata->free_fn = NULL;
340 userdata->userdata_len = strlen(work->work_group)+1;
341 pstrcpy(userdata->data, work->work_group);
343 node_status( subrec, &nmbname, answer_ip,
344 domain_master_node_status_success,
345 domain_master_node_status_fail,
346 userdata);
348 zero_free(userdata, size);
351 /****************************************************************************
352 Function called when a query for a WORKGROUP<1b> name fails.
353 ****************************************************************************/
354 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
355 struct response_record *rrec,
356 struct nmb_name *question_name, int fail_code)
358 if( DEBUGLVL( 0 ) )
360 dbgtext( "find_domain_master_name_query_fail:\n" );
361 dbgtext( "Unable to find the Domain Master Browser name " );
362 dbgtext( "%s for the workgroup %s.\n",
363 nmb_namestr(question_name), question_name->name );
364 dbgtext( "Unable to sync browse lists in this workgroup.\n" );
368 /****************************************************************************
369 As a local master browser for a workgroup find the domain master browser
370 name, announce ourselves as local master browser to it and then pull the
371 full domain browse lists from it onto the given subnet.
372 **************************************************************************/
374 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
375 struct work_record *work)
377 struct nmb_name nmbname;
379 /* Only do this if we are using a WINS server. */
380 if(we_are_a_wins_client() == False)
382 if( DEBUGLVL( 10 ) )
384 dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
385 dbgtext( "Ignoring, as we are not a WINS client.\n" );
387 return;
390 make_nmb_name(&nmbname,work->work_group,0x1b);
392 /* First, query for the WORKGROUP<1b> name from the WINS server. */
393 query_name(unicast_subnet, nmbname.name, nmbname.name_type,
394 find_domain_master_name_query_success,
395 find_domain_master_name_query_fail,
396 NULL);
400 /****************************************************************************
401 Function called when a node status query to a domain master browser IP succeeds.
402 This function is only called on query to a Samba 1.9.18 or above WINS server.
404 Note that adding the workgroup name is enough for this workgroup to be
405 browsable by clients, as clients query the WINS server or broadcast
406 nets for the WORKGROUP<1b> name when they want to browse a workgroup
407 they are not in. We do not need to do a sync with this Domain Master
408 Browser in order for our browse clients to see machines in this workgroup.
409 JRA.
410 ****************************************************************************/
412 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
413 struct userdata_struct *userdata,
414 struct res_rec *answers,
415 struct in_addr from_ip)
417 struct work_record *work;
418 fstring server_name;
420 server_name[0] = 0;
422 if( DEBUGLVL( 3 ) )
424 dbgtext( "get_domain_master_name_node_status_success:\n" );
425 dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
429 * Go through the list of names found at answers->rdata and look for
430 * the first WORKGROUP<0x1b> name.
433 if(answers->rdata != NULL)
435 char *p = answers->rdata;
436 int numnames = CVAL(p, 0);
438 p += 1;
440 while (numnames--)
442 char qname[17];
443 uint16 nb_flags;
444 int name_type;
446 StrnCpy(qname,p,15);
447 name_type = CVAL(p,15);
448 nb_flags = get_nb_flags(&p[16]);
449 trim_string(qname,NULL," ");
451 p += 18;
453 if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
454 server_name[0] == 0) {
455 /* this is almost certainly the server netbios name */
456 fstrcpy(server_name, qname);
457 continue;
460 if(!(nb_flags & NB_GROUP) && (name_type == 0x1b))
462 if( DEBUGLVL( 5 ) )
464 dbgtext( "get_domain_master_name_node_status_success:\n" );
465 dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
466 dbgtext( "is a domain master browser for workgroup " );
467 dbgtext( "%s. Adding this name.\n", qname );
471 * If we don't already know about this workgroup, add it
472 * to the workgroup list on the unicast_subnet.
474 if((work = find_workgroup_on_subnet( subrec, qname)) == NULL)
476 struct nmb_name nmbname;
478 * Add it - with an hour in the cache.
480 if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
481 return;
483 /* remember who the master is */
484 fstrcpy(work->local_master_browser_name, server_name);
485 make_nmb_name(&nmbname, server_name, 0x20);
486 work->dmb_name = nmbname;
487 work->dmb_addr = from_ip;
489 break;
493 else
494 if( DEBUGLVL( 0 ) )
496 dbgtext( "get_domain_master_name_node_status_success:\n" );
497 dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
498 dbgtext( "%s.\n", inet_ntoa(from_ip) );
502 /****************************************************************************
503 Function called when a node status query to a domain master browser IP fails.
504 ****************************************************************************/
506 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
507 struct response_record *rrec)
509 if( DEBUGLVL( 0 ) )
511 dbgtext( "get_domain_master_name_node_status_fail:\n" );
512 dbgtext( "Doing a node status request to the domain master browser " );
513 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
514 dbgtext( "Cannot get workgroup name.\n" );
518 /****************************************************************************
519 Function called when a query for *<1b> name succeeds.
520 ****************************************************************************/
522 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
523 struct userdata_struct *userdata_in,
524 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
527 * We now have a list of all the domain master browsers for all workgroups
528 * that have registered with the WINS server. Now do a node status request
529 * to each one and look for the first 1b name in the reply. This will be
530 * the workgroup name that we will add to the unicast subnet as a 'non-local'
531 * workgroup.
534 struct nmb_name nmbname;
535 struct in_addr send_ip;
536 int i;
538 if( DEBUGLVL( 5 ) )
540 dbgtext( "find_all_domain_master_names_query_succes:\n" );
541 dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
542 dbgtext( "IP addresses for Domain Master Browsers.\n" );
545 for(i = 0; i < rrec->rdlength / 6; i++)
547 /* Initiate the node status requests. */
548 make_nmb_name(&nmbname, "*", 0);
550 putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
553 * Don't send node status requests to ourself.
556 if(ismyip( send_ip ))
558 if( DEBUGLVL( 5 ) )
560 dbgtext( "find_all_domain_master_names_query_succes:\n" );
561 dbgtext( "Not sending node status to our own IP " );
562 dbgtext( "%s.\n", inet_ntoa(send_ip) );
564 continue;
567 if( DEBUGLVL( 5 ) )
569 dbgtext( "find_all_domain_master_names_query_success:\n" );
570 dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
573 node_status( subrec, &nmbname, send_ip,
574 get_domain_master_name_node_status_success,
575 get_domain_master_name_node_status_fail,
576 NULL);
580 /****************************************************************************
581 Function called when a query for *<1b> name fails.
582 ****************************************************************************/
583 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
584 struct response_record *rrec,
585 struct nmb_name *question_name, int fail_code)
587 if( DEBUGLVL( 10 ) )
589 dbgtext( "find_domain_master_name_query_fail:\n" );
590 dbgtext( "WINS server did not reply to a query for name " );
591 dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
592 dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
596 /****************************************************************************
597 If we are a domain master browser on the unicast subnet, do a query to the
598 WINS server for the *<1b> name. This will only work to a Samba WINS server,
599 so ignore it if we fail. If we succeed, contact each of the IP addresses in
600 turn and do a node status request to them. If this succeeds then look for a
601 <1b> name in the reply - this is the workgroup name. Add this to the unicast
602 subnet. This is expensive, so we only do this every 15 minutes.
603 **************************************************************************/
604 void collect_all_workgroup_names_from_wins_server(time_t t)
606 static time_t lastrun = 0;
607 struct work_record *work;
608 struct nmb_name nmbname;
610 /* Only do this if we are using a WINS server. */
611 if(we_are_a_wins_client() == False)
612 return;
614 /* Check to see if we are a domain master browser on the unicast subnet. */
615 if((work = find_workgroup_on_subnet( unicast_subnet, global_myworkgroup)) == NULL)
617 if( DEBUGLVL( 0 ) )
619 dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
620 dbgtext( "Cannot find my workgroup %s ", global_myworkgroup );
621 dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
623 return;
626 if(!AM_DOMAIN_MASTER_BROWSER(work))
627 return;
629 if ((lastrun != 0) && (t < lastrun + (15 * 60)))
630 return;
632 lastrun = t;
634 make_nmb_name(&nmbname,"*",0x1b);
636 /* First, query for the *<1b> name from the WINS server. */
637 query_name(unicast_subnet, nmbname.name, nmbname.name_type,
638 find_all_domain_master_names_query_success,
639 find_all_domain_master_names_query_fail,
640 NULL);
644 /****************************************************************************
645 If we are a domain master browser on the unicast subnet, do a regular sync
646 with all other DMBs that we know of on that subnet.
648 To prevent exponential network traffic with large numbers of workgroups
649 we use a randomised system where sync probability is inversely proportional
650 to the number of known workgroups
651 **************************************************************************/
652 void sync_all_dmbs(time_t t)
654 static time_t lastrun = 0;
655 struct work_record *work;
656 int count=0;
658 /* Only do this if we are using a WINS server. */
659 if(we_are_a_wins_client() == False)
660 return;
662 /* Check to see if we are a domain master browser on the
663 unicast subnet. */
664 work = find_workgroup_on_subnet(unicast_subnet, global_myworkgroup);
665 if (!work) return;
667 if (!AM_DOMAIN_MASTER_BROWSER(work))
668 return;
670 if ((lastrun != 0) && (t < lastrun + (5 * 60)))
671 return;
673 /* count how many syncs we might need to do */
674 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
675 if (strcmp(global_myworkgroup, work->work_group)) {
676 count++;
680 /* sync with a probability of 1/count */
681 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
682 if (strcmp(global_myworkgroup, work->work_group)) {
683 if (((unsigned)sys_random()) % count != 0) continue;
685 lastrun = t;
687 if (!work->dmb_name.name[0]) {
688 /* we don't know the DMB - assume it is
689 the same as the unicast local master */
690 make_nmb_name(&work->dmb_name,
691 work->local_master_browser_name,
692 0x20);
695 DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
696 work->dmb_name.name,
697 inet_ntoa(work->dmb_addr)));
698 sync_browse_lists(work,
699 work->dmb_name.name,
700 work->dmb_name.name_type,
701 work->dmb_addr, False, False);