bb09a5d433d82455fee681097bdeb6eab97cf774
[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       if (!d->my_interface)
205         continue;
206
207       for (work = d->workgrouplist; work; work = work->next)
208         {
209           uint32 stype = work->ServerType;
210           struct server_record *s;
211           BOOL announce = False;
212           
213           if (work->needannounce) {
214             /* drop back to a max 3 minute announce - this is to prevent a
215                single lost packet from stuffing things up for too long */
216             work->announce_interval = MIN(work->announce_interval, 
217                                           CHECK_TIME_MIN_HOST_ANNCE*60);
218             work->lastannounce_time = t - (work->announce_interval+1);
219           }
220           
221           /* announce every minute at first then progress to every 12 mins */
222           if (work->lastannounce_time && 
223               (t - work->lastannounce_time) < work->announce_interval)
224             continue;
225           
226           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
227             work->announce_interval += 60;
228           
229           work->lastannounce_time = t;
230
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)
267                 {
268                   if (AM_MASTER(work))
269                     {
270                       SIVAL(stypep,0,work->ServerType);
271                       
272                       DEBUG(2,("sending local master announce to %s for %s\n",
273                                inet_ntoa(d->bcast_ip),work->work_group));
274
275                       CVAL(outbuf,0) = ANN_LocalMasterAnnouncement;
276                       
277                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
278                                           PTR_DIFF(p,outbuf),
279                                           my_name,work->work_group,0,
280                                           0x1e,d->bcast_ip,
281                                           *iface_ip(d->bcast_ip));
282                       
283                       DEBUG(2,("sending domain announce to %s for %s\n",
284                                inet_ntoa(d->bcast_ip),work->work_group));
285
286                       CVAL(outbuf,0) = ANN_DomainAnnouncement;
287                       
288                       StrnCpy(namep,work->work_group,15);
289                       strupper(namep);
290                       StrnCpy(commentp,myname,15);
291                       strupper(commentp);
292                       
293                       SIVAL(stypep,0,(unsigned)0x80000000);
294                       p = commentp + strlen(commentp) + 1;
295                       
296                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
297                                           PTR_DIFF(p,outbuf),
298                                           my_name,MSBROWSE,0,0x01,d->bcast_ip,
299                                           *iface_ip(d->bcast_ip));
300                     }
301                   else
302                     {
303                       DEBUG(2,("sending host announce to %s for %s\n",
304                                inet_ntoa(d->bcast_ip),work->work_group));
305
306                       CVAL(outbuf,0) = ANN_HostAnnouncement;
307                       
308                       send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
309                                           PTR_DIFF(p,outbuf),
310                                           my_name,work->work_group,0,0x1d,
311                                           d->bcast_ip,*iface_ip(d->bcast_ip));
312                     }
313                 }
314             }
315           
316           if (work->needannounce)
317             {
318               work->needannounce = False;
319               break;
320               /* sorry: can't do too many announces. do some more later */
321             }
322         }
323     }
324 }
325
326
327 /****************************************************************************
328   announce myself as a master to all other primary domain conrollers.
329
330   BIG NOTE: this code will remain untested until some kind soul that has access
331   to a couple of windows NT advanced servers runs this version of nmbd for at
332   least 15 minutes.
333   
334   this actually gets done in search_and_sync_workgroups() via the
335   MASTER_SERVER_CHECK command, if there is a response from the
336   name query initiated here.  see response_name_query()
337   **************************************************************************/
338 void announce_master(void)
339 {
340   struct subnet_record *d;
341   static time_t last=0;
342   time_t t = time(NULL);
343   BOOL am_master = False; /* are we a master of some sort? :-) */
344
345   if (last && (t-last < CHECK_TIME_MST_ANNOUNCE * 60)) 
346     return; 
347
348   last = t;
349
350   for (d = subnetlist; d; d = d->next)
351     {
352       struct work_record *work;
353       for (work = d->workgrouplist; work; work = work->next)
354         {
355           if (AM_MASTER(work))
356             {
357               am_master = True;
358             }
359         }
360     }
361   
362   if (!am_master) return; /* only proceed if we are a master browser */
363   
364   for (d = subnetlist; d; d = d->next)
365     {
366       struct work_record *work;
367       for (work = d->workgrouplist; work; work = work->next)
368         {
369           struct server_record *s;
370           for (s = work->serverlist; s; s = s->next)
371             {
372               if (strequal(s->serv.name, myname)) continue;
373               
374               /* all PDCs (which should also be master browsers) */
375               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
376                 {
377                   /* check the existence of a pdc for this workgroup, and if
378                      one exists at the specified ip, sync with it and announce
379                      ourselves as a master browser to it */
380                   
381                   if (!*lp_domain_controller() ||
382                       !strequal(lp_domain_controller(), s->serv.name))
383                     {
384                       if (!lp_wins_support() && *lp_wins_server())
385                         {
386                           struct in_addr ip;
387                           ip = ipzero;
388                           
389                           queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
390                                                  MASTER_SERVER_CHECK,
391                                                  work->work_group,0x1b,0,
392                                                  False, False, ip);
393                         }
394                       else
395                         {
396                           struct subnet_record *d2;
397                           for (d2 = subnetlist; d2; d2 = d2->next)
398                             {
399                               queue_netbios_packet(ClientNMB,NMB_QUERY,
400                                                    MASTER_SERVER_CHECK,
401                                                    work->work_group,0x1b,0,
402                                                    True, False, d2->bcast_ip);
403                             }
404                         }
405                     }
406                 }
407             }
408           
409           /* now do primary domain controller - the one that's not
410              necessarily in our browse lists, although it ought to be
411              this pdc is the one that we get TOLD about through smb.conf.
412              basically, if it's on a subnet that we know about, it may end
413              up in our browse lists (which is why it's explicitly excluded
414              in the code above) */
415           
416           if (*lp_domain_controller())
417             {
418               struct in_addr ip;
419               BOOL bcast = False;
420               
421               ip = *interpret_addr2(lp_domain_controller());
422               
423               if (zero_ip(ip)) {
424                 ip = d->bcast_ip;
425                 bcast = True;
426               }
427
428               DEBUG(2, ("Searching for PDC %s at %s\n",
429                         lp_domain_controller(), inet_ntoa(ip)));
430               
431               /* check the existence of a pdc for this workgroup, and if
432                  one exists at the specified ip, sync with it and announce
433                  ourselves as a master browser to it */
434               queue_netbios_pkt_wins(ClientNMB, NMB_QUERY,MASTER_SERVER_CHECK,
435                                      work->work_group,0x1b, 0,
436                                      bcast, False, ip);
437             }
438         }
439     }
440 }