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