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