VERSION: Move on to beta6!
[Samba.git] / source4 / wrepl_server / wrepl_scavenging.c
blobd04064970c9f2f9a1e5f65ecba57e278f64aa4d0
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.h>
27 #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_c.h"
32 #include "librpc/gen_ndr/ndr_nbt.h"
33 #include "param/param.h"
35 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
36 TALLOC_CTX *mem_ctx,
37 const char *wins_owner)
39 if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
40 return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
41 wins_owner);
44 return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
45 wins_owner);
48 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
50 NTSTATUS status;
51 struct winsdb_record *rec = NULL;
52 struct ldb_result *res = NULL;
53 const char *owner_filter;
54 const char *filter;
55 unsigned int i;
56 int ret;
57 time_t now = time(NULL);
58 const char *now_timestr;
59 const char *action;
60 const char *old_state=NULL;
61 const char *new_state=NULL;
62 uint32_t modify_flags;
63 bool modify_record;
64 bool delete_record;
65 bool delete_tombstones;
66 struct timeval tombstone_extra_time;
67 const char *local_owner = service->wins_db->local_owner;
68 bool propagate = lpcfg_parm_bool(service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false);
70 now_timestr = ldb_timestring(tmp_mem, now);
71 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
72 owner_filter = wreplsrv_owner_filter(service, tmp_mem, local_owner);
73 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
74 filter = talloc_asprintf(tmp_mem,
75 "(&%s(objectClass=winsRecord)"
76 "(expireTime<=%s))",
77 owner_filter, now_timestr);
78 NT_STATUS_HAVE_NO_MEMORY(filter);
79 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
80 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
81 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
83 tombstone_extra_time = timeval_add(&service->startup_time,
84 service->config.tombstone_extra_timeout,
85 0);
86 delete_tombstones = timeval_expired(&tombstone_extra_time);
88 for (i=0; i < res->count; i++) {
89 bool has_replicas = false;
92 * we pass '0' as 'now' here,
93 * because we want to get the raw timestamps which are in the DB
95 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
96 NT_STATUS_NOT_OK_RETURN(status);
97 talloc_free(res->msgs[i]);
99 modify_flags = 0;
100 modify_record = false;
101 delete_record = false;
103 switch (rec->state) {
104 case WREPL_STATE_ACTIVE:
105 old_state = "active";
106 if (rec->is_static) {
108 *we store it again, so that it won't appear
109 * in the scavenging the next time
111 old_state = "active(static)";
112 new_state = "active(static)";
113 modify_flags = 0;
114 modify_record = true;
115 break;
117 if (rec->type != WREPL_TYPE_SGROUP || !propagate) {
118 new_state = "released";
119 rec->state = WREPL_STATE_RELEASED;
120 rec->expire_time= service->config.tombstone_interval + now;
121 modify_flags = 0;
122 modify_record = true;
123 break;
125 /* check if there's any replica address */
126 for (i=0;rec->addresses[i];i++) {
127 if (strcmp(rec->addresses[i]->wins_owner, local_owner) != 0) {
128 has_replicas = true;
129 rec->addresses[i]->expire_time= service->config.renew_interval + now;
132 if (has_replicas) {
133 /* if it has replica addresses propagate them */
134 new_state = "active(propagated)";
135 rec->state = WREPL_STATE_ACTIVE;
136 rec->expire_time= service->config.renew_interval + now;
137 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
138 modify_record = true;
139 break;
142 * if it doesn't have replica addresses, make it a tombstone,
143 * so that the released owned addresses are propagated
145 new_state = "tombstone";
146 rec->state = WREPL_STATE_TOMBSTONE;
147 rec->expire_time= time(NULL) +
148 service->config.tombstone_interval +
149 service->config.tombstone_timeout;
150 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
151 modify_record = true;
152 break;
154 case WREPL_STATE_RELEASED:
155 old_state = "released";
156 new_state = "tombstone";
157 rec->state = WREPL_STATE_TOMBSTONE;
158 rec->expire_time= service->config.tombstone_timeout + now;
159 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
160 modify_record = true;
161 break;
163 case WREPL_STATE_TOMBSTONE:
164 old_state = "tombstone";
165 new_state = "tombstone";
166 if (!delete_tombstones) break;
167 new_state = "deleted";
168 delete_record = true;
169 break;
171 case WREPL_STATE_RESERVED:
172 DEBUG(0,("%s: corrupted record: %s\n",
173 __location__, nbt_name_string(rec, rec->name)));
174 return NT_STATUS_INTERNAL_DB_CORRUPTION;
177 if (modify_record) {
178 action = "modify";
179 ret = winsdb_modify(service->wins_db, rec, modify_flags);
180 } else if (delete_record) {
181 action = "delete";
182 ret = winsdb_delete(service->wins_db, rec);
183 } else {
184 action = "skip";
185 ret = NBT_RCODE_OK;
188 if (ret != NBT_RCODE_OK) {
189 DEBUG(2,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
190 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
191 } else {
192 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
193 action, nbt_name_string(rec, rec->name), old_state, new_state));
196 talloc_free(rec);
199 return NT_STATUS_OK;
202 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
204 NTSTATUS status;
205 struct winsdb_record *rec = NULL;
206 struct ldb_result *res = NULL;
207 const char *owner_filter;
208 const char *filter;
209 unsigned int i;
210 int ret;
211 time_t now = time(NULL);
212 const char *now_timestr;
213 const char *action;
214 const char *old_state=NULL;
215 const char *new_state=NULL;
216 uint32_t modify_flags;
217 bool modify_record;
218 bool delete_record;
219 bool delete_tombstones;
220 struct timeval tombstone_extra_time;
222 now_timestr = ldb_timestring(tmp_mem, now);
223 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
224 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
225 service->wins_db->local_owner);
226 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
227 filter = talloc_asprintf(tmp_mem,
228 "(&(!%s)(objectClass=winsRecord)"
229 "(!(recordState=%u))(expireTime<=%s))",
230 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
231 NT_STATUS_HAVE_NO_MEMORY(filter);
232 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
233 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
234 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
236 tombstone_extra_time = timeval_add(&service->startup_time,
237 service->config.tombstone_extra_timeout,
239 delete_tombstones = timeval_expired(&tombstone_extra_time);
241 for (i=0; i < res->count; i++) {
243 * we pass '0' as 'now' here,
244 * because we want to get the raw timestamps which are in the DB
246 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
247 NT_STATUS_NOT_OK_RETURN(status);
248 talloc_free(res->msgs[i]);
250 modify_flags = 0;
251 modify_record = false;
252 delete_record = false;
254 switch (rec->state) {
255 case WREPL_STATE_ACTIVE:
256 DEBUG(0,("%s: corrupted record: %s\n",
257 __location__, nbt_name_string(rec, rec->name)));
258 return NT_STATUS_INTERNAL_DB_CORRUPTION;
260 case WREPL_STATE_RELEASED:
261 old_state = "released";
262 new_state = "tombstone";
263 rec->state = WREPL_STATE_TOMBSTONE;
264 rec->expire_time= service->config.tombstone_timeout + now;
265 modify_flags = 0;
266 modify_record = true;
267 break;
269 case WREPL_STATE_TOMBSTONE:
270 old_state = "tombstone";
271 new_state = "tombstone";
272 if (!delete_tombstones) break;
273 new_state = "deleted";
274 delete_record = true;
275 break;
277 case WREPL_STATE_RESERVED:
278 DEBUG(0,("%s: corrupted record: %s\n",
279 __location__, nbt_name_string(rec, rec->name)));
280 return NT_STATUS_INTERNAL_DB_CORRUPTION;
283 if (modify_record) {
284 action = "modify";
285 ret = winsdb_modify(service->wins_db, rec, modify_flags);
286 } else if (delete_record) {
287 action = "delete";
288 ret = winsdb_delete(service->wins_db, rec);
289 } else {
290 action = "skip";
291 ret = NBT_RCODE_OK;
294 if (ret != NBT_RCODE_OK) {
295 DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
296 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
297 } else {
298 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
299 action, nbt_name_string(rec, rec->name), old_state, new_state));
302 talloc_free(rec);
305 return NT_STATUS_OK;
308 struct verify_state {
309 struct imessaging_context *msg_ctx;
310 struct wreplsrv_service *service;
311 struct winsdb_record *rec;
312 struct nbtd_proxy_wins_challenge r;
315 static void verify_handler(struct tevent_req *subreq)
317 struct verify_state *s =
318 tevent_req_callback_data(subreq,
319 struct verify_state);
320 struct winsdb_record *rec = s->rec;
321 const char *action;
322 const char *old_state = "active";
323 const char *new_state = "active";
324 const char *new_owner = "replica";
325 uint32_t modify_flags = 0;
326 bool modify_record = false;
327 bool delete_record = false;
328 bool different = false;
329 int ret;
330 NTSTATUS status;
331 uint32_t i, j;
334 * - if the name isn't present anymore remove our record
335 * - if the name is found and not a normal group check if the addresses match,
336 * - if they don't match remove the record
337 * - if they match do nothing
338 * - if an error happens do nothing
340 status = dcerpc_nbtd_proxy_wins_challenge_r_recv(subreq, s);
341 TALLOC_FREE(subreq);
342 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
343 delete_record = true;
344 new_state = "deleted";
345 } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
346 for (i=0; i < s->r.out.num_addrs; i++) {
347 bool found = false;
348 for (j=0; rec->addresses[j]; j++) {
349 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
350 found = true;
351 break;
354 if (!found) {
355 different = true;
356 break;
359 } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
360 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
361 different = true;
365 if (different) {
367 * if the reply from the owning wins server has different addresses
368 * then take the ownership of the record and make it a tombstone
369 * this will then hopefully replicated to the original owner of the record
370 * which will then propagate it's own record, so that the current record will
371 * be replicated to to us
373 DEBUG(2,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
374 nbt_name_string(rec, rec->name), rec->wins_owner));
376 rec->state = WREPL_STATE_TOMBSTONE;
377 rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
378 for (i=0; rec->addresses[i]; i++) {
379 rec->addresses[i]->expire_time = rec->expire_time;
381 modify_record = true;
382 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
383 new_state = "tombstone";
384 new_owner = "owned";
385 } else if (NT_STATUS_IS_OK(status)) {
386 /* if the addresses are the same, just update the timestamps */
387 rec->expire_time = time(NULL) + s->service->config.verify_interval;
388 for (i=0; rec->addresses[i]; i++) {
389 rec->addresses[i]->expire_time = rec->expire_time;
391 modify_record = true;
392 modify_flags = 0;
393 new_state = "active";
396 if (modify_record) {
397 action = "modify";
398 ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
399 } else if (delete_record) {
400 action = "delete";
401 ret = winsdb_delete(s->service->wins_db, rec);
402 } else {
403 action = "skip";
404 ret = NBT_RCODE_OK;
407 if (ret != NBT_RCODE_OK) {
408 DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
409 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
410 } else {
411 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
412 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
413 rec->wins_owner, nt_errstr(status)));
416 talloc_free(s);
419 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
421 NTSTATUS status;
422 struct winsdb_record *rec = NULL;
423 struct ldb_result *res = NULL;
424 const char *owner_filter;
425 const char *filter;
426 unsigned int i;
427 int ret;
428 time_t now = time(NULL);
429 const char *now_timestr;
430 struct tevent_req *subreq;
431 struct verify_state *s;
432 struct dcerpc_binding_handle *irpc_handle;
434 now_timestr = ldb_timestring(tmp_mem, now);
435 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
436 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
437 service->wins_db->local_owner);
438 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
439 filter = talloc_asprintf(tmp_mem,
440 "(&(!%s)(objectClass=winsRecord)"
441 "(recordState=%u)(expireTime<=%s))",
442 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
443 NT_STATUS_HAVE_NO_MEMORY(filter);
444 ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
445 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
446 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
448 for (i=0; i < res->count; i++) {
450 * we pass '0' as 'now' here,
451 * because we want to get the raw timestamps which are in the DB
453 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
454 NT_STATUS_NOT_OK_RETURN(status);
455 talloc_free(res->msgs[i]);
457 if (rec->state != WREPL_STATE_ACTIVE) {
458 DEBUG(0,("%s: corrupted record: %s\n",
459 __location__, nbt_name_string(rec, rec->name)));
460 return NT_STATUS_INTERNAL_DB_CORRUPTION;
464 * ask the owning wins server if the record still exists,
465 * if not delete the record
467 * TODO: NOTE: this is a simpliefied version, to verify that
468 * a record still exist, I assume that w2k3 uses
469 * DCERPC calls or some WINSREPL packets for this,
470 * but we use a wins name query
472 DEBUG(2,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
473 rec->wins_owner, nbt_name_string(rec, rec->name),
474 (unsigned long long)rec->version));
476 s = talloc_zero(tmp_mem, struct verify_state);
477 NT_STATUS_HAVE_NO_MEMORY(s);
478 s->msg_ctx = service->task->msg_ctx;
479 s->service = service;
480 s->rec = talloc_steal(s, rec);
482 s->r.in.name = *rec->name;
483 s->r.in.num_addrs = 1;
484 s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
485 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
486 /* TODO: fix pidl to handle inline ipv4address arrays */
487 s->r.in.addrs[0].addr = rec->wins_owner;
489 irpc_handle = irpc_binding_handle_by_name(s,
490 service->task->msg_ctx,
491 "nbt_server",
492 &ndr_table_irpc);
493 if (irpc_handle == NULL) {
494 return NT_STATUS_INTERNAL_ERROR;
497 subreq = dcerpc_nbtd_proxy_wins_challenge_r_send(s,
498 service->task->event_ctx,
499 irpc_handle,
500 &s->r);
501 NT_STATUS_HAVE_NO_MEMORY(subreq);
503 tevent_req_set_callback(subreq, verify_handler, s);
505 talloc_steal(service, s);
508 return NT_STATUS_OK;
511 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
513 NTSTATUS status;
514 TALLOC_CTX *tmp_mem;
515 bool skip_first_run = false;
517 if (!timeval_expired(&service->scavenging.next_run)) {
518 return NT_STATUS_OK;
521 if (timeval_is_zero(&service->scavenging.next_run)) {
522 skip_first_run = true;
525 service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
526 status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
527 NT_STATUS_NOT_OK_RETURN(status);
530 * if it's the first time this functions is called (startup)
531 * the next_run is zero, in this case we should not do scavenging
533 if (skip_first_run) {
534 return NT_STATUS_OK;
537 if (service->scavenging.processing) {
538 return NT_STATUS_OK;
541 DEBUG(2,("wreplsrv_scavenging_run(): start\n"));
543 tmp_mem = talloc_new(service);
544 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
545 service->scavenging.processing = true;
546 status = wreplsrv_scavenging_owned_records(service,tmp_mem);
547 service->scavenging.processing = false;
548 talloc_free(tmp_mem);
549 NT_STATUS_NOT_OK_RETURN(status);
551 tmp_mem = talloc_new(service);
552 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
553 service->scavenging.processing = true;
554 status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
555 service->scavenging.processing = false;
556 talloc_free(tmp_mem);
557 NT_STATUS_NOT_OK_RETURN(status);
559 tmp_mem = talloc_new(service);
560 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
561 service->scavenging.processing = true;
562 status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
563 service->scavenging.processing = false;
564 talloc_free(tmp_mem);
565 NT_STATUS_NOT_OK_RETURN(status);
567 DEBUG(2,("wreplsrv_scavenging_run(): end\n"));
569 return NT_STATUS_OK;