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