Merge ldb_search() and ldb_search_exp_fmt() into a simgle function.
[Samba.git] / source4 / wrepl_server / wrepl_scavenging.c
bloba239403e3b0799dae1cda2186bbbf0e58058e7fd
1 /*
2 Unix SMB/CIFS implementation.
4 WINS Replication server
6 Copyright (C) Stefan Metzmacher 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/ndr_winsrepl.h"
24 #include "wrepl_server/wrepl_server.h"
25 #include "nbt_server/wins/winsdb.h"
26 #include "ldb/include/ldb.h"
27 #include "ldb/include/ldb_errors.h"
28 #include "system/time.h"
29 #include "smbd/service_task.h"
30 #include "lib/messaging/irpc.h"
31 #include "librpc/gen_ndr/ndr_irpc.h"
32 #include "librpc/gen_ndr/ndr_nbt.h"
34 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
35 TALLOC_CTX *mem_ctx,
36 const char *wins_owner)
38 if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
39 return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
40 wins_owner);
43 return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
44 wins_owner);
47 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
49 NTSTATUS status;
50 struct winsdb_record *rec = NULL;
51 struct ldb_result *res = NULL;
52 const char *owner_filter;
53 const char *filter;
54 uint32_t i;
55 int ret;
56 time_t now = time(NULL);
57 const char *now_timestr;
58 const char *action;
59 const char *old_state=NULL;
60 const char *new_state=NULL;
61 uint32_t modify_flags;
62 bool modify_record;
63 bool delete_record;
64 bool delete_tombstones;
65 struct timeval tombstone_extra_time;
67 now_timestr = ldb_timestring(tmp_mem, now);
68 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
69 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
70 service->wins_db->local_owner);
71 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
72 filter = talloc_asprintf(tmp_mem,
73 "(&%s(objectClass=winsRecord)"
74 "(expireTime<=%s))",
75 owner_filter, now_timestr);
76 NT_STATUS_HAVE_NO_MEMORY(filter);
77 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
78 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
79 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
81 tombstone_extra_time = timeval_add(&service->startup_time,
82 service->config.tombstone_extra_timeout,
83 0);
84 delete_tombstones = timeval_expired(&tombstone_extra_time);
86 for (i=0; i < res->count; i++) {
88 * we pass '0' as 'now' here,
89 * because we want to get the raw timestamps which are in the DB
91 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
92 NT_STATUS_NOT_OK_RETURN(status);
93 talloc_free(res->msgs[i]);
95 modify_flags = 0;
96 modify_record = false;
97 delete_record = false;
99 switch (rec->state) {
100 case WREPL_STATE_ACTIVE:
101 old_state = "active";
102 new_state = "active";
103 if (!rec->is_static) {
104 new_state = "released";
105 rec->state = WREPL_STATE_RELEASED;
106 rec->expire_time= service->config.tombstone_interval + now;
108 modify_flags = 0;
109 modify_record = true;
110 break;
112 case WREPL_STATE_RELEASED:
113 old_state = "released";
114 new_state = "tombstone";
115 rec->state = WREPL_STATE_TOMBSTONE;
116 rec->expire_time= service->config.tombstone_timeout + now;
117 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
118 modify_record = true;
119 break;
121 case WREPL_STATE_TOMBSTONE:
122 old_state = "tombstone";
123 new_state = "tombstone";
124 if (!delete_tombstones) break;
125 new_state = "deleted";
126 delete_record = true;
127 break;
129 case WREPL_STATE_RESERVED:
130 DEBUG(0,("%s: corrupted record: %s\n",
131 __location__, nbt_name_string(rec, rec->name)));
132 return NT_STATUS_INTERNAL_DB_CORRUPTION;
135 if (modify_record) {
136 action = "modify";
137 ret = winsdb_modify(service->wins_db, rec, modify_flags);
138 } else if (delete_record) {
139 action = "delete";
140 ret = winsdb_delete(service->wins_db, rec);
141 } else {
142 action = "skip";
143 ret = NBT_RCODE_OK;
146 if (ret != NBT_RCODE_OK) {
147 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
148 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
149 } else {
150 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
151 action, nbt_name_string(rec, rec->name), old_state, new_state));
154 talloc_free(rec);
157 return NT_STATUS_OK;
160 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
162 NTSTATUS status;
163 struct winsdb_record *rec = NULL;
164 struct ldb_result *res = NULL;
165 const char *owner_filter;
166 const char *filter;
167 uint32_t i;
168 int ret;
169 time_t now = time(NULL);
170 const char *now_timestr;
171 const char *action;
172 const char *old_state=NULL;
173 const char *new_state=NULL;
174 uint32_t modify_flags;
175 bool modify_record;
176 bool delete_record;
177 bool delete_tombstones;
178 struct timeval tombstone_extra_time;
180 now_timestr = ldb_timestring(tmp_mem, now);
181 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
182 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
183 service->wins_db->local_owner);
184 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
185 filter = talloc_asprintf(tmp_mem,
186 "(&(!%s)(objectClass=winsRecord)"
187 "(!(recordState=%u))(expireTime<=%s))",
188 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
189 NT_STATUS_HAVE_NO_MEMORY(filter);
190 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
191 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
192 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
194 tombstone_extra_time = timeval_add(&service->startup_time,
195 service->config.tombstone_extra_timeout,
197 delete_tombstones = timeval_expired(&tombstone_extra_time);
199 for (i=0; i < res->count; i++) {
201 * we pass '0' as 'now' here,
202 * because we want to get the raw timestamps which are in the DB
204 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
205 NT_STATUS_NOT_OK_RETURN(status);
206 talloc_free(res->msgs[i]);
208 modify_flags = 0;
209 modify_record = false;
210 delete_record = false;
212 switch (rec->state) {
213 case WREPL_STATE_ACTIVE:
214 DEBUG(0,("%s: corrupted record: %s\n",
215 __location__, nbt_name_string(rec, rec->name)));
216 return NT_STATUS_INTERNAL_DB_CORRUPTION;
218 case WREPL_STATE_RELEASED:
219 old_state = "released";
220 new_state = "tombstone";
221 rec->state = WREPL_STATE_TOMBSTONE;
222 rec->expire_time= service->config.tombstone_timeout + now;
223 modify_flags = 0;
224 modify_record = true;
225 break;
227 case WREPL_STATE_TOMBSTONE:
228 old_state = "tombstone";
229 new_state = "tombstone";
230 if (!delete_tombstones) break;
231 new_state = "deleted";
232 delete_record = true;
233 break;
235 case WREPL_STATE_RESERVED:
236 DEBUG(0,("%s: corrupted record: %s\n",
237 __location__, nbt_name_string(rec, rec->name)));
238 return NT_STATUS_INTERNAL_DB_CORRUPTION;
241 if (modify_record) {
242 action = "modify";
243 ret = winsdb_modify(service->wins_db, rec, modify_flags);
244 } else if (delete_record) {
245 action = "delete";
246 ret = winsdb_delete(service->wins_db, rec);
247 } else {
248 action = "skip";
249 ret = NBT_RCODE_OK;
252 if (ret != NBT_RCODE_OK) {
253 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
254 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
255 } else {
256 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
257 action, nbt_name_string(rec, rec->name), old_state, new_state));
260 talloc_free(rec);
263 return NT_STATUS_OK;
266 struct verify_state {
267 struct messaging_context *msg_ctx;
268 struct wreplsrv_service *service;
269 struct winsdb_record *rec;
270 struct nbtd_proxy_wins_challenge r;
273 static void verify_handler(struct irpc_request *ireq)
275 struct verify_state *s = talloc_get_type(ireq->async.private,
276 struct verify_state);
277 struct winsdb_record *rec = s->rec;
278 const char *action;
279 const char *old_state = "active";
280 const char *new_state = "active";
281 const char *new_owner = "replica";
282 uint32_t modify_flags = 0;
283 bool modify_record = false;
284 bool delete_record = false;
285 bool different = false;
286 int ret;
287 NTSTATUS status;
288 uint32_t i, j;
291 * - if the name isn't present anymore remove our record
292 * - if the name is found and not a normal group check if the addresses match,
293 * - if they don't match remove the record
294 * - if they match do nothing
295 * - if an error happens do nothing
297 status = irpc_call_recv(ireq);
298 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
299 delete_record = true;
300 new_state = "deleted";
301 } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
302 for (i=0; i < s->r.out.num_addrs; i++) {
303 bool found = false;
304 for (j=0; rec->addresses[j]; j++) {
305 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
306 found = true;
307 break;
310 if (!found) {
311 different = true;
312 break;
315 } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
316 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
317 different = true;
321 if (different) {
323 * if the reply from the owning wins server has different addresses
324 * then take the ownership of the record and make it a tombstone
325 * this will then hopefully replicated to the original owner of the record
326 * which will then propagate it's own record, so that the current record will
327 * be replicated to to us
329 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
330 nbt_name_string(rec, rec->name), rec->wins_owner));
332 rec->state = WREPL_STATE_TOMBSTONE;
333 rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
334 for (i=0; rec->addresses[i]; i++) {
335 rec->addresses[i]->expire_time = rec->expire_time;
337 modify_record = true;
338 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
339 new_state = "tombstone";
340 new_owner = "owned";
341 } else if (NT_STATUS_IS_OK(status)) {
342 /* if the addresses are the same, just update the timestamps */
343 rec->expire_time = time(NULL) + s->service->config.verify_interval;
344 for (i=0; rec->addresses[i]; i++) {
345 rec->addresses[i]->expire_time = rec->expire_time;
347 modify_record = true;
348 modify_flags = 0;
349 new_state = "active";
352 if (modify_record) {
353 action = "modify";
354 ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
355 } else if (delete_record) {
356 action = "delete";
357 ret = winsdb_delete(s->service->wins_db, rec);
358 } else {
359 action = "skip";
360 ret = NBT_RCODE_OK;
363 if (ret != NBT_RCODE_OK) {
364 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
365 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
366 } else {
367 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
368 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
369 rec->wins_owner, nt_errstr(status)));
372 talloc_free(s);
375 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
377 NTSTATUS status;
378 struct winsdb_record *rec = NULL;
379 struct ldb_result *res = NULL;
380 const char *owner_filter;
381 const char *filter;
382 uint32_t i;
383 int ret;
384 time_t now = time(NULL);
385 const char *now_timestr;
386 struct irpc_request *ireq;
387 struct verify_state *s;
388 struct server_id *nbt_servers;
390 nbt_servers = irpc_servers_byname(service->task->msg_ctx, tmp_mem, "nbt_server");
391 if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
392 return NT_STATUS_INTERNAL_ERROR;
395 now_timestr = ldb_timestring(tmp_mem, now);
396 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
397 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
398 service->wins_db->local_owner);
399 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
400 filter = talloc_asprintf(tmp_mem,
401 "(&(!%s)(objectClass=winsRecord)"
402 "(recordState=%u)(expireTime<=%s))",
403 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
404 NT_STATUS_HAVE_NO_MEMORY(filter);
405 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
406 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
407 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
409 for (i=0; i < res->count; i++) {
411 * we pass '0' as 'now' here,
412 * because we want to get the raw timestamps which are in the DB
414 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
415 NT_STATUS_NOT_OK_RETURN(status);
416 talloc_free(res->msgs[i]);
418 if (rec->state != WREPL_STATE_ACTIVE) {
419 DEBUG(0,("%s: corrupted record: %s\n",
420 __location__, nbt_name_string(rec, rec->name)));
421 return NT_STATUS_INTERNAL_DB_CORRUPTION;
425 * ask the owning wins server if the record still exists,
426 * if not delete the record
428 * TODO: NOTE: this is a simpliefied version, to verify that
429 * a record still exist, I assume that w2k3 uses
430 * DCERPC calls or some WINSREPL packets for this,
431 * but we use a wins name query
433 DEBUG(0,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
434 rec->wins_owner, nbt_name_string(rec, rec->name),
435 (unsigned long long)rec->version));
437 s = talloc_zero(tmp_mem, struct verify_state);
438 NT_STATUS_HAVE_NO_MEMORY(s);
439 s->msg_ctx = service->task->msg_ctx;
440 s->service = service;
441 s->rec = talloc_steal(s, rec);
443 s->r.in.name = *rec->name;
444 s->r.in.num_addrs = 1;
445 s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
446 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
447 /* TODO: fix pidl to handle inline ipv4address arrays */
448 s->r.in.addrs[0].addr = rec->wins_owner;
450 ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
451 irpc, NBTD_PROXY_WINS_CHALLENGE,
452 &s->r, s);
453 NT_STATUS_HAVE_NO_MEMORY(ireq);
455 ireq->async.fn = verify_handler;
456 ireq->async.private = s;
458 talloc_steal(service, s);
461 return NT_STATUS_OK;
464 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
466 NTSTATUS status;
467 TALLOC_CTX *tmp_mem;
468 bool skip_first_run = false;
470 if (!timeval_expired(&service->scavenging.next_run)) {
471 return NT_STATUS_OK;
474 if (timeval_is_zero(&service->scavenging.next_run)) {
475 skip_first_run = true;
478 service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
479 status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
480 NT_STATUS_NOT_OK_RETURN(status);
483 * if it's the first time this functions is called (startup)
484 * the next_run is zero, in this case we should not do scavenging
486 if (skip_first_run) {
487 return NT_STATUS_OK;
490 if (service->scavenging.processing) {
491 return NT_STATUS_OK;
494 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
496 tmp_mem = talloc_new(service);
497 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
498 service->scavenging.processing = true;
499 status = wreplsrv_scavenging_owned_records(service,tmp_mem);
500 service->scavenging.processing = false;
501 talloc_free(tmp_mem);
502 NT_STATUS_NOT_OK_RETURN(status);
504 tmp_mem = talloc_new(service);
505 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
506 service->scavenging.processing = true;
507 status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
508 service->scavenging.processing = false;
509 talloc_free(tmp_mem);
510 NT_STATUS_NOT_OK_RETURN(status);
512 tmp_mem = talloc_new(service);
513 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
514 service->scavenging.processing = true;
515 status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
516 service->scavenging.processing = false;
517 talloc_free(tmp_mem);
518 NT_STATUS_NOT_OK_RETURN(status);
520 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
522 return NT_STATUS_OK;