Race condition with multiple oplock break requests happens
[kai/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-1997
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 extern struct in_addr wins_ip;
36
37 /* this is our browse master/backup cache database */
38 static struct browse_cache_record *browserlist = NULL;
39
40
41 /***************************************************************************
42   add a browser into the list
43   **************************************************************************/
44 static void add_browse_cache(struct browse_cache_record *b)
45 {
46   struct browse_cache_record *b2;
47
48   if (!browserlist)
49     {
50       browserlist = b;
51       b->prev = NULL;
52       b->next = NULL;
53       return;
54     }
55   
56   for (b2 = browserlist; b2->next; b2 = b2->next) ;
57   
58   b2->next = b;
59   b->next = NULL;
60   b->prev = b2;
61 }
62
63
64 /*******************************************************************
65   remove old browse entries
66   ******************************************************************/
67 void expire_browse_cache(time_t t)
68 {
69   struct browse_cache_record *b;
70   struct browse_cache_record *nextb;
71   
72   /* expire old entries in the serverlist */
73   for (b = browserlist; b; b = nextb)
74     {
75       if (b->synced && b->sync_time < t)
76         {
77           DEBUG(3,("Removing dead cached browser %s\n",b->name));
78           nextb = b->next;
79           
80           if (b->prev) b->prev->next = b->next;
81           if (b->next) b->next->prev = b->prev;
82           
83           if (browserlist == b) browserlist = b->next; 
84           
85           free(b);
86         }
87       else
88         {
89           nextb = b->next;
90         }
91     }
92 }
93
94 /****************************************************************************
95   add a browser entry
96   ****************************************************************************/
97 struct browse_cache_record *add_browser_entry(char *name, int type, char *wg,
98                                               time_t ttl, struct subnet_record *d,
99                                               struct in_addr ip, BOOL local)
100 {
101   BOOL newentry=False;
102   
103   struct browse_cache_record *b;
104
105   /* search for the entry: if it's already in the cache, update that entry */
106   for (b = browserlist; b; b = b->next)
107     {
108       if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break;
109     }
110   
111   if (b && b->synced)
112     {
113       /* entries get left in the cache for a while. this stops sync'ing too
114          often if the network is large */
115       DEBUG(4, ("browser %s %s %s already sync'd at time %d\n",
116                 b->name, b->group, inet_ntoa(b->ip), b->sync_time));
117       return NULL;
118     }
119   
120   if (!b)
121     {
122       newentry = True;
123       b = (struct browse_cache_record *)malloc(sizeof(*b));
124       
125       if (!b) return(NULL);
126       
127       bzero((char *)b,sizeof(*b));
128     }
129   
130   /* update the entry */
131   ttl = time(NULL)+ttl;
132   
133   StrnCpy(b->name ,name,sizeof(b->name )-1);
134   StrnCpy(b->group,wg  ,sizeof(b->group)-1);
135   strupper(b->name);
136   strupper(b->group);
137   
138   b->ip     = ip;
139   b->type   = type;
140   b->local  = local; /* local server list sync or complete sync required */
141   b->subnet = d;
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 = b->subnet;
170   struct work_record *work;
171
172   /* Check panic conditions - these should not be true. */
173   if(b->subnet != wins_subnet) {
174       DEBUG(0, 
175         ("start_sync_browse_entry: ERROR sync requested on non-WINS subnet.\n"));
176       return;
177   }
178
179   if (!(work = find_workgroupstruct(d, b->group, False))) {
180       DEBUG(0, ("start_sync_browse_entry: failed to get a \
181 workgroup for a browse cache entry workgroup %s, server %s\n", 
182                 b->group, b->name));
183       return;
184   }
185
186   DEBUG(4, ("start_sync_browse_entry: Initiating %s sync with %s<0x20>, \
187 workgroup %s\n",
188              b->local ? "local" : "remote", b->name, b->group));
189
190   /* first check whether the server we intend to sync with exists. if it
191      doesn't, the server must have died. o dear. */
192
193   /* see response_netbios_packet() or expire_netbios_response_entries() */
194   /* We cheat here by using the my_comment field of the response_record 
195      struct as the workgroup name we are going to do the sync for. 
196      This is because the reply packet doesn't include the workgroup, but 
197      we need it when the reply comes back.
198   */
199   queue_netbios_packet(d,ClientNMB,NMB_QUERY,
200          b->local?NAME_QUERY_SYNC_LOCAL:NAME_QUERY_SYNC_REMOTE,
201          b->name,0x20,0,0,0,NULL,b->group,
202          False,False,b->ip,b->ip);
203
204   b->synced = True;
205 }
206
207
208 /****************************************************************************
209   search through browser list for an entry to sync with
210   **************************************************************************/
211 void do_browser_lists(time_t t)
212 {
213   struct browse_cache_record *b;
214   static time_t last = 0;
215   
216   if (t-last < 20) 
217    {
218      DEBUG(9,("do_browser_lists: returning due to t(%d) - last(%d) < 20\n",
219              t, last));
220      return; /* don't do too many of these at once! */
221                            /* XXXX equally this period should not be too long
222                               the server may die in the intervening gap */
223    } 
224   last = t;
225   
226   /* pick any entry in the list, preferably one whose time is up */
227   for (b = browserlist; b && b->next; b = b->next)
228     {
229       if (b->sync_time < t && b->synced == False) break;
230     }
231   
232   if (b && !b->synced)
233   {
234     /* sync with the selected entry then remove some dead entries */
235     DEBUG(4,("do_browser_lists: Initiating sync with %s, workgroup %s\n",
236               b->name, b->group));
237     start_sync_browse_entry(b);
238   }
239   else
240   {
241     DEBUG(9, ("do_browser_lists: no entries to sync.\n"));
242   }
243
244   expire_browse_cache(t - 60);
245 }
246