Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}