r25190: Update talloc version to 1.1.0 after API-incompatible changes.
[Samba.git] / source / wrepl_server / wrepl_scavenging.c
blob9b4ae18fe79a9f5e01a9417907602e5f17fc794b
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, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
78 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
79 talloc_steal(tmp_mem, res);
80 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
82 tombstone_extra_time = timeval_add(&service->startup_time,
83 service->config.tombstone_extra_timeout,
84 0);
85 delete_tombstones = timeval_expired(&tombstone_extra_time);
87 for (i=0; i < res->count; i++) {
89 * we pass '0' as 'now' here,
90 * because we want to get the raw timestamps which are in the DB
92 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
93 NT_STATUS_NOT_OK_RETURN(status);
94 talloc_free(res->msgs[i]);
96 modify_flags = 0;
97 modify_record = False;
98 delete_record = False;
100 switch (rec->state) {
101 case WREPL_STATE_ACTIVE:
102 old_state = "active";
103 new_state = "active";
104 if (!rec->is_static) {
105 new_state = "released";
106 rec->state = WREPL_STATE_RELEASED;
107 rec->expire_time= service->config.tombstone_interval + now;
109 modify_flags = 0;
110 modify_record = True;
111 break;
113 case WREPL_STATE_RELEASED:
114 old_state = "released";
115 new_state = "tombstone";
116 rec->state = WREPL_STATE_TOMBSTONE;
117 rec->expire_time= service->config.tombstone_timeout + now;
118 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
119 modify_record = True;
120 break;
122 case WREPL_STATE_TOMBSTONE:
123 old_state = "tombstone";
124 new_state = "tombstone";
125 if (!delete_tombstones) break;
126 new_state = "deleted";
127 delete_record = True;
128 break;
130 case WREPL_STATE_RESERVED:
131 DEBUG(0,("%s: corrupted record: %s\n",
132 __location__, nbt_name_string(rec, rec->name)));
133 return NT_STATUS_INTERNAL_DB_CORRUPTION;
136 if (modify_record) {
137 action = "modify";
138 ret = winsdb_modify(service->wins_db, rec, modify_flags);
139 } else if (delete_record) {
140 action = "delete";
141 ret = winsdb_delete(service->wins_db, rec);
142 } else {
143 action = "skip";
144 ret = NBT_RCODE_OK;
147 if (ret != NBT_RCODE_OK) {
148 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
149 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
150 } else {
151 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
152 action, nbt_name_string(rec, rec->name), old_state, new_state));
155 talloc_free(rec);
158 return NT_STATUS_OK;
161 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
163 NTSTATUS status;
164 struct winsdb_record *rec = NULL;
165 struct ldb_result *res = NULL;
166 const char *owner_filter;
167 const char *filter;
168 uint32_t i;
169 int ret;
170 time_t now = time(NULL);
171 const char *now_timestr;
172 const char *action;
173 const char *old_state=NULL;
174 const char *new_state=NULL;
175 uint32_t modify_flags;
176 BOOL modify_record;
177 BOOL delete_record;
178 BOOL delete_tombstones;
179 struct timeval tombstone_extra_time;
181 now_timestr = ldb_timestring(tmp_mem, now);
182 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
183 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
184 service->wins_db->local_owner);
185 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
186 filter = talloc_asprintf(tmp_mem,
187 "(&(!%s)(objectClass=winsRecord)"
188 "(!(recordState=%u))(expireTime<=%s))",
189 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
190 NT_STATUS_HAVE_NO_MEMORY(filter);
191 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
192 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
193 talloc_steal(tmp_mem, res);
194 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
196 tombstone_extra_time = timeval_add(&service->startup_time,
197 service->config.tombstone_extra_timeout,
199 delete_tombstones = timeval_expired(&tombstone_extra_time);
201 for (i=0; i < res->count; i++) {
203 * we pass '0' as 'now' here,
204 * because we want to get the raw timestamps which are in the DB
206 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
207 NT_STATUS_NOT_OK_RETURN(status);
208 talloc_free(res->msgs[i]);
210 modify_flags = 0;
211 modify_record = False;
212 delete_record = False;
214 switch (rec->state) {
215 case WREPL_STATE_ACTIVE:
216 DEBUG(0,("%s: corrupted record: %s\n",
217 __location__, nbt_name_string(rec, rec->name)));
218 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220 case WREPL_STATE_RELEASED:
221 old_state = "released";
222 new_state = "tombstone";
223 rec->state = WREPL_STATE_TOMBSTONE;
224 rec->expire_time= service->config.tombstone_timeout + now;
225 modify_flags = 0;
226 modify_record = True;
227 break;
229 case WREPL_STATE_TOMBSTONE:
230 old_state = "tombstone";
231 new_state = "tombstone";
232 if (!delete_tombstones) break;
233 new_state = "deleted";
234 delete_record = True;
235 break;
237 case WREPL_STATE_RESERVED:
238 DEBUG(0,("%s: corrupted record: %s\n",
239 __location__, nbt_name_string(rec, rec->name)));
240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
243 if (modify_record) {
244 action = "modify";
245 ret = winsdb_modify(service->wins_db, rec, modify_flags);
246 } else if (delete_record) {
247 action = "delete";
248 ret = winsdb_delete(service->wins_db, rec);
249 } else {
250 action = "skip";
251 ret = NBT_RCODE_OK;
254 if (ret != NBT_RCODE_OK) {
255 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
256 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
257 } else {
258 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
259 action, nbt_name_string(rec, rec->name), old_state, new_state));
262 talloc_free(rec);
265 return NT_STATUS_OK;
268 struct verify_state {
269 struct messaging_context *msg_ctx;
270 struct wreplsrv_service *service;
271 struct winsdb_record *rec;
272 struct nbtd_proxy_wins_challenge r;
275 static void verify_handler(struct irpc_request *ireq)
277 struct verify_state *s = talloc_get_type(ireq->async.private,
278 struct verify_state);
279 struct winsdb_record *rec = s->rec;
280 const char *action;
281 const char *old_state = "active";
282 const char *new_state = "active";
283 const char *new_owner = "replica";
284 uint32_t modify_flags = 0;
285 BOOL modify_record = False;
286 BOOL delete_record = False;
287 BOOL different = False;
288 int ret;
289 NTSTATUS status;
290 uint32_t i, j;
293 * - if the name isn't present anymore remove our record
294 * - if the name is found and not a normal group check if the addresses match,
295 * - if they don't match remove the record
296 * - if they match do nothing
297 * - if an error happens do nothing
299 status = irpc_call_recv(ireq);
300 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
301 delete_record = True;
302 new_state = "deleted";
303 } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
304 for (i=0; i < s->r.out.num_addrs; i++) {
305 BOOL found = False;
306 for (j=0; rec->addresses[j]; j++) {
307 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
308 found = True;
309 break;
312 if (!found) {
313 different = True;
314 break;
317 } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
318 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
319 different = True;
323 if (different) {
325 * if the reply from the owning wins server has different addresses
326 * then take the ownership of the record and make it a tombstone
327 * this will then hopefully replicated to the original owner of the record
328 * which will then propagate it's own record, so that the current record will
329 * be replicated to to us
331 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
332 nbt_name_string(rec, rec->name), rec->wins_owner));
334 rec->state = WREPL_STATE_TOMBSTONE;
335 rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
336 for (i=0; rec->addresses[i]; i++) {
337 rec->addresses[i]->expire_time = rec->expire_time;
339 modify_record = True;
340 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
341 new_state = "tombstone";
342 new_owner = "owned";
343 } else if (NT_STATUS_IS_OK(status)) {
344 /* if the addresses are the same, just update the timestamps */
345 rec->expire_time = time(NULL) + s->service->config.verify_interval;
346 for (i=0; rec->addresses[i]; i++) {
347 rec->addresses[i]->expire_time = rec->expire_time;
349 modify_record = True;
350 modify_flags = 0;
351 new_state = "active";
354 if (modify_record) {
355 action = "modify";
356 ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
357 } else if (delete_record) {
358 action = "delete";
359 ret = winsdb_delete(s->service->wins_db, rec);
360 } else {
361 action = "skip";
362 ret = NBT_RCODE_OK;
365 if (ret != NBT_RCODE_OK) {
366 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
367 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
368 } else {
369 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
370 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
371 rec->wins_owner, nt_errstr(status)));
374 talloc_free(s);
377 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
379 NTSTATUS status;
380 struct winsdb_record *rec = NULL;
381 struct ldb_result *res = NULL;
382 const char *owner_filter;
383 const char *filter;
384 uint32_t i;
385 int ret;
386 time_t now = time(NULL);
387 const char *now_timestr;
388 struct irpc_request *ireq;
389 struct verify_state *s;
390 struct server_id *nbt_servers;
392 nbt_servers = irpc_servers_byname(service->task->msg_ctx, tmp_mem, "nbt_server");
393 if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
394 return NT_STATUS_INTERNAL_ERROR;
397 now_timestr = ldb_timestring(tmp_mem, now);
398 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
399 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
400 service->wins_db->local_owner);
401 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
402 filter = talloc_asprintf(tmp_mem,
403 "(&(!%s)(objectClass=winsRecord)"
404 "(recordState=%u)(expireTime<=%s))",
405 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
406 NT_STATUS_HAVE_NO_MEMORY(filter);
407 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
408 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
409 talloc_steal(tmp_mem, res);
410 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
412 for (i=0; i < res->count; i++) {
414 * we pass '0' as 'now' here,
415 * because we want to get the raw timestamps which are in the DB
417 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
418 NT_STATUS_NOT_OK_RETURN(status);
419 talloc_free(res->msgs[i]);
421 if (rec->state != WREPL_STATE_ACTIVE) {
422 DEBUG(0,("%s: corrupted record: %s\n",
423 __location__, nbt_name_string(rec, rec->name)));
424 return NT_STATUS_INTERNAL_DB_CORRUPTION;
428 * ask the owning wins server if the record still exists,
429 * if not delete the record
431 * TODO: NOTE: this is a simpliefied version, to verify that
432 * a record still exist, I assume that w2k3 uses
433 * DCERPC calls or some WINSREPL packets for this,
434 * but we use a wins name query
436 DEBUG(0,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
437 rec->wins_owner, nbt_name_string(rec, rec->name),
438 (unsigned long long)rec->version));
440 s = talloc_zero(tmp_mem, struct verify_state);
441 NT_STATUS_HAVE_NO_MEMORY(s);
442 s->msg_ctx = service->task->msg_ctx;
443 s->service = service;
444 s->rec = talloc_steal(s, rec);
446 s->r.in.name = *rec->name;
447 s->r.in.num_addrs = 1;
448 s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
449 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
450 /* TODO: fix pidl to handle inline ipv4address arrays */
451 s->r.in.addrs[0].addr = rec->wins_owner;
453 ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
454 irpc, NBTD_PROXY_WINS_CHALLENGE,
455 &s->r, s);
456 NT_STATUS_HAVE_NO_MEMORY(ireq);
458 ireq->async.fn = verify_handler;
459 ireq->async.private = s;
461 talloc_steal(service, s);
464 return NT_STATUS_OK;
467 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
469 NTSTATUS status;
470 TALLOC_CTX *tmp_mem;
471 BOOL skip_first_run = False;
473 if (!timeval_expired(&service->scavenging.next_run)) {
474 return NT_STATUS_OK;
477 if (timeval_is_zero(&service->scavenging.next_run)) {
478 skip_first_run = True;
481 service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
482 status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
483 NT_STATUS_NOT_OK_RETURN(status);
486 * if it's the first time this functions is called (startup)
487 * the next_run is zero, in this case we should not do scavenging
489 if (skip_first_run) {
490 return NT_STATUS_OK;
493 if (service->scavenging.processing) {
494 return NT_STATUS_OK;
497 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
499 tmp_mem = talloc_new(service);
500 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
501 service->scavenging.processing = True;
502 status = wreplsrv_scavenging_owned_records(service,tmp_mem);
503 service->scavenging.processing = False;
504 talloc_free(tmp_mem);
505 NT_STATUS_NOT_OK_RETURN(status);
507 tmp_mem = talloc_new(service);
508 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
509 service->scavenging.processing = True;
510 status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
511 service->scavenging.processing = False;
512 talloc_free(tmp_mem);
513 NT_STATUS_NOT_OK_RETURN(status);
515 tmp_mem = talloc_new(service);
516 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
517 service->scavenging.processing = True;
518 status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
519 service->scavenging.processing = False;
520 talloc_free(tmp_mem);
521 NT_STATUS_NOT_OK_RETURN(status);
523 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
525 return NT_STATUS_OK;