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