r13080: fix crash bug
[jra/samba/.git] / source4 / wrepl_server / wrepl_scavenging.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    WINS Replication server
5    
6    Copyright (C) Stefan Metzmacher      2005
7    
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 2 of the License, or
11    (at your option) any later version.
12    
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.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_winsrepl.h"
25 #include "wrepl_server/wrepl_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "ldb/include/ldb.h"
28 #include "ldb/include/ldb_errors.h"
29 #include "system/time.h"
30 #include "smbd/service_task.h"
31 #include "lib/messaging/irpc.h"
32 #include "librpc/gen_ndr/ndr_irpc.h"
33
34 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
35                                   TALLOC_CTX *mem_ctx,
36                                   const char *wins_owner)
37 {
38         if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
39                 return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
40                                        wins_owner);
41         }
42
43         return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
44                                wins_owner);
45 }
46
47 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
48 {
49         NTSTATUS status;
50         struct winsdb_record *rec = NULL;
51         struct ldb_result *res = NULL;
52         const char *owner_filter;
53         const char *filter;
54         uint32_t i;
55         int ret;
56         time_t now = time(NULL);
57         const char *now_timestr;
58         const char *action;
59         const char *old_state;
60         uint32_t modify_flags;
61         BOOL modify_record;
62         BOOL delete_record;
63         BOOL delete_tombstones;
64         struct timeval tombstone_extra_time;
65
66         now_timestr = ldb_timestring(tmp_mem, now);
67         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
68         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
69                                              service->wins_db->local_owner);
70         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
71         filter = talloc_asprintf(tmp_mem,
72                                  "(&%s(objectClass=winsRecord)"
73                                  "(expireTime<=%s)(!(isStatic=1)))",
74                                  owner_filter, now_timestr);
75         NT_STATUS_HAVE_NO_MEMORY(filter);
76         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
77         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
78         talloc_steal(tmp_mem, res);
79         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
80
81         tombstone_extra_time = timeval_add(&service->startup_time,
82                                            service->config.tombstone_extra_timeout,
83                                            0);
84         delete_tombstones = timeval_expired(&tombstone_extra_time);
85
86         for (i=0; i < res->count; i++) {
87                 /*
88                  * we pass '0' as 'now' here,
89                  * because we want to get the raw timestamps which are in the DB
90                  */
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]);
94
95                 if (rec->is_static) {
96                         DEBUG(0,("%s: corrupted record: %s\n",
97                                 __location__, nbt_name_string(rec, rec->name)));
98                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
99                 }
100
101                 if (rec->expire_time > now) {
102                         DEBUG(0,("%s: corrupted record: %s\n",
103                                 __location__, nbt_name_string(rec, rec->name)));
104                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
105                 }
106
107                 modify_flags    = 0;
108                 modify_record   = False;
109                 delete_record   = False;
110
111                 switch (rec->state) {
112                 case WREPL_STATE_ACTIVE:
113                         old_state       = "active";
114                         rec->state      = WREPL_STATE_RELEASED;
115                         rec->expire_time= service->config.tombstone_interval + now;
116                         modify_flags    = 0;
117                         modify_record   = True;
118                         break;
119
120                 case WREPL_STATE_RELEASED:
121                         old_state       = "released";
122                         rec->state      = WREPL_STATE_TOMBSTONE;
123                         rec->expire_time= service->config.tombstone_timeout + now;
124                         modify_flags    = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
125                         modify_record   = True;
126                         break;
127
128                 case WREPL_STATE_TOMBSTONE:
129                         old_state       = "tombstone";
130                         if (!delete_tombstones) break;
131                         delete_record = True;
132                         break;
133
134                 case WREPL_STATE_RESERVED:
135                         DEBUG(0,("%s: corrupted record: %s\n",
136                                 __location__, nbt_name_string(rec, rec->name)));
137                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
138                 }
139
140                 if (modify_record) {
141                         action = "modify";
142                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
143                 } else if (delete_record) {
144                         action = "delete";
145                         ret = winsdb_delete(service->wins_db, rec);
146                 } else {
147                         action = "skip";
148                         ret = NBT_RCODE_OK;
149                 }
150
151                 if (ret != NBT_RCODE_OK) {
152                         DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s): error:%u\n",
153                                 action, nbt_name_string(rec, rec->name), old_state, ret));
154                 } else {
155                         DEBUG(4,("WINS scavenging: %s name: %s (owned:%s)\n",
156                                 action, nbt_name_string(rec, rec->name), old_state));
157                 }
158
159                 talloc_free(rec);
160         }
161
162         return NT_STATUS_OK;
163 }
164
165 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
166 {
167         NTSTATUS status;
168         struct winsdb_record *rec = NULL;
169         struct ldb_result *res = NULL;
170         const char *owner_filter;
171         const char *filter;
172         uint32_t i;
173         int ret;
174         time_t now = time(NULL);
175         const char *now_timestr;
176         const char *action;
177         const char *old_state;
178         uint32_t modify_flags;
179         BOOL modify_record;
180         BOOL delete_record;
181         BOOL delete_tombstones;
182         struct timeval tombstone_extra_time;
183
184         now_timestr = ldb_timestring(tmp_mem, now);
185         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
186         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
187                                              service->wins_db->local_owner);
188         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
189         filter = talloc_asprintf(tmp_mem,
190                                  "(&(!%s)(objectClass=winsRecord)"
191                                  "(!(recordState=%u))(expireTime<=%s)(!(isStatic=1)))",
192                                  owner_filter, WREPL_STATE_ACTIVE, now_timestr);
193         NT_STATUS_HAVE_NO_MEMORY(filter);
194         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
195         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
196         talloc_steal(tmp_mem, res);
197         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
198
199         tombstone_extra_time = timeval_add(&service->startup_time,
200                                            service->config.tombstone_extra_timeout,
201                                            0);
202         delete_tombstones = timeval_expired(&tombstone_extra_time);
203
204         for (i=0; i < res->count; i++) {
205                 /*
206                  * we pass '0' as 'now' here,
207                  * because we want to get the raw timestamps which are in the DB
208                  */
209                 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
210                 NT_STATUS_NOT_OK_RETURN(status);
211                 talloc_free(res->msgs[i]);
212
213                 if (rec->is_static) {
214                         DEBUG(0,("%s: corrupted record: %s\n",
215                                 __location__, nbt_name_string(rec, rec->name)));
216                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
217                 }
218
219                 if (rec->expire_time > now) {
220                         DEBUG(0,("%s: corrupted record: %s\n",
221                                 __location__, nbt_name_string(rec, rec->name)));
222                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
223                 }
224
225                 modify_flags    = 0;
226                 modify_record   = False;
227                 delete_record   = False;
228
229                 switch (rec->state) {
230                 case WREPL_STATE_ACTIVE:
231                         DEBUG(0,("%s: corrupted record: %s\n",
232                                 __location__, nbt_name_string(rec, rec->name)));
233                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
234
235                 case WREPL_STATE_RELEASED:
236                         old_state       = "released";
237                         rec->state      = WREPL_STATE_TOMBSTONE;
238                         rec->expire_time= service->config.tombstone_timeout + now;
239                         modify_flags    = 0;
240                         modify_record   = True;
241                         break;
242
243                 case WREPL_STATE_TOMBSTONE:
244                         old_state       = "tombstone";
245                         if (!delete_tombstones) break;
246                         delete_record = True;
247                         break;
248
249                 case WREPL_STATE_RESERVED:
250                         DEBUG(0,("%s: corrupted record: %s\n",
251                                 __location__, nbt_name_string(rec, rec->name)));
252                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
253                 }
254
255                 if (modify_record) {
256                         action = "modify";
257                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
258                 } else if (delete_record) {
259                         action = "delete";
260                         ret = winsdb_delete(service->wins_db, rec);
261                 } else {
262                         action = "skip";
263                         ret = NBT_RCODE_OK;
264                 }
265
266                 if (ret != NBT_RCODE_OK) {
267                         DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
268                                 action, nbt_name_string(rec, rec->name), old_state, ret));
269                 } else {
270                         DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
271                                 action, nbt_name_string(rec, rec->name), old_state));
272                 }
273
274                 talloc_free(rec);
275         }
276
277         return NT_STATUS_OK;
278 }
279
280 struct verify_state {
281         struct messaging_context *msg_ctx;
282         struct wreplsrv_service *service;
283         struct winsdb_record *rec;
284         struct nbtd_proxy_wins_challenge r;
285 };
286
287 static void verify_handler(struct irpc_request *ireq)
288 {
289         struct verify_state *s = talloc_get_type(ireq->async.private,
290                                  struct verify_state);
291         struct winsdb_record *rec = s->rec;
292         const char *action;
293         const char *old_state = "active";
294         BOOL modify_record = False;
295         BOOL delete_record = False;
296         BOOL different = False;
297         int ret;
298         NTSTATUS status;
299         uint32_t i, j;
300
301         /*
302          * - if the name isn't present anymore remove our record
303          * - if the name is found and not a normal group check if the addresses match,
304          *   - if they don't match remove the record
305          *   - if they match do nothing
306          * - if an error happens do nothing
307          */
308         status = irpc_call_recv(ireq);
309         if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
310                 delete_record = True;
311         } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
312                 for (i=0; i < s->r.out.num_addrs; i++) {
313                         BOOL found = False;
314                         for (j=0; rec->addresses[j]; j++) {
315                                 if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
316                                         found = True;
317                                         break;
318                                 }
319                         }
320                         if (!found) {
321                                 different = True;
322                                 break;
323                         }
324                 }
325         } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
326                 if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
327                         different = True;
328                 }
329         }
330
331         if (different) {
332                 DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: deleting record\n",
333                         nbt_name_string(rec, rec->name), rec->wins_owner));
334                 delete_record = True;
335         } else if (NT_STATUS_IS_OK(status)) {
336                 rec->expire_time = time(NULL) + s->service->config.verify_interval;
337                 for (i=0; rec->addresses[i]; i++) {
338                         rec->addresses[i]->expire_time = rec->expire_time;
339                 }
340                 modify_record = True;
341         }
342
343         if (modify_record) {
344                 action = "modify";
345                 ret = winsdb_modify(s->service->wins_db, rec, 0);
346         } else if (delete_record) {
347                 action = "delete";
348                 ret = winsdb_delete(s->service->wins_db, rec);
349         } else {
350                 action = "skip";
351                 ret = NBT_RCODE_OK;
352         }
353
354         if (ret != NBT_RCODE_OK) {
355                 DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
356                         action, nbt_name_string(rec, rec->name), old_state, ret));
357         } else {
358                 DEBUG(4,("WINS scavenging: %s name: %s (replica:%s): %s: %s\n",
359                         action, nbt_name_string(rec, rec->name), old_state, rec->wins_owner, nt_errstr(status)));
360         }
361
362         talloc_free(s);
363 }
364
365 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
366 {
367         NTSTATUS status;
368         struct winsdb_record *rec = NULL;
369         struct ldb_result *res = NULL;
370         const char *owner_filter;
371         const char *filter;
372         uint32_t i;
373         int ret;
374         time_t now = time(NULL);
375         const char *now_timestr;
376         struct irpc_request *ireq;
377         struct verify_state *s;
378         uint32_t *nbt_servers;
379
380         nbt_servers = irpc_servers_byname(service->task->msg_ctx, "nbt_server");
381         if ((nbt_servers == NULL) || (nbt_servers[0] == 0)) {
382                 return NT_STATUS_INTERNAL_ERROR;
383         }
384
385         now_timestr = ldb_timestring(tmp_mem, now);
386         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
387         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
388                                              service->wins_db->local_owner);
389         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
390         filter = talloc_asprintf(tmp_mem,
391                                  "(&(!%s)(objectClass=winsRecord)"
392                                  "(recordState=%u)(expireTime<=%s)(!(isStatic=1)))",
393                                  owner_filter, WREPL_STATE_ACTIVE, now_timestr);
394         NT_STATUS_HAVE_NO_MEMORY(filter);
395         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
396         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
397         talloc_steal(tmp_mem, res);
398         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
399
400         for (i=0; i < res->count; i++) {
401                 /*
402                  * we pass '0' as 'now' here,
403                  * because we want to get the raw timestamps which are in the DB
404                  */
405                 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
406                 NT_STATUS_NOT_OK_RETURN(status);
407                 talloc_free(res->msgs[i]);
408
409                 if (rec->is_static) {
410                         DEBUG(0,("%s: corrupted record: %s\n",
411                                 __location__, nbt_name_string(rec, rec->name)));
412                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
413                 }
414
415                 if (rec->expire_time > now) {
416                         DEBUG(0,("%s: corrupted record: %s\n",
417                                 __location__, nbt_name_string(rec, rec->name)));
418                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
419                 }
420
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;
425                 }
426
427                 /* 
428                  * ask the owning wins server if the record still exists,
429                  * if not delete the record
430                  *
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
435                  */
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), rec->version));
438
439                 s = talloc_zero(tmp_mem, struct verify_state);
440                 NT_STATUS_HAVE_NO_MEMORY(s);
441                 s->msg_ctx      = service->task->msg_ctx;
442                 s->service      = service;
443                 s->rec          = talloc_steal(s, rec);
444
445                 s->r.in.name            = *rec->name;
446                 s->r.in.num_addrs       = 1;
447                 s->r.in.addrs           = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
448                 NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
449                 /* TODO: fix pidl to handle inline ipv4address arrays */
450                 s->r.in.addrs[0].addr   = rec->wins_owner;
451
452                 ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
453                                       irpc, NBTD_PROXY_WINS_CHALLENGE,
454                                       &s->r, s);
455                 NT_STATUS_HAVE_NO_MEMORY(ireq);
456
457                 ireq->async.fn          = verify_handler;
458                 ireq->async.private     = s;
459
460                 talloc_steal(service, s);
461         }
462
463         return NT_STATUS_OK;
464 }
465
466 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
467 {
468         NTSTATUS status;
469         TALLOC_CTX *tmp_mem;
470         BOOL skip_first_run = False;
471
472         if (!timeval_expired(&service->scavenging.next_run)) {
473                 return NT_STATUS_OK;
474         }
475
476         if (timeval_is_zero(&service->scavenging.next_run)) {
477                 skip_first_run = True;
478         }
479
480         service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
481         status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
482         NT_STATUS_NOT_OK_RETURN(status);
483
484         /*
485          * if it's the first time this functions is called (startup)
486          * the next_run is zero, in this case we should not do scavenging
487          */
488         if (skip_first_run) {
489                 return NT_STATUS_OK;
490         }
491
492         if (service->scavenging.processing) {
493                 return NT_STATUS_OK;
494         }
495
496         DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
497
498         tmp_mem = talloc_new(service);
499         service->scavenging.processing = True;
500         status = wreplsrv_scavenging_owned_records(service,tmp_mem);
501         service->scavenging.processing = False;
502         talloc_free(tmp_mem);
503         NT_STATUS_NOT_OK_RETURN(status);
504
505         tmp_mem = talloc_new(service);  
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);
511
512         tmp_mem = talloc_new(service);
513         service->scavenging.processing = True;
514         status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
515         service->scavenging.processing = False;
516         talloc_free(tmp_mem);
517         NT_STATUS_NOT_OK_RETURN(status);
518
519         DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
520
521         return NT_STATUS_OK;
522 }