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