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