samba-tool dsacl: Create helper functions to remove code duplication
[Samba.git] / source3 / nmbd / nmbd_browsesync.c
blob181787df41015d69b7c853f8f9405d03099d58fb
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"
25 #include "lib/util/string_wrappers.h"
27 /* This is our local master browser list database. */
28 extern struct browse_cache_record *lmb_browserlist;
30 /****************************************************************************
31 As a domain master browser, do a sync with a local master browser.
32 **************************************************************************/
34 static void sync_with_lmb(struct browse_cache_record *browc)
36 struct work_record *work;
38 if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) ) {
39 if( DEBUGLVL( 0 ) ) {
40 dbgtext( "sync_with_lmb:\n" );
41 dbgtext( "Failed to get a workgroup for a local master browser " );
42 dbgtext( "cache entry workgroup " );
43 dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
45 return;
48 /* We should only be doing this if we are a domain master browser for
49 the given workgroup. Ensure this is so. */
51 if(!AM_DOMAIN_MASTER_BROWSER(work)) {
52 if( DEBUGLVL( 0 ) ) {
53 dbgtext( "sync_with_lmb:\n" );
54 dbgtext( "We are trying to sync with a local master browser " );
55 dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
56 dbgtext( "and we are not a domain master browser on this workgroup.\n" );
57 dbgtext( "Error!\n" );
59 return;
62 if( DEBUGLVL( 2 ) ) {
63 dbgtext( "sync_with_lmb:\n" );
64 dbgtext( "Initiating sync with local master browser " );
65 dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
66 dbgtext( "for workgroup %s\n", browc->work_group );
69 sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
71 browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
74 /****************************************************************************
75 Sync or expire any local master browsers.
76 **************************************************************************/
78 void dmb_expire_and_sync_browser_lists(time_t t)
80 static time_t last_run = 0;
81 struct browse_cache_record *browc;
83 /* Only do this every 20 seconds. */
84 if (t - last_run < 20)
85 return;
87 last_run = t;
89 expire_lmb_browsers(t);
91 for( browc = lmb_browserlist; browc; browc = browc->next ) {
92 if (browc->sync_time < t)
93 sync_with_lmb(browc);
97 /****************************************************************************
98 As a local master browser, send an announce packet to the domain master browser.
99 **************************************************************************/
101 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
103 char outbuf[1024];
104 unstring myname;
105 unstring dmb_name;
106 char *p;
108 if(ismyip_v4(work->dmb_addr)) {
109 if( DEBUGLVL( 2 ) ) {
110 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
111 dbgtext( "We are both a domain and a local master browser for " );
112 dbgtext( "workgroup %s. ", work->work_group );
113 dbgtext( "Do not announce to ourselves.\n" );
115 return;
118 memset(outbuf,'\0',sizeof(outbuf));
119 p = outbuf;
120 SCVAL(p,0,ANN_MasterAnnouncement);
121 p++;
123 unstrcpy(myname, lp_netbios_name());
124 if (!strupper_m(myname)) {
125 DEBUG(2,("strupper_m %s failed\n", myname));
126 return;
128 myname[15]='\0';
129 /* The call below does CH_UNIX -> CH_DOS conversion. JRA */
130 push_ascii(p, myname, sizeof(outbuf)-PTR_DIFF(p,outbuf)-1, STR_TERMINATE);
132 p = skip_string(outbuf,sizeof(outbuf),p);
134 if( DEBUGLVL( 4 ) ) {
135 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
136 dbgtext( "Sending local master announce to " );
137 dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
138 work->work_group );
141 /* Target name for send_mailslot must be in UNIX charset. */
142 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
143 send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
144 lp_netbios_name(), 0x0, dmb_name, 0x0,
145 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
148 /****************************************************************************
149 As a local master browser, do a sync with a domain master browser.
150 **************************************************************************/
152 static void sync_with_dmb(struct work_record *work)
154 unstring dmb_name;
156 if( DEBUGLVL( 2 ) ) {
157 dbgtext( "sync_with_dmb:\n" );
158 dbgtext( "Initiating sync with domain master browser " );
159 dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
160 dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
161 dbgtext( "for workgroup %s\n", work->work_group );
164 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
165 sync_browse_lists(work, dmb_name, work->dmb_name.name_type,
166 work->dmb_addr, False, True);
169 /****************************************************************************
170 Function called when a node status query to a domain master browser IP succeeds.
171 ****************************************************************************/
173 static void domain_master_node_status_success(struct subnet_record *subrec,
174 struct userdata_struct *userdata,
175 struct res_rec *answers,
176 struct in_addr from_ip)
178 struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
180 if( work == NULL ) {
181 if( DEBUGLVL( 0 ) ) {
182 dbgtext( "domain_master_node_status_success:\n" );
183 dbgtext( "Unable to find workgroup " );
184 dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
186 return;
189 if( DEBUGLVL( 3 ) ) {
190 dbgtext( "domain_master_node_status_success:\n" );
191 dbgtext( "Success in node status for workgroup " );
192 dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
195 /* Go through the list of names found at answers->rdata and look for
196 the first SERVER<0x20> name. */
198 if (answers->rdlength > 0) {
199 char *p = answers->rdata;
200 int numnames = CVAL(p, 0);
202 p += 1;
204 while (numnames--) {
205 unstring qname;
206 uint16_t nb_flags;
207 int name_type;
209 pull_ascii_nstring(qname, sizeof(qname), p);
210 name_type = CVAL(p,15);
211 nb_flags = get_nb_flags(&p[16]);
212 trim_char(qname,'\0',' ');
214 p += 18;
216 if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) {
217 struct nmb_name nmbname;
219 make_nmb_name(&nmbname, qname, name_type);
221 /* Copy the dmb name and IP address
222 into the workgroup struct. */
224 work->dmb_name = nmbname;
225 putip((char *)&work->dmb_addr, &from_ip);
227 /* Do the local master browser announcement to the domain
228 master browser name and IP. */
229 announce_local_master_browser_to_domain_master_browser( work );
231 /* Now synchronise lists with the domain master browser. */
232 sync_with_dmb(work);
233 break;
236 } else if( DEBUGLVL( 0 ) ) {
237 dbgtext( "domain_master_node_status_success:\n" );
238 dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
239 dbgtext( "%s.\n", inet_ntoa(from_ip) );
243 /****************************************************************************
244 Function called when a node status query to a domain master browser IP fails.
245 ****************************************************************************/
247 static void domain_master_node_status_fail(struct subnet_record *subrec,
248 struct response_record *rrec)
250 struct userdata_struct *userdata = rrec->userdata;
252 if( DEBUGLVL( 0 ) ) {
253 dbgtext( "domain_master_node_status_fail:\n" );
254 dbgtext( "Doing a node status request to the domain master browser\n" );
255 dbgtext( "for workgroup %s ", userdata ? userdata->data : "NULL" );
256 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
257 dbgtext( "Cannot sync browser lists.\n" );
261 /****************************************************************************
262 Function called when a query for a WORKGROUP<1b> name succeeds.
263 ****************************************************************************/
265 static void find_domain_master_name_query_success(struct subnet_record *subrec,
266 struct userdata_struct *userdata_in,
267 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
270 * Unfortunately, finding the IP address of the Domain Master Browser,
271 * as we have here, is not enough. We need to now do a sync to the
272 * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
273 * respond to the SMBSERVER name. To get this name from IP
274 * address we do a Node status request, and look for the first
275 * NAME<0x20> in the response, and take that as the server name.
276 * We also keep a cache of the Domain Master Browser name for this
277 * workgroup in the Workgroup struct, so that if the same IP addess
278 * is returned every time, we don't need to do the node status
279 * request.
282 struct work_record *work;
283 struct nmb_name nmbname;
284 struct userdata_struct *userdata;
285 size_t size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
286 unstring qname;
288 pull_ascii_nstring(qname, sizeof(qname), q_name->name);
289 if( !(work = find_workgroup_on_subnet(subrec, qname)) ) {
290 if( DEBUGLVL( 0 ) ) {
291 dbgtext( "find_domain_master_name_query_success:\n" );
292 dbgtext( "Failed to find workgroup %s\n", qname);
294 return;
297 /* First check if we already have a dmb for this workgroup. */
299 if(!is_zero_ip_v4(work->dmb_addr) && ip_equal_v4(work->dmb_addr, answer_ip)) {
300 /* Do the local master browser announcement to the domain
301 master browser name and IP. */
302 announce_local_master_browser_to_domain_master_browser( work );
304 /* Now synchronise lists with the domain master browser. */
305 sync_with_dmb(work);
306 return;
307 } else {
308 zero_ip_v4(&work->dmb_addr);
311 /* Now initiate the node status request. */
313 /* We used to use the name "*",0x0 here, but some Windows
314 * servers don't answer that name. However we *know* they
315 * have the name workgroup#1b (as we just looked it up).
316 * So do the node status request on this name instead.
317 * Found at LBL labs. JRA.
320 make_nmb_name(&nmbname,work->work_group,0x1b);
322 /* Put the workgroup name into the userdata so we know
323 what workgroup we're talking to when the reply comes
324 back. */
326 /* Setup the userdata_struct - this is copied so we can use
327 a stack variable for this. */
329 if((userdata = (struct userdata_struct *)SMB_MALLOC(size)) == NULL) {
330 DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
331 return;
334 userdata->copy_fn = NULL;
335 userdata->free_fn = NULL;
336 userdata->userdata_len = strlen(work->work_group)+1;
337 strlcpy(userdata->data, work->work_group, size - sizeof(*userdata));
339 node_status( subrec, &nmbname, answer_ip,
340 domain_master_node_status_success,
341 domain_master_node_status_fail,
342 userdata);
344 zero_free(userdata, size);
347 /****************************************************************************
348 Function called when a query for a WORKGROUP<1b> name fails.
349 ****************************************************************************/
351 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
352 struct response_record *rrec,
353 struct nmb_name *question_name, int fail_code)
355 if( DEBUGLVL( 0 ) ) {
356 dbgtext( "find_domain_master_name_query_fail:\n" );
357 dbgtext( "Unable to find the Domain Master Browser name " );
358 dbgtext( "%s for the workgroup %s.\n",
359 nmb_namestr(question_name), question_name->name );
360 dbgtext( "Unable to sync browse lists in this workgroup.\n" );
364 /****************************************************************************
365 As a local master browser for a workgroup find the domain master browser
366 name, announce ourselves as local master browser to it and then pull the
367 full domain browse lists from it onto the given subnet.
368 **************************************************************************/
370 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
371 struct work_record *work)
373 /* Only do this if we are using a WINS server. */
374 if(we_are_a_wins_client() == False) {
375 if( DEBUGLVL( 10 ) ) {
376 dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
377 dbgtext( "Ignoring, as we are not a WINS client.\n" );
379 return;
382 /* First, query for the WORKGROUP<1b> name from the WINS server. */
383 query_name(unicast_subnet, work->work_group, 0x1b,
384 find_domain_master_name_query_success,
385 find_domain_master_name_query_fail,
386 NULL);
389 /****************************************************************************
390 Function called when a node status query to a domain master browser IP succeeds.
391 This function is only called on query to a Samba 1.9.18 or above WINS server.
393 Note that adding the workgroup name is enough for this workgroup to be
394 browsable by clients, as clients query the WINS server or broadcast
395 nets for the WORKGROUP<1b> name when they want to browse a workgroup
396 they are not in. We do not need to do a sync with this Domain Master
397 Browser in order for our browse clients to see machines in this workgroup.
398 JRA.
399 ****************************************************************************/
401 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
402 struct userdata_struct *userdata,
403 struct res_rec *answers,
404 struct in_addr from_ip)
406 unstring server_name;
408 server_name[0] = 0;
410 if( DEBUGLVL( 3 ) ) {
411 dbgtext( "get_domain_master_name_node_status_success:\n" );
412 dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
416 * Go through the list of names found at answers->rdata and look for
417 * the first WORKGROUP<0x1b> name.
420 if (answers->rdlength > 0) {
421 char *p = answers->rdata;
422 int numnames = CVAL(p, 0);
424 p += 1;
426 while (numnames--) {
427 unstring qname;
428 uint16_t nb_flags;
429 int name_type;
431 pull_ascii_nstring(qname, sizeof(qname), p);
432 name_type = CVAL(p,15);
433 nb_flags = get_nb_flags(&p[16]);
434 trim_char(qname,'\0',' ');
436 p += 18;
438 if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
439 server_name[0] == 0) {
440 /* this is almost certainly the server netbios name */
441 strlcpy(server_name, qname, sizeof(server_name));
442 continue;
445 if(!(nb_flags & NB_GROUP) && (name_type == 0x1b)) {
446 struct work_record *work;
448 if( DEBUGLVL( 5 ) ) {
449 dbgtext( "get_domain_master_name_node_status_success:\n" );
450 dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
451 dbgtext( "is a domain master browser for workgroup " );
452 dbgtext( "%s. Adding this name.\n", qname );
456 * If we don't already know about this workgroup, add it
457 * to the workgroup list on the unicast_subnet.
460 work = find_workgroup_on_subnet( subrec, qname);
461 if (work == NULL) {
462 struct nmb_name nmbname;
464 * Add it - with an hour in the cache.
466 work = create_workgroup_on_subnet(subrec, qname, 60*60);
467 if (work == NULL) {
468 return;
471 /* remember who the master is */
472 strlcpy(work->local_master_browser_name,
473 server_name,
474 sizeof(work->local_master_browser_name));
475 make_nmb_name(&nmbname, server_name, 0x20);
476 work->dmb_name = nmbname;
477 work->dmb_addr = from_ip;
479 break;
482 } else if( DEBUGLVL( 1 ) ) {
483 dbgtext( "get_domain_master_name_node_status_success:\n" );
484 dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
485 dbgtext( "%s.\n", inet_ntoa(from_ip) );
489 /****************************************************************************
490 Function called when a node status query to a domain master browser IP fails.
491 ****************************************************************************/
493 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
494 struct response_record *rrec)
496 if( DEBUGLVL( 2 ) ) {
497 dbgtext( "get_domain_master_name_node_status_fail:\n" );
498 dbgtext( "Doing a node status request to the domain master browser " );
499 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
500 dbgtext( "Cannot get workgroup name.\n" );
504 /****************************************************************************
505 Function called when a query for *<1b> name succeeds.
506 ****************************************************************************/
508 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
509 struct userdata_struct *userdata_in,
510 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
513 * We now have a list of all the domain master browsers for all workgroups
514 * that have registered with the WINS server. Now do a node status request
515 * to each one and look for the first 1b name in the reply. This will be
516 * the workgroup name that we will add to the unicast subnet as a 'non-local'
517 * workgroup.
520 struct nmb_name nmbname;
521 struct in_addr send_ip;
522 int i;
524 if( DEBUGLVL( 5 ) ) {
525 dbgtext( "find_all_domain_master_names_query_succes:\n" );
526 dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
527 dbgtext( "IP addresses for Domain Master Browsers.\n" );
530 for(i = 0; i < rrec->rdlength / 6; i++) {
531 /* Initiate the node status requests. */
532 make_nmb_name(&nmbname, "*", 0);
534 putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
537 * Don't send node status requests to ourself.
540 if(ismyip_v4( send_ip )) {
541 if( DEBUGLVL( 5 ) ) {
542 dbgtext( "find_all_domain_master_names_query_succes:\n" );
543 dbgtext( "Not sending node status to our own IP " );
544 dbgtext( "%s.\n", inet_ntoa(send_ip) );
546 continue;
549 if( DEBUGLVL( 5 ) ) {
550 dbgtext( "find_all_domain_master_names_query_success:\n" );
551 dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
554 node_status( subrec, &nmbname, send_ip,
555 get_domain_master_name_node_status_success,
556 get_domain_master_name_node_status_fail,
557 NULL);
561 /****************************************************************************
562 Function called when a query for *<1b> name fails.
563 ****************************************************************************/
564 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
565 struct response_record *rrec,
566 struct nmb_name *question_name, int fail_code)
568 if( DEBUGLVL( 10 ) ) {
569 dbgtext( "find_domain_master_name_query_fail:\n" );
570 dbgtext( "WINS server did not reply to a query for name " );
571 dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
572 dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
576 /****************************************************************************
577 If we are a domain master browser on the unicast subnet, do a query to the
578 WINS server for the *<1b> name. This will only work to a Samba WINS server,
579 so ignore it if we fail. If we succeed, contact each of the IP addresses in
580 turn and do a node status request to them. If this succeeds then look for a
581 <1b> name in the reply - this is the workgroup name. Add this to the unicast
582 subnet. This is expensive, so we only do this every 15 minutes.
583 **************************************************************************/
585 void collect_all_workgroup_names_from_wins_server(time_t t)
587 static time_t lastrun = 0;
588 struct work_record *work;
590 /* Only do this if we are using a WINS server. */
591 if(we_are_a_wins_client() == False)
592 return;
594 /* Check to see if we are a domain master browser on the unicast subnet. */
595 if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL) {
596 if( DEBUGLVL( 0 ) ) {
597 dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
598 dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
599 dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
601 return;
604 if(!AM_DOMAIN_MASTER_BROWSER(work))
605 return;
607 if ((lastrun != 0) && (t < lastrun + (15 * 60)))
608 return;
610 lastrun = t;
612 /* First, query for the *<1b> name from the WINS server. */
613 query_name(unicast_subnet, "*", 0x1b,
614 find_all_domain_master_names_query_success,
615 find_all_domain_master_names_query_fail,
616 NULL);
620 /****************************************************************************
621 If we are a domain master browser on the unicast subnet, do a regular sync
622 with all other DMBs that we know of on that subnet.
624 To prevent exponential network traffic with large numbers of workgroups
625 we use a randomised system where sync probability is inversely proportional
626 to the number of known workgroups
627 **************************************************************************/
629 void sync_all_dmbs(time_t t)
631 static time_t lastrun = 0;
632 struct work_record *work;
633 size_t count=0;
635 /* Only do this if we are using a WINS server. */
636 if(we_are_a_wins_client() == False)
637 return;
639 /* Check to see if we are a domain master browser on the
640 unicast subnet. */
641 work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
642 if (!work)
643 return;
645 if (!AM_DOMAIN_MASTER_BROWSER(work))
646 return;
648 if ((lastrun != 0) && (t < lastrun + (5 * 60)))
649 return;
651 /* count how many syncs we might need to do */
652 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
653 if (strcmp(lp_workgroup(), work->work_group)) {
654 count++;
658 /* leave if we don't have to do any syncs */
659 if (count == 0) {
660 return;
663 /* sync with a probability of 1/count */
664 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
665 if (strcmp(lp_workgroup(), work->work_group)) {
666 unstring dmb_name;
668 if (((unsigned)sys_random()) % count != 0)
669 continue;
671 lastrun = t;
673 if (!work->dmb_name.name[0]) {
674 /* we don't know the DMB - assume it is
675 the same as the unicast local master */
676 make_nmb_name(&work->dmb_name,
677 work->local_master_browser_name,
678 0x20);
681 pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
683 DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
684 dmb_name, inet_ntoa(work->dmb_addr)));
686 sync_browse_lists(work,
687 dmb_name,
688 work->dmb_name.name_type,
689 work->dmb_addr, False, False);