winsdb: the we_are_owner in winsdb_lookup() needs to be per address
[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, 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
617         rec = talloc(mem_ctx, struct winsdb_record);
618         if (rec == NULL) {
619                 status = NT_STATUS_NO_MEMORY;
620                 goto failed;
621         }
622
623         status = winsdb_nbt_name(rec, msg->dn, &name);
624         if (!NT_STATUS_IS_OK(status)) goto failed;
625
626         if (strlen(name->name) > 15) {
627                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
628                 goto failed;
629         }
630         if (name->scope && strlen(name->scope) > 238) {
631                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
632                 goto failed;
633         }
634
635         /* parse it into a more convenient winsdb_record structure */
636         rec->name               = name;
637         rec->type               = ldb_msg_find_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
638         rec->state              = ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
639         rec->node               = ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
640         rec->is_static          = ldb_msg_find_attr_as_int(msg, "isStatic", 0);
641         rec->expire_time        = ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
642         rec->version            = ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
643         rec->wins_owner         = ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
644         rec->registered_by      = ldb_msg_find_attr_as_string(msg, "registeredBy", NULL);
645         talloc_steal(rec, rec->wins_owner);
646         talloc_steal(rec, rec->registered_by);
647
648         if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
649                 rec->wins_owner = h->local_owner;
650         }
651
652         el = ldb_msg_find_element(msg, "address");
653         if (el) {
654                 num_values = el->num_values;
655         } else {
656                 num_values = 0;
657         }
658
659         if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
660                 if (num_values != 1) {
661                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
662                         goto failed;
663                 }
664         }
665         if (rec->state == WREPL_STATE_ACTIVE) {
666                 if (num_values < 1) {
667                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
668                         goto failed;
669                 }
670         }
671         if (num_values > 25) {
672                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
673                 goto failed;
674         }
675
676         rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
677         if (rec->addresses == NULL) {
678                 status = NT_STATUS_NO_MEMORY;
679                 goto failed;
680         }
681
682         for (i=0,j=0;i<num_values;i++) {
683                 bool we_are_owner = false;
684
685                 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
686                 if (!NT_STATUS_IS_OK(status)) goto failed;
687
688                 if (strcmp(rec->addresses[j]->wins_owner, h->local_owner) == 0) {
689                         we_are_owner = true;
690                 }
691
692                 /*
693                  * the record isn't static and is active
694                  * then don't add the address if it's expired,
695                  * but only if we're the owner of the address
696                  *
697                  * This is important for SGROUP records,
698                  * because each server thinks he's the owner of the
699                  * record and the record isn't replicated on a
700                  * name_refresh. So addresses owned by another owner
701                  * could expire, but we still need to return them
702                  * (as windows does).
703                  */
704                 if (!rec->is_static &&
705                     rec->addresses[j]->expire_time <= now &&
706                     rec->state == WREPL_STATE_ACTIVE &&
707                     we_are_owner) {
708                         DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n", 
709                                  rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
710                                  timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
711                         talloc_free(rec->addresses[j]);
712                         rec->addresses[j] = NULL;
713                         continue;
714                 }
715                 j++;
716         }
717         rec->addresses[j] = NULL;
718         num_values = j;
719
720         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
721                 rec->expire_time = get_time_t_max();
722                 for (i=0;rec->addresses[i];i++) {
723                         rec->addresses[i]->expire_time = rec->expire_time;
724                 }
725         }
726
727         if (rec->state == WREPL_STATE_ACTIVE) {
728                 if (num_values < 1) {
729                         DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n", 
730                                  nbt_name_string(mem_ctx, rec->name)));
731                         rec->state = WREPL_STATE_RELEASED;
732                 }
733         }
734
735         *_rec = rec;
736         return NT_STATUS_OK;
737 failed:
738         if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
739                 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
740         }
741         talloc_free(rec);
742         return status;
743 }
744
745 /*
746   form a ldb_message from a winsdb_record
747 */
748 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
749                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
750 {
751         int i, ret=0;
752         size_t addr_count;
753         const char *expire_time;
754         struct ldb_message *msg = ldb_msg_new(mem_ctx);
755         if (msg == NULL) goto failed;
756
757         /* make sure we don't put in corrupted records */
758         addr_count = winsdb_addr_list_length(rec->addresses);
759         if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
760                 rec->state = WREPL_STATE_RELEASED;
761         }
762         if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
763                 rec->type = WREPL_TYPE_MHOMED;
764         }
765
766         expire_time = ldb_timestring(msg, rec->expire_time);
767         if (!expire_time) {
768                 goto failed;
769         }
770
771         msg->dn = winsdb_dn(msg, ldb, rec->name);
772         if (msg->dn == NULL) goto failed;
773         ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
774         if (rec->name->name && *rec->name->name) {
775                 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
776         }
777         if (rec->name->scope && *rec->name->scope) {
778                 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
779         }
780         ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
781         ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
782         ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
783         ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
784         ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
785         ret |= ldb_msg_add_empty(msg, "expireTime", 0, NULL);
786         if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
787                 ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
788         }
789         ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
790         ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
791         ret |= ldb_msg_add_empty(msg, "address", 0, NULL);
792         for (i=0;rec->addresses[i];i++) {
793                 ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
794         }
795         ret |= ldb_msg_add_empty(msg, "registeredBy", 0, NULL);
796         if (rec->registered_by) {
797                 ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
798                 if (ret != 0) goto failed;
799         }
800         return msg;
801
802 failed:
803         talloc_free(msg);
804         return NULL;
805 }
806
807 /*
808   save a WINS record into the database
809 */
810 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
811 {
812         struct ldb_message *msg;
813         struct ldb_context *wins_db = h->ldb;
814         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
815         int trans = -1;
816         int ret = 0;
817
818         trans = ldb_transaction_start(wins_db);
819         if (trans != LDB_SUCCESS) goto failed;
820
821         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
822                 /* passing '0' means auto-allocate a new one */
823                 rec->version = winsdb_set_maxVersion(h, 0);
824                 if (rec->version == 0) goto failed;
825         }
826         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
827                 rec->wins_owner = h->local_owner;
828         }
829
830         msg = winsdb_message(wins_db, rec, tmp_ctx);
831         if (msg == NULL) goto failed;
832         ret = ldb_add(wins_db, msg);
833         if (ret != 0) goto failed;
834
835         trans = ldb_transaction_commit(wins_db);
836         if (trans != LDB_SUCCESS) goto failed;
837
838         wins_hook(h, rec, WINS_HOOK_ADD, h->hook_script);
839
840         talloc_free(tmp_ctx);
841         return NBT_RCODE_OK;
842
843 failed:
844         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
845         talloc_free(tmp_ctx);
846         return NBT_RCODE_SVR;
847 }
848
849
850 /*
851   modify a WINS record in the database
852 */
853 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
854 {
855         struct ldb_message *msg;
856         struct ldb_context *wins_db = h->ldb;
857         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
858         int trans;
859         int ret;
860         int i;
861
862         trans = ldb_transaction_start(wins_db);
863         if (trans != LDB_SUCCESS) goto failed;
864
865         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
866                 /* passing '0' means auto-allocate a new one */
867                 rec->version = winsdb_set_maxVersion(h, 0);
868                 if (rec->version == 0) goto failed;
869         }
870         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
871                 rec->wins_owner = h->local_owner;
872         }
873
874         msg = winsdb_message(wins_db, rec, tmp_ctx);
875         if (msg == NULL) goto failed;
876
877         for (i=0;i<msg->num_elements;i++) {
878                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
879         }
880
881         ret = ldb_modify(wins_db, msg);
882         if (ret != 0) goto failed;
883
884         trans = ldb_transaction_commit(wins_db);
885         if (trans != LDB_SUCCESS) goto failed;
886
887         wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);
888
889         talloc_free(tmp_ctx);
890         return NBT_RCODE_OK;
891
892 failed:
893         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
894         talloc_free(tmp_ctx);
895         return NBT_RCODE_SVR;
896 }
897
898
899 /*
900   delete a WINS record from the database
901 */
902 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
903 {
904         struct ldb_context *wins_db = h->ldb;
905         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
906         struct ldb_dn *dn;
907         int trans;
908         int ret;
909
910         trans = ldb_transaction_start(wins_db);
911         if (trans != LDB_SUCCESS) goto failed;
912
913         dn = winsdb_dn(tmp_ctx, wins_db, rec->name);
914         if (dn == NULL) goto failed;
915
916         ret = ldb_delete(wins_db, dn);
917         if (ret != 0) goto failed;
918
919         trans = ldb_transaction_commit(wins_db);
920         if (trans != LDB_SUCCESS) goto failed;
921
922         wins_hook(h, rec, WINS_HOOK_DELETE, h->hook_script);
923
924         talloc_free(tmp_ctx);
925         return NBT_RCODE_OK;
926
927 failed:
928         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
929         talloc_free(tmp_ctx);
930         return NBT_RCODE_SVR;
931 }
932
933 static bool winsdb_check_or_add_module_list(struct tevent_context *ev_ctx, 
934                                             struct loadparm_context *lp_ctx, struct winsdb_handle *h)
935 {
936         int trans;
937         int ret;
938         struct ldb_dn *dn;
939         struct ldb_result *res = NULL;
940         struct ldb_message *msg = NULL;
941         TALLOC_CTX *tmp_ctx = talloc_new(h);
942         unsigned int flags = 0;
943
944         trans = ldb_transaction_start(h->ldb);
945         if (trans != LDB_SUCCESS) goto failed;
946
947         /* check if we have a special @MODULES record already */
948         dn = ldb_dn_new(tmp_ctx, h->ldb, "@MODULES");
949         if (!dn) goto failed;
950
951         /* find the record in the WINS database */
952         ret = ldb_search(h->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
953         if (ret != LDB_SUCCESS) goto failed;
954
955         if (res->count > 0) goto skip;
956
957         /* if there's no record, add one */
958         msg = ldb_msg_new(tmp_ctx);
959         if (!msg) goto failed;
960         msg->dn = dn;
961
962         ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
963         if (ret != 0) goto failed;
964
965         ret = ldb_add(h->ldb, msg);
966         if (ret != 0) goto failed;
967
968         trans = ldb_transaction_commit(h->ldb);
969         if (trans != LDB_SUCCESS) goto failed;
970
971         /* close and reopen the database, with the modules */
972         trans = LDB_ERR_OTHER;
973         talloc_free(h->ldb);
974         h->ldb = NULL;
975
976         if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
977                 flags |= LDB_FLG_NOSYNC;
978         }
979
980         h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
981                                   NULL, NULL, flags, NULL);
982         if (!h->ldb) goto failed;
983
984         talloc_free(tmp_ctx);
985         return true;
986
987 skip:
988         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
989         talloc_free(tmp_ctx);
990         return true;
991
992 failed:
993         if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
994         talloc_free(tmp_ctx);
995         return false;
996 }
997
998 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx, 
999                                      struct tevent_context *ev_ctx,
1000                                      struct loadparm_context *lp_ctx,
1001                                      const char *owner,
1002                                      enum winsdb_handle_caller caller)
1003 {
1004         struct winsdb_handle *h = NULL;
1005         unsigned int flags = 0;
1006         bool ret;
1007         int ldb_err;
1008
1009         h = talloc(mem_ctx, struct winsdb_handle);
1010         if (!h) return NULL;
1011
1012         if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
1013                 flags |= LDB_FLG_NOSYNC;
1014         }
1015
1016         h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
1017                                   NULL, NULL, flags, NULL);
1018         if (!h->ldb) goto failed;       
1019
1020         h->caller = caller;
1021         h->hook_script = lp_wins_hook(lp_ctx);
1022
1023         h->local_owner = talloc_strdup(h, owner);
1024         if (!h->local_owner) goto failed;
1025
1026         /* make sure the module list is available and used */
1027         ret = winsdb_check_or_add_module_list(ev_ctx, lp_ctx, h);
1028         if (!ret) goto failed;
1029
1030         ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1031         if (ldb_err != LDB_SUCCESS) goto failed;
1032
1033         return h;
1034 failed:
1035         talloc_free(h);
1036         return NULL;
1037 }