2 Unix SMB/Netbios implementation.
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.
28 extern struct in_addr ipzero;
29 extern pstring global_myname;
30 extern fstring global_myworkgroup;
32 /* This is our local master browser list database. */
33 extern ubi_dlList lmb_browserlist[];
35 /****************************************************************************
36 As a domain master browser, do a sync with a local master browser.
37 **************************************************************************/
38 static void sync_with_lmb(struct browse_cache_record *browc)
40 struct work_record *work;
42 if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) )
46 dbgtext( "sync_with_lmb:\n" );
47 dbgtext( "Failed to get a workgroup for a local master browser " );
48 dbgtext( "cache entry workgroup " );
49 dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
54 /* We should only be doing this if we are a domain master browser for
55 the given workgroup. Ensure this is so. */
57 if(!AM_DOMAIN_MASTER_BROWSER(work))
61 dbgtext( "sync_with_lmb:\n" );
62 dbgtext( "We are trying to sync with a local master browser " );
63 dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
64 dbgtext( "and we are not a domain master browser on this workgroup.\n" );
65 dbgtext( "Error!\n" );
72 dbgtext( "sync_with_lmb:\n" );
73 dbgtext( "Initiating sync with local master browser " );
74 dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
75 dbgtext( "for workgroup %s\n", browc->work_group );
78 sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
80 browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
83 /****************************************************************************
84 Sync or expire any local master browsers.
85 **************************************************************************/
86 void dmb_expire_and_sync_browser_lists(time_t t)
88 static time_t last_run = 0;
89 struct browse_cache_record *browc;
91 /* Only do this every 20 seconds. */
92 if (t - last_run < 20)
97 expire_lmb_browsers(t);
99 for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
101 browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
103 if (browc->sync_time < t)
104 sync_with_lmb(browc);
108 /****************************************************************************
109 As a local master browser, send an announce packet to the domain master browser.
110 **************************************************************************/
112 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
117 if(ismyip(work->dmb_addr))
121 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
122 dbgtext( "We are both a domain and a local master browser for " );
123 dbgtext( "workgroup %s. ", work->work_group );
124 dbgtext( "Do not announce to ourselves.\n" );
129 memset(outbuf,'\0',sizeof(outbuf));
131 CVAL(p,0) = ANN_MasterAnnouncement;
134 StrnCpy(p,global_myname,15);
136 p = skip_string(p,1);
140 dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
141 dbgtext( "Sending local master announce to " );
142 dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
146 send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
147 global_myname, 0x0, work->dmb_name.name, 0x0,
148 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
152 /****************************************************************************
153 As a local master browser, do a sync with a domain master browser.
154 **************************************************************************/
156 static void sync_with_dmb(struct work_record *work)
160 dbgtext( "sync_with_dmb:\n" );
161 dbgtext( "Initiating sync with domain master browser " );
162 dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
163 dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
164 dbgtext( "for workgroup %s\n", work->work_group );
167 sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type,
168 work->dmb_addr, False, True);
171 /****************************************************************************
172 Function called when a node status query to a domain master browser IP succeeds.
173 ****************************************************************************/
175 static void domain_master_node_status_success(struct subnet_record *subrec,
176 struct userdata_struct *userdata,
177 struct res_rec *answers,
178 struct in_addr from_ip)
180 struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
186 dbgtext( "domain_master_node_status_success:\n" );
187 dbgtext( "Unable to find workgroup " );
188 dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
195 dbgtext( "domain_master_node_status_success:\n" );
196 dbgtext( "Success in node status for workgroup " );
197 dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
200 /* Go through the list of names found at answers->rdata and look for
201 the first SERVER<0x20> name. */
203 if(answers->rdata != NULL)
205 char *p = answers->rdata;
206 int numnames = CVAL(p, 0);
217 name_type = CVAL(p,15);
218 nb_flags = get_nb_flags(&p[16]);
219 trim_string(qname,NULL," ");
223 if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
225 struct nmb_name nmbname;
227 make_nmb_name(&nmbname, qname, name_type);
229 /* Copy the dmb name and IP address
230 into the workgroup struct. */
232 work->dmb_name = nmbname;
233 putip((char *)&work->dmb_addr, &from_ip);
235 /* Do the local master browser announcement to the domain
236 master browser name and IP. */
237 announce_local_master_browser_to_domain_master_browser( work );
239 /* Now synchronise lists with the domain master browser. */
248 dbgtext( "domain_master_node_status_success:\n" );
249 dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
250 dbgtext( "%s.\n", inet_ntoa(from_ip) );
254 /****************************************************************************
255 Function called when a node status query to a domain master browser IP fails.
256 ****************************************************************************/
258 static void domain_master_node_status_fail(struct subnet_record *subrec,
259 struct response_record *rrec)
261 struct userdata_struct *userdata = rrec->userdata;
265 dbgtext( "domain_master_node_status_fail:\n" );
266 dbgtext( "Doing a node status request to the domain master browser\n" );
267 dbgtext( "for workgroup %s ", userdata->data );
268 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
269 dbgtext( "Cannot sync browser lists.\n" );
273 /****************************************************************************
274 Function called when a query for a WORKGROUP<1b> name succeeds.
275 ****************************************************************************/
277 static void find_domain_master_name_query_success(struct subnet_record *subrec,
278 struct userdata_struct *userdata_in,
279 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
282 * Unfortunately, finding the IP address of the Domain Master Browser,
283 * as we have here, is not enough. We need to now do a sync to the
284 * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
285 * respond to the SMBSERVER name. To get this name from IP
286 * address we do a Node status request, and look for the first
287 * NAME<0x20> in the response, and take that as the server name.
288 * We also keep a cache of the Domain Master Browser name for this
289 * workgroup in the Workgroup struct, so that if the same IP addess
290 * is returned every time, we don't need to do the node status
294 struct work_record *work;
295 struct nmb_name nmbname;
296 struct userdata_struct *userdata;
297 int size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
299 if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) )
303 dbgtext( "find_domain_master_name_query_success:\n" );
304 dbgtext( "Failed to find workgroup %s\n", q_name->name );
309 /* First check if we already have a dmb for this workgroup. */
311 if(!ip_equal(work->dmb_addr, ipzero) && ip_equal(work->dmb_addr, answer_ip))
313 /* Do the local master browser announcement to the domain
314 master browser name and IP. */
315 announce_local_master_browser_to_domain_master_browser( work );
317 /* Now synchronise lists with the domain master browser. */
322 putip((char *)&work->dmb_addr, &ipzero);
324 /* Now initiate the node status request. */
325 make_nmb_name(&nmbname,"*",0x0);
327 /* Put the workgroup name into the userdata so we know
328 what workgroup we're talking to when the reply comes
331 /* Setup the userdata_struct - this is copied so we can use
332 a stack variable for this. */
333 if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
335 DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
339 userdata->copy_fn = NULL;
340 userdata->free_fn = NULL;
341 userdata->userdata_len = strlen(work->work_group)+1;
342 pstrcpy(userdata->data, work->work_group);
344 node_status( subrec, &nmbname, answer_ip,
345 domain_master_node_status_success,
346 domain_master_node_status_fail,
349 zero_free(userdata, size);
352 /****************************************************************************
353 Function called when a query for a WORKGROUP<1b> name fails.
354 ****************************************************************************/
355 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
356 struct response_record *rrec,
357 struct nmb_name *question_name, int fail_code)
361 dbgtext( "find_domain_master_name_query_fail:\n" );
362 dbgtext( "Unable to find the Domain Master Browser name " );
363 dbgtext( "%s for the workgroup %s.\n",
364 nmb_namestr(question_name), question_name->name );
365 dbgtext( "Unable to sync browse lists in this workgroup.\n" );
369 /****************************************************************************
370 As a local master browser for a workgroup find the domain master browser
371 name, announce ourselves as local master browser to it and then pull the
372 full domain browse lists from it onto the given subnet.
373 **************************************************************************/
375 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
376 struct work_record *work)
378 struct nmb_name nmbname;
380 /* Only do this if we are using a WINS server. */
381 if(we_are_a_wins_client() == False)
385 dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
386 dbgtext( "Ignoring, as we are not a WINS client.\n" );
391 make_nmb_name(&nmbname,work->work_group,0x1b);
393 /* First, query for the WORKGROUP<1b> name from the WINS server. */
394 query_name(unicast_subnet, nmbname.name, nmbname.name_type,
395 find_domain_master_name_query_success,
396 find_domain_master_name_query_fail,
401 /****************************************************************************
402 Function called when a node status query to a domain master browser IP succeeds.
403 This function is only called on query to a Samba 1.9.18 or above WINS server.
405 Note that adding the workgroup name is enough for this workgroup to be
406 browsable by clients, as clients query the WINS server or broadcast
407 nets for the WORKGROUP<1b> name when they want to browse a workgroup
408 they are not in. We do not need to do a sync with this Domain Master
409 Browser in order for our browse clients to see machines in this workgroup.
411 ****************************************************************************/
413 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
414 struct userdata_struct *userdata,
415 struct res_rec *answers,
416 struct in_addr from_ip)
418 struct work_record *work;
425 dbgtext( "get_domain_master_name_node_status_success:\n" );
426 dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
430 * Go through the list of names found at answers->rdata and look for
431 * the first WORKGROUP<0x1b> name.
434 if(answers->rdata != NULL)
436 char *p = answers->rdata;
437 int numnames = CVAL(p, 0);
448 name_type = CVAL(p,15);
449 nb_flags = get_nb_flags(&p[16]);
450 trim_string(qname,NULL," ");
454 if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
455 server_name[0] == 0) {
456 /* this is almost certainly the server netbios name */
457 fstrcpy(server_name, qname);
461 if(!(nb_flags & NB_GROUP) && (name_type == 0x1b))
465 dbgtext( "get_domain_master_name_node_status_success:\n" );
466 dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
467 dbgtext( "is a domain master browser for workgroup " );
468 dbgtext( "%s. Adding this name.\n", qname );
472 * If we don't already know about this workgroup, add it
473 * to the workgroup list on the unicast_subnet.
475 if((work = find_workgroup_on_subnet( subrec, qname)) == NULL)
477 struct nmb_name nmbname;
479 * Add it - with an hour in the cache.
481 if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
484 /* remember who the master is */
485 fstrcpy(work->local_master_browser_name, server_name);
486 make_nmb_name(&nmbname, server_name, 0x20);
487 work->dmb_name = nmbname;
488 work->dmb_addr = from_ip;
497 dbgtext( "get_domain_master_name_node_status_success:\n" );
498 dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
499 dbgtext( "%s.\n", inet_ntoa(from_ip) );
503 /****************************************************************************
504 Function called when a node status query to a domain master browser IP fails.
505 ****************************************************************************/
507 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
508 struct response_record *rrec)
512 dbgtext( "get_domain_master_name_node_status_fail:\n" );
513 dbgtext( "Doing a node status request to the domain master browser " );
514 dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
515 dbgtext( "Cannot get workgroup name.\n" );
519 /****************************************************************************
520 Function called when a query for *<1b> name succeeds.
521 ****************************************************************************/
523 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
524 struct userdata_struct *userdata_in,
525 struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
528 * We now have a list of all the domain master browsers for all workgroups
529 * that have registered with the WINS server. Now do a node status request
530 * to each one and look for the first 1b name in the reply. This will be
531 * the workgroup name that we will add to the unicast subnet as a 'non-local'
535 struct nmb_name nmbname;
536 struct in_addr send_ip;
541 dbgtext( "find_all_domain_master_names_query_succes:\n" );
542 dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
543 dbgtext( "IP addresses for Domain Master Browsers.\n" );
546 for(i = 0; i < rrec->rdlength / 6; i++)
548 /* Initiate the node status requests. */
549 make_nmb_name(&nmbname, "*", 0);
551 putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
554 * Don't send node status requests to ourself.
557 if(ismyip( send_ip ))
561 dbgtext( "find_all_domain_master_names_query_succes:\n" );
562 dbgtext( "Not sending node status to our own IP " );
563 dbgtext( "%s.\n", inet_ntoa(send_ip) );
570 dbgtext( "find_all_domain_master_names_query_success:\n" );
571 dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
574 node_status( subrec, &nmbname, send_ip,
575 get_domain_master_name_node_status_success,
576 get_domain_master_name_node_status_fail,
581 /****************************************************************************
582 Function called when a query for *<1b> name fails.
583 ****************************************************************************/
584 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
585 struct response_record *rrec,
586 struct nmb_name *question_name, int fail_code)
590 dbgtext( "find_domain_master_name_query_fail:\n" );
591 dbgtext( "WINS server did not reply to a query for name " );
592 dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
593 dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
597 /****************************************************************************
598 If we are a domain master browser on the unicast subnet, do a query to the
599 WINS server for the *<1b> name. This will only work to a Samba WINS server,
600 so ignore it if we fail. If we succeed, contact each of the IP addresses in
601 turn and do a node status request to them. If this succeeds then look for a
602 <1b> name in the reply - this is the workgroup name. Add this to the unicast
603 subnet. This is expensive, so we only do this every 15 minutes.
604 **************************************************************************/
605 void collect_all_workgroup_names_from_wins_server(time_t t)
607 static time_t lastrun = 0;
608 struct work_record *work;
609 struct nmb_name nmbname;
611 /* Only do this if we are using a WINS server. */
612 if(we_are_a_wins_client() == False)
615 /* Check to see if we are a domain master browser on the unicast subnet. */
616 if((work = find_workgroup_on_subnet( unicast_subnet, global_myworkgroup)) == NULL)
620 dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
621 dbgtext( "Cannot find my workgroup %s ", global_myworkgroup );
622 dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
627 if(!AM_DOMAIN_MASTER_BROWSER(work))
630 if ((lastrun != 0) && (t < lastrun + (15 * 60)))
635 make_nmb_name(&nmbname,"*",0x1b);
637 /* First, query for the *<1b> name from the WINS server. */
638 query_name(unicast_subnet, nmbname.name, nmbname.name_type,
639 find_all_domain_master_names_query_success,
640 find_all_domain_master_names_query_fail,
645 /****************************************************************************
646 If we are a domain master browser on the unicast subnet, do a regular sync
647 with all other DMBs that we know of on that subnet.
649 To prevent exponential network traffic with large numbers of workgroups
650 we use a randomised system where sync probability is inversely proportional
651 to the number of known workgroups
652 **************************************************************************/
653 void sync_all_dmbs(time_t t)
655 static time_t lastrun = 0;
656 struct work_record *work;
659 /* Only do this if we are using a WINS server. */
660 if(we_are_a_wins_client() == False)
663 /* Check to see if we are a domain master browser on the
665 work = find_workgroup_on_subnet(unicast_subnet, global_myworkgroup);
668 if (!AM_DOMAIN_MASTER_BROWSER(work))
671 if ((lastrun != 0) && (t < lastrun + (5 * 60)))
674 /* count how many syncs we might need to do */
675 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
676 if (strcmp(global_myworkgroup, work->work_group)) {
681 /* sync with a probability of 1/count */
682 for (work=unicast_subnet->workgrouplist; work; work = work->next) {
683 if (strcmp(global_myworkgroup, work->work_group)) {
684 if (((unsigned)sys_random()) % count != 0) continue;
688 if (!work->dmb_name.name[0]) {
689 /* we don't know the DMB - assume it is
690 the same as the unicast local master */
691 make_nmb_name(&work->dmb_name,
692 work->local_master_browser_name,
696 DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
698 inet_ntoa(work->dmb_addr)));
699 sync_browse_lists(work,
701 work->dmb_name.name_type,
702 work->dmb_addr, False, False);