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