7d7a95334c97b4302e963514a8a78fe18d6b7385
[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 (*lp_domain_controller())
436         {
437           /* the domain controller option is used to manually specify
438              the domain master browser to sync with
439            */
440
441           /* XXXX i'm not sure we should be using the domain controller
442              option for this purpose.
443            */
444
445           name = lp_domain_controller();
446           type = 0x20;
447         }
448       else
449         {
450           /* assume that the domain master browser we want to sync
451              with is our own domain.
452            */
453           name = work->work_group;
454           type = 0x1b;
455         }
456
457       /* check the existence of a dmb for this workgroup, and if
458          one exists at the specified ip, sync with it and announce
459          ourselves as a master browser to it
460        */
461
462       if (!lp_wins_support() && *lp_wins_server() )
463         {
464           DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
465                      name, type, lp_wins_server()));
466
467           queue_netbios_pkt_wins(ClientNMB,
468                     NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
469                     name, type, 0,0,0,
470                     work->work_group,NULL,
471                     False, False, ipzero, ipzero);
472         }
473       else if(lp_wins_support()) 
474         {
475            /* We are the WINS server - query ourselves for the dmb name. */
476
477            struct nmb_name netb_name;
478            struct subnet_record *d = 0;
479            struct name_record *nr = 0;
480  
481            make_nmb_name(&netb_name, name, type, scope);
482
483            if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
484              {
485                DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
486 in our own WINS database.\n", work->work_group));
487                return;
488              }
489
490            /* Check that this isn't one of our addresses (ie. we are not domain master
491               ourselves) */
492            if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
493              {
494                DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
495 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
496                return;
497              }
498
499            /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
500               NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a 
501               remote WINS server. */
502
503            DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
504 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
505
506            queue_netbios_packet(wins_subnet, ClientNMB,
507                     NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
508                     name, type, 0,0,0,
509                     work->work_group,NULL,
510                     False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip);
511          }
512
513     }
514 }
515
516 /****************************************************************************
517   do all the "remote" announcements. These are used to put ourselves
518   on a remote browse list. They are done blind, no checking is done to
519   see if there is actually a browse master at the other end.
520   **************************************************************************/
521 void announce_remote(time_t t)
522 {
523   char *s,*ptr;
524   static time_t last_time = 0;
525   pstring s2;
526   struct in_addr addr;
527   char *comment,*workgroup;
528   int stype = DFLT_SERVER_TYPE;
529
530   if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
531     return;
532
533   last_time = t;
534
535   s = lp_remote_announce();
536   if (!*s) return;
537
538   comment = lp_serverstring();
539   workgroup = myworkgroup;
540
541   for (ptr=s; next_token(&ptr,s2,NULL); ) {
542     /* the entries are of the form a.b.c.d/WORKGROUP with 
543        WORKGROUP being optional */
544     char *wgroup;
545
546     wgroup = strchr(s2,'/');
547     if (wgroup) *wgroup++ = 0;
548     if (!wgroup || !*wgroup)
549       wgroup = workgroup;
550
551     addr = *interpret_addr2(s2);
552     
553     do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
554                      wgroup,0x1e,addr,
555                      REMOTE_ANNOUNCE_INTERVAL,
556                      myname,stype,comment);    
557   }
558
559 }