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"));