s4:descriptor - cosmetic
[Samba/aatanasov.git] / source3 / nmbd / nmbd_browsesync.c
blobb630fd234d63a8470431f330722cf429d6aea0af
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"
25 /* This is our local master browser list database. */
26 extern struct browse_cache_record *lmb_browserlist;
28 /****************************************************************************
29 As a domain master browser, do a sync with a local master browser.
30 **************************************************************************/
32 static void sync_with_lmb(struct browse_cache_record *browc)
34 struct work_record *work;
36 if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) ) {
37 if( DEBUGLVL( 0 ) ) {
38 dbgtext( "sync_with_lmb:\n" );
39 dbgtext( "Failed to get a workgroup for a local master browser " );
40 dbgtext( "cache entry workgroup " );
41 dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
43 return;
46 /* We should only be doing this if we are a domain master browser for
47 the given workgroup. Ensure this is so. */
49 if(!AM_DOMAIN_MASTER_BROWSER(work)) {
50 if( DEBUGLVL( 0 ) ) {
51 dbgtext( "sync_with_lmb:\n" );
52 dbgtext( "We are trying to sync with a local master browser " );
53 dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
54 dbgtext( "and we are not a domain master browser on this workgroup.\n" );
55 dbgtext( "Error!\n" );
57 return;
60 if( DEBUGLVL( 2 ) ) {
61 dbgtext( "sync_with_lmb:\n" );
62 dbgtext( "Initiating sync with local master browser " );
63 dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
64 dbgtext( "for workgroup %s\n", browc->work_group );
67 sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
69 browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
72 /****************************************************************************
73 Sync or expire any local master browsers.
74 **************************************************************************/
76 void dmb_expire_and_sync_browser_lists(time_t t)
78 static time_t last_run = 0;
79 struct browse_cache_record *browc;
81 /* Only do this every 20 seconds. */
82 if (t - last_run < 20)
83 return;
85 last_run = t;
87 expire_lmb_browsers(t);
89 for( browc = lmb_browserlist; browc; browc = browc->next ) {
90 if (browc->sync_time < t)
91 sync_with_lmb(browc);
95 /****************************************************************************
96 As a local master browser, send an announce packet to the domain master browser.
97 **************************************************************************/
99 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
101 char outbuf[1024];
102 unstring myname;
103 unstring dmb_name;
104 char *p;
106 if(ismyip_v4(work->dmb_addr)) {
107 if( DEBUGLVL( 2 ) ) {
108 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
109 dbgtext( "We are both a domain and a local master browser for " );
110 dbgtext( "workgroup %s. ", work->work_group );
111 dbgtext( "Do not announce to ourselves.\n" );
113 return;
116 memset(outbuf,'\0',sizeof(outbuf));
117 p = outbuf;
118 SCVAL(p,0,ANN_MasterAnnouncement);
119 p++;
121 unstrcpy(myname, global_myname());
122 strupper_m(myname);
123 myname[15]='\0';
124 /* The call below does CH_UNIX -> CH_DOS conversion. JRA */
125 push_ascii(p, myname, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
127 p = skip_string(outbuf,sizeof(outbuf),p);
129 if( DEBUGLVL( 4 ) ) {
130 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
131 dbgtext( "Sending local master announce to " );
132 dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
133 work->work_group );
136 /* Target name for send_mailslot must be in UNIX charset. */
137 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
138 send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
139 global_myname(), 0x0, dmb_name, 0x0,
140 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
143 /****************************************************************************
144 As a local master browser, do a sync with a domain master browser.
145 **************************************************************************/
147 static void sync_with_dmb(struct work_record *work)
149 unstring dmb_name;
151 if( DEBUGLVL( 2 ) ) {
152 dbgtext( "sync_with_dmb:\n" );
153 dbgtext( "Initiating sync with domain master browser " );
154 dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
155 dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
156 dbgtext( "for workgroup %s\n", work->work_group );
159 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
160 sync_browse_lists(work, dmb_name, work->dmb_name.name_type,
161 work->dmb_addr, False, True);
164 /****************************************************************************
165 Function called when a node status query to a domain master browser IP succeeds.
166 ****************************************************************************/
168 static void domain_master_node_status_success(struct subnet_record *subrec,
169 struct userdata_struct *userdata,
170 struct res_rec *answers,
171 struct in_addr from_ip)
173 struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
175 if( work == NULL ) {
176 if( DEBUGLVL( 0 ) ) {
177 dbgtext( "domain_master_node_status_success:\n" );
178 dbgtext( "Unable to find workgroup " );
179 dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
181 return;
184 if( DEBUGLVL( 3 ) ) {
185 dbgtext( "domain_master_node_status_success:\n" );
186 dbgtext( "Success in node status for workgroup " );
187 dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
190 /* Go through the list of names found at answers->rdata and look for
191 the first SERVER<0x20> name. */
193 if(answers->rdata != NULL) {
194 char *p = answers->rdata;
195 int numnames = CVAL(p, 0);
197 p += 1;
199 while (numnames--) {
200 unstring qname;
201 uint16 nb_flags;
202 int name_type;
204 pull_ascii_nstring(qname, sizeof(qname), p);
205 name_type = CVAL(p,15);
206 nb_flags = get_nb_flags(&p[16]);
207 trim_char(qname,'\0',' ');
209 p += 18;
211 if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) {
212 struct nmb_name nmbname;
214 make_nmb_name(&nmbname, qname, name_type);
216 /* Copy the dmb name and IP address
217 into the workgroup struct. */
219 work->dmb_name = nmbname;
220 putip((char *)&work->dmb_addr, &from_ip);
222 /* Do the local master browser announcement to the domain
223 master browser name and IP. */
224 announce_local_master_browser_to_domain_master_browser( work );
226 /* Now synchronise lists with the domain master browser. */
227 sync_with_dmb(work);
228 break;
231 } else if( DEBUGLVL( 0 ) ) {
232 dbgtext( "domain_master_node_status_success:\n" );
233 dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
234 dbgtext( "%s.\n", inet_ntoa(from_ip) );
238 /****************************************************************************
239 Function called when a node status query to a domain master browser IP fails.
240 ****************************************************************************/
242 static void domain_master_node_status_fail(struct subnet_record *subrec,
243 struct response_record *rrec)
245 struct userdata_struct *userdata = rrec->userdata;
247 if( DEBUGLVL( 0 ) ) {
248 dbgtext( "domain_master_node_status_fail:\n" );
249 dbgtext( "Doing a node status request to the domain master browser\n" );
250 dbgtext( "for workgroup %s ", userdata ? userdata->data : "NULL" );
251 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
252 dbgtext( "Cannot sync browser lists.\n" );
256 /****************************************************************************
257 Function called when a query for a WORKGROUP<1b> name succeeds.
258 ****************************************************************************/
260 static void find_domain_master_name_query_success(struct subnet_record *subrec,
261 struct userdata_struct *userdata_in,
262 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
265 * Unfortunately, finding the IP address of the Domain Master Browser,
266 * as we have here, is not enough. We need to now do a sync to the
267 * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
268 * respond to the SMBSERVER name. To get this name from IP
269 * address we do a Node status request, and look for the first
270 * NAME<0x20> in the response, and take that as the server name.
271 * We also keep a cache of the Domain Master Browser name for this
272 * workgroup in the Workgroup struct, so that if the same IP addess
273 * is returned every time, we don't need to do the node status
274 * request.
277 struct work_record *work;
278 struct nmb_name nmbname;
279 struct userdata_struct *userdata;
280 size_t size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
281 unstring qname;
283 pull_ascii_nstring(qname, sizeof(qname), q_name->name);
284 if( !(work = find_workgroup_on_subnet(subrec, qname)) ) {
285 if( DEBUGLVL( 0 ) ) {
286 dbgtext( "find_domain_master_name_query_success:\n" );
287 dbgtext( "Failed to find workgroup %s\n", qname);
289 return;
292 /* First check if we already have a dmb for this workgroup. */
294 if(!is_zero_ip_v4(work->dmb_addr) && ip_equal_v4(work->dmb_addr, answer_ip)) {
295 /* Do the local master browser announcement to the domain
296 master browser name and IP. */
297 announce_local_master_browser_to_domain_master_browser( work );
299 /* Now synchronise lists with the domain master browser. */
300 sync_with_dmb(work);
301 return;
302 } else {
303 zero_ip_v4(&work->dmb_addr);
306 /* Now initiate the node status request. */
308 /* We used to use the name "*",0x0 here, but some Windows
309 * servers don't answer that name. However we *know* they
310 * have the name workgroup#1b (as we just looked it up).
311 * So do the node status request on this name instead.
312 * Found at LBL labs. JRA.
315 make_nmb_name(&nmbname,work->work_group,0x1b);
317 /* Put the workgroup name into the userdata so we know
318 what workgroup we're talking to when the reply comes
319 back. */
321 /* Setup the userdata_struct - this is copied so we can use
322 a stack variable for this. */
324 if((userdata = (struct userdata_struct *)SMB_MALLOC(size)) == NULL) {
325 DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
326 return;
329 userdata->copy_fn = NULL;
330 userdata->free_fn = NULL;
331 userdata->userdata_len = strlen(work->work_group)+1;
332 overmalloc_safe_strcpy(userdata->data, work->work_group, size - sizeof(*userdata) - 1);
334 node_status( subrec, &nmbname, answer_ip,
335 domain_master_node_status_success,
336 domain_master_node_status_fail,
337 userdata);
339 zero_free(userdata, size);
342 /****************************************************************************
343 Function called when a query for a WORKGROUP<1b> name fails.
344 ****************************************************************************/
346 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
347 struct response_record *rrec,
348 struct nmb_name *question_name, int fail_code)
350 if( DEBUGLVL( 0 ) ) {
351 dbgtext( "find_domain_master_name_query_fail:\n" );
352 dbgtext( "Unable to find the Domain Master Browser name " );
353 dbgtext( "%s for the workgroup %s.\n",
354 nmb_namestr(question_name), question_name->name );
355 dbgtext( "Unable to sync browse lists in this workgroup.\n" );
359 /****************************************************************************
360 As a local master browser for a workgroup find the domain master browser
361 name, announce ourselves as local master browser to it and then pull the
362 full domain browse lists from it onto the given subnet.
363 **************************************************************************/
365 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
366 struct work_record *work)
368 /* Only do this if we are using a WINS server. */
369 if(we_are_a_wins_client() == False) {
370 if( DEBUGLVL( 10 ) ) {
371 dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
372 dbgtext( "Ignoring, as we are not a WINS client.\n" );
374 return;
377 /* First, query for the WORKGROUP<1b> name from the WINS server. */
378 query_name(unicast_subnet, work->work_group, 0x1b,
379 find_domain_master_name_query_success,
380 find_domain_master_name_query_fail,
381 NULL);
384 /****************************************************************************
385 Function called when a node status query to a domain master browser IP succeeds.
386 This function is only called on query to a Samba 1.9.18 or above WINS server.
388 Note that adding the workgroup name is enough for this workgroup to be
389 browsable by clients, as clients query the WINS server or broadcast
390 nets for the WORKGROUP<1b> name when they want to browse a workgroup
391 they are not in. We do not need to do a sync with this Domain Master
392 Browser in order for our browse clients to see machines in this workgroup.
393 JRA.
394 ****************************************************************************/
396 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
397 struct userdata_struct *userdata,
398 struct res_rec *answers,
399 struct in_addr from_ip)
401 struct work_record *work;
402 unstring server_name;
404 server_name[0] = 0;
406 if( DEBUGLVL( 3 ) ) {
407 dbgtext( "get_domain_master_name_node_status_success:\n" );
408 dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
412 * Go through the list of names found at answers->rdata and look for
413 * the first WORKGROUP<0x1b> name.
416 if(answers->rdata != NULL) {
417 char *p = answers->rdata;
418 int numnames = CVAL(p, 0);
420 p += 1;
422 while (numnames--) {
423 unstring qname;
424 uint16 nb_flags;
425 int name_type;
427 pull_ascii_nstring(qname, sizeof(qname), p);
428 name_type = CVAL(p,15);
429 nb_flags = get_nb_flags(&p[16]);
430 trim_char(qname,'\0',' ');
432 p += 18;
434 if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
435 server_name[0] == 0) {
436 /* this is almost certainly the server netbios name */
437 unstrcpy(server_name, qname);
438 continue;
441 if(!(nb_flags & NB_GROUP) && (name_type == 0x1b)) {
442 if( DEBUGLVL( 5 ) ) {
443 dbgtext( "get_domain_master_name_node_status_success:\n" );
444 dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
445 dbgtext( "is a domain master browser for workgroup " );
446 dbgtext( "%s. Adding this name.\n", qname );
450 * If we don't already know about this workgroup, add it
451 * to the workgroup list on the unicast_subnet.
454 if((work = find_workgroup_on_subnet( subrec, qname)) == NULL) {
455 struct nmb_name nmbname;
457 * Add it - with an hour in the cache.
459 if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
460 return;
462 /* remember who the master is */
463 unstrcpy(work->local_master_browser_name, server_name);
464 make_nmb_name(&nmbname, server_name, 0x20);
465 work->dmb_name = nmbname;
466 work->dmb_addr = from_ip;
468 break;
471 } else if( DEBUGLVL( 0 ) ) {
472 dbgtext( "get_domain_master_name_node_status_success:\n" );
473 dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
474 dbgtext( "%s.\n", inet_ntoa(from_ip) );
478 /****************************************************************************
479 Function called when a node status query to a domain master browser IP fails.
480 ****************************************************************************/
482 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
483 struct response_record *rrec)
485 if( DEBUGLVL( 0 ) ) {
486 dbgtext( "get_domain_master_name_node_status_fail:\n" );
487 dbgtext( "Doing a node status request to the domain master browser " );
488 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
489 dbgtext( "Cannot get workgroup name.\n" );
493 /****************************************************************************
494 Function called when a query for *<1b> name succeeds.
495 ****************************************************************************/
497 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
498 struct userdata_struct *userdata_in,
499 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
502 * We now have a list of all the domain master browsers for all workgroups
503 * that have registered with the WINS server. Now do a node status request
504 * to each one and look for the first 1b name in the reply. This will be
505 * the workgroup name that we will add to the unicast subnet as a 'non-local'
506 * workgroup.
509 struct nmb_name nmbname;
510 struct in_addr send_ip;
511 int i;
513 if( DEBUGLVL( 5 ) ) {
514 dbgtext( "find_all_domain_master_names_query_succes:\n" );
515 dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
516 dbgtext( "IP addresses for Domain Master Browsers.\n" );
519 for(i = 0; i < rrec->rdlength / 6; i++) {
520 /* Initiate the node status requests. */
521 make_nmb_name(&nmbname, "*", 0);
523 putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
526 * Don't send node status requests to ourself.
529 if(ismyip_v4( send_ip )) {
530 if( DEBUGLVL( 5 ) ) {
531 dbgtext( "find_all_domain_master_names_query_succes:\n" );
532 dbgtext( "Not sending node status to our own IP " );
533 dbgtext( "%s.\n", inet_ntoa(send_ip) );
535 continue;
538 if( DEBUGLVL( 5 ) ) {
539 dbgtext( "find_all_domain_master_names_query_success:\n" );
540 dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
543 node_status( subrec, &nmbname, send_ip,
544 get_domain_master_name_node_status_success,
545 get_domain_master_name_node_status_fail,
546 NULL);
550 /****************************************************************************
551 Function called when a query for *<1b> name fails.
552 ****************************************************************************/
553 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
554 struct response_record *rrec,
555 struct nmb_name *question_name, int fail_code)
557 if( DEBUGLVL( 10 ) ) {
558 dbgtext( "find_domain_master_name_query_fail:\n" );
559 dbgtext( "WINS server did not reply to a query for name " );
560 dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
561 dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
565 /****************************************************************************
566 If we are a domain master browser on the unicast subnet, do a query to the
567 WINS server for the *<1b> name. This will only work to a Samba WINS server,
568 so ignore it if we fail. If we succeed, contact each of the IP addresses in
569 turn and do a node status request to them. If this succeeds then look for a
570 <1b> name in the reply - this is the workgroup name. Add this to the unicast
571 subnet. This is expensive, so we only do this every 15 minutes.
572 **************************************************************************/
574 void collect_all_workgroup_names_from_wins_server(time_t t)
576 static time_t lastrun = 0;
577 struct work_record *work;
579 /* Only do this if we are using a WINS server. */
580 if(we_are_a_wins_client() == False)
581 return;
583 /* Check to see if we are a domain master browser on the unicast subnet. */
584 if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL) {
585 if( DEBUGLVL( 0 ) ) {
586 dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
587 dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
588 dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
590 return;
593 if(!AM_DOMAIN_MASTER_BROWSER(work))
594 return;
596 if ((lastrun != 0) && (t < lastrun + (15 * 60)))
597 return;
599 lastrun = t;
601 /* First, query for the *<1b> name from the WINS server. */
602 query_name(unicast_subnet, "*", 0x1b,
603 find_all_domain_master_names_query_success,
604 find_all_domain_master_names_query_fail,
605 NULL);
609 /****************************************************************************
610 If we are a domain master browser on the unicast subnet, do a regular sync
611 with all other DMBs that we know of on that subnet.
613 To prevent exponential network traffic with large numbers of workgroups
614 we use a randomised system where sync probability is inversely proportional
615 to the number of known workgroups
616 **************************************************************************/
618 void sync_all_dmbs(time_t t)
620 static time_t lastrun = 0;
621 struct work_record *work;
622 int count=0;
624 /* Only do this if we are using a WINS server. */
625 if(we_are_a_wins_client() == False)
626 return;
628 /* Check to see if we are a domain master browser on the
629 unicast subnet. */
630 work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
631 if (!work)
632 return;
634 if (!AM_DOMAIN_MASTER_BROWSER(work))
635 return;
637 if ((lastrun != 0) && (t < lastrun + (5 * 60)))
638 return;
640 /* count how many syncs we might need to do */
641 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
642 if (strcmp(lp_workgroup(), work->work_group)) {
643 count++;
647 /* sync with a probability of 1/count */
648 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
649 if (strcmp(lp_workgroup(), work->work_group)) {
650 unstring dmb_name;
652 if (((unsigned)sys_random()) % count != 0)
653 continue;
655 lastrun = t;
657 if (!work->dmb_name.name[0]) {
658 /* we don't know the DMB - assume it is
659 the same as the unicast local master */
660 make_nmb_name(&work->dmb_name,
661 work->local_master_browser_name,
662 0x20);
665 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
667 DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
668 dmb_name, inet_ntoa(work->dmb_addr)));
670 sync_browse_lists(work,
671 dmb_name,
672 work->dmb_name.name_type,
673 work->dmb_addr, False, False);