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"
34 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
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))",
43 return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
47 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
50 struct winsdb_record *rec = NULL;
51 struct ldb_result *res = NULL;
52 const char *owner_filter;
56 time_t now = time(NULL);
57 const char *now_timestr;
59 const char *old_state;
60 uint32_t modify_flags;
63 BOOL delete_tombstones;
64 struct timeval tombstone_extra_time;
66 now_timestr = ldb_timestring(tmp_mem, now);
67 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
68 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
69 service->wins_db->local_owner);
70 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
71 filter = talloc_asprintf(tmp_mem,
72 "(&%s(objectClass=winsRecord)"
73 "(expireTime<=%s)(!(isStatic=1)))",
74 owner_filter, now_timestr);
75 NT_STATUS_HAVE_NO_MEMORY(filter);
76 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
77 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
78 talloc_steal(tmp_mem, res);
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,
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]);
96 DEBUG(0,("%s: corrupted record: %s\n",
97 __location__, nbt_name_string(rec, rec->name)));
98 return NT_STATUS_INTERNAL_DB_CORRUPTION;
101 if (rec->expire_time > now) {
102 DEBUG(0,("%s: corrupted record: %s\n",
103 __location__, nbt_name_string(rec, rec->name)));
104 return NT_STATUS_INTERNAL_DB_CORRUPTION;
108 modify_record = False;
109 delete_record = False;
111 switch (rec->state) {
112 case WREPL_STATE_ACTIVE:
113 old_state = "active";
114 rec->state = WREPL_STATE_RELEASED;
115 rec->expire_time= service->config.tombstone_interval + now;
117 modify_record = True;
120 case WREPL_STATE_RELEASED:
121 old_state = "released";
122 rec->state = WREPL_STATE_TOMBSTONE;
123 rec->expire_time= service->config.tombstone_timeout + now;
124 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
125 modify_record = True;
128 case WREPL_STATE_TOMBSTONE:
129 old_state = "tombstone";
130 if (!delete_tombstones) break;
131 delete_record = True;
134 case WREPL_STATE_RESERVED:
135 DEBUG(0,("%s: corrupted record: %s\n",
136 __location__, nbt_name_string(rec, rec->name)));
137 return NT_STATUS_INTERNAL_DB_CORRUPTION;
142 ret = winsdb_modify(service->wins_db, rec, modify_flags);
143 } else if (delete_record) {
145 ret = winsdb_delete(service->wins_db, rec);
151 if (ret != NBT_RCODE_OK) {
152 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s): error:%u\n",
153 action, nbt_name_string(rec, rec->name), old_state, ret));
155 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s)\n",
156 action, nbt_name_string(rec, rec->name), old_state));
165 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
168 struct winsdb_record *rec = NULL;
169 struct ldb_result *res = NULL;
170 const char *owner_filter;
174 time_t now = time(NULL);
175 const char *now_timestr;
177 const char *old_state;
178 uint32_t modify_flags;
181 BOOL delete_tombstones;
182 struct timeval tombstone_extra_time;
184 now_timestr = ldb_timestring(tmp_mem, now);
185 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
186 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
187 service->wins_db->local_owner);
188 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
189 filter = talloc_asprintf(tmp_mem,
190 "(&(!%s)(objectClass=winsRecord)"
191 "(!(recordState=%u))(expireTime<=%s)(!(isStatic=1)))",
192 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
193 NT_STATUS_HAVE_NO_MEMORY(filter);
194 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
195 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
196 talloc_steal(tmp_mem, res);
197 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
199 tombstone_extra_time = timeval_add(&service->startup_time,
200 service->config.tombstone_extra_timeout,
202 delete_tombstones = timeval_expired(&tombstone_extra_time);
204 for (i=0; i < res->count; i++) {
206 * we pass '0' as 'now' here,
207 * because we want to get the raw timestamps which are in the DB
209 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
210 NT_STATUS_NOT_OK_RETURN(status);
211 talloc_free(res->msgs[i]);
213 if (rec->is_static) {
214 DEBUG(0,("%s: corrupted record: %s\n",
215 __location__, nbt_name_string(rec, rec->name)));
216 return NT_STATUS_INTERNAL_DB_CORRUPTION;
219 if (rec->expire_time > now) {
220 DEBUG(0,("%s: corrupted record: %s\n",
221 __location__, nbt_name_string(rec, rec->name)));
222 return NT_STATUS_INTERNAL_DB_CORRUPTION;
226 modify_record = False;
227 delete_record = False;
229 switch (rec->state) {
230 case WREPL_STATE_ACTIVE:
231 DEBUG(0,("%s: corrupted record: %s\n",
232 __location__, nbt_name_string(rec, rec->name)));
233 return NT_STATUS_INTERNAL_DB_CORRUPTION;
235 case WREPL_STATE_RELEASED:
236 old_state = "released";
237 rec->state = WREPL_STATE_TOMBSTONE;
238 rec->expire_time= service->config.tombstone_timeout + now;
240 modify_record = True;
243 case WREPL_STATE_TOMBSTONE:
244 old_state = "tombstone";
245 if (!delete_tombstones) break;
246 delete_record = True;
249 case WREPL_STATE_RESERVED:
250 DEBUG(0,("%s: corrupted record: %s\n",
251 __location__, nbt_name_string(rec, rec->name)));
252 return NT_STATUS_INTERNAL_DB_CORRUPTION;
257 ret = winsdb_modify(service->wins_db, rec, modify_flags);
258 } else if (delete_record) {
260 ret = winsdb_delete(service->wins_db, rec);
266 if (ret != NBT_RCODE_OK) {
267 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
268 action, nbt_name_string(rec, rec->name), old_state, ret));
270 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
271 action, nbt_name_string(rec, rec->name), old_state));
280 struct verify_state {
281 struct messaging_context *msg_ctx;
282 struct wreplsrv_service *service;
283 struct winsdb_record *rec;
284 struct nbtd_proxy_wins_challenge r;
287 static void verify_handler(struct irpc_request *ireq)
289 struct verify_state *s = talloc_get_type(ireq->async.private,
290 struct verify_state);
291 struct winsdb_record *rec = s->rec;
293 const char *old_state = "active";
294 BOOL modify_record = False;
295 BOOL delete_record = False;
296 BOOL different = False;
302 * - if the name isn't present anymore remove our record
303 * - if the name is found and not a normal group check if the addresses match,
304 * - if they don't match remove the record
305 * - if they match do nothing
306 * - if an error happens do nothing
308 status = irpc_call_recv(ireq);
309 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
310 delete_record = True;
311 } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
312 for (i=0; i < s->r.out.num_addrs; i++) {
314 for (j=0; rec->addresses[j]; j++) {
315 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
325 } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
326 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
332 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: deleting record\n",
333 nbt_name_string(rec, rec->name), rec->wins_owner));
334 delete_record = True;
335 } else if (NT_STATUS_IS_OK(status)) {
336 rec->expire_time = time(NULL) + s->service->config.verify_interval;
337 for (i=0; rec->addresses[i]; i++) {
338 rec->addresses[i]->expire_time = rec->expire_time;
340 modify_record = True;
345 ret = winsdb_modify(s->service->wins_db, rec, 0);
346 } else if (delete_record) {
348 ret = winsdb_delete(s->service->wins_db, rec);
354 if (ret != NBT_RCODE_OK) {
355 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
356 action, nbt_name_string(rec, rec->name), old_state, ret));
358 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s): %s: %s\n",
359 action, nbt_name_string(rec, rec->name), old_state, rec->wins_owner, nt_errstr(status)));
365 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
368 struct winsdb_record *rec = NULL;
369 struct ldb_result *res = NULL;
370 const char *owner_filter;
374 time_t now = time(NULL);
375 const char *now_timestr;
376 struct irpc_request *ireq;
377 struct verify_state *s;
378 uint32_t *nbt_servers;
380 nbt_servers = irpc_servers_byname(service->task->msg_ctx, "nbt_server");
381 if ((nbt_servers == NULL) || (nbt_servers[0] == 0)) {
382 return NT_STATUS_INTERNAL_ERROR;
385 now_timestr = ldb_timestring(tmp_mem, now);
386 NT_STATUS_HAVE_NO_MEMORY(now_timestr);
387 owner_filter = wreplsrv_owner_filter(service, tmp_mem,
388 service->wins_db->local_owner);
389 NT_STATUS_HAVE_NO_MEMORY(owner_filter);
390 filter = talloc_asprintf(tmp_mem,
391 "(&(!%s)(objectClass=winsRecord)"
392 "(recordState=%u)(expireTime<=%s)(!(isStatic=1)))",
393 owner_filter, WREPL_STATE_ACTIVE, now_timestr);
394 NT_STATUS_HAVE_NO_MEMORY(filter);
395 ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
396 if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
397 talloc_steal(tmp_mem, res);
398 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
400 for (i=0; i < res->count; i++) {
402 * we pass '0' as 'now' here,
403 * because we want to get the raw timestamps which are in the DB
405 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
406 NT_STATUS_NOT_OK_RETURN(status);
407 talloc_free(res->msgs[i]);
409 if (rec->is_static) {
410 DEBUG(0,("%s: corrupted record: %s\n",
411 __location__, nbt_name_string(rec, rec->name)));
412 return NT_STATUS_INTERNAL_DB_CORRUPTION;
415 if (rec->expire_time > now) {
416 DEBUG(0,("%s: corrupted record: %s\n",
417 __location__, nbt_name_string(rec, rec->name)));
418 return NT_STATUS_INTERNAL_DB_CORRUPTION;
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), rec->version));
439 s = talloc_zero(tmp_mem, struct verify_state);
440 NT_STATUS_HAVE_NO_MEMORY(s);
441 s->msg_ctx = service->task->msg_ctx;
442 s->service = service;
443 s->rec = talloc_steal(s, rec);
445 s->r.in.name = *rec->name;
446 s->r.in.num_addrs = 1;
447 s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
448 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
449 /* TODO: fix pidl to handle inline ipv4address arrays */
450 s->r.in.addrs[0].addr = rec->wins_owner;
452 ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
453 irpc, NBTD_PROXY_WINS_CHALLENGE,
455 NT_STATUS_HAVE_NO_MEMORY(ireq);
457 ireq->async.fn = verify_handler;
458 ireq->async.private = s;
460 talloc_steal(service, s);
466 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
470 BOOL skip_first_run = False;
472 if (!timeval_expired(&service->scavenging.next_run)) {
476 if (timeval_is_zero(&service->scavenging.next_run)) {
477 skip_first_run = True;
480 service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
481 status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
482 NT_STATUS_NOT_OK_RETURN(status);
485 * if it's the first time this functions is called (startup)
486 * the next_run is zero, in this case we should not do scavenging
488 if (skip_first_run) {
492 if (service->scavenging.processing) {
496 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
498 tmp_mem = talloc_new(service);
499 service->scavenging.processing = True;
500 status = wreplsrv_scavenging_owned_records(service,tmp_mem);
501 service->scavenging.processing = False;
502 talloc_free(tmp_mem);
503 NT_STATUS_NOT_OK_RETURN(status);
505 tmp_mem = talloc_new(service);
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 service->scavenging.processing = True;
514 status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
515 service->scavenging.processing = False;
516 talloc_free(tmp_mem);
517 NT_STATUS_NOT_OK_RETURN(status);
519 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));