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"
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
=NULL
;
60 const char *new_state
=NULL
;
61 uint32_t modify_flags
;
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)"
75 owner_filter
, now_timestr
);
76 NT_STATUS_HAVE_NO_MEMORY(filter
);
77 ret
= ldb_search(service
->wins_db
->ldb
, tmp_mem
, &res
, NULL
, LDB_SCOPE_SUBTREE
, NULL
, "%s", filter
);
78 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
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 modify_record
= false;
97 delete_record
= false;
100 case WREPL_STATE_ACTIVE
:
101 old_state
= "active";
102 new_state
= "active";
103 if (!rec
->is_static
) {
104 new_state
= "released";
105 rec
->state
= WREPL_STATE_RELEASED
;
106 rec
->expire_time
= service
->config
.tombstone_interval
+ now
;
109 modify_record
= true;
112 case WREPL_STATE_RELEASED
:
113 old_state
= "released";
114 new_state
= "tombstone";
115 rec
->state
= WREPL_STATE_TOMBSTONE
;
116 rec
->expire_time
= service
->config
.tombstone_timeout
+ now
;
117 modify_flags
= WINSDB_FLAG_ALLOC_VERSION
| WINSDB_FLAG_TAKE_OWNERSHIP
;
118 modify_record
= true;
121 case WREPL_STATE_TOMBSTONE
:
122 old_state
= "tombstone";
123 new_state
= "tombstone";
124 if (!delete_tombstones
) break;
125 new_state
= "deleted";
126 delete_record
= true;
129 case WREPL_STATE_RESERVED
:
130 DEBUG(0,("%s: corrupted record: %s\n",
131 __location__
, nbt_name_string(rec
, rec
->name
)));
132 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
137 ret
= winsdb_modify(service
->wins_db
, rec
, modify_flags
);
138 } else if (delete_record
) {
140 ret
= winsdb_delete(service
->wins_db
, rec
);
146 if (ret
!= NBT_RCODE_OK
) {
147 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
148 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
, ret
));
150 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
151 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
));
160 static NTSTATUS
wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service
*service
, TALLOC_CTX
*tmp_mem
)
163 struct winsdb_record
*rec
= NULL
;
164 struct ldb_result
*res
= NULL
;
165 const char *owner_filter
;
169 time_t now
= time(NULL
);
170 const char *now_timestr
;
172 const char *old_state
=NULL
;
173 const char *new_state
=NULL
;
174 uint32_t modify_flags
;
177 bool delete_tombstones
;
178 struct timeval tombstone_extra_time
;
180 now_timestr
= ldb_timestring(tmp_mem
, now
);
181 NT_STATUS_HAVE_NO_MEMORY(now_timestr
);
182 owner_filter
= wreplsrv_owner_filter(service
, tmp_mem
,
183 service
->wins_db
->local_owner
);
184 NT_STATUS_HAVE_NO_MEMORY(owner_filter
);
185 filter
= talloc_asprintf(tmp_mem
,
186 "(&(!%s)(objectClass=winsRecord)"
187 "(!(recordState=%u))(expireTime<=%s))",
188 owner_filter
, WREPL_STATE_ACTIVE
, now_timestr
);
189 NT_STATUS_HAVE_NO_MEMORY(filter
);
190 ret
= ldb_search(service
->wins_db
->ldb
, tmp_mem
, &res
, NULL
, LDB_SCOPE_SUBTREE
, NULL
, "%s", filter
);
191 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
192 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
194 tombstone_extra_time
= timeval_add(&service
->startup_time
,
195 service
->config
.tombstone_extra_timeout
,
197 delete_tombstones
= timeval_expired(&tombstone_extra_time
);
199 for (i
=0; i
< res
->count
; i
++) {
201 * we pass '0' as 'now' here,
202 * because we want to get the raw timestamps which are in the DB
204 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
205 NT_STATUS_NOT_OK_RETURN(status
);
206 talloc_free(res
->msgs
[i
]);
209 modify_record
= false;
210 delete_record
= false;
212 switch (rec
->state
) {
213 case WREPL_STATE_ACTIVE
:
214 DEBUG(0,("%s: corrupted record: %s\n",
215 __location__
, nbt_name_string(rec
, rec
->name
)));
216 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
218 case WREPL_STATE_RELEASED
:
219 old_state
= "released";
220 new_state
= "tombstone";
221 rec
->state
= WREPL_STATE_TOMBSTONE
;
222 rec
->expire_time
= service
->config
.tombstone_timeout
+ now
;
224 modify_record
= true;
227 case WREPL_STATE_TOMBSTONE
:
228 old_state
= "tombstone";
229 new_state
= "tombstone";
230 if (!delete_tombstones
) break;
231 new_state
= "deleted";
232 delete_record
= true;
235 case WREPL_STATE_RESERVED
:
236 DEBUG(0,("%s: corrupted record: %s\n",
237 __location__
, nbt_name_string(rec
, rec
->name
)));
238 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
243 ret
= winsdb_modify(service
->wins_db
, rec
, modify_flags
);
244 } else if (delete_record
) {
246 ret
= winsdb_delete(service
->wins_db
, rec
);
252 if (ret
!= NBT_RCODE_OK
) {
253 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
254 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
, ret
));
256 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
257 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
));
266 struct verify_state
{
267 struct messaging_context
*msg_ctx
;
268 struct wreplsrv_service
*service
;
269 struct winsdb_record
*rec
;
270 struct nbtd_proxy_wins_challenge r
;
273 static void verify_handler(struct irpc_request
*ireq
)
275 struct verify_state
*s
= talloc_get_type(ireq
->async
.private,
276 struct verify_state
);
277 struct winsdb_record
*rec
= s
->rec
;
279 const char *old_state
= "active";
280 const char *new_state
= "active";
281 const char *new_owner
= "replica";
282 uint32_t modify_flags
= 0;
283 bool modify_record
= false;
284 bool delete_record
= false;
285 bool different
= false;
291 * - if the name isn't present anymore remove our record
292 * - if the name is found and not a normal group check if the addresses match,
293 * - if they don't match remove the record
294 * - if they match do nothing
295 * - if an error happens do nothing
297 status
= irpc_call_recv(ireq
);
298 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND
, status
)) {
299 delete_record
= true;
300 new_state
= "deleted";
301 } else if (NT_STATUS_IS_OK(status
) && rec
->type
!= WREPL_TYPE_GROUP
) {
302 for (i
=0; i
< s
->r
.out
.num_addrs
; i
++) {
304 for (j
=0; rec
->addresses
[j
]; j
++) {
305 if (strcmp(s
->r
.out
.addrs
[i
].addr
, rec
->addresses
[j
]->address
) == 0) {
315 } else if (NT_STATUS_IS_OK(status
) && rec
->type
== WREPL_TYPE_GROUP
) {
316 if (s
->r
.out
.num_addrs
!= 1 || strcmp(s
->r
.out
.addrs
[0].addr
, "255.255.255.255") != 0) {
323 * if the reply from the owning wins server has different addresses
324 * then take the ownership of the record and make it a tombstone
325 * this will then hopefully replicated to the original owner of the record
326 * which will then propagate it's own record, so that the current record will
327 * be replicated to to us
329 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
330 nbt_name_string(rec
, rec
->name
), rec
->wins_owner
));
332 rec
->state
= WREPL_STATE_TOMBSTONE
;
333 rec
->expire_time
= time(NULL
) + s
->service
->config
.tombstone_timeout
;
334 for (i
=0; rec
->addresses
[i
]; i
++) {
335 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
337 modify_record
= true;
338 modify_flags
= WINSDB_FLAG_ALLOC_VERSION
| WINSDB_FLAG_TAKE_OWNERSHIP
;
339 new_state
= "tombstone";
341 } else if (NT_STATUS_IS_OK(status
)) {
342 /* if the addresses are the same, just update the timestamps */
343 rec
->expire_time
= time(NULL
) + s
->service
->config
.verify_interval
;
344 for (i
=0; rec
->addresses
[i
]; i
++) {
345 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
347 modify_record
= true;
349 new_state
= "active";
354 ret
= winsdb_modify(s
->service
->wins_db
, rec
, modify_flags
);
355 } else if (delete_record
) {
357 ret
= winsdb_delete(s
->service
->wins_db
, rec
);
363 if (ret
!= NBT_RCODE_OK
) {
364 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
365 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
, ret
));
367 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
368 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
,
369 rec
->wins_owner
, nt_errstr(status
)));
375 static NTSTATUS
wreplsrv_scavenging_replica_active_records(struct wreplsrv_service
*service
, TALLOC_CTX
*tmp_mem
)
378 struct winsdb_record
*rec
= NULL
;
379 struct ldb_result
*res
= NULL
;
380 const char *owner_filter
;
384 time_t now
= time(NULL
);
385 const char *now_timestr
;
386 struct irpc_request
*ireq
;
387 struct verify_state
*s
;
388 struct server_id
*nbt_servers
;
390 nbt_servers
= irpc_servers_byname(service
->task
->msg_ctx
, tmp_mem
, "nbt_server");
391 if ((nbt_servers
== NULL
) || (nbt_servers
[0].id
== 0)) {
392 return NT_STATUS_INTERNAL_ERROR
;
395 now_timestr
= ldb_timestring(tmp_mem
, now
);
396 NT_STATUS_HAVE_NO_MEMORY(now_timestr
);
397 owner_filter
= wreplsrv_owner_filter(service
, tmp_mem
,
398 service
->wins_db
->local_owner
);
399 NT_STATUS_HAVE_NO_MEMORY(owner_filter
);
400 filter
= talloc_asprintf(tmp_mem
,
401 "(&(!%s)(objectClass=winsRecord)"
402 "(recordState=%u)(expireTime<=%s))",
403 owner_filter
, WREPL_STATE_ACTIVE
, now_timestr
);
404 NT_STATUS_HAVE_NO_MEMORY(filter
);
405 ret
= ldb_search(service
->wins_db
->ldb
, tmp_mem
, &res
, NULL
, LDB_SCOPE_SUBTREE
, NULL
, "%s", filter
);
406 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
407 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
409 for (i
=0; i
< res
->count
; i
++) {
411 * we pass '0' as 'now' here,
412 * because we want to get the raw timestamps which are in the DB
414 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
415 NT_STATUS_NOT_OK_RETURN(status
);
416 talloc_free(res
->msgs
[i
]);
418 if (rec
->state
!= WREPL_STATE_ACTIVE
) {
419 DEBUG(0,("%s: corrupted record: %s\n",
420 __location__
, nbt_name_string(rec
, rec
->name
)));
421 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
425 * ask the owning wins server if the record still exists,
426 * if not delete the record
428 * TODO: NOTE: this is a simpliefied version, to verify that
429 * a record still exist, I assume that w2k3 uses
430 * DCERPC calls or some WINSREPL packets for this,
431 * but we use a wins name query
433 DEBUG(0,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
434 rec
->wins_owner
, nbt_name_string(rec
, rec
->name
),
435 (unsigned long long)rec
->version
));
437 s
= talloc_zero(tmp_mem
, struct verify_state
);
438 NT_STATUS_HAVE_NO_MEMORY(s
);
439 s
->msg_ctx
= service
->task
->msg_ctx
;
440 s
->service
= service
;
441 s
->rec
= talloc_steal(s
, rec
);
443 s
->r
.in
.name
= *rec
->name
;
444 s
->r
.in
.num_addrs
= 1;
445 s
->r
.in
.addrs
= talloc_array(s
, struct nbtd_proxy_wins_addr
, s
->r
.in
.num_addrs
);
446 NT_STATUS_HAVE_NO_MEMORY(s
->r
.in
.addrs
);
447 /* TODO: fix pidl to handle inline ipv4address arrays */
448 s
->r
.in
.addrs
[0].addr
= rec
->wins_owner
;
450 ireq
= IRPC_CALL_SEND(s
->msg_ctx
, nbt_servers
[0],
451 irpc
, NBTD_PROXY_WINS_CHALLENGE
,
453 NT_STATUS_HAVE_NO_MEMORY(ireq
);
455 ireq
->async
.fn
= verify_handler
;
456 ireq
->async
.private = s
;
458 talloc_steal(service
, s
);
464 NTSTATUS
wreplsrv_scavenging_run(struct wreplsrv_service
*service
)
468 bool skip_first_run
= false;
470 if (!timeval_expired(&service
->scavenging
.next_run
)) {
474 if (timeval_is_zero(&service
->scavenging
.next_run
)) {
475 skip_first_run
= true;
478 service
->scavenging
.next_run
= timeval_current_ofs(service
->config
.scavenging_interval
, 0);
479 status
= wreplsrv_periodic_schedule(service
, service
->config
.scavenging_interval
);
480 NT_STATUS_NOT_OK_RETURN(status
);
483 * if it's the first time this functions is called (startup)
484 * the next_run is zero, in this case we should not do scavenging
486 if (skip_first_run
) {
490 if (service
->scavenging
.processing
) {
494 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
496 tmp_mem
= talloc_new(service
);
497 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
498 service
->scavenging
.processing
= true;
499 status
= wreplsrv_scavenging_owned_records(service
,tmp_mem
);
500 service
->scavenging
.processing
= false;
501 talloc_free(tmp_mem
);
502 NT_STATUS_NOT_OK_RETURN(status
);
504 tmp_mem
= talloc_new(service
);
505 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
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 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
514 service
->scavenging
.processing
= true;
515 status
= wreplsrv_scavenging_replica_active_records(service
, tmp_mem
);
516 service
->scavenging
.processing
= false;
517 talloc_free(tmp_mem
);
518 NT_STATUS_NOT_OK_RETURN(status
);
520 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));