- add timeouts to connect() for password server connections. This
[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 extern int  updatecount;
46 extern int  workgroup_count;
47
48 extern struct in_addr ipgrp;
49
50
51
52 /****************************************************************************
53   send a announce request to the local net
54   **************************************************************************/
55 void announce_request(struct work_record *work, struct in_addr ip)
56 {
57   pstring outbuf;
58   char *p;
59
60   if (!work) return;
61
62   work->needannounce = True;
63
64   DEBUG(2,("sending announce request to %s for workgroup %s\n",
65            inet_ntoa(ip),work->work_group));
66
67   bzero(outbuf,sizeof(outbuf));
68   p = outbuf;
69   CVAL(p,0) = ANN_AnnouncementRequest;
70   p++;
71
72   CVAL(p,0) = work->token; /* (local) unique workgroup token id */
73   p++;
74   StrnCpy(p,myname,16);
75   strupper(p);
76   p = skip_string(p,1);
77   
78   /* XXXX note: if we sent the announcement request to 0x1d instead
79      of 0x1e, then we could get the master browser to announce to
80      us instead of the members of the workgroup. wha-hey! */
81
82   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
83                       myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
84 }
85
86
87 /****************************************************************************
88   request an announcement
89   **************************************************************************/
90 void do_announce_request(char *info, char *to_name, int announce_type, 
91                          int from,
92                          int to, struct in_addr dest_ip)
93 {
94   pstring outbuf;
95   char *p;
96   
97   bzero(outbuf,sizeof(outbuf));
98   p = outbuf;
99   CVAL(p,0) = announce_type; 
100   p++;
101   
102   DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n",
103            announce_type, info, inet_ntoa(dest_ip),to_name,to));
104   
105   StrnCpy(p,info,16);
106   strupper(p);
107   p = skip_string(p,1);
108   
109   send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
110                       myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
111 }
112
113
114 /****************************************************************************
115   find a server responsible for a workgroup, and sync browse lists
116   control ends up back here via response_name_query.
117   **************************************************************************/
118 void sync_server(enum state_type state, char *serv_name, char *work_name, 
119                  int name_type,
120                  struct in_addr ip)
121 {                     
122   /* with a domain master we can get the whole list (not local only list) */
123   BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
124
125   add_browser_entry(serv_name, name_type, work_name, 0, ip, local_only);
126
127   if (state == NAME_STATUS_DOM_SRV_CHK)
128   {
129     /* announce ourselves as a master browser to serv_name */
130     do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
131                           0x20, 0, ip);
132   }
133 }
134
135
136 /****************************************************************************
137   send a host announcement packet
138   **************************************************************************/
139 void do_announce_host(int command,
140                 char *from_name, int from_type, struct in_addr from_ip,
141                 char *to_name  , int to_type  , struct in_addr to_ip,
142                 time_t announce_interval,
143                 char *server_name, int server_type, char *server_comment)
144 {
145         pstring outbuf;
146         char *p;
147
148         bzero(outbuf,sizeof(outbuf));
149         p = outbuf+1;
150
151         /* command type */
152         CVAL(outbuf,0) = command;
153
154         /* announcement parameters */
155         CVAL(p,0) = updatecount;
156         SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */
157
158         StrnCpy(p+5,server_name,16);
159         strupper(p+5);
160
161         CVAL(p,21) = 0x02; /* major version */
162         CVAL(p,22) = 0x02; /* minor version */
163
164         SIVAL(p,23,server_type);
165         SSVAL(p,27,0x010f); /* browse version: got from NT/AS 4.00 */
166         SSVAL(p,29,0xaa55); /* browse signature */
167
168         strcpy(p+31,server_comment);
169         p += 31;
170         p = skip_string(p,1);
171
172     debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
173
174         /* send the announcement */
175         send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
176                                           PTR_DIFF(p,outbuf),
177                                           from_name, to_name,
178                                           from_type, to_type,
179                                           to_ip, from_ip);
180 }
181
182
183 /****************************************************************************
184   remove all samba's server entries
185   ****************************************************************************/
186 void remove_my_servers(void)
187 {
188         struct subnet_record *d; 
189         for (d = subnetlist; d; d = d->next)
190         {
191                 struct work_record *work;
192                 for (work = d->workgrouplist; work; work = work->next)
193                 {
194                         struct server_record *s;
195                         for (s = work->serverlist; s; s = s->next)
196                         {
197                                 if (!strequal(myname,s->serv.name)) continue;
198                                 announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
199                         }
200                 }
201         }
202 }
203
204
205 /****************************************************************************
206   announce a server entry
207   ****************************************************************************/
208 void announce_server(struct subnet_record *d, struct work_record *work,
209                      char *name, char *comment, time_t ttl, int server_type)
210 {
211         uint32 domain_type = SV_TYPE_DOMAIN_ENUM|DFLT_SERVER_TYPE;
212         BOOL wins_iface = ip_equal(d->bcast_ip, ipgrp);
213         
214         if (wins_iface && server_type != 0)
215         {
216                 /* wins pseudo-ip interface */
217                 if (!AM_MASTER(work))
218                 {
219                         /* non-master announce by unicast to the domain 
220                            master */
221                         if (!lp_wins_support() && *lp_wins_server())
222                         {
223                                 /* look up the domain master with the WINS server */
224                                 queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
225                                          NAME_QUERY_ANNOUNCE_HOST,
226                                          work->work_group,0x1b,0,ttl*1000,
227                                          server_type,name,comment,
228                                          False, False, ipzero, d->bcast_ip);
229                         }
230                         else
231                         {
232                                 /* we are the WINS server, but not the domain master.  */
233                                 /* XXXX we need to look up the domain master in our
234                                    WINS database list, and do_announce_host(). maybe
235                                    we could do a name query on the unsuspecting domain
236                                    master just to make sure it's awake. */
237                         }
238                 }
239
240                 /* XXXX any other kinds of announcements we need to consider here?
241                    e.g local master browsers... no. local master browsers do
242                    local master announcements to their domain master. they even
243                    use WINS lookup of the domain master if another wins server
244                    is being used! 
245                  */
246         }
247         else
248         {
249                 if (AM_MASTER(work))
250                 {
251                         DEBUG(3,("sending local master announce to %s for %s(1e)\n",
252                                                         inet_ntoa(d->bcast_ip),work->work_group));
253
254                         do_announce_host(ANN_LocalMasterAnnouncement,
255                                                         name            , 0x00, d->myip,
256                                                         work->work_group, 0x1e, d->bcast_ip,
257                                                         ttl*1000,
258                                                         name, server_type, comment);
259
260                         DEBUG(3,("sending domain announce to %s for %s\n",
261                                                         inet_ntoa(d->bcast_ip),work->work_group));
262
263                         /* XXXX should we do a domain-announce-kill? */
264                         if (server_type != 0)
265                         {
266                                 do_announce_host(ANN_DomainAnnouncement,
267                                                         name    , 0x00, d->myip,
268                                                         MSBROWSE, 0x01, d->bcast_ip,
269                                                         ttl*1000,
270                                                         work->work_group, server_type ? domain_type : 0,
271                                                         name);
272                         }
273                 }
274                 else
275                 {
276                         DEBUG(3,("sending host announce to %s for %s(1d)\n",
277                                                         inet_ntoa(d->bcast_ip),work->work_group));
278
279                         do_announce_host(ANN_HostAnnouncement,
280                                                         name            , 0x00, d->myip,
281                                                         work->work_group, 0x1d, d->bcast_ip,
282                                                         ttl*1000,
283                                                         name, server_type, comment);
284                 }
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, lp_serverstring(), 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       if (ip_equal(d->bcast_ip, ipgrp)) continue;
307
308       for (work = d->workgrouplist; work; work = work->next)
309         {
310           uint32 stype = work->ServerType;
311           struct server_record *s;
312           BOOL announce = False;
313           
314       /* must work on the code that does announcements at up to
315          30 seconds later if a master browser sends us a request
316          announce.
317        */
318
319           if (work->needannounce) {
320             /* drop back to a max 3 minute announce - this is to prevent a
321                single lost packet from stuffing things up for too long */
322             work->announce_interval = MIN(work->announce_interval,
323                                                 CHECK_TIME_MIN_HOST_ANNCE*60);
324             work->lastannounce_time = t - (work->announce_interval+1);
325           }
326           
327           /* announce every minute at first then progress to every 12 mins */
328           if (work->lastannounce_time && 
329               (t - work->lastannounce_time) < work->announce_interval)
330             continue;
331           
332           if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) 
333             work->announce_interval += 60;
334           
335           work->lastannounce_time = t;
336
337           for (s = work->serverlist; s; s = s->next) {
338             if (strequal(myname, s->serv.name)) { 
339               announce = True; 
340               break; 
341             }
342           }
343           
344           if (announce) {
345             announce_server(d,work,my_name,comment,
346                             work->announce_interval,stype);
347           }
348           
349           if (work->needannounce)
350           {
351               work->needannounce = False;
352               break;
353               /* sorry: can't do too many announces. do some more later */
354           }
355         }
356   }
357 }
358
359
360 /****************************************************************************
361   announce myself as a master to all other primary domain conrollers.
362
363   this actually gets done in search_and_sync_workgroups() via the
364   NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
365   name query initiated here.  see response_name_query()
366   **************************************************************************/
367 void announce_master(void)
368 {
369   struct subnet_record *d;
370   static time_t last=0;
371   time_t t = time(NULL);
372   BOOL am_master = False; /* are we a master of some sort? :-) */
373
374   if (!last) last = t;
375   if (t-last < CHECK_TIME_MST_ANNOUNCE * 60)
376         return;
377
378   last = t;
379
380   for (d = subnetlist; d; d = d->next)
381     {
382       struct work_record *work;
383       for (work = d->workgrouplist; work; work = work->next)
384         {
385           if (AM_MASTER(work))
386             {
387               am_master = True;
388             }
389         }
390     }
391   
392   if (!am_master) return; /* only proceed if we are a master browser */
393   
394   for (d = subnetlist; d; d = d->next)
395     {
396       struct work_record *work;
397       for (work = d->workgrouplist; work; work = work->next)
398         {
399           struct server_record *s;
400           for (s = work->serverlist; s; s = s->next)
401             {
402               if (strequal(s->serv.name, myname)) continue;
403               
404               /* all DOMs (which should also be master browsers) */
405               if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
406                 {
407                   /* check the existence of a pdc for this workgroup, and if
408                      one exists at the specified ip, sync with it and announce
409                      ourselves as a master browser to it */
410                   
411                   if (!*lp_domain_controller() ||
412                       !strequal(lp_domain_controller(), s->serv.name))
413                     {
414                       if (!lp_wins_support() && *lp_wins_server())
415                         {
416                           queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
417                                                  NAME_QUERY_DOM_SRV_CHK,
418                                                  work->work_group,0x1b,0,0,0,NULL,NULL,
419                                                  False, False, ipzero, ipzero);
420                         }
421                       else
422                         {
423                           struct subnet_record *d2;
424                           for (d2 = subnetlist; d2; d2 = d2->next)
425                             {
426                               queue_netbios_packet(d,ClientNMB,NMB_QUERY,
427                                                    NAME_QUERY_DOM_SRV_CHK,
428                                                    work->work_group,0x1b,0,0,0,NULL,NULL,
429                                                    True, False, d2->bcast_ip, d2->bcast_ip);
430                             }
431                         }
432                     }
433                 }
434             }
435           
436           /* now do primary domain controller - the one that's not
437              necessarily in our browse lists, although it ought to be
438              this pdc is the one that we get TOLD about through smb.conf.
439              basically, if it's on a subnet that we know about, it may end
440              up in our browse lists (which is why it's explicitly excluded
441              in the code above) */
442           
443           if (*lp_domain_controller())
444             {
445               struct in_addr ip;
446               BOOL bcast = False;
447               
448               ip = *interpret_addr2(lp_domain_controller());
449               
450               if (zero_ip(ip)) {
451                 ip = d->bcast_ip;
452                 bcast = True;
453               }
454
455               DEBUG(2, ("Searching for DOM %s at %s\n",
456                         lp_domain_controller(), inet_ntoa(ip)));
457               
458               /* check the existence of a pdc for this workgroup, and if
459                  one exists at the specified ip, sync with it and announce
460                  ourselves as a master browser to it */
461               queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
462                                      work->work_group,0x1b,0,0,0,NULL,NULL,
463                                      bcast, False, ip, ip);
464             }
465         }
466     }
467 }
468
469
470
471 /****************************************************************************
472   do all the "remote" announcements. These are used to put ourselves
473   on a remote browse list. They are done blind, no checking is done to
474   see if there is actually a browse master at the other end.
475   **************************************************************************/
476 void announce_remote(void)
477 {
478   char *s,*ptr;
479   static time_t last_time = 0;
480   time_t t = time(NULL);
481   pstring s2;
482   struct in_addr addr;
483   char *comment,*workgroup;
484   int stype = DFLT_SERVER_TYPE;
485
486   if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
487     return;
488
489   last_time = t;
490
491   s = lp_remote_announce();
492   if (!*s) return;
493
494   comment = lp_serverstring();
495   workgroup = lp_workgroup();
496
497   for (ptr=s; next_token(&ptr,s2,NULL); ) {
498     /* the entries are of the form a.b.c.d/WORKGROUP with 
499        WORKGROUP being optional */
500     char *wgroup;
501
502     wgroup = strchr(s2,'/');
503     if (wgroup) *wgroup++ = 0;
504     if (!wgroup || !*wgroup)
505       wgroup = workgroup;
506
507     addr = *interpret_addr2(s2);
508     
509     do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
510                      wgroup,0x1e,addr,
511                      REMOTE_ANNOUNCE_INTERVAL,
512                      myname,stype,comment);    
513   }
514
515 }