s4:lib/tevent: rename structs
[jra/samba/.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 "ldb_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, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
49         if (ret != LDB_SUCCESS) goto failed;
50         if (res->count > 1) goto failed;
51
52         if (res->count == 1) {
53                 maxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
54         }
55
56 failed:
57         talloc_free(tmp_ctx);
58         return maxVersion;
59 }
60
61 /*
62  if newVersion == 0 return the old maxVersion + 1 and save it
63  if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
64 */
65 uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
66 {
67         int trans;
68         int ret;
69         struct ldb_dn *dn;
70         struct ldb_result *res = NULL;
71         struct ldb_message *msg = NULL;
72         struct ldb_context *wins_db = h->ldb;
73         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
74         uint64_t oldMaxVersion = 0;
75
76         trans = ldb_transaction_start(wins_db);
77         if (trans != LDB_SUCCESS) goto failed;
78
79         dn = ldb_dn_new(tmp_ctx, wins_db, "CN=VERSION");
80         if (!dn) goto failed;
81
82         /* find the record in the WINS database */
83         ret = ldb_search(wins_db, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
84         if (ret != LDB_SUCCESS) goto failed;
85         if (res->count > 1) goto failed;
86
87         if (res->count == 1) {
88                 oldMaxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
89         }
90
91         if (newMaxVersion == 0) {
92                 newMaxVersion = oldMaxVersion + 1;
93         } else {
94                 newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
95         }
96
97         msg = ldb_msg_new(tmp_ctx);
98         if (!msg) goto failed;
99         msg->dn = dn;
100
101
102         ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
103         if (ret != 0) goto failed;
104         ret = ldb_msg_add_string(msg, "objectClass", "winsMaxVersion");
105         if (ret != 0) goto failed;
106         ret = ldb_msg_add_empty(msg, "maxVersion", LDB_FLAG_MOD_REPLACE, NULL);
107         if (ret != 0) goto failed;
108         ret = ldb_msg_add_fmt(msg, "maxVersion", "%llu", (long long)newMaxVersion);
109         if (ret != 0) goto failed;
110
111         ret = ldb_modify(wins_db, msg);
112         if (ret != 0) ret = ldb_add(wins_db, msg);
113         if (ret != 0) goto failed;
114
115         trans = ldb_transaction_commit(wins_db);
116         if (trans != LDB_SUCCESS) goto failed;
117
118         talloc_free(tmp_ctx);
119         return newMaxVersion;
120
121 failed:
122         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
123         talloc_free(tmp_ctx);
124         return 0;
125 }
126
127 uint64_t winsdb_get_seqnumber(struct winsdb_handle *h)
128 {
129         int ret;
130         struct ldb_context *ldb = h->ldb;
131         struct ldb_dn *dn;
132         struct ldb_result *res = NULL;
133         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
134         uint64_t seqnumber = 0;
135
136         dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO");
137         if (!dn) goto failed;
138
139         /* find the record in the WINS database */
140         ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
141         if (ret != LDB_SUCCESS) goto failed;
142         if (res->count > 1) goto failed;
143
144         if (res->count == 1) {
145                 seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0);
146         }
147
148 failed:
149         talloc_free(tmp_ctx);
150         return seqnumber;
151 }
152
153 /*
154   return a DN for a nbt_name
155 */
156 static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct nbt_name *name)
157 {
158         struct ldb_dn *dn;
159
160         dn = ldb_dn_new_fmt(mem_ctx, ldb, "type=0x%02X", name->type);
161         if (ldb_dn_is_valid(dn) && name->name && *name->name) {
162                 ldb_dn_add_child_fmt(dn, "name=%s", name->name);
163         }
164         if (ldb_dn_is_valid(dn) && name->scope && *name->scope) {
165                 ldb_dn_add_child_fmt(dn, "scope=%s", name->scope);
166         }
167         return dn;
168 }
169
170 static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
171 {
172         NTSTATUS status;
173         struct nbt_name *name;
174         unsigned int comp_num;
175         uint32_t cur = 0;
176
177         name = talloc(mem_ctx, struct nbt_name);
178         if (!name) {
179                 status = NT_STATUS_NO_MEMORY;
180                 goto failed;
181         }
182
183         comp_num = ldb_dn_get_comp_num(dn);
184
185         if (comp_num > 3) {
186                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
187                 goto failed;
188         }
189
190         if (comp_num > cur && strcasecmp("scope", ldb_dn_get_component_name(dn, cur)) == 0) {
191                 name->scope     = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
192                 cur++;
193         } else {
194                 name->scope     = NULL;
195         }
196
197         if (comp_num > cur && strcasecmp("name", ldb_dn_get_component_name(dn, cur)) == 0) {
198                 name->name      = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->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 (comp_num > cur && strcasecmp("type", ldb_dn_get_component_name(dn, cur)) == 0) {
209                 name->type      = strtoul((char *)ldb_dn_get_component_val(dn, cur)->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           = (const char *)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, NULL);
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, tmp_ctx, &res,
585                          winsdb_dn(tmp_ctx, wins_db, name),
586                          LDB_SCOPE_BASE, NULL, NULL);
587
588         if (ret != LDB_SUCCESS || res->count > 1) {
589                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
590                 goto failed;
591         } else if (res->count== 0) {
592                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
593                 goto failed;
594         }
595
596         status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
597         if (!NT_STATUS_IS_OK(status)) goto failed;
598
599         talloc_steal(mem_ctx, rec);
600         talloc_free(tmp_ctx);
601         *_rec = rec;
602         return NT_STATUS_OK;
603
604 failed:
605         talloc_free(tmp_ctx);
606         return status;
607 }
608
609 NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
610 {
611         NTSTATUS status;
612         struct winsdb_record *rec;
613         struct ldb_message_element *el;
614         struct nbt_name *name;
615         uint32_t i, j, num_values;
616         bool we_are_owner = false;
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_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
639         rec->state              = ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
640         rec->node               = ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
641         rec->is_static          = ldb_msg_find_attr_as_int(msg, "isStatic", 0);
642         rec->expire_time        = ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
643         rec->version            = ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
644         rec->wins_owner         = ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
645         rec->registered_by      = ldb_msg_find_attr_as_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         if (strcmp(rec->wins_owner, h->local_owner) == 0) {
678                 we_are_owner = true;
679         }
680
681         /* 
682          * see if it has already expired
683          * 
684          * NOTE: only expire owned records this way!
685          *       w2k3 resolves expired replicas
686          *       which are in active state
687          */
688         if (!rec->is_static &&
689             rec->expire_time <= now &&
690             rec->state == WREPL_STATE_ACTIVE &&
691             we_are_owner) {
692                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
693                          nbt_name_string(mem_ctx, rec->name), timestring(mem_ctx, rec->expire_time)));
694                 rec->state = WREPL_STATE_RELEASED;
695         }
696
697         rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
698         if (rec->addresses == NULL) {
699                 status = NT_STATUS_NO_MEMORY;
700                 goto failed;
701         }
702
703         for (i=0,j=0;i<num_values;i++) {
704                 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
705                 if (!NT_STATUS_IS_OK(status)) goto failed;
706
707                 /*
708                  * the record isn't static and is active
709                  * then don't add the address if it's expired
710                  */
711                 if (!rec->is_static &&
712                     rec->addresses[j]->expire_time <= now &&
713                     rec->state == WREPL_STATE_ACTIVE &&
714                     we_are_owner) {
715                         DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n", 
716                                  rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
717                                  timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
718                         talloc_free(rec->addresses[j]);
719                         rec->addresses[j] = NULL;
720                         continue;
721                 }
722                 j++;
723         }
724         rec->addresses[j] = NULL;
725         num_values = j;
726
727         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
728                 rec->expire_time = get_time_t_max();
729                 for (i=0;rec->addresses[i];i++) {
730                         rec->addresses[i]->expire_time = rec->expire_time;
731                 }
732         }
733
734         if (rec->state == WREPL_STATE_ACTIVE) {
735                 if (num_values < 1) {
736                         DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n", 
737                                  nbt_name_string(mem_ctx, rec->name)));
738                         rec->state = WREPL_STATE_RELEASED;
739                 }
740         }
741
742         *_rec = rec;
743         return NT_STATUS_OK;
744 failed:
745         if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
746                 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
747         }
748         talloc_free(rec);
749         return status;
750 }
751
752 /*
753   form a ldb_message from a winsdb_record
754 */
755 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
756                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
757 {
758         int i, ret=0;
759         size_t addr_count;
760         const char *expire_time;
761         struct ldb_message *msg = ldb_msg_new(mem_ctx);
762         if (msg == NULL) goto failed;
763
764         /* make sure we don't put in corrupted records */
765         addr_count = winsdb_addr_list_length(rec->addresses);
766         if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
767                 rec->state = WREPL_STATE_RELEASED;
768         }
769         if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
770                 rec->type = WREPL_TYPE_MHOMED;
771         }
772
773         expire_time = ldb_timestring(msg, rec->expire_time);
774         if (!expire_time) {
775                 goto failed;
776         }
777
778         msg->dn = winsdb_dn(msg, ldb, rec->name);
779         if (msg->dn == NULL) goto failed;
780         ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
781         if (rec->name->name && *rec->name->name) {
782                 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
783         }
784         if (rec->name->scope && *rec->name->scope) {
785                 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
786         }
787         ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
788         ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
789         ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
790         ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
791         ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
792         ret |= ldb_msg_add_empty(msg, "expireTime", 0, NULL);
793         if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
794                 ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
795         }
796         ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
797         ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
798         ret |= ldb_msg_add_empty(msg, "address", 0, NULL);
799         for (i=0;rec->addresses[i];i++) {
800                 ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
801         }
802         ret |= ldb_msg_add_empty(msg, "registeredBy", 0, NULL);
803         if (rec->registered_by) {
804                 ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
805                 if (ret != 0) goto failed;
806         }
807         return msg;
808
809 failed:
810         talloc_free(msg);
811         return NULL;
812 }
813
814 /*
815   save a WINS record into the database
816 */
817 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
818 {
819         struct ldb_message *msg;
820         struct ldb_context *wins_db = h->ldb;
821         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
822         int trans = -1;
823         int ret = 0;
824
825         trans = ldb_transaction_start(wins_db);
826         if (trans != LDB_SUCCESS) goto failed;
827
828         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
829                 /* passing '0' means auto-allocate a new one */
830                 rec->version = winsdb_set_maxVersion(h, 0);
831                 if (rec->version == 0) goto failed;
832         }
833         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
834                 rec->wins_owner = h->local_owner;
835         }
836
837         msg = winsdb_message(wins_db, rec, tmp_ctx);
838         if (msg == NULL) goto failed;
839         ret = ldb_add(wins_db, msg);
840         if (ret != 0) goto failed;
841
842         trans = ldb_transaction_commit(wins_db);
843         if (trans != LDB_SUCCESS) goto failed;
844
845         wins_hook(h, rec, WINS_HOOK_ADD, h->hook_script);
846
847         talloc_free(tmp_ctx);
848         return NBT_RCODE_OK;
849
850 failed:
851         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
852         talloc_free(tmp_ctx);
853         return NBT_RCODE_SVR;
854 }
855
856
857 /*
858   modify a WINS record in the database
859 */
860 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
861 {
862         struct ldb_message *msg;
863         struct ldb_context *wins_db = h->ldb;
864         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
865         int trans;
866         int ret;
867         int i;
868
869         trans = ldb_transaction_start(wins_db);
870         if (trans != LDB_SUCCESS) goto failed;
871
872         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
873                 /* passing '0' means auto-allocate a new one */
874                 rec->version = winsdb_set_maxVersion(h, 0);
875                 if (rec->version == 0) goto failed;
876         }
877         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
878                 rec->wins_owner = h->local_owner;
879         }
880
881         msg = winsdb_message(wins_db, rec, tmp_ctx);
882         if (msg == NULL) goto failed;
883
884         for (i=0;i<msg->num_elements;i++) {
885                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
886         }
887
888         ret = ldb_modify(wins_db, msg);
889         if (ret != 0) goto failed;
890
891         trans = ldb_transaction_commit(wins_db);
892         if (trans != LDB_SUCCESS) goto failed;
893
894         wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);
895
896         talloc_free(tmp_ctx);
897         return NBT_RCODE_OK;
898
899 failed:
900         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
901         talloc_free(tmp_ctx);
902         return NBT_RCODE_SVR;
903 }
904
905
906 /*
907   delete a WINS record from the database
908 */
909 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
910 {
911         struct ldb_context *wins_db = h->ldb;
912         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
913         struct ldb_dn *dn;
914         int trans;
915         int ret;
916
917         trans = ldb_transaction_start(wins_db);
918         if (trans != LDB_SUCCESS) goto failed;
919
920         dn = winsdb_dn(tmp_ctx, wins_db, rec->name);
921         if (dn == NULL) goto failed;
922
923         ret = ldb_delete(wins_db, dn);
924         if (ret != 0) goto failed;
925
926         trans = ldb_transaction_commit(wins_db);
927         if (trans != LDB_SUCCESS) goto failed;
928
929         wins_hook(h, rec, WINS_HOOK_DELETE, h->hook_script);
930
931         talloc_free(tmp_ctx);
932         return NBT_RCODE_OK;
933
934 failed:
935         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
936         talloc_free(tmp_ctx);
937         return NBT_RCODE_SVR;
938 }
939
940 static bool winsdb_check_or_add_module_list(struct tevent_context *ev_ctx, 
941                                             struct loadparm_context *lp_ctx, struct winsdb_handle *h)
942 {
943         int trans;
944         int ret;
945         struct ldb_dn *dn;
946         struct ldb_result *res = NULL;
947         struct ldb_message *msg = NULL;
948         TALLOC_CTX *tmp_ctx = talloc_new(h);
949         unsigned int flags = 0;
950
951         trans = ldb_transaction_start(h->ldb);
952         if (trans != LDB_SUCCESS) goto failed;
953
954         /* check if we have a special @MODULES record already */
955         dn = ldb_dn_new(tmp_ctx, h->ldb, "@MODULES");
956         if (!dn) goto failed;
957
958         /* find the record in the WINS database */
959         ret = ldb_search(h->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
960         if (ret != LDB_SUCCESS) goto failed;
961
962         if (res->count > 0) goto skip;
963
964         /* if there's no record, add one */
965         msg = ldb_msg_new(tmp_ctx);
966         if (!msg) goto failed;
967         msg->dn = dn;
968
969         ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
970         if (ret != 0) goto failed;
971
972         ret = ldb_add(h->ldb, msg);
973         if (ret != 0) goto failed;
974
975         trans = ldb_transaction_commit(h->ldb);
976         if (trans != LDB_SUCCESS) goto failed;
977
978         /* close and reopen the database, with the modules */
979         trans = LDB_ERR_OTHER;
980         talloc_free(h->ldb);
981         h->ldb = NULL;
982
983         if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
984                 flags |= LDB_FLG_NOSYNC;
985         }
986
987         h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
988                                   NULL, NULL, flags, NULL);
989         if (!h->ldb) goto failed;
990
991         talloc_free(tmp_ctx);
992         return true;
993
994 skip:
995         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
996         talloc_free(tmp_ctx);
997         return true;
998
999 failed:
1000         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
1001         talloc_free(tmp_ctx);
1002         return false;
1003 }
1004
1005 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx, 
1006                                      struct tevent_context *ev_ctx,
1007                                      struct loadparm_context *lp_ctx,
1008                                      const char *owner,
1009                                      enum winsdb_handle_caller caller)
1010 {
1011         struct winsdb_handle *h = NULL;
1012         unsigned int flags = 0;
1013         bool ret;
1014         int ldb_err;
1015
1016         h = talloc(mem_ctx, struct winsdb_handle);
1017         if (!h) return NULL;
1018
1019         if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
1020                 flags |= LDB_FLG_NOSYNC;
1021         }
1022
1023         h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
1024                                   NULL, NULL, flags, NULL);
1025         if (!h->ldb) goto failed;       
1026
1027         h->caller = caller;
1028         h->hook_script = lp_wins_hook(lp_ctx);
1029
1030         h->local_owner = talloc_strdup(h, owner);
1031         if (!h->local_owner) goto failed;
1032
1033         /* make sure the module list is available and used */
1034         ret = winsdb_check_or_add_module_list(ev_ctx, lp_ctx, h);
1035         if (!ret) goto failed;
1036
1037         ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1038         if (ldb_err != LDB_SUCCESS) goto failed;
1039
1040         return h;
1041 failed:
1042         talloc_free(h);
1043         return NULL;
1044 }