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/>.
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"
33
#include "param/param.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;
67
const char *local_owner = service->wins_db->local_owner;
68
bool propagate = lp_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)"
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,
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]);
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)";
114
modify_record = true;
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;
122
modify_record = true;
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) {
129
rec->addresses[i]->expire_time= service->config.renew_interval + now;
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;
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;
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;
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;
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;
179
ret = winsdb_modify(service->wins_db, rec, modify_flags);
180
} else if (delete_record) {
182
ret = winsdb_delete(service->wins_db, rec);
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));
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));
202
static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
205
struct winsdb_record *rec = NULL;
206
struct ldb_result *res = NULL;
207
const char *owner_filter;
211
time_t now = time(NULL);
212
const char *now_timestr;
214
const char *old_state=NULL;
215
const char *new_state=NULL;
216
uint32_t modify_flags;
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]);
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;
266
modify_record = true;
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;
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;
285
ret = winsdb_modify(service->wins_db, rec, modify_flags);
286
} else if (delete_record) {
288
ret = winsdb_delete(service->wins_db, rec);
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));
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));
308
struct verify_state {
309
struct messaging_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 irpc_request *ireq)
317
struct verify_state *s = talloc_get_type(ireq->async.private_data,
318
struct verify_state);
319
struct winsdb_record *rec = s->rec;
321
const char *old_state = "active";
322
const char *new_state = "active";
323
const char *new_owner = "replica";
324
uint32_t modify_flags = 0;
325
bool modify_record = false;
326
bool delete_record = false;
327
bool different = false;
333
* - if the name isn't present anymore remove our record
334
* - if the name is found and not a normal group check if the addresses match,
335
* - if they don't match remove the record
336
* - if they match do nothing
337
* - if an error happens do nothing
339
status = irpc_call_recv(ireq);
340
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
341
delete_record = true;
342
new_state = "deleted";
343
} else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
344
for (i=0; i < s->r.out.num_addrs; i++) {
346
for (j=0; rec->addresses[j]; j++) {
347
if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
357
} else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
358
if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
365
* if the reply from the owning wins server has different addresses
366
* then take the ownership of the record and make it a tombstone
367
* this will then hopefully replicated to the original owner of the record
368
* which will then propagate it's own record, so that the current record will
369
* be replicated to to us
371
DEBUG(2,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
372
nbt_name_string(rec, rec->name), rec->wins_owner));
374
rec->state = WREPL_STATE_TOMBSTONE;
375
rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
376
for (i=0; rec->addresses[i]; i++) {
377
rec->addresses[i]->expire_time = rec->expire_time;
379
modify_record = true;
380
modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
381
new_state = "tombstone";
383
} else if (NT_STATUS_IS_OK(status)) {
384
/* if the addresses are the same, just update the timestamps */
385
rec->expire_time = time(NULL) + s->service->config.verify_interval;
386
for (i=0; rec->addresses[i]; i++) {
387
rec->addresses[i]->expire_time = rec->expire_time;
389
modify_record = true;
391
new_state = "active";
396
ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
397
} else if (delete_record) {
399
ret = winsdb_delete(s->service->wins_db, rec);
405
if (ret != NBT_RCODE_OK) {
406
DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
407
action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
409
DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
410
action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
411
rec->wins_owner, nt_errstr(status)));
417
static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
420
struct winsdb_record *rec = NULL;
421
struct ldb_result *res = NULL;
422
const char *owner_filter;
426
time_t now = time(NULL);
427
const char *now_timestr;
428
struct irpc_request *ireq;
429
struct verify_state *s;
430
struct server_id *nbt_servers;
432
nbt_servers = irpc_servers_byname(service->task->msg_ctx, tmp_mem, "nbt_server");
433
if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
434
return NT_STATUS_INTERNAL_ERROR;
437
now_timestr = ldb_timestring(tmp_mem, now);
438
NT_STATUS_HAVE_NO_MEMORY(now_timestr);
439
owner_filter = wreplsrv_owner_filter(service, tmp_mem,
440
service->wins_db->local_owner);
441
NT_STATUS_HAVE_NO_MEMORY(owner_filter);
442
filter = talloc_asprintf(tmp_mem,
443
"(&(!%s)(objectClass=winsRecord)"
444
"(recordState=%u)(expireTime<=%s))",
445
owner_filter, WREPL_STATE_ACTIVE, now_timestr);
446
NT_STATUS_HAVE_NO_MEMORY(filter);
447
ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
448
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
449
DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
451
for (i=0; i < res->count; i++) {
453
* we pass '0' as 'now' here,
454
* because we want to get the raw timestamps which are in the DB
456
status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
457
NT_STATUS_NOT_OK_RETURN(status);
458
talloc_free(res->msgs[i]);
460
if (rec->state != WREPL_STATE_ACTIVE) {
461
DEBUG(0,("%s: corrupted record: %s\n",
462
__location__, nbt_name_string(rec, rec->name)));
463
return NT_STATUS_INTERNAL_DB_CORRUPTION;
467
* ask the owning wins server if the record still exists,
468
* if not delete the record
470
* TODO: NOTE: this is a simpliefied version, to verify that
471
* a record still exist, I assume that w2k3 uses
472
* DCERPC calls or some WINSREPL packets for this,
473
* but we use a wins name query
475
DEBUG(2,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
476
rec->wins_owner, nbt_name_string(rec, rec->name),
477
(unsigned long long)rec->version));
479
s = talloc_zero(tmp_mem, struct verify_state);
480
NT_STATUS_HAVE_NO_MEMORY(s);
481
s->msg_ctx = service->task->msg_ctx;
482
s->service = service;
483
s->rec = talloc_steal(s, rec);
485
s->r.in.name = *rec->name;
486
s->r.in.num_addrs = 1;
487
s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
488
NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
489
/* TODO: fix pidl to handle inline ipv4address arrays */
490
s->r.in.addrs[0].addr = rec->wins_owner;
492
ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
493
irpc, NBTD_PROXY_WINS_CHALLENGE,
495
NT_STATUS_HAVE_NO_MEMORY(ireq);
497
ireq->async.fn = verify_handler;
498
ireq->async.private_data= s;
500
talloc_steal(service, s);
506
NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
510
bool skip_first_run = false;
512
if (!timeval_expired(&service->scavenging.next_run)) {
516
if (timeval_is_zero(&service->scavenging.next_run)) {
517
skip_first_run = true;
520
service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
521
status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
522
NT_STATUS_NOT_OK_RETURN(status);
525
* if it's the first time this functions is called (startup)
526
* the next_run is zero, in this case we should not do scavenging
528
if (skip_first_run) {
532
if (service->scavenging.processing) {
536
DEBUG(2,("wreplsrv_scavenging_run(): start\n"));
538
tmp_mem = talloc_new(service);
539
NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
540
service->scavenging.processing = true;
541
status = wreplsrv_scavenging_owned_records(service,tmp_mem);
542
service->scavenging.processing = false;
543
talloc_free(tmp_mem);
544
NT_STATUS_NOT_OK_RETURN(status);
546
tmp_mem = talloc_new(service);
547
NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
548
service->scavenging.processing = true;
549
status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
550
service->scavenging.processing = false;
551
talloc_free(tmp_mem);
552
NT_STATUS_NOT_OK_RETURN(status);
554
tmp_mem = talloc_new(service);
555
NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
556
service->scavenging.processing = true;
557
status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
558
service->scavenging.processing = false;
559
talloc_free(tmp_mem);
560
NT_STATUS_NOT_OK_RETURN(status);
562
DEBUG(2,("wreplsrv_scavenging_run(): end\n"));