lots of changes to nmbd
[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           if (!d->my_interface) {
398             stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
399                        SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
400                        SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
401           }
402
403           for (s = work->serverlist; s; s = s->next) {
404             if (strequal(myname, s->serv.name)) { 
405               announce = True; 
406               break; 
407             }
408           }
409           
410           if (announce)
411           {
412                 announce_server(d,work,my_name,comment,work->announce_interval,stype);
413           }
414           
415           if (work->needannounce)
416           {
417               work->needannounce = False;
418               break;
419               /* sorry: can't do too many announces. do some more later */
420           }
421         }
422   }
423 }
424
425
426 /****************************************************************************
427   announce myself as a master to all other primary domain conrollers.
428
429   BIG NOTE: this code will remain untested until some kind soul that has access
430   to a couple of windows NT advanced servers runs this version of nmbd for at
431   least 15 minutes.
432   
433   this actually gets done in search_and_sync_workgroups() via the
434   NAME_QUERY_PDC_SRV_CHK command, if there is a response from the
435   name query initiated here.  see response_name_query()
436   **************************************************************************/
437 void announce_master(void)
438 {
439   struct subnet_record *d;
440   static time_t last=0;
441   time_t t = time(NULL);
442   BOOL am_master = False; /* are we a master of some sort? :-) */
443
444   if (!last) last = t;
445   if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
446         return;
447
448   last = t;
449
450   for (d = subnetlist; d; d = d->next)
451     {
452       struct work_record *work;
453       for (work = d->workgrouplist; work; work = work->next)
454         {
455           if (AM_MASTER(work))
456             {
457               am_master = True;
458             }
459         }
460     }
461   
462   if (!am_master) return; /* only proceed if we are a master browser */
463   
464   for (d = subnetlist; d; d = d->next)
465     {
466       struct work_record *work;
467       for (work = d->workgrouplist; work; work = work->next)
468         {
469           struct server_record *s;
470           for (s = work->serverlist; s; s = s->next)
471             {
472               if (strequal(s->serv.name, myname)) continue;
473               
474               /* all PDCs (which should also be master browsers) */
475               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
476                 {
477                   /* check the existence of a pdc for this workgroup, and if
478                      one exists at the specified ip, sync with it and announce
479                      ourselves as a master browser to it */
480                   
481                   if (!*lp_domain_controller() ||
482                       !strequal(lp_domain_controller(), s->serv.name))
483                     {
484                       if (!lp_wins_support() && *lp_wins_server())
485                         {
486                           struct in_addr ip;
487                           ip = ipzero;
488                           
489                           queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
490                                                  NAME_QUERY_PDC_SRV_CHK,
491                                                  work->work_group,0x1b,0,0,
492                                                  False, False, ip, ip);
493                         }
494                       else
495                         {
496                           struct subnet_record *d2;
497                           for (d2 = subnetlist; d2; d2 = d2->next)
498                             {
499                               queue_netbios_packet(d,ClientNMB,NMB_QUERY,
500                                                    NAME_QUERY_PDC_SRV_CHK,
501                                                    work->work_group,0x1b,0,0,
502                                                    True, False, d2->bcast_ip, d2->bcast_ip);
503                             }
504                         }
505                     }
506                 }
507             }
508           
509           /* now do primary domain controller - the one that's not
510              necessarily in our browse lists, although it ought to be
511              this pdc is the one that we get TOLD about through smb.conf.
512              basically, if it's on a subnet that we know about, it may end
513              up in our browse lists (which is why it's explicitly excluded
514              in the code above) */
515           
516           if (*lp_domain_controller())
517             {
518               struct in_addr ip;
519               BOOL bcast = False;
520               
521               ip = *interpret_addr2(lp_domain_controller());
522               
523               if (zero_ip(ip)) {
524                 ip = d->bcast_ip;
525                 bcast = True;
526               }
527
528               DEBUG(2, ("Searching for PDC %s at %s\n",
529                         lp_domain_controller(), inet_ntoa(ip)));
530               
531               /* check the existence of a pdc for this workgroup, and if
532                  one exists at the specified ip, sync with it and announce
533                  ourselves as a master browser to it */
534               queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_PDC_SRV_CHK,
535                                      work->work_group,0x1b, 0, 0,
536                                      bcast, False, ip, ip);
537             }
538         }
539     }
540 }