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