8ebf7b8c9007091c29ce8283a9e8fc0e35a43e37
[samba.git] / source3 / namebrowse.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    30 July 96: David.Chappell@mail.trincoll.edu
27    Expanded multiple workgroup domain master browser support.
28
29 */
30
31 #include "includes.h"
32 #include "smb.h"
33
34 extern int ClientNMB;
35
36 extern int DEBUGLEVEL;
37
38 /* this is our browse master/backup cache database */
39 static struct browse_cache_record *browserlist = NULL;
40
41
42 /***************************************************************************
43   add a browser into the list
44   **************************************************************************/
45 static void add_browse_cache(struct browse_cache_record *b)
46 {
47   struct browse_cache_record *b2;
48
49   if (!browserlist)
50     {
51       browserlist = b;
52       b->prev = NULL;
53       b->next = NULL;
54       return;
55     }
56   
57   for (b2 = browserlist; b2->next; b2 = b2->next) ;
58   
59   b2->next = b;
60   b->next = NULL;
61   b->prev = b2;
62 }
63
64
65 /*******************************************************************
66   remove old browse entries
67   ******************************************************************/
68 void expire_browse_cache(time_t t)
69 {
70   struct browse_cache_record *b;
71   struct browse_cache_record *nextb;
72   
73   /* expire old entries in the serverlist */
74   for (b = browserlist; b; b = nextb)
75     {
76       if (b->synced && b->sync_time < t)
77     {
78       DEBUG(3,("Removing dead cached browser %s\n",b->name));
79       nextb = b->next;
80       
81       if (b->prev) b->prev->next = b->next;
82       if (b->next) b->next->prev = b->prev;
83       
84       if (browserlist == b) browserlist = b->next; 
85       
86       free(b);
87     }
88       else
89     {
90       nextb = b->next;
91     }
92     }
93 }
94
95
96 /****************************************************************************
97   add a browser entry
98   ****************************************************************************/
99 struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
100                           time_t ttl, struct in_addr ip, BOOL local)
101 {
102   BOOL newentry=False;
103   
104   struct browse_cache_record *b;
105
106   /* search for the entry: if it's already in the cache, update that entry */
107   for (b = browserlist; b; b = b->next)
108     {
109       if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break;
110     }
111   
112   if (b && b->synced)
113     {
114       /* entries get left in the cache for a while. this stops sync'ing too
115      often if the network is large */
116       DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
117         b->name, b->group, inet_ntoa(b->ip), b->sync_time));
118       return NULL;
119     }
120   
121   if (!b)
122     {
123       newentry = True;
124       b = (struct browse_cache_record *)malloc(sizeof(*b));
125       
126       if (!b) return(NULL);
127       
128       bzero((char *)b,sizeof(*b));
129     }
130   
131   /* update the entry */
132   ttl = time(NULL)+ttl;
133   
134   StrnCpy(b->name ,name,sizeof(b->name )-1);
135   StrnCpy(b->group,wg  ,sizeof(b->group)-1);
136   strupper(b->name);
137   strupper(b->group);
138   
139   b->ip     = ip;
140   b->type   = type;
141   b->local  = local; /* local server list sync or complete sync required */
142   
143   if (newentry || ttl < b->sync_time) 
144     b->sync_time = ttl;
145   
146   if (newentry)
147     {
148       b->synced = False;
149       add_browse_cache(b);
150       
151       DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n",
152            wg, name, type, inet_ntoa(ip),ttl));
153     }
154   else
155     {
156       DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n",
157            wg, name, type, inet_ntoa(ip),ttl));
158     }
159   
160   return(b);
161 }
162
163
164 /****************************************************************************
165 find a server responsible for a workgroup, and sync browse lists
166 **************************************************************************/
167 static void start_sync_browse_entry(struct browse_cache_record *b)
168 {                     
169   struct subnet_record *d;
170   struct work_record *work;
171
172   if( (d = find_subnet(b->ip)) == (struct subnet_record *)NULL ) return;
173   
174   if (!(work = find_workgroupstruct(d, b->group, False))) return;
175     
176   /* only sync if we are the master */
177   if (AM_MASTER(work)) {
178     
179       /* first check whether the group we intend to sync with exists. if it
180          doesn't, the server must have died. o dear. */
181     
182       /* see response_netbios_packet() or expire_netbios_response_entries() */
183       queue_netbios_packet(d,ClientNMB,NMB_QUERY,
184                        b->local?NAME_QUERY_SYNC_LOCAL:NAME_QUERY_SYNC_REMOTE,
185                        work->token,b->group,0x20,0,0,0,NULL,NULL,
186                        False,False,b->ip,b->ip);
187   }
188     
189   b->synced = True;
190 }
191
192
193 /****************************************************************************
194   search through browser list for an entry to sync with
195   **************************************************************************/
196 void do_browser_lists(void)
197 {
198   struct browse_cache_record *b;
199   static time_t last = 0;
200   time_t t = time(NULL);
201   
202   if (t-last < 20) return; /* don't do too many of these at once! */
203                            /* XXXX equally this period should not be too long
204                               the server may die in the intervening gap */
205   
206   last = t;
207   
208   /* pick any entry in the list, preferably one whose time is up */
209   for (b = browserlist; b && b->next; b = b->next)
210     {
211       if (b->sync_time < t && b->synced == False) break;
212     }
213   
214   if (b && !b->synced)
215   {
216     /* sync with the selected entry then remove some dead entries */
217     start_sync_browse_entry(b);
218     expire_browse_cache(t - 60);
219   }
220
221 }
222