I am removing these from the source code in preparation for an
[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_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_subnet, myworkgroup, False);
404
405   if (work)
406     {
407       char *name;
408       int   type;
409
410         {
411           /* assume that the domain master browser we want to sync
412              with is our own domain.
413            */
414           name = work->work_group;
415           type = 0x1b;
416         }
417
418       /* check the existence of a dmb for this workgroup, and if
419          one exists at the specified ip, sync with it and announce
420          ourselves as a master browser to it
421        */
422
423       if (!lp_wins_support() && *lp_wins_server() )
424         {
425           DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
426                      name, type, lp_wins_server()));
427
428           queue_netbios_pkt_wins(ClientNMB,
429                     NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
430                     name, type, 0,0,0,
431                     work->work_group,NULL,
432                     ipzero, ipzero);
433         }
434       else if(lp_wins_support()) 
435         {
436            /* We are the WINS server - query ourselves for the dmb name. */
437
438            struct nmb_name netb_name;
439            struct name_record *nr = 0;
440
441            d = NULL;
442
443            make_nmb_name(&netb_name, name, type, scope);
444
445            if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
446              {
447                DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
448 in our own WINS database.\n", work->work_group));
449                return;
450              }
451
452            /* Check that this isn't one of our addresses (ie. we are not domain master
453               ourselves) */
454            if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
455              {
456                DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
457 is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
458                return;
459              }
460
461            /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
462               NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a 
463               remote WINS server. */
464
465            DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
466 for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
467
468            queue_netbios_packet(wins_subnet, ClientNMB,
469                     NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
470                     name, type, 0,0,0,
471                     work->work_group,NULL,
472                     False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip, 0);
473          }
474
475     }
476 }
477
478 /****************************************************************************
479   do all the "remote" announcements. These are used to put ourselves
480   on a remote browse list. They are done blind, no checking is done to
481   see if there is actually a browse master at the other end.
482   **************************************************************************/
483 void announce_remote(time_t t)
484 {
485   char *s,*ptr;
486   static time_t last_time = 0;
487   pstring s2;
488   struct in_addr addr;
489   char *comment,*workgroup;
490   int stype = lp_default_server_announce();
491
492   if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
493     return;
494
495   last_time = t;
496
497   s = lp_remote_announce();
498   if (!*s) return;
499
500   comment = lp_serverstring();
501   workgroup = myworkgroup;
502
503   for (ptr=s; next_token(&ptr,s2,NULL); ) 
504   {
505     /* the entries are of the form a.b.c.d/WORKGROUP with 
506        WORKGROUP being optional */
507     char *wgroup;
508     int n;
509
510     wgroup = strchr(s2,'/');
511     if (wgroup) *wgroup++ = 0;
512     if (!wgroup || !*wgroup)
513       wgroup = workgroup;
514
515     addr = *interpret_addr2(s2);
516     
517     /* Announce all our names including aliases */
518     for (n=0; my_netbios_names[n]; n++) 
519     {
520       char *name = my_netbios_names[n];
521       do_announce_host(ANN_HostAnnouncement,name,0x20,*iface_ip(addr),
522                      wgroup,0x1e,addr,
523                      REMOTE_ANNOUNCE_INTERVAL,
524                      name,stype,comment);    
525     }
526   }
527
528 }