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"
27 #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_c.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
= lpcfg_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 imessaging_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 tevent_req
*subreq
)
317 struct verify_state
*s
=
318 tevent_req_callback_data(subreq
,
319 struct verify_state
);
320 struct winsdb_record
*rec
= s
->rec
;
322 const char *old_state
= "active";
323 const char *new_state
= "active";
324 const char *new_owner
= "replica";
325 uint32_t modify_flags
= 0;
326 bool modify_record
= false;
327 bool delete_record
= false;
328 bool different
= false;
334 * - if the name isn't present anymore remove our record
335 * - if the name is found and not a normal group check if the addresses match,
336 * - if they don't match remove the record
337 * - if they match do nothing
338 * - if an error happens do nothing
340 status
= dcerpc_nbtd_proxy_wins_challenge_r_recv(subreq
, s
);
342 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND
, status
)) {
343 delete_record
= true;
344 new_state
= "deleted";
345 } else if (NT_STATUS_IS_OK(status
) && rec
->type
!= WREPL_TYPE_GROUP
) {
346 for (i
=0; i
< s
->r
.out
.num_addrs
; i
++) {
348 for (j
=0; rec
->addresses
[j
]; j
++) {
349 if (strcmp(s
->r
.out
.addrs
[i
].addr
, rec
->addresses
[j
]->address
) == 0) {
359 } else if (NT_STATUS_IS_OK(status
) && rec
->type
== WREPL_TYPE_GROUP
) {
360 if (s
->r
.out
.num_addrs
!= 1 || strcmp(s
->r
.out
.addrs
[0].addr
, "255.255.255.255") != 0) {
367 * if the reply from the owning wins server has different addresses
368 * then take the ownership of the record and make it a tombstone
369 * this will then hopefully replicated to the original owner of the record
370 * which will then propagate it's own record, so that the current record will
371 * be replicated to to us
373 DEBUG(2,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
374 nbt_name_string(rec
, rec
->name
), rec
->wins_owner
));
376 rec
->state
= WREPL_STATE_TOMBSTONE
;
377 rec
->expire_time
= time(NULL
) + s
->service
->config
.tombstone_timeout
;
378 for (i
=0; rec
->addresses
[i
]; i
++) {
379 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
381 modify_record
= true;
382 modify_flags
= WINSDB_FLAG_ALLOC_VERSION
| WINSDB_FLAG_TAKE_OWNERSHIP
;
383 new_state
= "tombstone";
385 } else if (NT_STATUS_IS_OK(status
)) {
386 /* if the addresses are the same, just update the timestamps */
387 rec
->expire_time
= time(NULL
) + s
->service
->config
.verify_interval
;
388 for (i
=0; rec
->addresses
[i
]; i
++) {
389 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
391 modify_record
= true;
393 new_state
= "active";
398 ret
= winsdb_modify(s
->service
->wins_db
, rec
, modify_flags
);
399 } else if (delete_record
) {
401 ret
= winsdb_delete(s
->service
->wins_db
, rec
);
407 if (ret
!= NBT_RCODE_OK
) {
408 DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
409 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
, ret
));
411 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
412 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
,
413 rec
->wins_owner
, nt_errstr(status
)));
419 static NTSTATUS
wreplsrv_scavenging_replica_active_records(struct wreplsrv_service
*service
, TALLOC_CTX
*tmp_mem
)
422 struct winsdb_record
*rec
= NULL
;
423 struct ldb_result
*res
= NULL
;
424 const char *owner_filter
;
428 time_t now
= time(NULL
);
429 const char *now_timestr
;
430 struct tevent_req
*subreq
;
431 struct verify_state
*s
;
432 struct dcerpc_binding_handle
*irpc_handle
;
434 now_timestr
= ldb_timestring(tmp_mem
, now
);
435 NT_STATUS_HAVE_NO_MEMORY(now_timestr
);
436 owner_filter
= wreplsrv_owner_filter(service
, tmp_mem
,
437 service
->wins_db
->local_owner
);
438 NT_STATUS_HAVE_NO_MEMORY(owner_filter
);
439 filter
= talloc_asprintf(tmp_mem
,
440 "(&(!%s)(objectClass=winsRecord)"
441 "(recordState=%u)(expireTime<=%s))",
442 owner_filter
, WREPL_STATE_ACTIVE
, now_timestr
);
443 NT_STATUS_HAVE_NO_MEMORY(filter
);
444 ret
= ldb_search(service
->wins_db
->ldb
, tmp_mem
, &res
, NULL
, LDB_SCOPE_SUBTREE
, NULL
, "%s", filter
);
445 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
446 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
448 for (i
=0; i
< res
->count
; i
++) {
450 * we pass '0' as 'now' here,
451 * because we want to get the raw timestamps which are in the DB
453 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
454 NT_STATUS_NOT_OK_RETURN(status
);
455 talloc_free(res
->msgs
[i
]);
457 if (rec
->state
!= WREPL_STATE_ACTIVE
) {
458 DEBUG(0,("%s: corrupted record: %s\n",
459 __location__
, nbt_name_string(rec
, rec
->name
)));
460 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
464 * ask the owning wins server if the record still exists,
465 * if not delete the record
467 * TODO: NOTE: this is a simpliefied version, to verify that
468 * a record still exist, I assume that w2k3 uses
469 * DCERPC calls or some WINSREPL packets for this,
470 * but we use a wins name query
472 DEBUG(2,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
473 rec
->wins_owner
, nbt_name_string(rec
, rec
->name
),
474 (unsigned long long)rec
->version
));
476 s
= talloc_zero(tmp_mem
, struct verify_state
);
477 NT_STATUS_HAVE_NO_MEMORY(s
);
478 s
->msg_ctx
= service
->task
->msg_ctx
;
479 s
->service
= service
;
480 s
->rec
= talloc_steal(s
, rec
);
482 s
->r
.in
.name
= *rec
->name
;
483 s
->r
.in
.num_addrs
= 1;
484 s
->r
.in
.addrs
= talloc_array(s
, struct nbtd_proxy_wins_addr
, s
->r
.in
.num_addrs
);
485 NT_STATUS_HAVE_NO_MEMORY(s
->r
.in
.addrs
);
486 /* TODO: fix pidl to handle inline ipv4address arrays */
487 s
->r
.in
.addrs
[0].addr
= rec
->wins_owner
;
489 irpc_handle
= irpc_binding_handle_by_name(s
,
490 service
->task
->msg_ctx
,
493 if (irpc_handle
== NULL
) {
494 return NT_STATUS_INTERNAL_ERROR
;
497 subreq
= dcerpc_nbtd_proxy_wins_challenge_r_send(s
,
498 service
->task
->event_ctx
,
501 NT_STATUS_HAVE_NO_MEMORY(subreq
);
503 tevent_req_set_callback(subreq
, verify_handler
, s
);
505 talloc_steal(service
, s
);
511 NTSTATUS
wreplsrv_scavenging_run(struct wreplsrv_service
*service
)
515 bool skip_first_run
= false;
517 if (!timeval_expired(&service
->scavenging
.next_run
)) {
521 if (timeval_is_zero(&service
->scavenging
.next_run
)) {
522 skip_first_run
= true;
525 service
->scavenging
.next_run
= timeval_current_ofs(service
->config
.scavenging_interval
, 0);
526 status
= wreplsrv_periodic_schedule(service
, service
->config
.scavenging_interval
);
527 NT_STATUS_NOT_OK_RETURN(status
);
530 * if it's the first time this functions is called (startup)
531 * the next_run is zero, in this case we should not do scavenging
533 if (skip_first_run
) {
537 if (service
->scavenging
.processing
) {
541 DEBUG(2,("wreplsrv_scavenging_run(): start\n"));
543 tmp_mem
= talloc_new(service
);
544 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
545 service
->scavenging
.processing
= true;
546 status
= wreplsrv_scavenging_owned_records(service
,tmp_mem
);
547 service
->scavenging
.processing
= false;
548 talloc_free(tmp_mem
);
549 NT_STATUS_NOT_OK_RETURN(status
);
551 tmp_mem
= talloc_new(service
);
552 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
553 service
->scavenging
.processing
= true;
554 status
= wreplsrv_scavenging_replica_non_active_records(service
, tmp_mem
);
555 service
->scavenging
.processing
= false;
556 talloc_free(tmp_mem
);
557 NT_STATUS_NOT_OK_RETURN(status
);
559 tmp_mem
= talloc_new(service
);
560 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
561 service
->scavenging
.processing
= true;
562 status
= wreplsrv_scavenging_replica_active_records(service
, tmp_mem
);
563 service
->scavenging
.processing
= false;
564 talloc_free(tmp_mem
);
565 NT_STATUS_NOT_OK_RETURN(status
);
567 DEBUG(2,("wreplsrv_scavenging_run(): end\n"));