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