r12434: implement database scavenging, the only missing part is the verifying of...
[samba.git] / source / wrepl_server / wrepl_server.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 "dlinklist.h"
25 #include "lib/events/events.h"
26 #include "lib/socket/socket.h"
27 #include "smbd/service_task.h"
28 #include "smbd/service_stream.h"
29 #include "lib/messaging/irpc.h"
30 #include "librpc/gen_ndr/ndr_winsrepl.h"
31 #include "wrepl_server/wrepl_server.h"
32 #include "nbt_server/wins/winsdb.h"
33 #include "ldb/include/ldb.h"
34 #include "ldb/include/ldb_errors.h"
35
36 static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx)
37 {
38         return ldb_wrap_connect(mem_ctx, private_path(mem_ctx, lp_wins_config_url()),
39                                 system_session(mem_ctx), NULL, 0, NULL);
40 }
41
42 /*
43   open winsdb
44 */
45 static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service)
46 {
47         service->wins_db     = winsdb_connect(service);
48         if (!service->wins_db) {
49                 return NT_STATUS_INTERNAL_DB_ERROR;
50         }
51
52         service->config.ldb = wins_config_db_connect(service);
53         if (!service->config.ldb) {
54                 return NT_STATUS_INTERNAL_DB_ERROR;
55         }
56
57         /* the default renew interval is 6 days */
58         service->config.renew_interval    = lp_parm_int(-1,"wreplsrv","renew_interval", 6*24*60*60);
59
60         /* the default tombstone (extinction) interval is 6 days */
61         service->config.tombstone_interval= lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
62
63         /* the default tombstone (extinction) timeout is 1 day */
64         service->config.tombstone_timeout = lp_parm_int(-1,"wreplsrv","tombstone_timeout", 1*24*60*60);
65
66         /* the default tombstone extra timeout is 3 days */
67         service->config.tombstone_extra_timeout = lp_parm_int(-1,"wreplsrv","tombstone_extra_timeout", 3*24*60*60);
68
69         /* the default verify interval is 24 days */
70         service->config.verify_interval   = lp_parm_int(-1,"wreplsrv","verify_interval", 24*24*60*60);
71
72         /* the default scavenging interval is 'renew_interval/2' */
73         service->config.scavenging_interval=lp_parm_int(-1,"wreplsrv","scavenging_interval",
74                                                         service->config.renew_interval/2);
75
76         /* the maximun interval to the next periodic processing event */
77         service->config.periodic_interval = lp_parm_int(-1,"wreplsrv","periodic_interval", 60);
78
79         return NT_STATUS_OK;
80 }
81
82 struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, const char *peer_addr)
83 {
84         struct wreplsrv_partner *cur;
85
86         for (cur = service->partners; cur; cur = cur->next) {
87                 if (strcmp(cur->address, peer_addr) == 0) {
88                         return cur;
89                 }
90         }
91
92         return NULL;
93 }
94
95 /*
96   load our replication partners
97 */
98 static NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service)
99 {
100         struct ldb_result *res = NULL;
101         int ret;
102         TALLOC_CTX *tmp_ctx = talloc_new(service);
103         int i;
104
105         /* find the record in the WINS database */
106         ret = ldb_search(service->config.ldb, ldb_dn_explode(tmp_ctx, "CN=PARTNERS"), LDB_SCOPE_SUBTREE,
107                          "(objectClass=wreplPartner)", NULL, &res);
108         if (ret != LDB_SUCCESS) goto failed;
109         talloc_steal(tmp_ctx, res);
110         if (res->count == 0) goto done;
111
112         for (i=0; i < res->count; i++) {
113                 struct wreplsrv_partner *partner;
114
115                 partner = talloc_zero(service, struct wreplsrv_partner);
116                 if (partner == NULL) goto failed;
117
118                 partner->service                = service;
119                 partner->address                = ldb_msg_find_string(res->msgs[i], "address", NULL);
120                 if (!partner->address) goto failed;
121                 partner->name                   = ldb_msg_find_string(res->msgs[i], "name", partner->address);
122                 partner->type                   = ldb_msg_find_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH);
123                 partner->pull.interval          = ldb_msg_find_uint(res->msgs[i], "pullInterval",
124                                                                     WINSREPL_DEFAULT_PULL_INTERVAL);
125                 partner->pull.retry_interval    = ldb_msg_find_uint(res->msgs[i], "pullRetryInterval",
126                                                                     WINSREPL_DEFAULT_PULL_RETRY_INTERVAL);
127                 partner->our_address            = ldb_msg_find_string(res->msgs[i], "ourAddress", NULL);
128                 partner->push.change_count      = ldb_msg_find_uint(res->msgs[i], "pushChangeCount",
129                                                                     WINSREPL_DEFAULT_PUSH_CHANGE_COUNT);
130                 partner->push.use_inform        = ldb_msg_find_uint(res->msgs[i], "pushUseInform", False);
131
132                 talloc_steal(partner, partner->address);
133                 talloc_steal(partner, partner->name);
134                 talloc_steal(partner, partner->our_address);
135
136                 DLIST_ADD(service->partners, partner);
137         }
138 done:
139         talloc_free(tmp_ctx);
140         return NT_STATUS_OK;
141 failed:
142         talloc_free(tmp_ctx);
143         return NT_STATUS_FOOBAR;
144 }
145
146 BOOL wreplsrv_is_our_address(struct wreplsrv_service *service, const char *address)
147 {
148         const char *our_address;
149
150         if (lp_interfaces() && lp_bind_interfaces_only()) {
151                 int num_interfaces = iface_count();
152                 int i;
153                 for(i = 0; i < num_interfaces; i++) {
154                         our_address = iface_n_ip(i);
155                         if (strcasecmp(our_address, address) == 0) {
156                                 return True;
157                         }
158                 }
159         } else {
160                 our_address = lp_socket_address();
161                 if (strcasecmp(our_address, address) == 0) {
162                         return True;
163                 }
164         }
165
166         return False;
167 }
168
169 uint64_t wreplsrv_local_max_version(struct wreplsrv_service *service)
170 {
171         int ret;
172         struct ldb_context *ldb = service->wins_db;
173         struct ldb_dn *dn;
174         struct ldb_result *res = NULL;
175         TALLOC_CTX *tmp_ctx = talloc_new(service);
176         uint64_t maxVersion = 0;
177
178         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
179         if (!dn) goto failed;
180
181         /* find the record in the WINS database */
182         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, 
183                          NULL, NULL, &res);
184         if (ret != LDB_SUCCESS) goto failed;
185         talloc_steal(tmp_ctx, res);
186         if (res->count > 1) goto failed;
187
188         if (res->count == 1) {
189                 maxVersion = ldb_msg_find_uint64(res->msgs[0], "maxVersion", 0);
190         }
191
192 failed:
193         talloc_free(tmp_ctx);
194         return maxVersion;
195 }
196
197 NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service,
198                                    TALLOC_CTX *mem_ctx,
199                                    struct wrepl_table *table_out,
200                                    const char *our_ip,
201                                    const char *initiator,
202                                    BOOL full_table)
203 {
204         struct wreplsrv_owner *cur;
205         uint64_t local_max_version;
206         uint32_t i = 0;
207
208         table_out->partner_count        = 0;
209         table_out->partners             = NULL;
210         table_out->initiator            = initiator;
211
212         local_max_version = wreplsrv_local_max_version(service);
213         if (local_max_version > 0) {
214                 table_out->partner_count++;
215         }
216
217         for (cur = service->table; full_table && cur; cur = cur->next) {
218                 table_out->partner_count++;
219         }
220
221         table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count);
222         NT_STATUS_HAVE_NO_MEMORY(table_out->partners);
223
224         if (local_max_version > 0) {
225                 table_out->partners[i].address          = our_ip;
226                 table_out->partners[i].min_version      = 0;
227                 table_out->partners[i].max_version      = local_max_version;
228                 table_out->partners[i].type             = 1;
229                 i++;
230         }
231
232         for (cur = service->table; full_table && cur; cur = cur->next) {
233                 table_out->partners[i] = cur->owner;
234                 i++;
235         }
236
237         return NT_STATUS_OK;
238 }
239
240 struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_owner *table, const char *wins_owner)
241 {
242         struct wreplsrv_owner *cur;
243
244         for (cur = table; cur; cur = cur->next) {
245                 if (strcmp(cur->owner.address, wins_owner) == 0) {
246                         return cur;
247                 }
248         }
249
250         return NULL;
251 }
252
253 /*
254  update the wins_owner_table max_version, if the given version is the highest version
255  if no entry for the wins_owner exists yet, create one
256 */
257 NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service,
258                             TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table,
259                             const char *wins_owner, uint64_t version)
260 {
261         struct wreplsrv_owner *table = *_table;
262         struct wreplsrv_owner *cur;
263
264         if (strcmp(WINSDB_OWNER_LOCAL, wins_owner) == 0) {
265                 return NT_STATUS_OK;
266         }
267
268         cur = wreplsrv_find_owner(table, wins_owner);
269
270         /* if it doesn't exists yet, create one */
271         if (!cur) {
272                 cur = talloc_zero(mem_ctx, struct wreplsrv_owner);
273                 NT_STATUS_HAVE_NO_MEMORY(cur);
274
275                 cur->owner.address      = talloc_strdup(cur, wins_owner);
276                 NT_STATUS_HAVE_NO_MEMORY(cur->owner.address);
277                 cur->owner.min_version  = 0;
278                 cur->owner.max_version  = 0;
279                 cur->owner.type         = 1; /* don't know why this is always 1 */
280
281                 cur->partner            = wreplsrv_find_partner(service, wins_owner);
282
283                 DLIST_ADD(table, cur);
284                 *_table = table;
285         }
286
287         /* the min_version is always 0 here, and won't be updated */
288
289         /* if the given version is higher the then current nax_version, update */
290         if (cur->owner.max_version < version) {
291                 cur->owner.max_version = version;
292         }
293
294         return NT_STATUS_OK;
295 }
296
297 /*
298   load the partner table
299 */
300 static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service)
301 {
302         struct ldb_result *res = NULL;
303         int ret;
304         NTSTATUS status;
305         TALLOC_CTX *tmp_ctx = talloc_new(service);
306         int i;
307         const char *wins_owner;
308         uint64_t version;
309         const char * const attrs[] = {
310                 "winsOwner",
311                 "versionID",
312                 NULL
313         };
314
315         /* find the record in the WINS database */
316         ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE,
317                          "(objectClass=winsRecord)", attrs, &res);
318         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
319         if (ret != LDB_SUCCESS) goto failed;
320         talloc_steal(tmp_ctx, res);
321         if (res->count == 0) goto done;
322
323         for (i=0; i < res->count; i++) {
324                 wins_owner     = ldb_msg_find_string(res->msgs[i], "winsOwner", NULL);
325                 version        = ldb_msg_find_uint64(res->msgs[i], "versionID", 0);
326
327                 if (wins_owner) { 
328                         status = wreplsrv_add_table(service,
329                                                     service, &service->table,
330                                                     wins_owner, version);
331                         if (!NT_STATUS_IS_OK(status)) goto failed;
332                 }
333                 talloc_free(res->msgs[i]);
334
335                 /* TODO: what's abut the per address owners? */
336         }
337 done:
338         talloc_free(tmp_ctx);
339         return NT_STATUS_OK;
340 failed:
341         talloc_free(tmp_ctx);
342         return status;
343 }
344
345 /*
346   setup our replication partners
347 */
348 static NTSTATUS wreplsrv_setup_partners(struct wreplsrv_service *service)
349 {
350         NTSTATUS status;
351
352         status = wreplsrv_load_partners(service);
353         NT_STATUS_NOT_OK_RETURN(status);
354
355         status = wreplsrv_load_table(service);
356         NT_STATUS_NOT_OK_RETURN(status);
357
358         return NT_STATUS_OK;
359 }
360
361 /*
362   startup the wrepl task
363 */
364 static void wreplsrv_task_init(struct task_server *task)
365 {
366         NTSTATUS status;
367         struct wreplsrv_service *service;
368
369         service = talloc_zero(task, struct wreplsrv_service);
370         if (!service) {
371                 task_server_terminate(task, "wreplsrv_task_init: out of memory");
372                 return;
373         }
374         service->task           = task;
375         service->startup_time   = timeval_current();
376         task->private           = service;
377
378         /*
379          * setup up all partners, and open the winsdb
380          */
381         status = wreplsrv_open_winsdb(service);
382         if (!NT_STATUS_IS_OK(status)) {
383                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_open_winsdb() failed");
384                 return;
385         }
386
387         /*
388          * setup timed events for each partner we want to pull from
389          */
390         status = wreplsrv_setup_partners(service);
391         if (!NT_STATUS_IS_OK(status)) {
392                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_partners() failed");
393                 return;
394         }
395
396         /* 
397          * setup listen sockets, so we can anwser requests from our partners,
398          * which pull from us
399          */
400         status = wreplsrv_setup_sockets(service);
401         if (!NT_STATUS_IS_OK(status)) {
402                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_sockets() failed");
403                 return;
404         }
405
406         status = wreplsrv_setup_periodic(service);
407         if (!NT_STATUS_IS_OK(status)) {
408                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed");
409                 return;
410         }
411
412         irpc_add_name(task->msg_ctx, "wrepl_server");
413 }
414
415 /*
416   initialise the WREPL server
417  */
418 static NTSTATUS wreplsrv_init(struct event_context *event_ctx, const struct model_ops *model_ops)
419 {
420         if (!lp_wins_support()) {
421                 return NT_STATUS_OK;
422         }
423
424         return task_server_startup(event_ctx, model_ops, wreplsrv_task_init);
425 }
426
427 /*
428   register ourselves as a available server
429 */
430 NTSTATUS server_service_wrepl_init(void)
431 {
432         return register_server_service("wrepl", wreplsrv_init);
433 }