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
, NULL
, LDB_SCOPE_SUBTREE
, filter
, NULL
, &res
);
78 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
79 talloc_steal(tmp_mem
, res
);
80 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
82 tombstone_extra_time
= timeval_add(&service
->startup_time
,
83 service
->config
.tombstone_extra_timeout
,
85 delete_tombstones
= timeval_expired(&tombstone_extra_time
);
87 for (i
=0; i
< res
->count
; i
++) {
89 * we pass '0' as 'now' here,
90 * because we want to get the raw timestamps which are in the DB
92 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
93 NT_STATUS_NOT_OK_RETURN(status
);
94 talloc_free(res
->msgs
[i
]);
97 modify_record
= False
;
98 delete_record
= False
;
100 switch (rec
->state
) {
101 case WREPL_STATE_ACTIVE
:
102 old_state
= "active";
103 new_state
= "active";
104 if (!rec
->is_static
) {
105 new_state
= "released";
106 rec
->state
= WREPL_STATE_RELEASED
;
107 rec
->expire_time
= service
->config
.tombstone_interval
+ now
;
110 modify_record
= True
;
113 case WREPL_STATE_RELEASED
:
114 old_state
= "released";
115 new_state
= "tombstone";
116 rec
->state
= WREPL_STATE_TOMBSTONE
;
117 rec
->expire_time
= service
->config
.tombstone_timeout
+ now
;
118 modify_flags
= WINSDB_FLAG_ALLOC_VERSION
| WINSDB_FLAG_TAKE_OWNERSHIP
;
119 modify_record
= True
;
122 case WREPL_STATE_TOMBSTONE
:
123 old_state
= "tombstone";
124 new_state
= "tombstone";
125 if (!delete_tombstones
) break;
126 new_state
= "deleted";
127 delete_record
= True
;
130 case WREPL_STATE_RESERVED
:
131 DEBUG(0,("%s: corrupted record: %s\n",
132 __location__
, nbt_name_string(rec
, rec
->name
)));
133 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
138 ret
= winsdb_modify(service
->wins_db
, rec
, modify_flags
);
139 } else if (delete_record
) {
141 ret
= winsdb_delete(service
->wins_db
, rec
);
147 if (ret
!= NBT_RCODE_OK
) {
148 DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
149 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
, ret
));
151 DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
152 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
));
161 static NTSTATUS
wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service
*service
, TALLOC_CTX
*tmp_mem
)
164 struct winsdb_record
*rec
= NULL
;
165 struct ldb_result
*res
= NULL
;
166 const char *owner_filter
;
170 time_t now
= time(NULL
);
171 const char *now_timestr
;
173 const char *old_state
=NULL
;
174 const char *new_state
=NULL
;
175 uint32_t modify_flags
;
178 BOOL delete_tombstones
;
179 struct timeval tombstone_extra_time
;
181 now_timestr
= ldb_timestring(tmp_mem
, now
);
182 NT_STATUS_HAVE_NO_MEMORY(now_timestr
);
183 owner_filter
= wreplsrv_owner_filter(service
, tmp_mem
,
184 service
->wins_db
->local_owner
);
185 NT_STATUS_HAVE_NO_MEMORY(owner_filter
);
186 filter
= talloc_asprintf(tmp_mem
,
187 "(&(!%s)(objectClass=winsRecord)"
188 "(!(recordState=%u))(expireTime<=%s))",
189 owner_filter
, WREPL_STATE_ACTIVE
, now_timestr
);
190 NT_STATUS_HAVE_NO_MEMORY(filter
);
191 ret
= ldb_search(service
->wins_db
->ldb
, NULL
, LDB_SCOPE_SUBTREE
, filter
, NULL
, &res
);
192 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
193 talloc_steal(tmp_mem
, res
);
194 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
196 tombstone_extra_time
= timeval_add(&service
->startup_time
,
197 service
->config
.tombstone_extra_timeout
,
199 delete_tombstones
= timeval_expired(&tombstone_extra_time
);
201 for (i
=0; i
< res
->count
; i
++) {
203 * we pass '0' as 'now' here,
204 * because we want to get the raw timestamps which are in the DB
206 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
207 NT_STATUS_NOT_OK_RETURN(status
);
208 talloc_free(res
->msgs
[i
]);
211 modify_record
= False
;
212 delete_record
= False
;
214 switch (rec
->state
) {
215 case WREPL_STATE_ACTIVE
:
216 DEBUG(0,("%s: corrupted record: %s\n",
217 __location__
, nbt_name_string(rec
, rec
->name
)));
218 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
220 case WREPL_STATE_RELEASED
:
221 old_state
= "released";
222 new_state
= "tombstone";
223 rec
->state
= WREPL_STATE_TOMBSTONE
;
224 rec
->expire_time
= service
->config
.tombstone_timeout
+ now
;
226 modify_record
= True
;
229 case WREPL_STATE_TOMBSTONE
:
230 old_state
= "tombstone";
231 new_state
= "tombstone";
232 if (!delete_tombstones
) break;
233 new_state
= "deleted";
234 delete_record
= True
;
237 case WREPL_STATE_RESERVED
:
238 DEBUG(0,("%s: corrupted record: %s\n",
239 __location__
, nbt_name_string(rec
, rec
->name
)));
240 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
245 ret
= winsdb_modify(service
->wins_db
, rec
, modify_flags
);
246 } else if (delete_record
) {
248 ret
= winsdb_delete(service
->wins_db
, rec
);
254 if (ret
!= NBT_RCODE_OK
) {
255 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
256 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
, ret
));
258 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
259 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_state
));
268 struct verify_state
{
269 struct messaging_context
*msg_ctx
;
270 struct wreplsrv_service
*service
;
271 struct winsdb_record
*rec
;
272 struct nbtd_proxy_wins_challenge r
;
275 static void verify_handler(struct irpc_request
*ireq
)
277 struct verify_state
*s
= talloc_get_type(ireq
->async
.private,
278 struct verify_state
);
279 struct winsdb_record
*rec
= s
->rec
;
281 const char *old_state
= "active";
282 const char *new_state
= "active";
283 const char *new_owner
= "replica";
284 uint32_t modify_flags
= 0;
285 BOOL modify_record
= False
;
286 BOOL delete_record
= False
;
287 BOOL different
= False
;
293 * - if the name isn't present anymore remove our record
294 * - if the name is found and not a normal group check if the addresses match,
295 * - if they don't match remove the record
296 * - if they match do nothing
297 * - if an error happens do nothing
299 status
= irpc_call_recv(ireq
);
300 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND
, status
)) {
301 delete_record
= True
;
302 new_state
= "deleted";
303 } else if (NT_STATUS_IS_OK(status
) && rec
->type
!= WREPL_TYPE_GROUP
) {
304 for (i
=0; i
< s
->r
.out
.num_addrs
; i
++) {
306 for (j
=0; rec
->addresses
[j
]; j
++) {
307 if (strcmp(s
->r
.out
.addrs
[i
].addr
, rec
->addresses
[j
]->address
) == 0) {
317 } else if (NT_STATUS_IS_OK(status
) && rec
->type
== WREPL_TYPE_GROUP
) {
318 if (s
->r
.out
.num_addrs
!= 1 || strcmp(s
->r
.out
.addrs
[0].addr
, "255.255.255.255") != 0) {
325 * if the reply from the owning wins server has different addresses
326 * then take the ownership of the record and make it a tombstone
327 * this will then hopefully replicated to the original owner of the record
328 * which will then propagate it's own record, so that the current record will
329 * be replicated to to us
331 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
332 nbt_name_string(rec
, rec
->name
), rec
->wins_owner
));
334 rec
->state
= WREPL_STATE_TOMBSTONE
;
335 rec
->expire_time
= time(NULL
) + s
->service
->config
.tombstone_timeout
;
336 for (i
=0; rec
->addresses
[i
]; i
++) {
337 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
339 modify_record
= True
;
340 modify_flags
= WINSDB_FLAG_ALLOC_VERSION
| WINSDB_FLAG_TAKE_OWNERSHIP
;
341 new_state
= "tombstone";
343 } else if (NT_STATUS_IS_OK(status
)) {
344 /* if the addresses are the same, just update the timestamps */
345 rec
->expire_time
= time(NULL
) + s
->service
->config
.verify_interval
;
346 for (i
=0; rec
->addresses
[i
]; i
++) {
347 rec
->addresses
[i
]->expire_time
= rec
->expire_time
;
349 modify_record
= True
;
351 new_state
= "active";
356 ret
= winsdb_modify(s
->service
->wins_db
, rec
, modify_flags
);
357 } else if (delete_record
) {
359 ret
= winsdb_delete(s
->service
->wins_db
, rec
);
365 if (ret
!= NBT_RCODE_OK
) {
366 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
367 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
, ret
));
369 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
370 action
, nbt_name_string(rec
, rec
->name
), old_state
, new_owner
, new_state
,
371 rec
->wins_owner
, nt_errstr(status
)));
377 static NTSTATUS
wreplsrv_scavenging_replica_active_records(struct wreplsrv_service
*service
, TALLOC_CTX
*tmp_mem
)
380 struct winsdb_record
*rec
= NULL
;
381 struct ldb_result
*res
= NULL
;
382 const char *owner_filter
;
386 time_t now
= time(NULL
);
387 const char *now_timestr
;
388 struct irpc_request
*ireq
;
389 struct verify_state
*s
;
390 struct server_id
*nbt_servers
;
392 nbt_servers
= irpc_servers_byname(service
->task
->msg_ctx
, tmp_mem
, "nbt_server");
393 if ((nbt_servers
== NULL
) || (nbt_servers
[0].id
== 0)) {
394 return NT_STATUS_INTERNAL_ERROR
;
397 now_timestr
= ldb_timestring(tmp_mem
, now
);
398 NT_STATUS_HAVE_NO_MEMORY(now_timestr
);
399 owner_filter
= wreplsrv_owner_filter(service
, tmp_mem
,
400 service
->wins_db
->local_owner
);
401 NT_STATUS_HAVE_NO_MEMORY(owner_filter
);
402 filter
= talloc_asprintf(tmp_mem
,
403 "(&(!%s)(objectClass=winsRecord)"
404 "(recordState=%u)(expireTime<=%s))",
405 owner_filter
, WREPL_STATE_ACTIVE
, now_timestr
);
406 NT_STATUS_HAVE_NO_MEMORY(filter
);
407 ret
= ldb_search(service
->wins_db
->ldb
, NULL
, LDB_SCOPE_SUBTREE
, filter
, NULL
, &res
);
408 if (ret
!= LDB_SUCCESS
) return NT_STATUS_INTERNAL_DB_CORRUPTION
;
409 talloc_steal(tmp_mem
, res
);
410 DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter
, res
->count
));
412 for (i
=0; i
< res
->count
; i
++) {
414 * we pass '0' as 'now' here,
415 * because we want to get the raw timestamps which are in the DB
417 status
= winsdb_record(service
->wins_db
, res
->msgs
[i
], tmp_mem
, 0, &rec
);
418 NT_STATUS_NOT_OK_RETURN(status
);
419 talloc_free(res
->msgs
[i
]);
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
),
438 (unsigned long long)rec
->version
));
440 s
= talloc_zero(tmp_mem
, struct verify_state
);
441 NT_STATUS_HAVE_NO_MEMORY(s
);
442 s
->msg_ctx
= service
->task
->msg_ctx
;
443 s
->service
= service
;
444 s
->rec
= talloc_steal(s
, rec
);
446 s
->r
.in
.name
= *rec
->name
;
447 s
->r
.in
.num_addrs
= 1;
448 s
->r
.in
.addrs
= talloc_array(s
, struct nbtd_proxy_wins_addr
, s
->r
.in
.num_addrs
);
449 NT_STATUS_HAVE_NO_MEMORY(s
->r
.in
.addrs
);
450 /* TODO: fix pidl to handle inline ipv4address arrays */
451 s
->r
.in
.addrs
[0].addr
= rec
->wins_owner
;
453 ireq
= IRPC_CALL_SEND(s
->msg_ctx
, nbt_servers
[0],
454 irpc
, NBTD_PROXY_WINS_CHALLENGE
,
456 NT_STATUS_HAVE_NO_MEMORY(ireq
);
458 ireq
->async
.fn
= verify_handler
;
459 ireq
->async
.private = s
;
461 talloc_steal(service
, s
);
467 NTSTATUS
wreplsrv_scavenging_run(struct wreplsrv_service
*service
)
471 BOOL skip_first_run
= False
;
473 if (!timeval_expired(&service
->scavenging
.next_run
)) {
477 if (timeval_is_zero(&service
->scavenging
.next_run
)) {
478 skip_first_run
= True
;
481 service
->scavenging
.next_run
= timeval_current_ofs(service
->config
.scavenging_interval
, 0);
482 status
= wreplsrv_periodic_schedule(service
, service
->config
.scavenging_interval
);
483 NT_STATUS_NOT_OK_RETURN(status
);
486 * if it's the first time this functions is called (startup)
487 * the next_run is zero, in this case we should not do scavenging
489 if (skip_first_run
) {
493 if (service
->scavenging
.processing
) {
497 DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
499 tmp_mem
= talloc_new(service
);
500 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
501 service
->scavenging
.processing
= True
;
502 status
= wreplsrv_scavenging_owned_records(service
,tmp_mem
);
503 service
->scavenging
.processing
= False
;
504 talloc_free(tmp_mem
);
505 NT_STATUS_NOT_OK_RETURN(status
);
507 tmp_mem
= talloc_new(service
);
508 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
509 service
->scavenging
.processing
= True
;
510 status
= wreplsrv_scavenging_replica_non_active_records(service
, tmp_mem
);
511 service
->scavenging
.processing
= False
;
512 talloc_free(tmp_mem
);
513 NT_STATUS_NOT_OK_RETURN(status
);
515 tmp_mem
= talloc_new(service
);
516 NT_STATUS_HAVE_NO_MEMORY(tmp_mem
);
517 service
->scavenging
.processing
= True
;
518 status
= wreplsrv_scavenging_replica_active_records(service
, tmp_mem
);
519 service
->scavenging
.processing
= False
;
520 talloc_free(tmp_mem
);
521 NT_STATUS_NOT_OK_RETURN(status
);
523 DEBUG(4,("wreplsrv_scavenging_run(): end\n"));