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