Cleanup of DC enumeration in get_dcs()
[ira/wip.git] / source3 / libsmb / conncache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon connection manager
5
6    Copyright (C) Tim Potter             2001
7    Copyright (C) Andrew Bartlett        2002
8    Copyright (C) Gerald (Jerry) Carter  2003
9    Copyright (C) Marc VanHeyningen      2008
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25
26 #include "includes.h"
27
28 /**
29  * @file
30  * Negative connection cache implemented in terms of gencache API
31  *
32  * The negative connection cache stores names of servers which have
33  * been unresponsive so that we don't waste time repeatedly trying
34  * to contact them.  It used to use an in-memory linked list, but
35  * this limited its utility to a single process
36  */
37
38
39 /**
40  * prefix used for all entries put into the general cache
41  */
42 static const char NEGATIVE_CONN_CACHE_PREFIX[] = "NEG_CONN_CACHE";
43
44 /**
45  * Marshalls the domain and server name into the key for the gencache
46  * record
47  *
48  * @param[in] domain required
49  * @param[in] server may be a FQDN or an IP address
50  * @return the resulting string, which the caller is responsible for
51  *   SAFE_FREE()ing
52  * @retval NULL returned on error
53  */
54 static char *negative_conn_cache_keystr(const char *domain, const char *server)
55 {
56         const char NEGATIVE_CONN_CACHE_KEY_FMT[] = "%s/%s,%s";
57         char *keystr = NULL;
58
59         SMB_ASSERT(domain != NULL);
60         if (server == NULL)
61                 server = "";
62
63         keystr = talloc_asprintf(talloc_tos(),NEGATIVE_CONN_CACHE_KEY_FMT,
64                                  NEGATIVE_CONN_CACHE_PREFIX, domain, server);
65         if (keystr == NULL) {
66                 DEBUG(0, ("negative_conn_cache_keystr: malloc error\n"));
67         }
68
69         return keystr;
70 }
71
72 /**
73  * Marshalls the NT status into a printable value field for the gencache
74  * record
75  *
76  * @param[in] status
77  * @return the resulting string, which the caller is responsible for
78  *   SAFE_FREE()ing
79  * @retval NULL returned on error
80  */
81 static char *negative_conn_cache_valuestr(NTSTATUS status)
82 {
83         char *valuestr = NULL;
84
85         valuestr = talloc_asprintf(talloc_tos(), "%x", NT_STATUS_V(status));
86         if (valuestr == NULL) {
87                 DEBUG(0, ("negative_conn_cache_valuestr: malloc error\n"));
88         }
89
90         return valuestr;
91 }
92
93 /**
94  * Un-marshalls the NT status from a printable field for the gencache
95  * record
96  *
97  * @param[in] value  The value field from the record
98  * @return the decoded NT status
99  * @retval NT_STATUS_OK returned on error
100  */
101 static NTSTATUS negative_conn_cache_valuedecode(const char *value)
102 {
103         NTSTATUS result = NT_STATUS_OK;
104
105         SMB_ASSERT(value != NULL);
106         if (sscanf(value, "%x", &(NT_STATUS_V(result))) != 1)
107                 DEBUG(0, ("negative_conn_cache_valuestr: unable to parse "
108                           "value field '%s'\n", value));
109         return result;
110 }
111
112 /**
113  * Function passed to gencache_iterate to remove any matching items
114  * from the list
115  *
116  * @param[in] key Key to the record found and to be deleted
117  * @param[in] value Value to the record (ignored)
118  * @param[in] timeout Timeout remaining for the record (ignored)
119  * @param[in] dptr Handle for passing additional data (ignored)
120  */
121 static void delete_matches(const char *key, const char *value,
122     time_t timeout, void *dptr)
123 {
124         gencache_del(key);
125 }
126
127
128 /**
129  * Checks for a given domain/server record in the negative cache
130  *
131  * @param[in] domain
132  * @param[in] server may be either a FQDN or an IP address
133  * @return The cached failure status
134  * @retval NT_STATUS_OK returned if no record is found or an error occurs
135  */
136 NTSTATUS check_negative_conn_cache( const char *domain, const char *server)
137 {
138         NTSTATUS result = NT_STATUS_OK;
139         char *key = NULL;
140         char *value = NULL;
141
142         key = negative_conn_cache_keystr(domain, server);
143         if (key == NULL)
144                 goto done;
145
146         if (gencache_get(key, &value, (time_t *) NULL))
147                 result = negative_conn_cache_valuedecode(value);
148  done:
149         DEBUG(9,("check_negative_conn_cache returning result %d for domain %s "
150                   "server %s\n", NT_STATUS_V(result), domain, server));
151         TALLOC_FREE(key);
152         SAFE_FREE(value);
153         return result;
154 }
155
156 /**
157  * Delete any negative cache entry for the given domain/server
158  *
159  * @param[in] domain
160  * @param[in] server may be either a FQDN or an IP address
161  */
162 void delete_negative_conn_cache(const char *domain, const char *server)
163 {
164         char *key = NULL;
165
166         key = negative_conn_cache_keystr(domain, server);
167         if (key == NULL)
168                 goto done;
169
170         gencache_del(key);
171         DEBUG(9,("delete_negative_conn_cache removing domain %s server %s\n",
172                   domain, server));
173  done:
174         TALLOC_FREE(key);
175         return;
176 }
177
178
179 /**
180  * Add an entry to the failed connection cache
181  *
182  * @param[in] domain
183  * @param[in] server may be a FQDN or an IP addr in printable form
184  * @param[in] result error to cache; must not be NT_STATUS_OK
185  */
186 void add_failed_connection_entry(const char *domain, const char *server,
187     NTSTATUS result)
188 {
189         char *key = NULL;
190         char *value = NULL;
191
192         SMB_ASSERT(!NT_STATUS_IS_OK(result));
193
194         key = negative_conn_cache_keystr(domain, server);
195         if (key == NULL) {
196                 DEBUG(0, ("add_failed_connection_entry: key creation error\n"));
197                 goto done;
198         }
199
200         value = negative_conn_cache_valuestr(result);
201         if (value == NULL) {
202                 DEBUG(0, ("add_failed_connection_entry: value creation error\n"));
203                 goto done;
204         }
205
206         if (gencache_set(key, value,
207                          time((time_t *) NULL)
208                          + FAILED_CONNECTION_CACHE_TIMEOUT))
209                 DEBUG(9,("add_failed_connection_entry: added domain %s (%s) "
210                           "to failed conn cache\n", domain, server ));
211         else
212                 DEBUG(1,("add_failed_connection_entry: failed to add "
213                           "domain %s (%s) to failed conn cache\n",
214                           domain, server));
215         
216  done:
217         TALLOC_FREE(key);
218         TALLOC_FREE(value);
219         return;
220 }
221
222 /**
223  * Deletes all records from the negative connection cache in all domains
224  */
225 void flush_negative_conn_cache( void )
226 {
227         flush_negative_conn_cache_for_domain("*");
228 }
229
230
231 /**
232  * Deletes all records for a specified domain from the negative connection
233  * cache
234  *
235  * @param[in] domain String to match against domain portion of keys, or "*"
236  *  to match all domains
237  */
238 void flush_negative_conn_cache_for_domain(const char *domain)
239 {
240         char *key_pattern = NULL;
241
242         key_pattern = negative_conn_cache_keystr(domain,"*");
243         if (key_pattern == NULL) {
244                 DEBUG(0, ("flush_negative_conn_cache_for_domain: "
245                           "key creation error\n"));
246                 goto done;
247         }
248
249         gencache_iterate(delete_matches, (void *) NULL, key_pattern);
250         DEBUG(8, ("flush_negative_conn_cache_for_domain: flushed domain %s\n",
251                   domain));
252         
253  done:
254         TALLOC_FREE(key_pattern);
255         return;
256 }