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