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