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