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