minor cleanups
[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                                 /* XXXX we need to look up the domain master in our
330                                    WINS database list, and do_announce_host(). maybe
331                                    we could do a name query on the unsuspecting domain
332                                    master just to make sure it's awake. */
333                         }
334                 }
335
336                 if (AM_DOMCTL(work))
337                 {
338                         /* XXXX announce to backup domain masters? */
339                 }
340
341                 /* XXXX any other kinds of announcements we need to consider here?
342                    e.g local master browsers... no. local master browsers do
343                    local master announcements to their domain master. they even
344                    use WINS lookup of the domain master if another wins server
345                    is being used! 
346                  */
347         }
348         else
349         {
350                 if (AM_MASTER(work))
351                 {
352                         DEBUG(3,("sending local master announce to %s for %s(1e)\n",
353                                                         inet_ntoa(d->bcast_ip),work->work_group));
354
355                         do_announce_host(ANN_LocalMasterAnnouncement,
356                                                         name            , 0x00, d->myip,
357                                                         work->work_group, 0x1e, d->bcast_ip,
358                                                         ttl*1000,
359                                                         name, server_type, comment);
360
361                         DEBUG(3,("sending domain announce to %s for %s\n",
362                                                         inet_ntoa(d->bcast_ip),work->work_group));
363
364                         /* XXXX should we do a domain-announce-kill? */
365                         if (server_type != 0)
366                         {
367                                 if (AM_DOMCTL(work))
368                                 {
369                                         domain_type |= SV_TYPE_DOMAIN_CTRL;
370                                 }
371                                 do_announce_host(ANN_DomainAnnouncement,
372                                                         name    , 0x00, d->myip,
373                                                         MSBROWSE, 0x01, d->bcast_ip,
374                                                         ttl*1000,
375                                                         work->work_group, server_type ? domain_type : 0,
376                                                         name);
377                         }
378                 }
379                 else
380                 {
381                         DEBUG(3,("sending host announce to %s for %s(1d)\n",
382                                                         inet_ntoa(d->bcast_ip),work->work_group));
383
384                         do_announce_host(ANN_HostAnnouncement,
385                                                         name            , 0x00, d->myip,
386                                                         work->work_group, 0x1d, d->bcast_ip,
387                                                         ttl*1000,
388                                                         name, server_type, comment);
389                 }
390         }
391 }
392
393 /****************************************************************************
394   construct a host announcement unicast
395   **************************************************************************/
396 void announce_host(void)
397 {
398   time_t t = time(NULL);
399   struct subnet_record *d;
400   pstring comment;
401   char *my_name;
402
403   StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
404
405   my_name = *myname ? myname : "NoName";
406
407   for (d = subnetlist; d; d = d->next)
408     {
409       struct work_record *work;
410       
411       if (ip_equal(d->bcast_ip, ipgrp)) continue;
412
413       for (work = d->workgrouplist; work; work = work->next)
414         {
415           uint32 stype = work->ServerType;
416           struct server_record *s;
417           BOOL announce = False;
418           
419       /* must work on the code that does announcements at up to
420          30 seconds later if a master browser sends us a request
421          announce.
422        */
423
424           if (work->needannounce) {
425             /* drop back to a max 3 minute announce - this is to prevent a
426                single lost packet from stuffing things up for too long */
427             work->announce_interval = MIN(work->announce_interval,
428                                                 CHECK_TIME_MIN_HOST_ANNCE*60);
429             work->lastannounce_time = t - (work->announce_interval+1);
430           }
431           
432           /* announce every minute at first then progress to every 12 mins */
433           if (work->lastannounce_time && 
434               (t - work->lastannounce_time) < work->announce_interval)
435             continue;
436           
437           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
438             work->announce_interval += 60;
439           
440           work->lastannounce_time = t;
441
442           /*
443           if (!d->my_interface) {
444             stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
445                        SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
446                        SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
447           }
448           */
449
450           for (s = work->serverlist; s; s = s->next) {
451             if (strequal(myname, s->serv.name)) { 
452               announce = True; 
453               break; 
454             }
455           }
456           
457           if (announce)
458           {
459                 announce_server(d,work,my_name,comment,work->announce_interval,stype);
460           }
461           
462           if (work->needannounce)
463           {
464               work->needannounce = False;
465               break;
466               /* sorry: can't do too many announces. do some more later */
467           }
468         }
469   }
470 }
471
472
473 /****************************************************************************
474   announce myself as a master to all other primary domain conrollers.
475
476   this actually gets done in search_and_sync_workgroups() via the
477   NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
478   name query initiated here.  see response_name_query()
479   **************************************************************************/
480 void announce_master(void)
481 {
482   struct subnet_record *d;
483   static time_t last=0;
484   time_t t = time(NULL);
485   BOOL am_master = False; /* are we a master of some sort? :-) */
486
487   if (!last) last = t;
488   if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
489         return;
490
491   last = t;
492
493   for (d = subnetlist; d; d = d->next)
494     {
495       struct work_record *work;
496       for (work = d->workgrouplist; work; work = work->next)
497         {
498           if (AM_MASTER(work))
499             {
500               am_master = True;
501             }
502         }
503     }
504   
505   if (!am_master) return; /* only proceed if we are a master browser */
506   
507   for (d = subnetlist; d; d = d->next)
508     {
509       struct work_record *work;
510       for (work = d->workgrouplist; work; work = work->next)
511         {
512           struct server_record *s;
513           for (s = work->serverlist; s; s = s->next)
514             {
515               if (strequal(s->serv.name, myname)) continue;
516               
517               /* all DOMs (which should also be master browsers) */
518               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
519                 {
520                   /* check the existence of a pdc for this workgroup, and if
521                      one exists at the specified ip, sync with it and announce
522                      ourselves as a master browser to it */
523                   
524                   if (!*lp_domain_controller() ||
525                       !strequal(lp_domain_controller(), s->serv.name))
526                     {
527                       if (!lp_wins_support() && *lp_wins_server())
528                         {
529                           queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
530                                                  NAME_QUERY_DOM_SRV_CHK,
531                                                  work->work_group,0x1b,0,0,0,NULL,NULL,
532                                                  False, False, ipzero, ipzero);
533                         }
534                       else
535                         {
536                           struct subnet_record *d2;
537                           for (d2 = subnetlist; d2; d2 = d2->next)
538                             {
539                               queue_netbios_packet(d,ClientNMB,NMB_QUERY,
540                                                    NAME_QUERY_DOM_SRV_CHK,
541                                                    work->work_group,0x1b,0,0,0,NULL,NULL,
542                                                    True, False, d2->bcast_ip, d2->bcast_ip);
543                             }
544                         }
545                     }
546                 }
547             }
548           
549           /* now do primary domain controller - the one that's not
550              necessarily in our browse lists, although it ought to be
551              this pdc is the one that we get TOLD about through smb.conf.
552              basically, if it's on a subnet that we know about, it may end
553              up in our browse lists (which is why it's explicitly excluded
554              in the code above) */
555           
556           if (*lp_domain_controller())
557             {
558               struct in_addr ip;
559               BOOL bcast = False;
560               
561               ip = *interpret_addr2(lp_domain_controller());
562               
563               if (zero_ip(ip)) {
564                 ip = d->bcast_ip;
565                 bcast = True;
566               }
567
568               DEBUG(2, ("Searching for DOM %s at %s\n",
569                         lp_domain_controller(), inet_ntoa(ip)));
570               
571               /* check the existence of a pdc for this workgroup, and if
572                  one exists at the specified ip, sync with it and announce
573                  ourselves as a master browser to it */
574               queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
575                                      work->work_group,0x1b,0,0,0,NULL,NULL,
576                                      bcast, False, ip, ip);
577             }
578         }
579     }
580 }