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