updates from Luke to rename "domains" more accurately to "subnets"
[samba.git] / source / 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 #include "loadparm.h"
30
31 #define TEST_CODE
32
33 extern int DEBUGLEVEL;
34 extern BOOL CanRecurse;
35
36 extern struct in_addr ipzero;
37
38 extern pstring myname;
39
40 extern int ClientDGRAM;
41 extern int ClientNMB;
42
43 /* this is our domain/workgroup/server database */
44 extern struct subnet_record *subnetlist;
45
46 /* machine comment for host announcements */
47 extern  pstring ServerComment;
48
49 extern int  updatecount;
50 extern int  workgroup_count;
51
52 /* what server type are we currently */
53
54 #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
55
56 /****************************************************************************
57   send a announce request to the local net
58   **************************************************************************/
59 void announce_request(struct work_record *work, struct in_addr ip)
60 {
61   pstring outbuf;
62   char *p;
63
64   if (!work) return;
65
66   work->needannounce = True;
67
68   DEBUG(2,("sending announce request to %s for workgroup %s\n",
69            inet_ntoa(ip),work->work_group));
70
71   bzero(outbuf,sizeof(outbuf));
72   p = outbuf;
73   CVAL(p,0) = ANN_AnnouncementRequest;
74   p++;
75
76   CVAL(p,0) = work->token; /* flags?? XXXX probably a token*/
77   p++;
78   StrnCpy(p,myname,16);
79   strupper(p);
80   p = skip_string(p,1);
81   
82   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
83                       myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
84 }
85
86
87 /****************************************************************************
88   request an announcement
89   **************************************************************************/
90 void do_announce_request(char *info, char *to_name, int announce_type, 
91                          int from,
92                          int to, struct in_addr dest_ip)
93 {
94   pstring outbuf;
95   char *p;
96   
97   bzero(outbuf,sizeof(outbuf));
98   p = outbuf;
99   CVAL(p,0) = announce_type; 
100   p++;
101   
102   DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
103            announce_type, info, inet_ntoa(dest_ip),to_name,to));
104   
105   StrnCpy(p,info,16);
106   strupper(p);
107   p = skip_string(p,1);
108   
109   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
110                       myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
111 }
112
113 /****************************************************************************
114   construct a host announcement unicast
115   **************************************************************************/
116 void announce_backup(void)
117 {
118   static time_t lastrun = 0;
119   time_t t = time(NULL);
120   pstring outbuf;
121   char *p;
122   struct subnet_record *d1;
123   int tok;
124   
125   if (!lastrun) lastrun = t;
126   if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60) return;
127   lastrun = t;
128   
129   for (tok = 0; tok <= workgroup_count; tok++)
130     {
131       for (d1 = subnetlist; d1; d1 = d1->next)
132         {
133           struct work_record *work;
134           struct subnet_record *d;
135           
136           /* search for unique workgroup: only the name matters */
137           for (work = d1->workgrouplist;
138                work && (tok != work->token);
139                work = work->next);
140           
141           if (!work) continue;
142
143           /* found one: announce it across all domains */
144           for (d = subnetlist; d; d = d->next)
145             {
146               int type=0;
147
148               if (AM_DOMCTL(work)) {
149                 type = 0x1b;
150               } else if (AM_MASTER(work)) {
151                 type = 0x1d;
152               } else {
153                 continue;
154               }
155               
156               DEBUG(2,("sending announce backup %s workgroup %s(%d)\n",
157                        inet_ntoa(d->bcast_ip),work->work_group,
158                        work->token));
159               
160               bzero(outbuf,sizeof(outbuf));
161               p = outbuf;
162               CVAL(p,0) = ANN_GetBackupListReq;
163               p++;
164               
165               CVAL(p,0) = 1; /* count? */
166               SIVAL(p,1,work->token); /* workgroup unique key index */
167               p += 5;
168               p++;
169               
170               send_mailslot_reply(BROWSE_MAILSLOT,
171                                   ClientDGRAM,outbuf,
172                                   PTR_DIFF(p,outbuf),
173                                   myname, work->work_group,
174                                   0x0,type,d->bcast_ip,
175                                   *iface_ip(d->bcast_ip));
176             }
177         }
178     }
179 }
180
181
182 /****************************************************************************
183   construct a host announcement unicast
184   **************************************************************************/
185 void announce_host(void)
186 {
187   time_t t = time(NULL);
188   pstring outbuf;
189   char *p;
190   char *namep;
191   char *stypep;
192   char *commentp;
193   pstring comment;
194   char *my_name;
195   struct subnet_record *d;
196
197   StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
198
199   my_name = *myname ? myname : "NoName";
200
201   for (d = subnetlist; d; d = d->next)
202     {
203       struct work_record *work;
204       
205       if (!d->my_interface)
206         continue;
207
208       for (work = d->workgrouplist; work; work = work->next)
209         {
210           uint32 stype = work->ServerType;
211           struct server_record *s;
212           BOOL announce = False;
213           
214           if (work->needannounce) {
215             /* drop back to a max 3 minute announce - this is to prevent a
216                single lost packet from stuffing things up for too long */
217             work->announce_interval = MIN(work->announce_interval, 
218                                           CHECK_TIME_MIN_HOST_ANNCE*60);
219             work->lastannounce_time = t - (work->announce_interval+1);
220           }
221           
222           /* announce every minute at first then progress to every 12 mins */
223           if (work->lastannounce_time && 
224               (t - work->lastannounce_time) < work->announce_interval)
225             continue;
226           
227           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
228             work->announce_interval += 60;
229           
230           work->lastannounce_time = t;
231
232           if (!d->my_interface) {
233             stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
234                        SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
235                        SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
236           }
237
238           for (s = work->serverlist; s; s = s->next) {
239             if (strequal(myname, s->serv.name)) { 
240               announce = True; 
241               break; 
242             }
243           }
244           
245           if (announce)
246             {
247               bzero(outbuf,sizeof(outbuf));
248               p = outbuf+1;
249               
250               CVAL(p,0) = updatecount;
251               /* ms - despite the spec */
252               SIVAL(p,1,work->announce_interval*1000); 
253               namep = p+5;
254               StrnCpy(namep,my_name,16);
255               strupper(namep);
256               CVAL(p,21) = 2; /* major version */
257               CVAL(p,22) = 2; /* minor version */
258               stypep = p+23;
259               SIVAL(p,23,stype);
260               SSVAL(p,27,0xaa55); /* browse signature */
261               SSVAL(p,29,1); /* browse version */
262               commentp = p+31;
263               strcpy(commentp,comment);
264               p = p+31;
265               p = skip_string(p,1);
266               
267               if (d->my_interface)
268                 {
269                   if (AM_MASTER(work))
270                     {
271                       SIVAL(stypep,0,work->ServerType);
272                       
273                       DEBUG(2,("sending local master announce to %s for %s\n",
274                                inet_ntoa(d->bcast_ip),work->work_group));
275
276                       CVAL(outbuf,0) = ANN_LocalMasterAnnouncement;
277                       
278                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
279                                           PTR_DIFF(p,outbuf),
280                                           my_name,work->work_group,0,
281                                           0x1e,d->bcast_ip,
282                                           *iface_ip(d->bcast_ip));
283                       
284                       DEBUG(2,("sending domain announce to %s for %s\n",
285                                inet_ntoa(d->bcast_ip),work->work_group));
286
287                       CVAL(outbuf,0) = ANN_DomainAnnouncement;
288                       
289                       StrnCpy(namep,work->work_group,15);
290                       strupper(namep);
291                       StrnCpy(commentp,myname,15);
292                       strupper(commentp);
293                       
294                       SIVAL(stypep,0,(unsigned)0x80000000);
295                       p = commentp + strlen(commentp) + 1;
296                       
297                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
298                                           PTR_DIFF(p,outbuf),
299                                           my_name,MSBROWSE,0,0x01,d->bcast_ip,
300                                           *iface_ip(d->bcast_ip));
301                     }
302                   else
303                     {
304                       DEBUG(2,("sending host announce to %s for %s\n",
305                                inet_ntoa(d->bcast_ip),work->work_group));
306
307                       CVAL(outbuf,0) = ANN_HostAnnouncement;
308                       
309                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
310                                           PTR_DIFF(p,outbuf),
311                                           my_name,work->work_group,0,0x1d,
312                                           d->bcast_ip,*iface_ip(d->bcast_ip));
313                     }
314                 }
315             }
316           
317           if (work->needannounce)
318             {
319               work->needannounce = False;
320               break;
321               /* sorry: can't do too many announces. do some more later */
322             }
323         }
324     }
325 }
326
327
328 /****************************************************************************
329   announce myself as a master to all other primary domain conrollers.
330
331   BIG NOTE: this code will remain untested until some kind soul that has access
332   to a couple of windows NT advanced servers runs this version of nmbd for at
333   least 15 minutes.
334   
335   this actually gets done in search_and_sync_workgroups() via the
336   MASTER_SERVER_CHECK command, if there is a response from the
337   name query initiated here.  see response_name_query()
338   **************************************************************************/
339 void announce_master(void)
340 {
341   struct subnet_record *d;
342   static time_t last=0;
343   time_t t = time(NULL);
344   BOOL am_master = False; /* are we a master of some sort? :-) */
345
346   if (last && (t-last < CHECK_TIME_MST_ANNOUNCE * 60)) 
347     return; 
348
349   last = t;
350
351   for (d = subnetlist; d; d = d->next)
352     {
353       struct work_record *work;
354       for (work = d->workgrouplist; work; work = work->next)
355         {
356           if (AM_MASTER(work))
357             {
358               am_master = True;
359             }
360         }
361     }
362   
363   if (!am_master) return; /* only proceed if we are a master browser */
364   
365   for (d = subnetlist; d; d = d->next)
366     {
367       struct work_record *work;
368       for (work = d->workgrouplist; work; work = work->next)
369         {
370           struct server_record *s;
371           for (s = work->serverlist; s; s = s->next)
372             {
373               if (strequal(s->serv.name, myname)) continue;
374               
375               /* all PDCs (which should also be master browsers) */
376               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
377                 {
378                   /* check the existence of a pdc for this workgroup, and if
379                      one exists at the specified ip, sync with it and announce
380                      ourselves as a master browser to it */
381                   
382                   if (!*lp_domain_controller() ||
383                       !strequal(lp_domain_controller(), s->serv.name))
384                     {
385                       if (!lp_wins_support() && *lp_wins_server())
386                         {
387                           struct in_addr ip;
388                           ip = ipzero;
389                           
390                           queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
391                                                  MASTER_SERVER_CHECK,
392                                                  work->work_group,0x1b,0,
393                                                  False, False, ip);
394                         }
395                       else
396                         {
397                           struct subnet_record *d2;
398                           for (d2 = subnetlist; d2; d2 = d2->next)
399                             {
400                               queue_netbios_packet(ClientNMB,NMB_QUERY,
401                                                    MASTER_SERVER_CHECK,
402                                                    work->work_group,0x1b,0,
403                                                    True, False, d2->bcast_ip);
404                             }
405                         }
406                     }
407                 }
408             }
409           
410           /* now do primary domain controller - the one that's not
411              necessarily in our browse lists, although it ought to be
412              this pdc is the one that we get TOLD about through smb.conf.
413              basically, if it's on a subnet that we know about, it may end
414              up in our browse lists (which is why it's explicitly excluded
415              in the code above) */
416           
417           if (*lp_domain_controller())
418             {
419               struct in_addr ip;
420               BOOL bcast = False;
421               
422               ip = *interpret_addr2(lp_domain_controller());
423               
424               if (zero_ip(ip)) {
425                 ip = d->bcast_ip;
426                 bcast = True;
427               }
428
429               DEBUG(2, ("Searching for PDC %s at %s\n",
430                         lp_domain_controller(), inet_ntoa(ip)));
431               
432               /* check the existence of a pdc for this workgroup, and if
433                  one exists at the specified ip, sync with it and announce
434                  ourselves as a master browser to it */
435               queue_netbios_pkt_wins(ClientNMB, NMB_QUERY,MASTER_SERVER_CHECK,
436                                      work->work_group,0x1b, 0,
437                                      bcast, False, ip);
438             }
439         }
440     }
441 }