Removed use of 'domain controller' parameter for browsing system
[kai/samba.git] / source / nameannounce.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-1997
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21    Revision History:
22
23    14 jan 96: lkcl@pires.co.uk
24    added multiple workgroup domain master support
25
26 */
27
28 #include "includes.h"
29
30 #define TEST_CODE
31
32 extern int DEBUGLEVEL;
33 extern BOOL CanRecurse;
34
35 extern struct in_addr ipzero;
36
37 extern pstring myname;
38 extern fstring myworkgroup;
39
40 extern int ClientDGRAM;
41 extern int ClientNMB;
42
43 /* this is our domain/workgroup/server database */
44 extern struct subnet_record *subnetlist;
45
46 extern int  updatecount;
47 extern int  workgroup_count;
48
49 extern struct in_addr wins_ip;
50
51 extern pstring scope;
52
53 /****************************************************************************
54   send a announce request to the local net
55   **************************************************************************/
56 void announce_request(struct work_record *work, struct in_addr ip)
57 {
58   pstring outbuf;
59   char *p;
60
61   if (!work) return;
62
63   work->needannounce = True;
64
65   DEBUG(2,("sending announce request to %s for workgroup %s\n",
66            inet_ntoa(ip),work->work_group));
67
68   bzero(outbuf,sizeof(outbuf));
69   p = outbuf;
70   CVAL(p,0) = ANN_AnnouncementRequest;
71   p++;
72
73   CVAL(p,0) = work->token; /* (local) unique workgroup token id */
74   p++;
75   StrnCpy(p,myname,16);
76   strupper(p);
77   p = skip_string(p,1);
78   
79   /* XXXX note: if we sent the announcement request to 0x1d instead
80      of 0x1e, then we could get the master browser to announce to
81      us instead of the members of the workgroup. wha-hey! */
82
83   send_mailslot_reply(False, BROWSE_MAILSLOT,ClientDGRAM,
84               outbuf,PTR_DIFF(p,outbuf),
85                       myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
86 }
87
88
89 /****************************************************************************
90   request an announcement
91   **************************************************************************/
92 void do_announce_request(char *info, char *to_name, int announce_type, 
93                          int from,
94                          int to, struct in_addr dest_ip)
95 {
96   pstring outbuf;
97   char *p;
98   
99   bzero(outbuf,sizeof(outbuf));
100   p = outbuf;
101   CVAL(p,0) = announce_type; 
102   p++;
103   
104   DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
105            announce_type, info, inet_ntoa(dest_ip),to_name,to));
106   
107   StrnCpy(p,info,16);
108   strupper(p);
109   p = skip_string(p,1);
110   
111   send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
112               outbuf,PTR_DIFF(p,outbuf),
113                       myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
114 }
115
116
117 /****************************************************************************
118   find a server responsible for a workgroup, and sync browse lists
119   control ends up back here via response_name_query.
120   **************************************************************************/
121 void sync_server(enum state_type state, char *serv_name, char *work_name, 
122                  int name_type,
123                  struct subnet_record *d,
124                  struct in_addr ip)
125 {                     
126   /* with a domain master we can get the whole list (not local only list) */
127   BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
128
129   add_browser_entry(serv_name, name_type, work_name, 0, d, ip, local_only);
130
131   if (state == NAME_STATUS_DOM_SRV_CHK)
132   {
133     /* announce ourselves as a master browser to serv_name */
134     do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
135                           0x20, 0, ip);
136   }
137 }
138
139
140 /****************************************************************************
141   send a host announcement packet
142   **************************************************************************/
143 void do_announce_host(int command,
144                 char *from_name, int from_type, struct in_addr from_ip,
145                 char *to_name  , int to_type  , struct in_addr to_ip,
146                 time_t announce_interval,
147                 char *server_name, int server_type, char *server_comment)
148 {
149         pstring outbuf;
150         char *p;
151
152         bzero(outbuf,sizeof(outbuf));
153         p = outbuf+1;
154
155         /* command type */
156         CVAL(outbuf,0) = command;
157
158         /* announcement parameters */
159         CVAL(p,0) = updatecount;
160         SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
161
162         StrnCpy(p+5,server_name,16);
163         strupper(p+5);
164
165         CVAL(p,21) = MAJOR_VERSION; /* major version */
166         CVAL(p,22) = MINOR_VERSION; /* minor version */
167
168         SIVAL(p,23,server_type);
169         /* browse version: got from NT/AS 4.00  - Value defined in smb.h (JHT)*/
170         SSVAL(p,27,BROWSER_ELECTION_VERSION);
171         SSVAL(p,29,BROWSER_CONSTANT); /* browse signature */
172
173         strcpy(p+31,server_comment);
174         p += 31;
175         p = skip_string(p,1);
176
177     debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
178
179         /* send the announcement */
180         send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,outbuf,
181                                           PTR_DIFF(p,outbuf),
182                                           from_name, to_name,
183                                           from_type, to_type,
184                                           to_ip, from_ip);
185 }
186
187
188 /****************************************************************************
189 announce all samba's server entries as 'gone'.
190 ****************************************************************************/
191 void announce_my_servers_removed(void)
192 {
193         struct subnet_record *d; 
194         for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
195         {
196                 struct work_record *work;
197                 for (work = d->workgrouplist; work; work = work->next)
198                 {
199                         struct server_record *s;
200                         for (s = work->serverlist; s; s = s->next)
201                         {
202                                 if (!strequal(myname,s->serv.name)) continue;
203                                 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
204                         }
205                 }
206         }
207 }
208
209
210 /****************************************************************************
211   announce a server entry
212   ****************************************************************************/
213 void announce_server(struct subnet_record *d, struct work_record *work,
214                      char *name, char *comment, time_t ttl, int server_type)
215 {
216     /* domain type cannot have anything in it that might confuse
217        a client into thinking that the domain is in fact a server.
218        (SV_TYPE_SERVER_UNIX, for example)
219      */
220         uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
221         BOOL wins_iface = ip_equal(d->bcast_ip, wins_ip);
222         
223         if (wins_iface && server_type != 0)
224         {
225                 /* wins pseudo-ip interface */
226                 if (!AM_MASTER(work))
227                 {
228                         /* non-master announce by unicast to the domain 
229                            master */
230                         if (!lp_wins_support() && *lp_wins_server())
231                         {
232                                 /* look up the domain master with the WINS server */
233                                 queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
234                                          NAME_QUERY_ANNOUNCE_HOST,
235                                          work->work_group,0x1b,0,ttl*1000,
236                                          server_type,name,comment,
237                                          False, False, ipzero, d->bcast_ip);
238                         }
239                         else
240                         {
241                                 /* we are the WINS server, but not the domain master.  */
242                                 /* XXXX we need to look up the domain master in our
243                                    WINS database list, and do_announce_host(). maybe
244                                    we could do a name query on the unsuspecting domain
245                                    master just to make sure it's awake. */
246                         }
247                 }
248
249                 /* XXXX any other kinds of announcements we need to consider here?
250                    e.g local master browsers... no. local master browsers do
251                    local master announcements to their domain master. they even
252                    use WINS lookup of the domain master if another wins server
253                    is being used! 
254                  */
255         }
256         else
257         {
258                 if (AM_MASTER(work))
259                 {
260                         DEBUG(3,("sending local master announce to %s for %s(1e)\n",
261                                                         inet_ntoa(d->bcast_ip),work->work_group));
262
263                         do_announce_host(ANN_LocalMasterAnnouncement,
264                                                         name            , 0x00, d->myip,
265                                                         work->work_group, 0x1e, d->bcast_ip,
266                                                         ttl,
267                                                         name, server_type, comment);
268
269                         DEBUG(3,("sending domain announce to %s for %s\n",
270                                                         inet_ntoa(d->bcast_ip),work->work_group));
271
272                         /* XXXX should we do a domain-announce-kill? */
273                         if (server_type != 0)
274                         {
275                                 do_announce_host(ANN_DomainAnnouncement,
276                                                         name    , 0x00, d->myip,
277                                                         MSBROWSE, 0x01, d->bcast_ip,
278                                                         ttl,
279                                                         work->work_group, server_type ? domain_type : 0,
280                                                         name);
281                         }
282                 }
283                 else
284                 {
285                         DEBUG(3,("sending host announce to %s for %s(1d)\n",
286                                                         inet_ntoa(d->bcast_ip),work->work_group));
287
288                         do_announce_host(ANN_HostAnnouncement,
289                                                         name            , 0x00, d->myip,
290                                                         work->work_group, 0x1d, d->bcast_ip,
291                                                         ttl,
292                                                         name, server_type, comment);
293                 }
294         }
295 }
296
297 /****************************************************************************
298   construct a host announcement unicast
299   **************************************************************************/
300 void announce_host(time_t t)
301 {
302   struct subnet_record *d;
303   pstring comment;
304   char *my_name;
305
306   StrnCpy(comment, lp_serverstring(), 43);
307
308   my_name = *myname ? myname : "NoName";
309
310   for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
311     {
312       struct work_record *work;
313       
314       for (work = d->workgrouplist; work; work = work->next)
315         {
316           uint32 stype = work->ServerType;
317           struct server_record *s;
318           BOOL announce = False;
319           
320           /* must work on the code that does announcements at up to
321              30 seconds later if a master browser sends us a request
322              announce.
323              */
324
325           if (work->needannounce) {
326             /* drop back to a max 3 minute announce - this is to prevent a
327                single lost packet from stuffing things up for too long */
328             work->announce_interval = MIN(work->announce_interval,
329                                           CHECK_TIME_MIN_HOST_ANNCE*60);
330             work->lastannounce_time = t - (work->announce_interval+1);
331           }
332           
333           /* announce every minute at first then progress to every 12 mins */
334           if (work->lastannounce_time && 
335               (t - work->lastannounce_time) < work->announce_interval)
336             continue;
337           
338           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
339             work->announce_interval += 60;
340           
341           work->lastannounce_time = t;
342           
343           for (s = work->serverlist; s; s = s->next) {
344             if (strequal(myname, s->serv.name)) { 
345               announce = True; 
346               break; 
347             }
348           }
349           
350           if (announce) {
351             announce_server(d,work,my_name,comment,
352                             work->announce_interval,stype);
353           }
354           
355           if (work->needannounce)
356             {
357               work->needannounce = False;
358               break;
359               /* sorry: can't do too many announces. do some more later */
360             }
361         }
362     }
363 }
364
365 /* Announce timer. Moved into global static so it can be reset
366    when a machine becomes a master browser. */
367 static time_t announce_timer_last=0;
368
369 /****************************************************************************
370  Reset the announce_timer so that a master browser announce will be done
371  immediately.
372  ****************************************************************************/
373
374 void reset_announce_timer()
375 {
376   announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
377 }
378
379 /****************************************************************************
380   announce myself as a master to all other domain master browsers.
381
382   this actually gets done in search_and_sync_workgroups() via the
383   NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
384   name query initiated here.  see response_name_query()
385   **************************************************************************/
386 void announce_master(time_t t)
387 {
388   struct subnet_record *d;
389   struct work_record *work;
390   BOOL am_master = False; /* are we a master of some sort? :-) */
391
392   if (!announce_timer_last) announce_timer_last = t;
393   if (t-announce_timer_last < CHECK_TIME_MST_ANNOUNCE * 60)
394     {
395       DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
396                  t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
397       return;
398     }
399
400   if(wins_subnet == NULL)
401     {
402       DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
403       return;
404     }
405
406   announce_timer_last = t;
407
408   for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
409     {
410       struct work_record *work;
411       for (work = d->workgrouplist; work; work = work->next)
412         {
413           if (AM_MASTER(work))
414             {
415               am_master = True;
416               DEBUG(4,( "announce_master: am_master = %d for \
417 workgroup %s\n", am_master, work->work_group));
418             }
419         }
420     }
421  
422   if (!am_master) return; /* only proceed if we are a master browser */
423   
424   /* Note that we don't do this if we are domain master browser
425      and that we *only* do this on the WINS subnet. */
426
427   /* Try and find our workgroup on the WINS subnet */
428   work = find_workgroupstruct(wins_subnet, myworkgroup, False);
429
430   if (work)
431     {
432       char *name;
433       int   type;
434
435 #if 0 /* I don't think this option should be used for this purpose. 
436          JRA.
437        */
438       if (*lp_domain_controller())
439         {
440           /* the domain controller option is used to manually specify
441              the domain master browser to sync with
442            */
443
444           /* XXXX i'm not sure we should be using the domain controller
445              option for this purpose.
446            */
447
448           name = lp_domain_controller();
449           type = 0x20;
450         }
451       else
452 #endif /* REMOVE SUSPECT CODE. */
453         {
454           /* assume that the domain master browser we want to sync
455              with is our own domain.
456            */
457           name = work->work_group;
458           type = 0x1b;
459         }
460
461       /* check the existence of a dmb for this workgroup, and if
462          one exists at the specified ip, sync with it and announce
463          ourselves as a master browser to it
464        */
465
466       if (!lp_wins_support() && *lp_wins_server() )
467         {
468           DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
469                      name, type, lp_wins_server()));
470
471           queue_netbios_pkt_wins(ClientNMB,
472                     NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
473                     name, type, 0,0,0,
474                     work->work_group,NULL,
475                     False, False, ipzero, ipzero);
476         }
477       else if(lp_wins_support()) 
478         {
479            /* We are the WINS server - query ourselves for the dmb name. */
480
481            struct nmb_name netb_name;
482            struct subnet_record *d = 0;
483            struct name_record *nr = 0;
484  
485            make_nmb_name(&netb_name, name, type, scope);
486
487            if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
488              {
489                DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
490 in our own WINS database.\n", work->work_group));
491                return;
492              }
493
494            /* Check that this isn't one of our addresses (ie. we are not domain master
495               ourselves) */
496            if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
497              {
498                DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
499 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
500                return;
501              }
502
503            /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
504               NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a 
505               remote WINS server. */
506
507            DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
508 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
509
510            queue_netbios_packet(wins_subnet, ClientNMB,
511                     NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
512                     name, type, 0,0,0,
513                     work->work_group,NULL,
514                     False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip);
515          }
516
517     }
518 }
519
520 /****************************************************************************
521   do all the "remote" announcements. These are used to put ourselves
522   on a remote browse list. They are done blind, no checking is done to
523   see if there is actually a browse master at the other end.
524   **************************************************************************/
525 void announce_remote(time_t t)
526 {
527   char *s,*ptr;
528   static time_t last_time = 0;
529   pstring s2;
530   struct in_addr addr;
531   char *comment,*workgroup;
532   int stype = DFLT_SERVER_TYPE;
533
534   if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
535     return;
536
537   last_time = t;
538
539   s = lp_remote_announce();
540   if (!*s) return;
541
542   comment = lp_serverstring();
543   workgroup = myworkgroup;
544
545   for (ptr=s; next_token(&ptr,s2,NULL); ) {
546     /* the entries are of the form a.b.c.d/WORKGROUP with 
547        WORKGROUP being optional */
548     char *wgroup;
549
550     wgroup = strchr(s2,'/');
551     if (wgroup) *wgroup++ = 0;
552     if (!wgroup || !*wgroup)
553       wgroup = workgroup;
554
555     addr = *interpret_addr2(s2);
556     
557     do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
558                      wgroup,0x1e,addr,
559                      REMOTE_ANNOUNCE_INTERVAL,
560                      myname,stype,comment);    
561   }
562
563 }