preparing for release of 1.9.16alpha18
[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-1995
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
39 extern int ClientDGRAM;
40 extern int ClientNMB;
41
42 /* this is our domain/workgroup/server database */
43 extern struct subnet_record *subnetlist;
44
45 /* machine comment for host announcements */
46 extern  pstring ServerComment;
47
48 extern int  updatecount;
49 extern int  workgroup_count;
50
51 extern struct in_addr ipgrp;
52
53
54
55 /****************************************************************************
56   send a announce request to the local net
57   **************************************************************************/
58 void announce_request(struct work_record *work, struct in_addr ip)
59 {
60   pstring outbuf;
61   char *p;
62
63   if (!work) return;
64
65   work->needannounce = True;
66
67   DEBUG(2,("sending announce request to %s for workgroup %s\n",
68            inet_ntoa(ip),work->work_group));
69
70   bzero(outbuf,sizeof(outbuf));
71   p = outbuf;
72   CVAL(p,0) = ANN_AnnouncementRequest;
73   p++;
74
75   CVAL(p,0) = work->token; /* (local) unique workgroup token id */
76   p++;
77   StrnCpy(p,myname,16);
78   strupper(p);
79   p = skip_string(p,1);
80   
81   /* XXXX note: if we sent the announcement request to 0x1d instead
82      of 0x1e, then we could get the master browser to announce to
83      us instead of the members of the workgroup. wha-hey! */
84
85   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
86                       myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
87 }
88
89
90 /****************************************************************************
91   request an announcement
92   **************************************************************************/
93 void do_announce_request(char *info, char *to_name, int announce_type, 
94                          int from,
95                          int to, struct in_addr dest_ip)
96 {
97   pstring outbuf;
98   char *p;
99   
100   bzero(outbuf,sizeof(outbuf));
101   p = outbuf;
102   CVAL(p,0) = announce_type; 
103   p++;
104   
105   DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
106            announce_type, info, inet_ntoa(dest_ip),to_name,to));
107   
108   StrnCpy(p,info,16);
109   strupper(p);
110   p = skip_string(p,1);
111   
112   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,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 in_addr ip)
124 {                     
125   /* with a domain master we can get the whole list (not local only list) */
126   BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
127
128   add_browser_entry(serv_name, name_type, work_name, 0, ip, local_only);
129
130   if (state == NAME_STATUS_DOM_SRV_CHK)
131   {
132     /* announce ourselves as a master browser to serv_name */
133     do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
134                           0x20, 0, ip);
135   }
136 }
137
138
139 /****************************************************************************
140   construct a host announcement unicast
141
142   this function should not be used heavily, and only when we are _not_
143   a master browser and _not_ a primary domain controller.
144
145   **************************************************************************/
146 void announce_backup(void)
147 {
148   static time_t lastrun = 0;
149   time_t t = time(NULL);
150   pstring outbuf;
151   char *p;
152   struct subnet_record *d1;
153   int tok;
154   static uint32 id_count = 0;
155   
156   if (!lastrun) lastrun = t;
157 #if 1
158   if (t < lastrun + 1 * 60)
159 #else
160   if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60)
161 #endif
162         return;
163   lastrun = t;
164   
165   DEBUG(4,("checking backups...\n"));
166
167   for (tok = 0; tok <= workgroup_count; tok++)
168     {
169       for (d1 = subnetlist; d1; d1 = d1->next)
170         {
171           struct work_record *work;
172           struct subnet_record *d;
173           
174           /* search for unique workgroup: only the name matters */
175           for (work = d1->workgrouplist;
176                work && (tok != work->token);
177                work = work->next);
178           
179           if (!work) continue;
180
181       if (AM_MASTER(work) && AM_DOMCTL(work)) continue;
182
183           /* found one: announce it across all domains */
184           for (d = subnetlist; d; d = d->next)
185             {
186               
187               DEBUG(2,("sending announce backup %s workgroup %s(%d)\n",
188                        inet_ntoa(d->bcast_ip),work->work_group,
189                        work->token));
190               
191               bzero(outbuf,sizeof(outbuf));
192               p = outbuf;
193
194               CVAL(p,0) = ANN_GetBackupListReq;
195               CVAL(p,1) = work->token; /* workgroup unique key index */
196               SIVAL(p,2,++id_count); /* unique count. not that we use it! */
197
198               p += 6;
199               
200           debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
201
202               if (!AM_DOMCTL(work))
203           {
204             /* only ask for a list of backup domain controllers
205                if we are not a domain controller ourselves */
206                         
207                 send_mailslot_reply(BROWSE_MAILSLOT,
208                                   ClientDGRAM,outbuf,
209                                   PTR_DIFF(p,outbuf),
210                                   myname, work->work_group,
211                                   0x0,0x1b,d->bcast_ip,
212                                   *iface_ip(d->bcast_ip));
213           }
214
215           debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
216
217               if (!AM_MASTER(work))
218           {
219             /* only ask for a list of master browsers if we
220                are not a master browser ourselves */
221
222                 send_mailslot_reply(BROWSE_MAILSLOT,
223                                   ClientDGRAM,outbuf,
224                                   PTR_DIFF(p,outbuf),
225                                   myname, work->work_group,
226                                   0x0,0x1d,d->bcast_ip,
227                                   *iface_ip(d->bcast_ip));
228           }
229             }
230         }
231     }
232 }
233
234
235 /****************************************************************************
236   send a host announcement packet
237   **************************************************************************/
238 void do_announce_host(int command,
239                 char *from_name, int from_type, struct in_addr from_ip,
240                 char *to_name  , int to_type  , struct in_addr to_ip,
241                 time_t announce_interval,
242                 char *server_name, int server_type, char *server_comment)
243 {
244         pstring outbuf;
245         char *p;
246
247         bzero(outbuf,sizeof(outbuf));
248         p = outbuf+1;
249
250         /* command type */
251         CVAL(outbuf,0) = command;
252
253         /* announcement parameters */
254         CVAL(p,0) = updatecount;
255         SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
256
257         StrnCpy(p+5,server_name,16);
258         strupper(p+5);
259
260         CVAL(p,21) = 0x02; /* major version */
261         CVAL(p,22) = 0x02; /* minor version */
262
263         SIVAL(p,23,server_type);
264         SSVAL(p,27,0x010f); /* browse version: got from NT/AS 4.00 */
265         SSVAL(p,29,0xaa55); /* browse signature */
266
267         strcpy(p+31,server_comment);
268         p += 31;
269         p = skip_string(p,1);
270
271     debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
272
273         /* send the announcement */
274         send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
275                                           PTR_DIFF(p,outbuf),
276                                           from_name, to_name,
277                                           from_type, to_type,
278                                           to_ip, from_ip);
279 }
280
281
282 /****************************************************************************
283   remove all samba's server entries
284   ****************************************************************************/
285 void remove_my_servers(void)
286 {
287         struct subnet_record *d; 
288         for (d = subnetlist; d; d = d->next)
289         {
290                 struct work_record *work;
291                 for (work = d->workgrouplist; work; work = work->next)
292                 {
293                         struct server_record *s;
294                         for (s = work->serverlist; s; s = s->next)
295                         {
296                                 if (!strequal(myname,s->serv.name)) continue;
297                                 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
298                         }
299                 }
300         }
301 }
302
303
304 /****************************************************************************
305   announce a server entry
306   ****************************************************************************/
307 void announce_server(struct subnet_record *d, struct work_record *work,
308                                         char *name, char *comment, time_t ttl, int server_type)
309 {
310         uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_SERVER_UNIX;
311         BOOL wins_iface = ip_equal(d->bcast_ip, ipgrp);
312         
313         if (wins_iface && server_type != 0)
314         {
315                 /* wins pseudo-ip interface */
316                 if (!AM_MASTER(work))
317                 {
318                         /* non-master announce by unicast to the domain master */
319                         if (!lp_wins_support() && *lp_wins_server())
320                         {
321                                 /* look up the domain master with the WINS server */
322                                 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
323                                          NAME_QUERY_ANNOUNCE_HOST,
324                                          work->work_group,0x1b,0,ttl*1000,
325                                          server_type,name,comment,
326                                          False, False, ipzero, d->bcast_ip);
327                         }
328                         else
329                         {
330                                 /* we are the WINS server, but not the domain master.  */
331                                 /* XXXX we need to look up the domain master in our
332                                    WINS database list, and do_announce_host(). maybe
333                                    we could do a name query on the unsuspecting domain
334                                    master just to make sure it's awake. */
335                         }
336                 }
337
338                 if (AM_DOMCTL(work))
339                 {
340                         /* XXXX announce to backup domain masters? */
341                 }
342
343                 /* XXXX any other kinds of announcements we need to consider here?
344                    e.g local master browsers... no. local master browsers do
345                    local master announcements to their domain master. they even
346                    use WINS lookup of the domain master if another wins server
347                    is being used! 
348                  */
349         }
350         else
351         {
352                 if (AM_MASTER(work))
353                 {
354                         DEBUG(3,("sending local master announce to %s for %s(1e)\n",
355                                                         inet_ntoa(d->bcast_ip),work->work_group));
356
357                         do_announce_host(ANN_LocalMasterAnnouncement,
358                                                         name            , 0x00, d->myip,
359                                                         work->work_group, 0x1e, d->bcast_ip,
360                                                         ttl*1000,
361                                                         name, server_type, comment);
362
363                         DEBUG(3,("sending domain announce to %s for %s\n",
364                                                         inet_ntoa(d->bcast_ip),work->work_group));
365
366                         /* XXXX should we do a domain-announce-kill? */
367                         if (server_type != 0)
368                         {
369                                 if (AM_DOMCTL(work))
370                                 {
371                                         domain_type |= SV_TYPE_DOMAIN_CTRL;
372                                 }
373                                 do_announce_host(ANN_DomainAnnouncement,
374                                                         name    , 0x00, d->myip,
375                                                         MSBROWSE, 0x01, d->bcast_ip,
376                                                         ttl*1000,
377                                                         work->work_group, server_type ? domain_type : 0,
378                                                         name);
379                         }
380                 }
381                 else
382                 {
383                         DEBUG(3,("sending host announce to %s for %s(1d)\n",
384                                                         inet_ntoa(d->bcast_ip),work->work_group));
385
386                         do_announce_host(ANN_HostAnnouncement,
387                                                         name            , 0x00, d->myip,
388                                                         work->work_group, 0x1d, d->bcast_ip,
389                                                         ttl*1000,
390                                                         name, server_type, comment);
391                 }
392         }
393 }
394
395 /****************************************************************************
396   construct a host announcement unicast
397   **************************************************************************/
398 void announce_host(void)
399 {
400   time_t t = time(NULL);
401   struct subnet_record *d;
402   pstring comment;
403   char *my_name;
404
405   StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
406
407   my_name = *myname ? myname : "NoName";
408
409   for (d = subnetlist; d; d = d->next)
410     {
411       struct work_record *work;
412       
413       if (ip_equal(d->bcast_ip, ipgrp)) continue;
414
415       for (work = d->workgrouplist; work; work = work->next)
416         {
417           uint32 stype = work->ServerType;
418           struct server_record *s;
419           BOOL announce = False;
420           
421       /* must work on the code that does announcements at up to
422          30 seconds later if a master browser sends us a request
423          announce.
424        */
425
426           if (work->needannounce) {
427             /* drop back to a max 3 minute announce - this is to prevent a
428                single lost packet from stuffing things up for too long */
429             work->announce_interval = MIN(work->announce_interval,
430                                                 CHECK_TIME_MIN_HOST_ANNCE*60);
431             work->lastannounce_time = t - (work->announce_interval+1);
432           }
433           
434           /* announce every minute at first then progress to every 12 mins */
435           if (work->lastannounce_time && 
436               (t - work->lastannounce_time) < work->announce_interval)
437             continue;
438           
439           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
440             work->announce_interval += 60;
441           
442           work->lastannounce_time = t;
443
444           /*
445           if (!d->my_interface) {
446             stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
447                        SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
448                        SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
449           }
450           */
451
452           for (s = work->serverlist; s; s = s->next) {
453             if (strequal(myname, s->serv.name)) { 
454               announce = True; 
455               break; 
456             }
457           }
458           
459           if (announce)
460           {
461                 announce_server(d,work,my_name,comment,work->announce_interval,stype);
462           }
463           
464           if (work->needannounce)
465           {
466               work->needannounce = False;
467               break;
468               /* sorry: can't do too many announces. do some more later */
469           }
470         }
471   }
472 }
473
474
475 /****************************************************************************
476   announce myself as a master to all other primary domain conrollers.
477
478   this actually gets done in search_and_sync_workgroups() via the
479   NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
480   name query initiated here.  see response_name_query()
481   **************************************************************************/
482 void announce_master(void)
483 {
484   struct subnet_record *d;
485   static time_t last=0;
486   time_t t = time(NULL);
487   BOOL am_master = False; /* are we a master of some sort? :-) */
488
489   if (!last) last = t;
490   if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
491         return;
492
493   last = t;
494
495   for (d = subnetlist; d; d = d->next)
496     {
497       struct work_record *work;
498       for (work = d->workgrouplist; work; work = work->next)
499         {
500           if (AM_MASTER(work))
501             {
502               am_master = True;
503             }
504         }
505     }
506   
507   if (!am_master) return; /* only proceed if we are a master browser */
508   
509   for (d = subnetlist; d; d = d->next)
510     {
511       struct work_record *work;
512       for (work = d->workgrouplist; work; work = work->next)
513         {
514           struct server_record *s;
515           for (s = work->serverlist; s; s = s->next)
516             {
517               if (strequal(s->serv.name, myname)) continue;
518               
519               /* all DOMs (which should also be master browsers) */
520               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
521                 {
522                   /* check the existence of a pdc for this workgroup, and if
523                      one exists at the specified ip, sync with it and announce
524                      ourselves as a master browser to it */
525                   
526                   if (!*lp_domain_controller() ||
527                       !strequal(lp_domain_controller(), s->serv.name))
528                     {
529                       if (!lp_wins_support() && *lp_wins_server())
530                         {
531                           queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
532                                                  NAME_QUERY_DOM_SRV_CHK,
533                                                  work->work_group,0x1b,0,0,0,NULL,NULL,
534                                                  False, False, ipzero, ipzero);
535                         }
536                       else
537                         {
538                           struct subnet_record *d2;
539                           for (d2 = subnetlist; d2; d2 = d2->next)
540                             {
541                               queue_netbios_packet(d,ClientNMB,NMB_QUERY,
542                                                    NAME_QUERY_DOM_SRV_CHK,
543                                                    work->work_group,0x1b,0,0,0,NULL,NULL,
544                                                    True, False, d2->bcast_ip, d2->bcast_ip);
545                             }
546                         }
547                     }
548                 }
549             }
550           
551           /* now do primary domain controller - the one that's not
552              necessarily in our browse lists, although it ought to be
553              this pdc is the one that we get TOLD about through smb.conf.
554              basically, if it's on a subnet that we know about, it may end
555              up in our browse lists (which is why it's explicitly excluded
556              in the code above) */
557           
558           if (*lp_domain_controller())
559             {
560               struct in_addr ip;
561               BOOL bcast = False;
562               
563               ip = *interpret_addr2(lp_domain_controller());
564               
565               if (zero_ip(ip)) {
566                 ip = d->bcast_ip;
567                 bcast = True;
568               }
569
570               DEBUG(2, ("Searching for DOM %s at %s\n",
571                         lp_domain_controller(), inet_ntoa(ip)));
572               
573               /* check the existence of a pdc for this workgroup, and if
574                  one exists at the specified ip, sync with it and announce
575                  ourselves as a master browser to it */
576               queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
577                                      work->work_group,0x1b,0,0,0,NULL,NULL,
578                                      bcast, False, ip, ip);
579             }
580         }
581     }
582 }
583
584
585
586 /****************************************************************************
587   do all the "remote" announcements. These are used to put ourselves
588   on a remote browse list. They are done blind, no checking is done to
589   see if there is actually a browse master at the other end.
590   **************************************************************************/
591 void announce_remote(void)
592 {
593   char *s,*ptr;
594   static time_t last_time = 0;
595   time_t t = time(NULL);
596   pstring s2;
597   struct in_addr addr;
598   char *comment,*workgroup;
599   int stype = SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_PRINTQ_SERVER |
600     SV_TYPE_SERVER_UNIX;
601
602   if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
603     return;
604
605   last_time = t;
606
607   s = lp_remote_announce();
608   if (!*s) return;
609
610   comment = lp_serverstring();
611   workgroup = lp_workgroup();
612
613   for (ptr=s; next_token(&ptr,s2,NULL); ) {
614     /* the entries are of the form a.b.c.d/WORKGROUP with 
615        WORKGROUP being optional */
616     char *wgroup;
617
618     wgroup = strchr(s2,'/');
619     if (wgroup) *wgroup++ = 0;
620     if (!wgroup || !*wgroup)
621       wgroup = workgroup;
622
623     addr = *interpret_addr2(s2);
624     
625     do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
626                      wgroup,0x1e,addr,
627                      REMOTE_ANNOUNCE_INTERVAL,
628                      myname,stype,comment);    
629   }
630
631 }