r13117: the caller may reference the winsdb_addr struct,
[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 "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
524         for (; addresses[i]; i++) {
525                 addresses[i] = addresses[i+1];
526         }
527
528         return;
529 }
530
531 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
532 {
533         size_t i;
534
535         for (i=0; addresses[i]; i++) {
536                 if (strcmp(addresses[i]->address, address) == 0) {
537                         return addresses[i];
538                 }
539         }
540
541         return NULL;
542 }
543
544 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
545 {
546         size_t i;
547         for (i=0; addresses[i]; i++);
548         return i;
549 }
550
551 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
552 {
553         size_t len = winsdb_addr_list_length(addresses);
554         const char **str_list=NULL;
555         size_t i;
556
557         for (i=0; i < len; i++) {
558                 str_list = str_list_add(str_list, addresses[i]->address);
559                 if (!str_list[i]) {
560                         return NULL;
561                 }
562         }
563         talloc_steal(mem_ctx, str_list);
564         return str_list;
565 }
566
567 /*
568   load a WINS entry from the database
569 */
570 NTSTATUS winsdb_lookup(struct winsdb_handle *h, 
571                        struct nbt_name *name,
572                        TALLOC_CTX *mem_ctx,
573                        struct winsdb_record **_rec)
574 {
575         NTSTATUS status;
576         struct ldb_result *res = NULL;
577         int ret;
578         struct winsdb_record *rec;
579         struct ldb_context *wins_db = h->ldb;
580         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
581         time_t now = time(NULL);
582
583         /* find the record in the WINS database */
584         ret = ldb_search(wins_db, winsdb_dn(tmp_ctx, name), LDB_SCOPE_BASE, 
585                          NULL, NULL, &res);
586
587         if (ret != LDB_SUCCESS || res->count > 1) {
588                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
589                 goto failed;
590         } else if (res->count== 0) {
591                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
592                 goto failed;
593         }
594
595         talloc_steal(tmp_ctx, res);
596
597         status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
598         if (!NT_STATUS_IS_OK(status)) goto failed;
599
600         talloc_steal(mem_ctx, rec);
601         talloc_free(tmp_ctx);
602         *_rec = rec;
603         return NT_STATUS_OK;
604
605 failed:
606         talloc_free(tmp_ctx);
607         return status;
608 }
609
610 NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
611 {
612         NTSTATUS status;
613         struct winsdb_record *rec;
614         struct ldb_message_element *el;
615         struct nbt_name *name;
616         uint32_t i, j, num_values;
617
618         rec = talloc(mem_ctx, struct winsdb_record);
619         if (rec == NULL) {
620                 status = NT_STATUS_NO_MEMORY;
621                 goto failed;
622         }
623
624         status = winsdb_nbt_name(rec, msg->dn, &name);
625         if (!NT_STATUS_IS_OK(status)) goto failed;
626
627         if (strlen(name->name) > 15) {
628                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
629                 goto failed;
630         }
631         if (name->scope && strlen(name->scope) > 238) {
632                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
633                 goto failed;
634         }
635
636         /* parse it into a more convenient winsdb_record structure */
637         rec->name               = name;
638         rec->type               = ldb_msg_find_int(msg, "recordType", WREPL_TYPE_UNIQUE);
639         rec->state              = ldb_msg_find_int(msg, "recordState", WREPL_STATE_RELEASED);
640         rec->node               = ldb_msg_find_int(msg, "nodeType", WREPL_NODE_B);
641         rec->is_static          = ldb_msg_find_int(msg, "isStatic", 0);
642         rec->expire_time        = ldb_string_to_time(ldb_msg_find_string(msg, "expireTime", NULL));
643         rec->version            = ldb_msg_find_uint64(msg, "versionID", 0);
644         rec->wins_owner         = ldb_msg_find_string(msg, "winsOwner", NULL);
645         rec->registered_by      = ldb_msg_find_string(msg, "registeredBy", NULL);
646         talloc_steal(rec, rec->wins_owner);
647         talloc_steal(rec, rec->registered_by);
648
649         if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
650                 rec->wins_owner = h->local_owner;
651         }
652
653         el = ldb_msg_find_element(msg, "address");
654         if (el) {
655                 num_values = el->num_values;
656         } else {
657                 num_values = 0;
658         }
659
660         if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
661                 if (num_values != 1) {
662                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
663                         goto failed;
664                 }
665         }
666         if (rec->state == WREPL_STATE_ACTIVE) {
667                 if (num_values < 1) {
668                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
669                         goto failed;
670                 }
671         }
672         if (num_values > 25) {
673                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
674                 goto failed;
675         }
676
677         /* see if it has already expired */
678         if (!rec->is_static &&
679             rec->expire_time <= now &&
680             rec->state == WREPL_STATE_ACTIVE) {
681                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
682                          nbt_name_string(mem_ctx, rec->name), timestring(mem_ctx, rec->expire_time)));
683                 rec->state = WREPL_STATE_RELEASED;
684         }
685
686         rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
687         if (rec->addresses == NULL) {
688                 status = NT_STATUS_NO_MEMORY;
689                 goto failed;
690         }
691
692         for (i=0,j=0;i<num_values;i++) {
693                 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
694                 if (!NT_STATUS_IS_OK(status)) goto failed;
695
696                 /*
697                  * the record isn't static and is active
698                  * then don't add the address if it's expired
699                  */
700                 if (!rec->is_static &&
701                     rec->addresses[j]->expire_time <= now &&
702                     rec->state == WREPL_STATE_ACTIVE) {
703                         DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n", 
704                                  rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
705                                  timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
706                         talloc_free(rec->addresses[j]);
707                         rec->addresses[j] = NULL;
708                         continue;
709                 }
710                 j++;
711         }
712         rec->addresses[j] = NULL;
713         num_values = j;
714
715         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
716                 rec->expire_time = get_time_t_max();
717                 for (i=0;rec->addresses[i];i++) {
718                         rec->addresses[i]->expire_time = rec->expire_time;
719                 }
720         }
721
722         if (rec->state == WREPL_STATE_ACTIVE) {
723                 if (num_values < 1) {
724                         DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n", 
725                                  nbt_name_string(mem_ctx, rec->name)));
726                         rec->state = WREPL_STATE_RELEASED;
727                 }
728         }
729
730         *_rec = rec;
731         return NT_STATUS_OK;
732 failed:
733         if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
734                 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_linearize(rec, msg->dn)));
735         }
736         talloc_free(rec);
737         return status;
738 }
739
740 /*
741   form a ldb_message from a winsdb_record
742 */
743 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
744                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
745 {
746         int i, ret=0;
747         size_t addr_count;
748         const char *expire_time;
749         struct ldb_message *msg = ldb_msg_new(mem_ctx);
750         if (msg == NULL) goto failed;
751
752         /* make sure we don't put in corrupted records */
753         addr_count = winsdb_addr_list_length(rec->addresses);
754         if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
755                 rec->state = WREPL_STATE_RELEASED;
756         }
757         if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
758                 rec->type = WREPL_TYPE_MHOMED;
759         }
760
761         expire_time = ldb_timestring(msg, rec->expire_time);
762         if (!expire_time) {
763                 goto failed;
764         }
765
766         msg->dn = winsdb_dn(msg, rec->name);
767         if (msg->dn == NULL) goto failed;
768         ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
769         if (rec->name->name && *rec->name->name) {
770                 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
771         }
772         if (rec->name->scope && *rec->name->scope) {
773                 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
774         }
775         ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
776         ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
777         ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
778         ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
779         ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
780         ret |= ldb_msg_add_empty(msg, "expireTime", 0);
781         if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
782                 ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
783         }
784         ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
785         ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
786         ret |= ldb_msg_add_empty(msg, "address", 0);
787         for (i=0;rec->addresses[i];i++) {
788                 ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
789         }
790         ret |= ldb_msg_add_empty(msg, "registeredBy", 0);
791         if (rec->registered_by) {
792                 ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
793                 if (ret != 0) goto failed;
794         }
795         return msg;
796
797 failed:
798         talloc_free(msg);
799         return NULL;
800 }
801
802 /*
803   save a WINS record into the database
804 */
805 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
806 {
807         struct ldb_message *msg;
808         struct ldb_context *wins_db = h->ldb;
809         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
810         int trans = -1;
811         int ret = 0;
812
813         trans = ldb_transaction_start(wins_db);
814         if (trans != LDB_SUCCESS) goto failed;
815
816         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
817                 /* passing '0' means auto-allocate a new one */
818                 rec->version = winsdb_set_maxVersion(h, 0);
819                 if (rec->version == 0) goto failed;
820         }
821         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
822                 rec->wins_owner = h->local_owner;
823         }
824
825         msg = winsdb_message(wins_db, rec, tmp_ctx);
826         if (msg == NULL) goto failed;
827         ret = ldb_add(wins_db, msg);
828         if (ret != 0) goto failed;
829
830         trans = ldb_transaction_commit(wins_db);
831         if (trans != LDB_SUCCESS) goto failed;
832
833         wins_hook(h, rec, WINS_HOOK_ADD);
834
835         talloc_free(tmp_ctx);
836         return NBT_RCODE_OK;
837
838 failed:
839         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
840         talloc_free(tmp_ctx);
841         return NBT_RCODE_SVR;
842 }
843
844
845 /*
846   modify a WINS record in the database
847 */
848 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
849 {
850         struct ldb_message *msg;
851         struct ldb_context *wins_db = h->ldb;
852         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
853         int trans;
854         int ret;
855         int i;
856
857         trans = ldb_transaction_start(wins_db);
858         if (trans != LDB_SUCCESS) goto failed;
859
860         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
861                 /* passing '0' means auto-allocate a new one */
862                 rec->version = winsdb_set_maxVersion(h, 0);
863                 if (rec->version == 0) goto failed;
864         }
865         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
866                 rec->wins_owner = h->local_owner;
867         }
868
869         msg = winsdb_message(wins_db, rec, tmp_ctx);
870         if (msg == NULL) goto failed;
871
872         for (i=0;i<msg->num_elements;i++) {
873                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
874         }
875
876         ret = ldb_modify(wins_db, msg);
877         if (ret != 0) goto failed;
878
879         trans = ldb_transaction_commit(wins_db);
880         if (trans != LDB_SUCCESS) goto failed;
881
882         wins_hook(h, rec, WINS_HOOK_MODIFY);
883
884         talloc_free(tmp_ctx);
885         return NBT_RCODE_OK;
886
887 failed:
888         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
889         talloc_free(tmp_ctx);
890         return NBT_RCODE_SVR;
891 }
892
893
894 /*
895   delete a WINS record from the database
896 */
897 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
898 {
899         struct ldb_context *wins_db = h->ldb;
900         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
901         const struct ldb_dn *dn;
902         int trans;
903         int ret;
904
905         trans = ldb_transaction_start(wins_db);
906         if (trans != LDB_SUCCESS) goto failed;
907
908         dn = winsdb_dn(tmp_ctx, rec->name);
909         if (dn == NULL) goto failed;
910
911         ret = ldb_delete(wins_db, dn);
912         if (ret != 0) goto failed;
913
914         trans = ldb_transaction_commit(wins_db);
915         if (trans != LDB_SUCCESS) goto failed;
916
917         wins_hook(h, rec, WINS_HOOK_DELETE);
918
919         talloc_free(tmp_ctx);
920         return NBT_RCODE_OK;
921
922 failed:
923         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
924         talloc_free(tmp_ctx);
925         return NBT_RCODE_SVR;
926 }
927
928 static BOOL winsdb_check_or_add_module_list(struct winsdb_handle *h)
929 {
930         int trans;
931         int ret;
932         struct ldb_dn *dn;
933         struct ldb_result *res = NULL;
934         struct ldb_message *msg = NULL;
935         TALLOC_CTX *tmp_ctx = talloc_new(h);
936         unsigned int flags = 0;
937
938         trans = ldb_transaction_start(h->ldb);
939         if (trans != LDB_SUCCESS) goto failed;
940
941         /* check if we have a special @MODULES record already */
942         dn = ldb_dn_explode(tmp_ctx, "@MODULES");
943         if (!dn) goto failed;
944
945         /* find the record in the WINS database */
946         ret = ldb_search(h->ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
947         if (ret != LDB_SUCCESS) goto failed;
948         talloc_steal(tmp_ctx, res);
949
950         if (res->count > 0) goto skip;
951
952         /* if there's no record, add one */
953         msg = ldb_msg_new(tmp_ctx);
954         if (!msg) goto failed;
955         msg->dn = dn;
956
957         ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
958         if (ret != 0) goto failed;
959
960         ret = ldb_add(h->ldb, msg);
961         if (ret != 0) goto failed;
962
963         trans = ldb_transaction_commit(h->ldb);
964         if (trans != LDB_SUCCESS) goto failed;
965
966         /* close and reopen the database, with the modules */
967         trans = LDB_ERR_OTHER;
968         talloc_free(h->ldb);
969         h->ldb = NULL;
970
971         if (lp_parm_bool(-1,"winsdb", "nosync", False)) {
972                 flags |= LDB_FLG_NOSYNC;
973         }
974
975         h->ldb = ldb_wrap_connect(h, lock_path(h, lp_wins_url()),
976                                   NULL, NULL, flags, NULL);
977         if (!h->ldb) goto failed;
978
979         talloc_free(tmp_ctx);
980         return True;
981
982 skip:
983         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
984         talloc_free(tmp_ctx);
985         return True;
986
987 failed:
988         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
989         talloc_free(tmp_ctx);
990         return False;
991 }
992
993 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx, enum winsdb_handle_caller caller)
994 {
995         struct winsdb_handle *h = NULL;
996         const char *owner;
997         unsigned int flags = 0;
998         BOOL ret;
999         int ldb_err;
1000
1001         h = talloc(mem_ctx, struct winsdb_handle);
1002         if (!h) return NULL;
1003
1004         if (lp_parm_bool(-1,"winsdb", "nosync", False)) {
1005                 flags |= LDB_FLG_NOSYNC;
1006         }
1007
1008         h->ldb = ldb_wrap_connect(h, lock_path(h, lp_wins_url()),
1009                                   NULL, NULL, flags, NULL);
1010         if (!h->ldb) goto failed;       
1011
1012         h->caller = caller;
1013
1014         owner = lp_parm_string(-1, "winsdb", "local_owner");
1015         if (!owner) {
1016                 owner = iface_n_ip(0);
1017         }
1018
1019         h->local_owner = talloc_strdup(h, owner);
1020         if (!h->local_owner) goto failed;
1021
1022         /* make sure the module list is available and used */
1023         ret = winsdb_check_or_add_module_list(h);
1024         if (!ret) goto failed;
1025
1026         ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1027         if (ldb_err != LDB_SUCCESS) goto failed;
1028
1029         return h;
1030 failed:
1031         talloc_free(h);
1032         return NULL;
1033 }