r15956: fix warnings
[jelmer/samba4-debian.git] / source / nbt_server / wins / winsdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    WINS database routines
5
6    Copyright (C) Andrew Tridgell        2005
7    Copyright (C) Stefan Metzmacher      2005
8       
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "nbt_server/nbt_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "lib/ldb/include/ldb.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "librpc/gen_ndr/ndr_nbt.h"
30 #include "system/time.h"
31 #include "db_wrap.h"
32 #include "system/network.h"
33 #include "netif/netif.h"
34
35 uint64_t winsdb_get_maxVersion(struct winsdb_handle *h)
36 {
37         int ret;
38         struct ldb_context *ldb = h->ldb;
39         struct ldb_dn *dn;
40         struct ldb_result *res = NULL;
41         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
42         uint64_t maxVersion = 0;
43
44         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
45         if (!dn) goto failed;
46
47         /* find the record in the WINS database */
48         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, 
49                          NULL, NULL, &res);
50         if (ret != LDB_SUCCESS) goto failed;
51         talloc_steal(tmp_ctx, res);
52         if (res->count > 1) goto failed;
53
54         if (res->count == 1) {
55                 maxVersion = ldb_msg_find_uint64(res->msgs[0], "maxVersion", 0);
56         }
57
58 failed:
59         talloc_free(tmp_ctx);
60         return maxVersion;
61 }
62
63 /*
64  if newVersion == 0 return the old maxVersion + 1 and save it
65  if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
66 */
67 uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
68 {
69         int trans;
70         int ret;
71         struct ldb_dn *dn;
72         struct ldb_result *res = NULL;
73         struct ldb_message *msg = NULL;
74         struct ldb_context *wins_db = h->ldb;
75         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
76         uint64_t oldMaxVersion = 0;
77
78         trans = ldb_transaction_start(wins_db);
79         if (trans != LDB_SUCCESS) goto failed;
80
81         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
82         if (!dn) goto failed;
83
84         /* find the record in the WINS database */
85         ret = ldb_search(wins_db, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
86
87         if (ret != LDB_SUCCESS) goto failed;
88         if (res->count > 1) goto failed;
89
90         talloc_steal(tmp_ctx, res);
91
92         if (res->count == 1) {
93                 oldMaxVersion = ldb_msg_find_uint64(res->msgs[0], "maxVersion", 0);
94         }
95
96         if (newMaxVersion == 0) {
97                 newMaxVersion = oldMaxVersion + 1;
98         } else {
99                 newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
100         }
101
102         msg = ldb_msg_new(tmp_ctx);
103         if (!msg) goto failed;
104         msg->dn = dn;
105
106
107         ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
108         if (ret != 0) goto failed;
109         ret = ldb_msg_add_string(msg, "objectClass", "winsMaxVersion");
110         if (ret != 0) goto failed;
111         ret = ldb_msg_add_empty(msg, "maxVersion", LDB_FLAG_MOD_REPLACE);
112         if (ret != 0) goto failed;
113         ret = ldb_msg_add_fmt(msg, "maxVersion", "%llu", (long long)newMaxVersion);
114         if (ret != 0) goto failed;
115
116         ret = ldb_modify(wins_db, msg);
117         if (ret != 0) ret = ldb_add(wins_db, msg);
118         if (ret != 0) goto failed;
119
120         trans = ldb_transaction_commit(wins_db);
121         if (trans != LDB_SUCCESS) goto failed;
122
123         talloc_free(tmp_ctx);
124         return newMaxVersion;
125
126 failed:
127         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
128         talloc_free(tmp_ctx);
129         return 0;
130 }
131
132 uint64_t winsdb_get_seqnumber(struct winsdb_handle *h)
133 {
134         int ret;
135         struct ldb_context *ldb = h->ldb;
136         struct ldb_dn *dn;
137         struct ldb_result *res = NULL;
138         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
139         uint64_t seqnumber = 0;
140
141         dn = ldb_dn_explode(tmp_ctx, "@BASEINFO");
142         if (!dn) goto failed;
143
144         /* find the record in the WINS database */
145         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, 
146                          NULL, NULL, &res);
147         if (ret != LDB_SUCCESS) goto failed;
148         talloc_steal(tmp_ctx, res);
149         if (res->count > 1) goto failed;
150
151         if (res->count == 1) {
152                 seqnumber = ldb_msg_find_uint64(res->msgs[0], "sequenceNumber", 0);
153         }
154
155 failed:
156         talloc_free(tmp_ctx);
157         return seqnumber;
158 }
159
160 /*
161   return a DN for a nbt_name
162 */
163 static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct nbt_name *name)
164 {
165         struct ldb_dn *dn;
166
167         dn = ldb_dn_string_compose(mem_ctx, NULL, "type=0x%02X", name->type);
168         if (dn && name->name && *name->name) {
169                 dn = ldb_dn_string_compose(mem_ctx, dn, "name=%s", name->name);
170         }
171         if (dn && name->scope && *name->scope) {
172                 dn = ldb_dn_string_compose(mem_ctx, dn, "scope=%s", name->scope);
173         }
174         return dn;
175 }
176
177 static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
178 {
179         NTSTATUS status;
180         struct nbt_name *name;
181         uint32_t cur = 0;
182
183         name = talloc(mem_ctx, struct nbt_name);
184         if (!name) {
185                 status = NT_STATUS_NO_MEMORY;
186                 goto failed;
187         }
188
189         if (dn->comp_num > 3) {
190                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
191                 goto failed;
192         }
193
194         if (dn->comp_num > cur && strcasecmp("scope", dn->components[cur].name) == 0) {
195                 name->scope     = (const char *)talloc_steal(name, dn->components[cur].value.data);
196                 cur++;
197         } else {
198                 name->scope     = NULL;
199         }
200
201         if (dn->comp_num > cur && strcasecmp("name", dn->components[cur].name) == 0) {
202                 name->name      = (const char *)talloc_steal(name, dn->components[cur].value.data);
203                 cur++;
204         } else {
205                 name->name      = talloc_strdup(name, "");
206                 if (!name->name) {
207                         status = NT_STATUS_NO_MEMORY;
208                         goto failed;
209                 }
210         }
211
212         if (dn->comp_num > cur && strcasecmp("type", dn->components[cur].name) == 0) {
213                 name->type      = strtoul((char *)dn->components[cur].value.data, NULL, 0);
214                 cur++;
215         } else {
216                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
217                 goto failed;
218         }
219
220         *_name = name;
221         return NT_STATUS_OK;
222 failed:
223         talloc_free(name);
224         return status;
225 }
226
227 /*
228  decode the winsdb_addr("address") attribute:
229  "172.31.1.1" or 
230  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
231  are valid records
232 */
233 static NTSTATUS winsdb_addr_decode(struct winsdb_handle *h, struct winsdb_record *rec, struct ldb_val *val,
234                                    TALLOC_CTX *mem_ctx, struct winsdb_addr **_addr)
235 {
236         NTSTATUS status;
237         struct winsdb_addr *addr;
238         const char *address;
239         const char *wins_owner;
240         const char *expire_time;
241         char *p;
242
243         addr = talloc(mem_ctx, struct winsdb_addr);
244         if (!addr) {
245                 status = NT_STATUS_NO_MEMORY;
246                 goto failed;
247         }
248
249         address = (char *)val->data;
250
251         p = strchr(address, ';');
252         if (!p) {
253                 /* support old entries, with only the address */
254                 addr->address           = (const char *)talloc_steal(addr, val->data);
255                 addr->wins_owner        = talloc_reference(addr, rec->wins_owner);
256                 if (!addr->wins_owner) {
257                         status = NT_STATUS_NO_MEMORY;
258                         goto failed;
259                 }
260                 addr->expire_time       = rec->expire_time;
261                 *_addr = addr;
262                 return NT_STATUS_OK;
263         }
264
265         *p = '\0';p++;
266         addr->address = talloc_strdup(addr, address);
267         if (!addr->address) {
268                 status = NT_STATUS_NO_MEMORY;
269                 goto failed;
270         }
271
272         if (strncmp("winsOwner:", p, 10) != 0) {
273                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
274                 goto failed;
275         }
276         wins_owner = p + 10;
277         p = strchr(wins_owner, ';');
278         if (!p) {
279                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
280                 goto failed;
281         }
282
283         *p = '\0';p++;
284         if (strcmp(wins_owner, "0.0.0.0") == 0) {
285                 wins_owner = h->local_owner;
286         }
287         addr->wins_owner = talloc_strdup(addr, wins_owner);
288         if (!addr->wins_owner) {
289                 status = NT_STATUS_NO_MEMORY;
290                 goto failed;
291         }
292
293         if (strncmp("expireTime:", p, 11) != 0) {
294                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
295                 goto failed;
296         }
297
298         expire_time = p + 11;
299         p = strchr(expire_time, ';');
300         if (!p) {
301                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
302                 goto failed;
303         }
304
305         *p = '\0';p++;
306         addr->expire_time = ldb_string_to_time(expire_time);
307
308         *_addr = addr;
309         return NT_STATUS_OK;
310 failed:
311         talloc_free(addr);
312         return status;
313 }
314
315 /*
316  encode the winsdb_addr("address") attribute like this:
317  non-static record:
318  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
319  static record:
320  "172.31.1.1"
321 */
322 static int ldb_msg_add_winsdb_addr(struct ldb_message *msg, struct winsdb_record *rec,
323                                    const char *attr_name, struct winsdb_addr *addr)
324 {
325         struct ldb_val val;
326         const char *str;
327
328         if (rec->is_static) {
329                 str = talloc_strdup(msg, addr->address);
330                 if (!str) return -1;
331         } else {
332                 char *expire_time;
333                 expire_time = ldb_timestring(msg, addr->expire_time);
334                 if (!expire_time) return -1;
335                 str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s;",
336                                       addr->address, addr->wins_owner,
337                                       expire_time);
338                 talloc_free(expire_time);
339                 if (!str) return -1;
340         }
341
342         val.data = discard_const_p(uint8_t, str);
343         val.length = strlen(str);
344
345         return ldb_msg_add_value(msg, attr_name, &val);
346 }
347
348 struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
349 {
350         struct winsdb_addr **addresses;
351
352         addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
353         if (!addresses) return NULL;
354
355         addresses[0] = NULL;
356
357         return addresses;
358 }
359
360 static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr **p2, void *opaque)
361 {
362         struct winsdb_addr *a1 = talloc_get_type(*p1, struct winsdb_addr);
363         struct winsdb_addr *a2 = talloc_get_type(*p2, struct winsdb_addr);
364         struct winsdb_handle *h= talloc_get_type(opaque, struct winsdb_handle);
365         BOOL a1_owned = False;
366         BOOL a2_owned = False;
367
368         /*
369          * first the owned addresses with the newest to the oldest address
370          * then the replica addresses with the newest to the oldest address
371          */
372         if (a2->expire_time != a1->expire_time) {
373                 return a2->expire_time - a1->expire_time;
374         }
375
376         if (strcmp(a2->wins_owner, h->local_owner) == 0) {
377                 a2_owned = True;
378         }
379
380         if (strcmp(a1->wins_owner, h->local_owner) == 0) {
381                 a1_owned = True;
382         }
383
384         return a2_owned - a1_owned;
385 }
386
387 struct winsdb_addr **winsdb_addr_list_add(struct winsdb_handle *h, const struct winsdb_record *rec,
388                                           struct winsdb_addr **addresses, const char *address,
389                                           const char *wins_owner, time_t expire_time,
390                                           BOOL is_name_registration)
391 {
392         struct winsdb_addr *old_addr = NULL;
393         size_t len = 0;
394         size_t i;
395         BOOL found_old_replica = False;
396
397         /*
398          * count the addresses and maybe
399          * find an old entry for the new address
400          */
401         for (i=0; addresses[i]; i++) {
402                 if (old_addr) continue;
403                 if (strcmp(addresses[i]->address, address) == 0) {
404                         old_addr = addresses[i];
405                 }
406         }
407         len = i;
408
409         /*
410          * the address is already there
411          * and we can replace it
412          */
413         if (old_addr) {
414                 goto remove_old_addr;
415         }
416
417         /*
418          * if we don't have 25 addresses already,
419          * we can just add the new address
420          */
421         if (len < 25) {
422                 goto add_new_addr;
423         }
424
425         /*
426          * if we haven't found the address,
427          * and we have already have 25 addresses
428          * if so then we need to do the following:
429          * - if it isn't a name registration, then just ignore the new address
430          * - if it is a name registration, then first search for 
431          *   the oldest replica and if there's no replica address
432          *   search the oldest owned address
433          */
434         if (!is_name_registration) {
435                 return addresses;
436         }
437
438         /*
439          * find the oldest replica address, if there's no replica
440          * record at all, find the oldest owned address
441          */
442         for (i=0; addresses[i]; i++) {
443                 BOOL cur_is_replica = False;
444                 /* find out if the current address is a replica */
445                 if (strcmp(addresses[i]->wins_owner, h->local_owner) != 0) {
446                         cur_is_replica = True;
447                 }
448
449                 /*
450                  * if we already found a replica address and the current address
451                  * is not a replica, then skip it
452                  */
453                 if (found_old_replica && !cur_is_replica) continue;
454
455                 /*
456                  * if we found the first replica address, reset the address
457                  * that would be replaced
458                  */
459                 if (!found_old_replica && cur_is_replica) {
460                         found_old_replica = True;
461                         old_addr = addresses[i];
462                         continue;
463                 }
464
465                 /*
466                  * if the first address isn't a replica, just start with 
467                  * the first one
468                  */
469                 if (!old_addr) {
470                         old_addr = addresses[i];
471                         continue;
472                 }
473
474                 /*
475                  * see if we find an older address
476                  */
477                 if (addresses[i]->expire_time < old_addr->expire_time) {
478                         old_addr = addresses[i];
479                         continue;
480                 }
481         }
482
483 remove_old_addr:
484         winsdb_addr_list_remove(addresses, old_addr->address);
485         len --;
486
487 add_new_addr:
488         addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
489         if (!addresses) return NULL;
490
491         addresses[len] = talloc(addresses, struct winsdb_addr);
492         if (!addresses[len]) {
493                 talloc_free(addresses);
494                 return NULL;
495         }
496
497         addresses[len]->address = talloc_strdup(addresses[len], address);
498         if (!addresses[len]->address) {
499                 talloc_free(addresses);
500                 return NULL;
501         }
502
503         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
504         if (!addresses[len]->wins_owner) {
505                 talloc_free(addresses);
506                 return NULL;
507         }
508
509         addresses[len]->expire_time = expire_time;
510
511         addresses[len+1] = NULL;
512
513         ldb_qsort(addresses, len+1 , sizeof(addresses[0]), h, (ldb_qsort_cmp_fn_t)winsdb_addr_sort_list);
514
515         return addresses;
516 }
517
518 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
519 {
520         size_t i;
521
522         for (i=0; addresses[i]; i++) {
523                 if (strcmp(addresses[i]->address, address) == 0) {
524                         break;
525                 }
526         }
527
528         for (; addresses[i]; i++) {
529                 addresses[i] = addresses[i+1];
530         }
531
532         return;
533 }
534
535 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
536 {
537         size_t i;
538
539         for (i=0; addresses[i]; i++) {
540                 if (strcmp(addresses[i]->address, address) == 0) {
541                         return addresses[i];
542                 }
543         }
544
545         return NULL;
546 }
547
548 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
549 {
550         size_t i;
551         for (i=0; addresses[i]; i++);
552         return i;
553 }
554
555 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
556 {
557         size_t len = winsdb_addr_list_length(addresses);
558         const char **str_list=NULL;
559         size_t i;
560
561         for (i=0; i < len; i++) {
562                 str_list = str_list_add(str_list, addresses[i]->address);
563                 if (!str_list[i]) {
564                         return NULL;
565                 }
566         }
567         talloc_steal(mem_ctx, str_list);
568         return str_list;
569 }
570
571 /*
572   load a WINS entry from the database
573 */
574 NTSTATUS winsdb_lookup(struct winsdb_handle *h, 
575                        struct nbt_name *name,
576                        TALLOC_CTX *mem_ctx,
577                        struct winsdb_record **_rec)
578 {
579         NTSTATUS status;
580         struct ldb_result *res = NULL;
581         int ret;
582         struct winsdb_record *rec;
583         struct ldb_context *wins_db = h->ldb;
584         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
585         time_t now = time(NULL);
586
587         /* find the record in the WINS database */
588         ret = ldb_search(wins_db, winsdb_dn(tmp_ctx, name), LDB_SCOPE_BASE, 
589                          NULL, NULL, &res);
590
591         if (ret != LDB_SUCCESS || res->count > 1) {
592                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
593                 goto failed;
594         } else if (res->count== 0) {
595                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
596                 goto failed;
597         }
598
599         talloc_steal(tmp_ctx, res);
600
601         status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
602         if (!NT_STATUS_IS_OK(status)) goto failed;
603
604         talloc_steal(mem_ctx, rec);
605         talloc_free(tmp_ctx);
606         *_rec = rec;
607         return NT_STATUS_OK;
608
609 failed:
610         talloc_free(tmp_ctx);
611         return status;
612 }
613
614 NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
615 {
616         NTSTATUS status;
617         struct winsdb_record *rec;
618         struct ldb_message_element *el;
619         struct nbt_name *name;
620         uint32_t i, j, num_values;
621
622         rec = talloc(mem_ctx, struct winsdb_record);
623         if (rec == NULL) {
624                 status = NT_STATUS_NO_MEMORY;
625                 goto failed;
626         }
627
628         status = winsdb_nbt_name(rec, msg->dn, &name);
629         if (!NT_STATUS_IS_OK(status)) goto failed;
630
631         if (strlen(name->name) > 15) {
632                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
633                 goto failed;
634         }
635         if (name->scope && strlen(name->scope) > 238) {
636                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
637                 goto failed;
638         }
639
640         /* parse it into a more convenient winsdb_record structure */
641         rec->name               = name;
642         rec->type               = ldb_msg_find_int(msg, "recordType", WREPL_TYPE_UNIQUE);
643         rec->state              = ldb_msg_find_int(msg, "recordState", WREPL_STATE_RELEASED);
644         rec->node               = ldb_msg_find_int(msg, "nodeType", WREPL_NODE_B);
645         rec->is_static          = ldb_msg_find_int(msg, "isStatic", 0);
646         rec->expire_time        = ldb_string_to_time(ldb_msg_find_string(msg, "expireTime", NULL));
647         rec->version            = ldb_msg_find_uint64(msg, "versionID", 0);
648         rec->wins_owner         = ldb_msg_find_string(msg, "winsOwner", NULL);
649         rec->registered_by      = ldb_msg_find_string(msg, "registeredBy", NULL);
650         talloc_steal(rec, rec->wins_owner);
651         talloc_steal(rec, rec->registered_by);
652
653         if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
654                 rec->wins_owner = h->local_owner;
655         }
656
657         el = ldb_msg_find_element(msg, "address");
658         if (el) {
659                 num_values = el->num_values;
660         } else {
661                 num_values = 0;
662         }
663
664         if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
665                 if (num_values != 1) {
666                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
667                         goto failed;
668                 }
669         }
670         if (rec->state == WREPL_STATE_ACTIVE) {
671                 if (num_values < 1) {
672                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
673                         goto failed;
674                 }
675         }
676         if (num_values > 25) {
677                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
678                 goto failed;
679         }
680
681         /* see if it has already expired */
682         if (!rec->is_static &&
683             rec->expire_time <= now &&
684             rec->state == WREPL_STATE_ACTIVE) {
685                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
686                          nbt_name_string(mem_ctx, rec->name), timestring(mem_ctx, rec->expire_time)));
687                 rec->state = WREPL_STATE_RELEASED;
688         }
689
690         rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
691         if (rec->addresses == NULL) {
692                 status = NT_STATUS_NO_MEMORY;
693                 goto failed;
694         }
695
696         for (i=0,j=0;i<num_values;i++) {
697                 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
698                 if (!NT_STATUS_IS_OK(status)) goto failed;
699
700                 /*
701                  * the record isn't static and is active
702                  * then don't add the address if it's expired
703                  */
704                 if (!rec->is_static &&
705                     rec->addresses[j]->expire_time <= now &&
706                     rec->state == WREPL_STATE_ACTIVE) {
707                         DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n", 
708                                  rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
709                                  timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
710                         talloc_free(rec->addresses[j]);
711                         rec->addresses[j] = NULL;
712                         continue;
713                 }
714                 j++;
715         }
716         rec->addresses[j] = NULL;
717         num_values = j;
718
719         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
720                 rec->expire_time = get_time_t_max();
721                 for (i=0;rec->addresses[i];i++) {
722                         rec->addresses[i]->expire_time = rec->expire_time;
723                 }
724         }
725
726         if (rec->state == WREPL_STATE_ACTIVE) {
727                 if (num_values < 1) {
728                         DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n", 
729                                  nbt_name_string(mem_ctx, rec->name)));
730                         rec->state = WREPL_STATE_RELEASED;
731                 }
732         }
733
734         *_rec = rec;
735         return NT_STATUS_OK;
736 failed:
737         if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
738                 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_linearize(rec, msg->dn)));
739         }
740         talloc_free(rec);
741         return status;
742 }
743
744 /*
745   form a ldb_message from a winsdb_record
746 */
747 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
748                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
749 {
750         int i, ret=0;
751         size_t addr_count;
752         const char *expire_time;
753         struct ldb_message *msg = ldb_msg_new(mem_ctx);
754         if (msg == NULL) goto failed;
755
756         /* make sure we don't put in corrupted records */
757         addr_count = winsdb_addr_list_length(rec->addresses);
758         if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
759                 rec->state = WREPL_STATE_RELEASED;
760         }
761         if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
762                 rec->type = WREPL_TYPE_MHOMED;
763         }
764
765         expire_time = ldb_timestring(msg, rec->expire_time);
766         if (!expire_time) {
767                 goto failed;
768         }
769
770         msg->dn = winsdb_dn(msg, rec->name);
771         if (msg->dn == NULL) goto failed;
772         ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
773         if (rec->name->name && *rec->name->name) {
774                 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
775         }
776         if (rec->name->scope && *rec->name->scope) {
777                 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
778         }
779         ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
780         ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
781         ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
782         ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
783         ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
784         ret |= ldb_msg_add_empty(msg, "expireTime", 0);
785         if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
786                 ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
787         }
788         ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
789         ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
790         ret |= ldb_msg_add_empty(msg, "address", 0);
791         for (i=0;rec->addresses[i];i++) {
792                 ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
793         }
794         ret |= ldb_msg_add_empty(msg, "registeredBy", 0);
795         if (rec->registered_by) {
796                 ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
797                 if (ret != 0) goto failed;
798         }
799         return msg;
800
801 failed:
802         talloc_free(msg);
803         return NULL;
804 }
805
806 /*
807   save a WINS record into the database
808 */
809 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
810 {
811         struct ldb_message *msg;
812         struct ldb_context *wins_db = h->ldb;
813         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
814         int trans = -1;
815         int ret = 0;
816
817         trans = ldb_transaction_start(wins_db);
818         if (trans != LDB_SUCCESS) goto failed;
819
820         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
821                 /* passing '0' means auto-allocate a new one */
822                 rec->version = winsdb_set_maxVersion(h, 0);
823                 if (rec->version == 0) goto failed;
824         }
825         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
826                 rec->wins_owner = h->local_owner;
827         }
828
829         msg = winsdb_message(wins_db, rec, tmp_ctx);
830         if (msg == NULL) goto failed;
831         ret = ldb_add(wins_db, msg);
832         if (ret != 0) goto failed;
833
834         trans = ldb_transaction_commit(wins_db);
835         if (trans != LDB_SUCCESS) goto failed;
836
837         wins_hook(h, rec, WINS_HOOK_ADD);
838
839         talloc_free(tmp_ctx);
840         return NBT_RCODE_OK;
841
842 failed:
843         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
844         talloc_free(tmp_ctx);
845         return NBT_RCODE_SVR;
846 }
847
848
849 /*
850   modify a WINS record in the database
851 */
852 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
853 {
854         struct ldb_message *msg;
855         struct ldb_context *wins_db = h->ldb;
856         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
857         int trans;
858         int ret;
859         int i;
860
861         trans = ldb_transaction_start(wins_db);
862         if (trans != LDB_SUCCESS) goto failed;
863
864         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
865                 /* passing '0' means auto-allocate a new one */
866                 rec->version = winsdb_set_maxVersion(h, 0);
867                 if (rec->version == 0) goto failed;
868         }
869         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
870                 rec->wins_owner = h->local_owner;
871         }
872
873         msg = winsdb_message(wins_db, rec, tmp_ctx);
874         if (msg == NULL) goto failed;
875
876         for (i=0;i<msg->num_elements;i++) {
877                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
878         }
879
880         ret = ldb_modify(wins_db, msg);
881         if (ret != 0) goto failed;
882
883         trans = ldb_transaction_commit(wins_db);
884         if (trans != LDB_SUCCESS) goto failed;
885
886         wins_hook(h, rec, WINS_HOOK_MODIFY);
887
888         talloc_free(tmp_ctx);
889         return NBT_RCODE_OK;
890
891 failed:
892         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
893         talloc_free(tmp_ctx);
894         return NBT_RCODE_SVR;
895 }
896
897
898 /*
899   delete a WINS record from the database
900 */
901 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
902 {
903         struct ldb_context *wins_db = h->ldb;
904         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
905         const struct ldb_dn *dn;
906         int trans;
907         int ret;
908
909         trans = ldb_transaction_start(wins_db);
910         if (trans != LDB_SUCCESS) goto failed;
911
912         dn = winsdb_dn(tmp_ctx, rec->name);
913         if (dn == NULL) goto failed;
914
915         ret = ldb_delete(wins_db, dn);
916         if (ret != 0) goto failed;
917
918         trans = ldb_transaction_commit(wins_db);
919         if (trans != LDB_SUCCESS) goto failed;
920
921         wins_hook(h, rec, WINS_HOOK_DELETE);
922
923         talloc_free(tmp_ctx);
924         return NBT_RCODE_OK;
925
926 failed:
927         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
928         talloc_free(tmp_ctx);
929         return NBT_RCODE_SVR;
930 }
931
932 static BOOL winsdb_check_or_add_module_list(struct winsdb_handle *h)
933 {
934         int trans;
935         int ret;
936         struct ldb_dn *dn;
937         struct ldb_result *res = NULL;
938         struct ldb_message *msg = NULL;
939         TALLOC_CTX *tmp_ctx = talloc_new(h);
940         unsigned int flags = 0;
941
942         trans = ldb_transaction_start(h->ldb);
943         if (trans != LDB_SUCCESS) goto failed;
944
945         /* check if we have a special @MODULES record already */
946         dn = ldb_dn_explode(tmp_ctx, "@MODULES");
947         if (!dn) goto failed;
948
949         /* find the record in the WINS database */
950         ret = ldb_search(h->ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
951         if (ret != LDB_SUCCESS) goto failed;
952         talloc_steal(tmp_ctx, res);
953
954         if (res->count > 0) goto skip;
955
956         /* if there's no record, add one */
957         msg = ldb_msg_new(tmp_ctx);
958         if (!msg) goto failed;
959         msg->dn = dn;
960
961         ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
962         if (ret != 0) goto failed;
963
964         ret = ldb_add(h->ldb, msg);
965         if (ret != 0) goto failed;
966
967         trans = ldb_transaction_commit(h->ldb);
968         if (trans != LDB_SUCCESS) goto failed;
969
970         /* close and reopen the database, with the modules */
971         trans = LDB_ERR_OTHER;
972         talloc_free(h->ldb);
973         h->ldb = NULL;
974
975         if (lp_parm_bool(-1,"winsdb", "nosync", False)) {
976                 flags |= LDB_FLG_NOSYNC;
977         }
978
979         h->ldb = ldb_wrap_connect(h, lock_path(h, lp_wins_url()),
980                                   NULL, NULL, flags, NULL);
981         if (!h->ldb) goto failed;
982
983         talloc_free(tmp_ctx);
984         return True;
985
986 skip:
987         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
988         talloc_free(tmp_ctx);
989         return True;
990
991 failed:
992         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
993         talloc_free(tmp_ctx);
994         return False;
995 }
996
997 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx, enum winsdb_handle_caller caller)
998 {
999         struct winsdb_handle *h = NULL;
1000         const char *owner;
1001         unsigned int flags = 0;
1002         BOOL ret;
1003         int ldb_err;
1004
1005         h = talloc(mem_ctx, struct winsdb_handle);
1006         if (!h) return NULL;
1007
1008         if (lp_parm_bool(-1,"winsdb", "nosync", False)) {
1009                 flags |= LDB_FLG_NOSYNC;
1010         }
1011
1012         h->ldb = ldb_wrap_connect(h, lock_path(h, lp_wins_url()),
1013                                   NULL, NULL, flags, NULL);
1014         if (!h->ldb) goto failed;       
1015
1016         h->caller = caller;
1017
1018         owner = lp_parm_string(-1, "winsdb", "local_owner");
1019         if (!owner) {
1020                 owner = iface_n_ip(0);
1021         }
1022
1023         h->local_owner = talloc_strdup(h, owner);
1024         if (!h->local_owner) goto failed;
1025
1026         /* make sure the module list is available and used */
1027         ret = winsdb_check_or_add_module_list(h);
1028         if (!ret) goto failed;
1029
1030         ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1031         if (ldb_err != LDB_SUCCESS) goto failed;
1032
1033         return h;
1034 failed:
1035         talloc_free(h);
1036         return NULL;
1037 }