Fix length on mailslots. Looks like it should have been 0x17, not decimal 17.
[jra/samba/.git] / source3 / nmbd / nmbd_browsesync.c
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-1998
7    
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 2 of the License, or
11    (at your option) any later version.
12    
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.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21    
22 */
23
24 #include "includes.h"
25 #include "smb.h"
26
27 extern pstring global_myname;
28 extern fstring global_myworkgroup;
29
30 /* This is our local master browser list database. */
31 extern ubi_dlList lmb_browserlist[];
32
33 /****************************************************************************
34 As a domain master browser, do a sync with a local master browser.
35 **************************************************************************/
36 static void sync_with_lmb(struct browse_cache_record *browc)
37 {                     
38   struct work_record *work;
39
40   if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) )
41   {
42     if( DEBUGLVL( 0 ) )
43     {
44       dbgtext( "sync_with_lmb:\n" );
45       dbgtext( "Failed to get a workgroup for a local master browser " );
46       dbgtext( "cache entry workgroup " );
47       dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
48     }
49     return;
50   }
51
52   /* We should only be doing this if we are a domain master browser for
53      the given workgroup. Ensure this is so. */
54
55   if(!AM_DOMAIN_MASTER_BROWSER(work))
56   {
57     if( DEBUGLVL( 0 ) )
58     {
59       dbgtext( "sync_with_lmb:\n" );
60       dbgtext( "We are trying to sync with a local master browser " );
61       dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
62       dbgtext( "and we are not a domain master browser on this workgroup.\n" );
63       dbgtext( "Error!\n" );
64     }
65     return;
66   }
67
68   if( DEBUGLVL( 2 ) )
69   {
70     dbgtext( "sync_with_lmb:\n" );
71     dbgtext( "Initiating sync with local master browser " );
72     dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
73     dbgtext( "for workgroup %s\n", browc->work_group );
74   }
75
76   sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
77
78   browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
79 }
80
81 /****************************************************************************
82 Sync or expire any local master browsers.
83 **************************************************************************/
84 void dmb_expire_and_sync_browser_lists(time_t t)
85 {
86   static time_t last_run = 0;
87   struct browse_cache_record *browc;
88
89   /* Only do this every 20 seconds. */  
90   if (t - last_run < 20) 
91    return;
92
93   last_run = t;
94
95   expire_lmb_browsers(t);
96
97   for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
98        browc;
99        browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
100   {
101     if (browc->sync_time < t)
102       sync_with_lmb(browc);
103   }
104 }
105
106 /****************************************************************************
107 As a local master browser, send an announce packet to the domain master browser.
108 **************************************************************************/
109
110 static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
111 {
112   pstring outbuf;
113   char *p;
114
115   if(ismyip(work->dmb_addr))
116   {
117     if( DEBUGLVL( 2 ) )
118     {
119       dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
120       dbgtext( "We are both a domain and a local master browser for " );
121       dbgtext( "workgroup %s.  ", work->work_group );
122       dbgtext( "Do not announce to ourselves.\n" );
123     }
124     return;
125   }
126
127   memset(outbuf,'\0',sizeof(outbuf));
128   p = outbuf;
129   SCVAL(p,0,ANN_MasterAnnouncement);
130   p++;
131
132   StrnCpy(p,global_myname,15);
133   strupper(p);
134   p = skip_string(p,1);
135
136   if( DEBUGLVL( 4 ) )
137   {
138     dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
139     dbgtext( "Sending local master announce to " );
140     dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
141                                        work->work_group );
142   }
143
144   send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
145                 global_myname, 0x0, work->dmb_name.name, 0x0, 
146                 work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
147
148 }
149
150 /****************************************************************************
151 As a local master browser, do a sync with a domain master browser.
152 **************************************************************************/
153
154 static void sync_with_dmb(struct work_record *work)
155 {
156   if( DEBUGLVL( 2 ) )
157   {
158     dbgtext( "sync_with_dmb:\n" );
159     dbgtext( "Initiating sync with domain master browser " );
160     dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
161     dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
162     dbgtext( "for workgroup %s\n", work->work_group );
163   }
164
165   sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type, 
166                     work->dmb_addr, False, True);
167 }
168
169 /****************************************************************************
170   Function called when a node status query to a domain master browser IP succeeds.
171 ****************************************************************************/
172
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)
177 {
178   struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
179
180   if( work == NULL )
181   {
182     if( DEBUGLVL( 0 ) )
183     {
184       dbgtext( "domain_master_node_status_success:\n" );
185       dbgtext( "Unable to find workgroup " );
186       dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
187     }
188     return;
189   }
190
191   if( DEBUGLVL( 3 ) )
192   {
193     dbgtext( "domain_master_node_status_success:\n" );
194     dbgtext( "Success in node status for workgroup " );
195     dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
196   }
197
198   /* Go through the list of names found at answers->rdata and look for
199      the first SERVER<0x20> name. */
200
201   if(answers->rdata != NULL)
202   {
203     char *p = answers->rdata;
204     int numnames = CVAL(p, 0);
205
206     p += 1;
207
208     while (numnames--)
209     {
210       char qname[17];
211       uint16 nb_flags;
212       int name_type;
213
214       StrnCpy(qname,p,15);
215       name_type = CVAL(p,15);
216       nb_flags = get_nb_flags(&p[16]);
217       trim_string(qname,NULL," ");
218
219       p += 18;
220
221       if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
222       {
223         struct nmb_name nmbname;
224
225         make_nmb_name(&nmbname, qname, name_type);
226
227         /* Copy the dmb name and IP address
228            into the workgroup struct. */
229
230         work->dmb_name = nmbname;
231         putip((char *)&work->dmb_addr, &from_ip);
232
233         /* Do the local master browser announcement to the domain
234            master browser name and IP. */
235         announce_local_master_browser_to_domain_master_browser( work );
236
237         /* Now synchronise lists with the domain master browser. */
238         sync_with_dmb(work);
239         break;
240       }
241     }
242   }
243   else
244     if( DEBUGLVL( 0 ) )
245     {
246       dbgtext( "domain_master_node_status_success:\n" );
247       dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
248       dbgtext( "%s.\n", inet_ntoa(from_ip) );
249     }
250 }
251
252 /****************************************************************************
253   Function called when a node status query to a domain master browser IP fails.
254 ****************************************************************************/
255
256 static void domain_master_node_status_fail(struct subnet_record *subrec,
257                        struct response_record *rrec)
258 {
259   struct userdata_struct *userdata = rrec->userdata;
260
261   if( DEBUGLVL( 0 ) )
262   {
263     dbgtext( "domain_master_node_status_fail:\n" );
264     dbgtext( "Doing a node status request to the domain master browser\n" );
265     dbgtext( "for workgroup %s ", userdata->data );
266     dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
267     dbgtext( "Cannot sync browser lists.\n" );
268   }
269 }
270
271 /****************************************************************************
272   Function called when a query for a WORKGROUP<1b> name succeeds.
273 ****************************************************************************/
274
275 static void find_domain_master_name_query_success(struct subnet_record *subrec,
276                         struct userdata_struct *userdata_in,
277                         struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
278 {
279   /* 
280    * Unfortunately, finding the IP address of the Domain Master Browser,
281    * as we have here, is not enough. We need to now do a sync to the
282    * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
283    * respond to the SMBSERVER name. To get this name from IP
284    * address we do a Node status request, and look for the first
285    * NAME<0x20> in the response, and take that as the server name.
286    * We also keep a cache of the Domain Master Browser name for this
287    * workgroup in the Workgroup struct, so that if the same IP addess
288    * is returned every time, we don't need to do the node status
289    * request.
290    */
291
292   struct work_record *work;
293   struct nmb_name nmbname;
294   struct userdata_struct *userdata;
295   int size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
296
297   if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) )
298   {
299     if( DEBUGLVL( 0 ) )
300       {
301       dbgtext( "find_domain_master_name_query_success:\n" );
302       dbgtext( "Failed to find workgroup %s\n", q_name->name );
303       }
304     return;
305   }
306
307   /* First check if we already have a dmb for this workgroup. */
308
309   if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip))
310   {
311     /* Do the local master browser announcement to the domain
312        master browser name and IP. */
313     announce_local_master_browser_to_domain_master_browser( work );
314
315     /* Now synchronise lists with the domain master browser. */
316     sync_with_dmb(work);
317     return;
318   }
319   else
320     zero_ip(&work->dmb_addr);
321
322   /* Now initiate the node status request. */
323   make_nmb_name(&nmbname,"*",0x0);
324
325   /* Put the workgroup name into the userdata so we know
326      what workgroup we're talking to when the reply comes
327      back. */
328
329   /* Setup the userdata_struct - this is copied so we can use
330      a stack variable for this. */
331   if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
332   {
333     DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
334     return;
335   }
336
337   userdata->copy_fn = NULL;
338   userdata->free_fn = NULL;
339   userdata->userdata_len = strlen(work->work_group)+1;
340   pstrcpy(userdata->data, work->work_group);
341
342   node_status( subrec, &nmbname, answer_ip, 
343                domain_master_node_status_success,
344                domain_master_node_status_fail,
345                userdata);
346
347   zero_free(userdata, size);
348 }
349
350 /****************************************************************************
351   Function called when a query for a WORKGROUP<1b> name fails.
352   ****************************************************************************/
353 static void find_domain_master_name_query_fail(struct subnet_record *subrec,
354                                     struct response_record *rrec,
355                                     struct nmb_name *question_name, int fail_code)
356 {
357   if( DEBUGLVL( 0 ) )
358   {
359     dbgtext( "find_domain_master_name_query_fail:\n" );
360     dbgtext( "Unable to find the Domain Master Browser name " );
361     dbgtext( "%s for the workgroup %s.\n",
362              nmb_namestr(question_name), question_name->name );
363     dbgtext( "Unable to sync browse lists in this workgroup.\n" );
364   }
365 }
366
367 /****************************************************************************
368 As a local master browser for a workgroup find the domain master browser
369 name, announce ourselves as local master browser to it and then pull the
370 full domain browse lists from it onto the given subnet.
371 **************************************************************************/
372
373 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
374                                                    struct work_record *work)
375 {
376   struct nmb_name nmbname;
377
378   /* Only do this if we are using a WINS server. */
379   if(we_are_a_wins_client() == False)
380   {
381     if( DEBUGLVL( 10 ) )
382     {
383       dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
384       dbgtext( "Ignoring, as we are not a WINS client.\n" );
385     }
386     return;
387   }
388
389   make_nmb_name(&nmbname,work->work_group,0x1b);
390
391   /* First, query for the WORKGROUP<1b> name from the WINS server. */
392   query_name(unicast_subnet, nmbname.name, nmbname.name_type,
393              find_domain_master_name_query_success,
394              find_domain_master_name_query_fail,
395              NULL);
396
397 }
398
399 /****************************************************************************
400   Function called when a node status query to a domain master browser IP succeeds.
401   This function is only called on query to a Samba 1.9.18 or above WINS server.
402
403   Note that adding the workgroup name is enough for this workgroup to be
404   browsable by clients, as clients query the WINS server or broadcast 
405   nets for the WORKGROUP<1b> name when they want to browse a workgroup
406   they are not in. We do not need to do a sync with this Domain Master
407   Browser in order for our browse clients to see machines in this workgroup.
408   JRA.
409 ****************************************************************************/
410
411 static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
412                                               struct userdata_struct *userdata,
413                                               struct res_rec *answers,
414                                               struct in_addr from_ip)
415 {
416   struct work_record *work;
417   fstring server_name;
418
419   server_name[0] = 0;
420
421   if( DEBUGLVL( 3 ) )
422   {
423     dbgtext( "get_domain_master_name_node_status_success:\n" );
424     dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
425   }
426
427   /* 
428    * Go through the list of names found at answers->rdata and look for
429    * the first WORKGROUP<0x1b> name.
430    */
431
432   if(answers->rdata != NULL)
433   {
434     char *p = answers->rdata;
435     int numnames = CVAL(p, 0);
436
437     p += 1;
438
439     while (numnames--)
440     {
441       char qname[17];
442       uint16 nb_flags;
443       int name_type;
444
445       StrnCpy(qname,p,15);
446       name_type = CVAL(p,15);
447       nb_flags = get_nb_flags(&p[16]);
448       trim_string(qname,NULL," ");
449
450       p += 18;
451
452       if(!(nb_flags & NB_GROUP) && (name_type == 0x00) && 
453          server_name[0] == 0) {
454               /* this is almost certainly the server netbios name */
455               fstrcpy(server_name, qname);
456               continue;
457       }
458
459       if(!(nb_flags & NB_GROUP) && (name_type == 0x1b))
460       {
461         if( DEBUGLVL( 5 ) )
462         {
463           dbgtext( "get_domain_master_name_node_status_success:\n" );
464           dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
465           dbgtext( "is a domain master browser for workgroup " );
466           dbgtext( "%s. Adding this name.\n", qname );
467         }
468
469         /* 
470          * If we don't already know about this workgroup, add it
471          * to the workgroup list on the unicast_subnet.
472          */
473         if((work = find_workgroup_on_subnet( subrec, qname)) == NULL)
474         {
475                 struct nmb_name nmbname;
476                 /* 
477                  * Add it - with an hour in the cache.
478                  */
479                 if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
480                         return;
481
482                 /* remember who the master is */
483                 fstrcpy(work->local_master_browser_name, server_name);
484                 make_nmb_name(&nmbname, server_name, 0x20);
485                 work->dmb_name = nmbname;
486                 work->dmb_addr = from_ip;
487         }
488         break;
489       }
490     }
491   }
492   else
493     if( DEBUGLVL( 0 ) )
494     {
495       dbgtext( "get_domain_master_name_node_status_success:\n" );
496       dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
497       dbgtext( "%s.\n", inet_ntoa(from_ip) );
498     }
499 }
500
501 /****************************************************************************
502   Function called when a node status query to a domain master browser IP fails.
503 ****************************************************************************/
504
505 static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
506                        struct response_record *rrec)
507 {
508   if( DEBUGLVL( 0 ) )
509   {
510     dbgtext( "get_domain_master_name_node_status_fail:\n" );
511     dbgtext( "Doing a node status request to the domain master browser " );
512     dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
513     dbgtext( "Cannot get workgroup name.\n" );
514   }
515 }
516
517 /****************************************************************************
518   Function called when a query for *<1b> name succeeds.
519 ****************************************************************************/
520
521 static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
522                         struct userdata_struct *userdata_in,
523                         struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
524 {
525   /* 
526    * We now have a list of all the domain master browsers for all workgroups
527    * that have registered with the WINS server. Now do a node status request
528    * to each one and look for the first 1b name in the reply. This will be
529    * the workgroup name that we will add to the unicast subnet as a 'non-local'
530    * workgroup.
531    */
532
533   struct nmb_name nmbname;
534   struct in_addr send_ip;
535   int i;
536
537   if( DEBUGLVL( 5 ) )
538   {
539     dbgtext( "find_all_domain_master_names_query_succes:\n" );
540     dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
541     dbgtext( "IP addresses for Domain Master Browsers.\n" );
542   }
543
544   for(i = 0; i < rrec->rdlength / 6; i++)
545   {
546     /* Initiate the node status requests. */
547     make_nmb_name(&nmbname, "*", 0);
548
549     putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
550
551     /* 
552      * Don't send node status requests to ourself.
553      */
554
555     if(ismyip( send_ip ))
556     {
557       if( DEBUGLVL( 5 ) )
558       {
559         dbgtext( "find_all_domain_master_names_query_succes:\n" );
560         dbgtext( "Not sending node status to our own IP " );
561         dbgtext( "%s.\n", inet_ntoa(send_ip) );
562       }
563       continue;
564     }
565
566     if( DEBUGLVL( 5 ) )
567     {
568       dbgtext( "find_all_domain_master_names_query_success:\n" );
569       dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
570     }
571
572     node_status( subrec, &nmbname, send_ip, 
573                  get_domain_master_name_node_status_success,
574                  get_domain_master_name_node_status_fail,
575                  NULL);
576   }
577 }
578
579 /****************************************************************************
580   Function called when a query for *<1b> name fails.
581   ****************************************************************************/
582 static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
583                                     struct response_record *rrec,
584                                     struct nmb_name *question_name, int fail_code)
585 {
586   if( DEBUGLVL( 10 ) )
587   {
588     dbgtext( "find_domain_master_name_query_fail:\n" );
589     dbgtext( "WINS server did not reply to a query for name " );
590     dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
591     dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
592   }
593 }
594
595 /****************************************************************************
596  If we are a domain master browser on the unicast subnet, do a query to the
597  WINS server for the *<1b> name. This will only work to a Samba WINS server,
598  so ignore it if we fail. If we succeed, contact each of the IP addresses in
599  turn and do a node status request to them. If this succeeds then look for a
600  <1b> name in the reply - this is the workgroup name. Add this to the unicast
601  subnet. This is expensive, so we only do this every 15 minutes.
602 **************************************************************************/
603 void collect_all_workgroup_names_from_wins_server(time_t t)
604 {
605   static time_t lastrun = 0;
606   struct work_record *work;
607   struct nmb_name nmbname;
608
609   /* Only do this if we are using a WINS server. */
610   if(we_are_a_wins_client() == False)
611     return;
612
613   /* Check to see if we are a domain master browser on the unicast subnet. */
614   if((work = find_workgroup_on_subnet( unicast_subnet, global_myworkgroup)) == NULL)
615   {
616     if( DEBUGLVL( 0 ) )
617     {
618       dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
619       dbgtext( "Cannot find my workgroup %s ", global_myworkgroup );
620       dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
621     }
622     return;
623   }
624
625   if(!AM_DOMAIN_MASTER_BROWSER(work))
626     return;
627
628   if ((lastrun != 0) && (t < lastrun + (15 * 60)))
629     return;
630      
631   lastrun = t;
632
633   make_nmb_name(&nmbname,"*",0x1b);
634
635   /* First, query for the *<1b> name from the WINS server. */
636   query_name(unicast_subnet, nmbname.name, nmbname.name_type,
637              find_all_domain_master_names_query_success,
638              find_all_domain_master_names_query_fail,
639              NULL);
640
641
642
643 /****************************************************************************
644  If we are a domain master browser on the unicast subnet, do a regular sync
645  with all other DMBs that we know of on that subnet.
646
647 To prevent exponential network traffic with large numbers of workgroups
648 we use a randomised system where sync probability is inversely proportional
649 to the number of known workgroups
650 **************************************************************************/
651 void sync_all_dmbs(time_t t)
652 {
653         static time_t lastrun = 0;
654         struct work_record *work;
655         int count=0;
656
657         /* Only do this if we are using a WINS server. */
658         if(we_are_a_wins_client() == False)
659                 return;
660
661         /* Check to see if we are a domain master browser on the
662            unicast subnet. */
663         work = find_workgroup_on_subnet(unicast_subnet, global_myworkgroup);
664         if (!work) return;
665
666         if (!AM_DOMAIN_MASTER_BROWSER(work))
667                 return;
668
669         if ((lastrun != 0) && (t < lastrun + (5 * 60)))
670                 return;
671      
672         /* count how many syncs we might need to do */
673         for (work=unicast_subnet->workgrouplist; work; work = work->next) {
674                 if (strcmp(global_myworkgroup, work->work_group)) {
675                         count++;
676                 }
677         }
678
679         /* sync with a probability of 1/count */
680         for (work=unicast_subnet->workgrouplist; work; work = work->next) {
681                 if (strcmp(global_myworkgroup, work->work_group)) {
682                         if (((unsigned)sys_random()) % count != 0) continue;
683
684                         lastrun = t;
685
686                         if (!work->dmb_name.name[0]) {
687                                 /* we don't know the DMB - assume it is
688                                    the same as the unicast local master */
689                                 make_nmb_name(&work->dmb_name, 
690                                               work->local_master_browser_name,
691                                               0x20);
692                         }
693
694                         DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
695                                  work->dmb_name.name, 
696                                  inet_ntoa(work->dmb_addr)));
697                         sync_browse_lists(work, 
698                                           work->dmb_name.name,
699                                           work->dmb_name.name_type, 
700                                           work->dmb_addr, False, False);
701                 }
702         }
703