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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "librpc/gen_ndr/ndr_winsrepl.h"
25 #include "wrepl_server/wrepl_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "ldb/include/ldb.h"
28 #include "ldb/include/ldb_errors.h"
29 #include "system/time.h"
30 #include "smbd/service_task.h"
31 #include "lib/messaging/irpc.h"
32 #include "librpc/gen_ndr/ndr_irpc.h"
33 #include "librpc/gen_ndr/ndr_nbt.h"
35 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
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))",
44 return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
48 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
51 struct winsdb_record *rec = NULL;
52 struct ldb_result *res = NULL;
53 const char *owner_filter;
57 time_t now = time(NULL);
58 const char *now_timestr;
60 const char *old_state=NULL;
61 const char *new_state=NULL;
62 uint32_t modify_flags;
65 BOOL delete_tombstones;
66 struct timeval tombstone_extra_time;
68 now_timestr = ldb_timestring(tmp_mem, now);
69 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
70 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
71 service->wins_db->local_owner);
72 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
73 filter = talloc_asprintf(tmp_mem,
74 "(&%s(objectClass=winsRecord)"
76 owner_filter, now_timestr);
77 NT_STATUS_HAVE_NO_MEMORY(filter);
78 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
79 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
80 talloc_steal(tmp_mem, res);
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,
86 delete_tombstones = timeval_expired(&tombstone_extra_time);
88 for (i=0; i < res->count; i++) {
90 * we pass '0' as 'now' here,
91 * because we want to get the raw timestamps which are in the DB
93 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
94 NT_STATUS_NOT_OK_RETURN(status);
95 talloc_free(res->msgs[i]);
98 modify_record = False;
99 delete_record = False;
101 switch (rec->state) {
102 case WREPL_STATE_ACTIVE:
103 old_state = "active";
104 new_state = "active";
105 if (!rec->is_static) {
106 new_state = "released";
107 rec->state = WREPL_STATE_RELEASED;
108 rec->expire_time= service->config.tombstone_interval + now;
111 modify_record = True;
114 case WREPL_STATE_RELEASED:
115 old_state = "released";
116 new_state = "tombstone";
117 rec->state = WREPL_STATE_TOMBSTONE;
118 rec->expire_time= service->config.tombstone_timeout + now;
119 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
120 modify_record = True;
123 case WREPL_STATE_TOMBSTONE:
124 old_state = "tombstone";
125 new_state = "tombstone";
126 if (!delete_tombstones) break;
127 new_state = "deleted";
128 delete_record = True;
131 case WREPL_STATE_RESERVED:
132 DEBUG(0,("%s: corrupted record: %s\n",
133 __location__, nbt_name_string(rec, rec->name)));
134 return NT_STATUS_INTERNAL_DB_CORRUPTION;
139 ret = winsdb_modify(service->wins_db, rec, modify_flags);
140 } else if (delete_record) {
142 ret = winsdb_delete(service->wins_db, rec);
148 if (ret != NBT_RCODE_OK) {
149 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
150 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
152 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
153 action, nbt_name_string(rec, rec->name), old_state, new_state));
162 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
165 struct winsdb_record *rec = NULL;
166 struct ldb_result *res = NULL;
167 const char *owner_filter;
171 time_t now = time(NULL);
172 const char *now_timestr;
174 const char *old_state=NULL;
175 const char *new_state=NULL;
176 uint32_t modify_flags;
179 BOOL delete_tombstones;
180 struct timeval tombstone_extra_time;
182 now_timestr = ldb_timestring(tmp_mem, now);
183 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
184 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
185 service->wins_db->local_owner);
186 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
187 filter = talloc_asprintf(tmp_mem,
188 "(&(!%s)(objectClass=winsRecord)"
189 "(!(recordState=%u))(expireTime<=%s))",
190 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
191 NT_STATUS_HAVE_NO_MEMORY(filter);
192 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
193 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
194 talloc_steal(tmp_mem, res);
195 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
197 tombstone_extra_time = timeval_add(&service->startup_time,
198 service->config.tombstone_extra_timeout,
200 delete_tombstones = timeval_expired(&tombstone_extra_time);
202 for (i=0; i < res->count; i++) {
204 * we pass '0' as 'now' here,
205 * because we want to get the raw timestamps which are in the DB
207 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
208 NT_STATUS_NOT_OK_RETURN(status);
209 talloc_free(res->msgs[i]);
212 modify_record = False;
213 delete_record = False;
215 switch (rec->state) {
216 case WREPL_STATE_ACTIVE:
217 DEBUG(0,("%s: corrupted record: %s\n",
218 __location__, nbt_name_string(rec, rec->name)));
219 return NT_STATUS_INTERNAL_DB_CORRUPTION;
221 case WREPL_STATE_RELEASED:
222 old_state = "released";
223 new_state = "tombstone";
224 rec->state = WREPL_STATE_TOMBSTONE;
225 rec->expire_time= service->config.tombstone_timeout + now;
227 modify_record = True;
230 case WREPL_STATE_TOMBSTONE:
231 old_state = "tombstone";
232 new_state = "tombstone";
233 if (!delete_tombstones) break;
234 new_state = "deleted";
235 delete_record = True;
238 case WREPL_STATE_RESERVED:
239 DEBUG(0,("%s: corrupted record: %s\n",
240 __location__, nbt_name_string(rec, rec->name)));
241 return NT_STATUS_INTERNAL_DB_CORRUPTION;
246 ret = winsdb_modify(service->wins_db, rec, modify_flags);
247 } else if (delete_record) {
249 ret = winsdb_delete(service->wins_db, rec);
255 if (ret != NBT_RCODE_OK) {
256 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
257 action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
259 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
260 action, nbt_name_string(rec, rec->name), old_state, new_state));
269 struct verify_state {
270 struct messaging_context *msg_ctx;
271 struct wreplsrv_service *service;
272 struct winsdb_record *rec;
273 struct nbtd_proxy_wins_challenge r;
276 static void verify_handler(struct irpc_request *ireq)
278 struct verify_state *s = talloc_get_type(ireq->async.private,
279 struct verify_state);
280 struct winsdb_record *rec = s->rec;
282 const char *old_state = "active";
283 const char *new_state = "active";
284 const char *new_owner = "replica";
285 uint32_t modify_flags = 0;
286 BOOL modify_record = False;
287 BOOL delete_record = False;
288 BOOL different = False;
294 * - if the name isn't present anymore remove our record
295 * - if the name is found and not a normal group check if the addresses match,
296 * - if they don't match remove the record
297 * - if they match do nothing
298 * - if an error happens do nothing
300 status = irpc_call_recv(ireq);
301 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
302 delete_record = True;
303 new_state = "deleted";
304 } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
305 for (i=0; i < s->r.out.num_addrs; i++) {
307 for (j=0; rec->addresses[j]; j++) {
308 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
318 } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
319 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
326 * if the reply from the owning wins server has different addresses
327 * then take the ownership of the record and make it a tombstone
328 * this will then hopefully replicated to the original owner of the record
329 * which will then propagate it's own record, so that the current record will
330 * be replicated to to us
332 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
333 nbt_name_string(rec, rec->name), rec->wins_owner));
335 rec->state = WREPL_STATE_TOMBSTONE;
336 rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
337 for (i=0; rec->addresses[i]; i++) {
338 rec->addresses[i]->expire_time = rec->expire_time;
340 modify_record = True;
341 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
342 new_state = "tombstone";
344 } else if (NT_STATUS_IS_OK(status)) {
345 /* if the addresses are the same, just update the timestamps */
346 rec->expire_time = time(NULL) + s->service->config.verify_interval;
347 for (i=0; rec->addresses[i]; i++) {
348 rec->addresses[i]->expire_time = rec->expire_time;
350 modify_record = True;
352 new_state = "active";
357 ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
358 } else if (delete_record) {
360 ret = winsdb_delete(s->service->wins_db, rec);
366 if (ret != NBT_RCODE_OK) {
367 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
368 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
370 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
371 action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
372 rec->wins_owner, nt_errstr(status)));
378 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
381 struct winsdb_record *rec = NULL;
382 struct ldb_result *res = NULL;
383 const char *owner_filter;
387 time_t now = time(NULL);
388 const char *now_timestr;
389 struct irpc_request *ireq;
390 struct verify_state *s;
391 struct server_id *nbt_servers;
393 nbt_servers = irpc_servers_byname(service->task->msg_ctx, tmp_mem, "nbt_server");
394 if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
395 return NT_STATUS_INTERNAL_ERROR;
398 now_timestr = ldb_timestring(tmp_mem, now);
399 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
400 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
401 service->wins_db->local_owner);
402 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
403 filter = talloc_asprintf(tmp_mem,
404 "(&(!%s)(objectClass=winsRecord)"
405 "(recordState=%u)(expireTime<=%s))",
406 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
407 NT_STATUS_HAVE_NO_MEMORY(filter);
408 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
409 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
410 talloc_steal(tmp_mem, res);
411 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
413 for (i=0; i < res->count; i++) {
415 * we pass '0' as 'now' here,
416 * because we want to get the raw timestamps which are in the DB
418 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
419 NT_STATUS_NOT_OK_RETURN(status);
420 talloc_free(res->msgs[i]);
422 if (rec->state != WREPL_STATE_ACTIVE) {
423 DEBUG(0,("%s: corrupted record: %s\n",
424 __location__, nbt_name_string(rec, rec->name)));
425 return NT_STATUS_INTERNAL_DB_CORRUPTION;
429 * ask the owning wins server if the record still exists,
430 * if not delete the record
432 * TODO: NOTE: this is a simpliefied version, to verify that
433 * a record still exist, I assume that w2k3 uses
434 * DCERPC calls or some WINSREPL packets for this,
435 * but we use a wins name query
437 DEBUG(0,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
438 rec->wins_owner, nbt_name_string(rec, rec->name),
439 (unsigned long long)rec->version));
441 s = talloc_zero(tmp_mem, struct verify_state);
442 NT_STATUS_HAVE_NO_MEMORY(s);
443 s->msg_ctx = service->task->msg_ctx;
444 s->service = service;
445 s->rec = talloc_steal(s, rec);
447 s->r.in.name = *rec->name;
448 s->r.in.num_addrs = 1;
449 s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
450 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
451 /* TODO: fix pidl to handle inline ipv4address arrays */
452 s->r.in.addrs[0].addr = rec->wins_owner;
454 ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
455 irpc, NBTD_PROXY_WINS_CHALLENGE,
457 NT_STATUS_HAVE_NO_MEMORY(ireq);
459 ireq->async.fn = verify_handler;
460 ireq->async.private = s;
462 talloc_steal(service, s);
468 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
472 BOOL skip_first_run = False;
474 if (!timeval_expired(&service->scavenging.next_run)) {
478 if (timeval_is_zero(&service->scavenging.next_run)) {
479 skip_first_run = True;
482 service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
483 status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
484 NT_STATUS_NOT_OK_RETURN(status);
487 * if it's the first time this functions is called (startup)
488 * the next_run is zero, in this case we should not do scavenging
490 if (skip_first_run) {
494 if (service->scavenging.processing) {
498 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
500 tmp_mem = talloc_new(service);
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 service->scavenging.processing = True;
509 status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
510 service->scavenging.processing = False;
511 talloc_free(tmp_mem);
512 NT_STATUS_NOT_OK_RETURN(status);
514 tmp_mem = talloc_new(service);
515 service->scavenging.processing = True;
516 status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
517 service->scavenging.processing = False;
518 talloc_free(tmp_mem);
519 NT_STATUS_NOT_OK_RETURN(status);
521 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));