5163c4aea9ad1c627cde2cb7eeb5a7ff8e4e976d
[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 #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)
126         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   send a host announcement packet
184   **************************************************************************/
185 void do_announce_host(int command,
186                 char *from_name, int from_type, struct in_addr from_ip,
187                 char *to_name  , int to_type  , struct in_addr to_ip,
188                 int updatecount, time_t announce_interval,
189                 char *server_name, int server_type, char *server_comment)
190 {
191         pstring outbuf;
192         char *p;
193
194         bzero(outbuf,sizeof(outbuf));
195         p = outbuf+1;
196
197         /* command type */
198         CVAL(outbuf,0) = command;
199
200         /* announcement parameters */
201         CVAL(p,0) = updatecount;
202         SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
203
204         StrnCpy(p+5,server_name,16);
205         strupper(p+5);
206
207         CVAL(p,21) = 2; /* major version */
208         CVAL(p,22) = 2; /* minor version */
209
210         SIVAL(p,23,server_type);
211         SSVAL(p,27,0xaa55); /* browse signature */
212         SSVAL(p,29,1); /* browse version */
213
214         strcpy(p+31,server_comment);
215         p += 31;
216         p = skip_string(p,1);
217
218         /* send the announcement */
219         send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
220                                           PTR_DIFF(p,outbuf),
221                                           from_name, to_name,
222                                           from_type, to_type,
223                                           to_ip, from_ip);
224 }
225
226
227 /****************************************************************************
228   announce a server entry
229   ****************************************************************************/
230 void announce_server(struct subnet_record *d, struct work_record *work,
231                                         char *name, char *comment, time_t ttl, int server_type)
232 {
233         if (AM_MASTER(work))
234         {
235                 DEBUG(3,("sending local master announce to %s for %s(1e)\n",
236                                                 inet_ntoa(d->bcast_ip),work->work_group));
237
238                 do_announce_host(ANN_LocalMasterAnnouncement,
239                                                 name            , 0x00, d->myip,
240                                                 work->work_group, 0x1e, d->bcast_ip,
241                                                 updatecount, ttl*1000,
242                                                 name, server_type, comment);
243
244                 DEBUG(3,("sending domain announce to %s for %s\n",
245                                                 inet_ntoa(d->bcast_ip),work->work_group));
246
247                 /* XXXX should we do a domain-announce-kill? */
248                 if (server_type != 0)
249                 {
250                         do_announce_host(ANN_DomainAnnouncement,
251                                                 work->work_group, 0x00, d->myip,
252                                                 MSBROWSE        , 0x01, d->bcast_ip,
253                                                 updatecount, ttl*1000,
254                                                 name, server_type ? SV_TYPE_DOMAIN_ENUM : 0, comment);
255                 }
256         }
257         else
258         {
259                 DEBUG(3,("sending host announce to %s for %s(1d)\n",
260                                                 inet_ntoa(d->bcast_ip),work->work_group));
261
262                 do_announce_host(ANN_HostAnnouncement,
263                                                 name            , 0x00, d->myip,
264                                                 work->work_group, 0x1d, d->bcast_ip,
265                                                 updatecount, ttl*1000,
266                                                 name, server_type, comment);
267         }
268 }
269
270 /****************************************************************************
271   construct a host announcement unicast
272   **************************************************************************/
273 void announce_host(void)
274 {
275   time_t t = time(NULL);
276   struct subnet_record *d;
277   pstring comment;
278   char *my_name;
279
280   StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
281
282   my_name = *myname ? myname : "NoName";
283
284   for (d = subnetlist; d; d = d->next)
285     {
286       struct work_record *work;
287       
288       for (work = d->workgrouplist; work; work = work->next)
289         {
290           uint32 stype = work->ServerType;
291           struct server_record *s;
292           BOOL announce = False;
293           
294           if (work->needannounce) {
295             /* drop back to a max 3 minute announce - this is to prevent a
296                single lost packet from stuffing things up for too long */
297             work->announce_interval = MIN(work->announce_interval, 
298                                           CHECK_TIME_MIN_HOST_ANNCE*60);
299             work->lastannounce_time = t - (work->announce_interval+1);
300           }
301           
302           /* announce every minute at first then progress to every 12 mins */
303           if (work->lastannounce_time && 
304               (t - work->lastannounce_time) < work->announce_interval)
305             continue;
306           
307           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
308             work->announce_interval += 60;
309           
310           work->lastannounce_time = t;
311
312           if (!d->my_interface) {
313             stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
314                        SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
315                        SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
316           }
317
318           for (s = work->serverlist; s; s = s->next) {
319             if (strequal(myname, s->serv.name)) { 
320               announce = True; 
321               break; 
322             }
323           }
324           
325           if (announce)
326             {
327                 announce_server(d,work,my_name,comment,work->announce_interval,stype);
328             }
329           
330           if (work->needannounce)
331             {
332               work->needannounce = False;
333               break;
334               /* sorry: can't do too many announces. do some more later */
335             }
336         }
337     }
338 }
339
340
341 /****************************************************************************
342   announce myself as a master to all other primary domain conrollers.
343
344   BIG NOTE: this code will remain untested until some kind soul that has access
345   to a couple of windows NT advanced servers runs this version of nmbd for at
346   least 15 minutes.
347   
348   this actually gets done in search_and_sync_workgroups() via the
349   NAME_QUERY_MST_SRV_CHK command, if there is a response from the
350   name query initiated here.  see response_name_query()
351   **************************************************************************/
352 void announce_master(void)
353 {
354   struct subnet_record *d;
355   static time_t last=0;
356   time_t t = time(NULL);
357   BOOL am_master = False; /* are we a master of some sort? :-) */
358
359   if (last && (t-last < CHECK_TIME_MST_ANNOUNCE * 60)) 
360     return; 
361
362   last = t;
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           if (AM_MASTER(work))
370             {
371               am_master = True;
372             }
373         }
374     }
375   
376   if (!am_master) return; /* only proceed if we are a master browser */
377   
378   for (d = subnetlist; d; d = d->next)
379     {
380       struct work_record *work;
381       for (work = d->workgrouplist; work; work = work->next)
382         {
383           struct server_record *s;
384           for (s = work->serverlist; s; s = s->next)
385             {
386               if (strequal(s->serv.name, myname)) continue;
387               
388               /* all PDCs (which should also be master browsers) */
389               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
390                 {
391                   /* check the existence of a pdc for this workgroup, and if
392                      one exists at the specified ip, sync with it and announce
393                      ourselves as a master browser to it */
394                   
395                   if (!*lp_domain_controller() ||
396                       !strequal(lp_domain_controller(), s->serv.name))
397                     {
398                       if (!lp_wins_support() && *lp_wins_server())
399                         {
400                           struct in_addr ip;
401                           ip = ipzero;
402                           
403                           queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
404                                                  NAME_QUERY_MST_SRV_CHK,
405                                                  work->work_group,0x1b,0,0,
406                                                  False, False, ip);
407                         }
408                       else
409                         {
410                           struct subnet_record *d2;
411                           for (d2 = subnetlist; d2; d2 = d2->next)
412                             {
413                               queue_netbios_packet(d,ClientNMB,NMB_QUERY,
414                                                    NAME_QUERY_MST_SRV_CHK,
415                                                    work->work_group,0x1b,0,0,
416                                                    True, False, d2->bcast_ip);
417                             }
418                         }
419                     }
420                 }
421             }
422           
423           /* now do primary domain controller - the one that's not
424              necessarily in our browse lists, although it ought to be
425              this pdc is the one that we get TOLD about through smb.conf.
426              basically, if it's on a subnet that we know about, it may end
427              up in our browse lists (which is why it's explicitly excluded
428              in the code above) */
429           
430           if (*lp_domain_controller())
431             {
432               struct in_addr ip;
433               BOOL bcast = False;
434               
435               ip = *interpret_addr2(lp_domain_controller());
436               
437               if (zero_ip(ip)) {
438                 ip = d->bcast_ip;
439                 bcast = True;
440               }
441
442               DEBUG(2, ("Searching for PDC %s at %s\n",
443                         lp_domain_controller(), inet_ntoa(ip)));
444               
445               /* check the existence of a pdc for this workgroup, and if
446                  one exists at the specified ip, sync with it and announce
447                  ourselves as a master browser to it */
448               queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_MST_SRV_CHK,
449                                      work->work_group,0x1b, 0, 0,
450                                      bcast, False, ip);
451             }
452         }
453     }
454 }