added a new type to mkproto.awk so it can handle shmem.c
[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 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         strcpy(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       struct work_record *work;
387       for (work = d->workgrouplist; work; work = work->next)
388         {
389           if (AM_MASTER(work))
390             {
391               am_master = True;
392               DEBUG(4,( "announce_master: am_master = %d for \
393 workgroup %s\n", am_master, work->work_group));
394             }
395         }
396     }
397  
398   if (!am_master) return; /* only proceed if we are a master browser */
399   
400   /* Note that we don't do this if we are domain master browser
401      and that we *only* do this on the WINS subnet. */
402
403   /* Try and find our workgroup on the WINS subnet */
404   work = find_workgroupstruct(wins_subnet, myworkgroup, False);
405
406   if (work)
407     {
408       char *name;
409       int   type;
410
411         {
412           /* assume that the domain master browser we want to sync
413              with is our own domain.
414            */
415           name = work->work_group;
416           type = 0x1b;
417         }
418
419       /* check the existence of a dmb for this workgroup, and if
420          one exists at the specified ip, sync with it and announce
421          ourselves as a master browser to it
422        */
423
424       if (!lp_wins_support() && *lp_wins_server() )
425         {
426           DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
427                      name, type, lp_wins_server()));
428
429           queue_netbios_pkt_wins(ClientNMB,
430                     NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
431                     name, type, 0,0,0,
432                     work->work_group,NULL,
433                     False, True, ipzero, ipzero);
434         }
435       else if(lp_wins_support()) 
436         {
437            /* We are the WINS server - query ourselves for the dmb name. */
438
439            struct nmb_name netb_name;
440            struct subnet_record *d = 0;
441            struct name_record *nr = 0;
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);
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 }