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