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