ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / nbt_server / wins / winsdb.c
blob7df40c331352e2647e5ae6c7fbe234761a572e91
1 /*
2 Unix SMB/CIFS implementation.
4 WINS database routines
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2005
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.
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.
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/>.
23 #include "includes.h"
24 #include "nbt_server/nbt_server.h"
25 #include "nbt_server/wins/winsdb.h"
26 #include <ldb.h>
27 #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 #include "lib/util/smb_strtox.h"
35 #include "lib/util/tsort.h"
37 #undef strcasecmp
39 uint64_t winsdb_get_maxVersion(struct winsdb_handle *h)
41 int ret;
42 struct ldb_context *ldb = h->ldb;
43 struct ldb_dn *dn;
44 struct ldb_result *res = NULL;
45 TALLOC_CTX *tmp_ctx = talloc_new(ldb);
46 uint64_t maxVersion = 0;
48 dn = ldb_dn_new(tmp_ctx, ldb, "CN=VERSION");
49 if (!dn) goto failed;
51 /* find the record in the WINS database */
52 ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
53 if (ret != LDB_SUCCESS) goto failed;
54 if (res->count > 1) goto failed;
56 if (res->count == 1) {
57 maxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
60 failed:
61 talloc_free(tmp_ctx);
62 return maxVersion;
66 if newVersion == 0 return the old maxVersion + 1 and save it
67 if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
69 uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
71 int trans;
72 int ret;
73 struct ldb_dn *dn;
74 struct ldb_result *res = NULL;
75 struct ldb_message *msg = NULL;
76 struct ldb_context *wins_db = h->ldb;
77 TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
78 uint64_t oldMaxVersion = 0;
80 trans = ldb_transaction_start(wins_db);
81 if (trans != LDB_SUCCESS) goto failed;
83 dn = ldb_dn_new(tmp_ctx, wins_db, "CN=VERSION");
84 if (!dn) goto failed;
86 /* find the record in the WINS database */
87 ret = ldb_search(wins_db, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
88 if (ret != LDB_SUCCESS) goto failed;
89 if (res->count > 1) goto failed;
91 if (res->count == 1) {
92 oldMaxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
95 if (newMaxVersion == 0) {
96 newMaxVersion = oldMaxVersion + 1;
97 } else {
98 newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
101 msg = ldb_msg_new(tmp_ctx);
102 if (!msg) goto failed;
103 msg->dn = dn;
106 ret = ldb_msg_append_string(msg, "objectClass", "winsMaxVersion",
107 LDB_FLAG_MOD_REPLACE);
108 if (ret != LDB_SUCCESS) goto failed;
109 ret = ldb_msg_append_fmt(msg, LDB_FLAG_MOD_REPLACE,
110 "maxVersion", "%llu", (long long)newMaxVersion);
111 if (ret != LDB_SUCCESS) goto failed;
113 ret = ldb_modify(wins_db, msg);
114 if (ret != LDB_SUCCESS) ret = ldb_add(wins_db, msg);
115 if (ret != LDB_SUCCESS) goto failed;
117 trans = ldb_transaction_commit(wins_db);
118 if (trans != LDB_SUCCESS) goto failed;
120 talloc_free(tmp_ctx);
121 return newMaxVersion;
123 failed:
124 if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
125 talloc_free(tmp_ctx);
126 return 0;
130 return a DN for a nbt_name
132 static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
133 const struct nbt_name *name)
135 struct ldb_dn *dn;
137 dn = ldb_dn_new_fmt(mem_ctx, ldb, "type=0x%02X", name->type);
138 if (ldb_dn_is_valid(dn) && name->name && *name->name) {
139 ldb_dn_add_child_fmt(dn, "name=%s", name->name);
141 if (ldb_dn_is_valid(dn) && name->scope && *name->scope) {
142 ldb_dn_add_child_fmt(dn, "scope=%s", name->scope);
144 return dn;
147 static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
149 NTSTATUS status;
150 struct nbt_name *name;
151 unsigned int comp_num;
152 uint32_t cur = 0;
153 int error = 0;
155 name = talloc(mem_ctx, struct nbt_name);
156 if (!name) {
157 status = NT_STATUS_NO_MEMORY;
158 goto failed;
161 comp_num = ldb_dn_get_comp_num(dn);
163 if (comp_num > 3) {
164 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
165 goto failed;
168 if (comp_num > cur && strcasecmp("scope", ldb_dn_get_component_name(dn, cur)) == 0) {
169 name->scope = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
170 cur++;
171 } else {
172 name->scope = NULL;
175 if (comp_num > cur && strcasecmp("name", ldb_dn_get_component_name(dn, cur)) == 0) {
176 name->name = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
177 cur++;
178 } else {
179 name->name = talloc_strdup(name, "");
180 if (!name->name) {
181 status = NT_STATUS_NO_MEMORY;
182 goto failed;
186 if (comp_num > cur && strcasecmp("type", ldb_dn_get_component_name(dn, cur)) == 0) {
187 name->type =
188 smb_strtoul(
189 (char *)ldb_dn_get_component_val(dn, cur)->data,
190 NULL,
192 &error,
193 SMB_STR_STANDARD);
194 if (error != 0) {
195 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
196 goto failed;
198 cur++;
199 } else {
200 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
201 goto failed;
204 *_name = name;
205 return NT_STATUS_OK;
206 failed:
207 talloc_free(name);
208 return status;
212 decode the winsdb_addr("address") attribute:
213 "172.31.1.1" or
214 "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
215 are valid records
217 static NTSTATUS winsdb_addr_decode(struct winsdb_handle *h, struct winsdb_record *rec, struct ldb_val *val,
218 TALLOC_CTX *mem_ctx, struct winsdb_addr **_addr)
220 NTSTATUS status;
221 struct winsdb_addr *addr;
222 const char *address;
223 const char *wins_owner;
224 const char *expire_time;
225 char *p;
227 addr = talloc(mem_ctx, struct winsdb_addr);
228 if (!addr) {
229 status = NT_STATUS_NO_MEMORY;
230 goto failed;
233 address = (char *)val->data;
235 p = strchr(address, ';');
236 if (!p) {
237 /* support old entries, with only the address */
238 addr->address = (const char *)talloc_steal(addr, val->data);
239 addr->wins_owner = talloc_strdup(addr, rec->wins_owner);
240 if (!addr->wins_owner) {
241 status = NT_STATUS_NO_MEMORY;
242 goto failed;
244 addr->expire_time = rec->expire_time;
245 *_addr = addr;
246 return NT_STATUS_OK;
249 *p = '\0'; p++;
250 addr->address = talloc_strdup(addr, address);
251 if (!addr->address) {
252 status = NT_STATUS_NO_MEMORY;
253 goto failed;
256 if (strncmp("winsOwner:", p, 10) != 0) {
257 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
258 goto failed;
260 wins_owner = p + 10;
261 p = strchr(wins_owner, ';');
262 if (!p) {
263 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
264 goto failed;
267 *p = '\0';p++;
268 if (strcmp(wins_owner, "0.0.0.0") == 0) {
269 wins_owner = h->local_owner;
271 addr->wins_owner = talloc_strdup(addr, wins_owner);
272 if (!addr->wins_owner) {
273 status = NT_STATUS_NO_MEMORY;
274 goto failed;
277 if (strncmp("expireTime:", p, 11) != 0) {
278 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
279 goto failed;
282 expire_time = p + 11;
283 p = strchr(expire_time, ';');
284 if (!p) {
285 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
286 goto failed;
289 *p = '\0';p++;
290 addr->expire_time = ldb_string_to_time(expire_time);
292 *_addr = addr;
293 return NT_STATUS_OK;
294 failed:
295 talloc_free(addr);
296 return status;
300 encode the winsdb_addr("address") attribute like this:
301 non-static record:
302 "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
303 static record:
304 "172.31.1.1"
306 static int ldb_msg_add_winsdb_addr(struct ldb_message *msg, struct winsdb_record *rec,
307 const char *attr_name, struct winsdb_addr *addr)
309 const char *str;
311 if (rec->is_static) {
312 str = talloc_strdup(msg, addr->address);
313 if (!str) return LDB_ERR_OPERATIONS_ERROR;
314 } else {
315 char *expire_time;
316 expire_time = ldb_timestring(msg, addr->expire_time);
317 if (!expire_time) return LDB_ERR_OPERATIONS_ERROR;
318 str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s;",
319 addr->address, addr->wins_owner,
320 expire_time);
321 talloc_free(expire_time);
322 if (!str) return LDB_ERR_OPERATIONS_ERROR;
325 return ldb_msg_add_string(msg, attr_name, str);
328 struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
330 struct winsdb_addr **addresses;
332 addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
333 if (!addresses) return NULL;
335 addresses[0] = NULL;
337 return addresses;
340 static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr **p2, void *opaque)
342 struct winsdb_addr *a1 = talloc_get_type(*p1, struct winsdb_addr);
343 struct winsdb_addr *a2 = talloc_get_type(*p2, struct winsdb_addr);
344 struct winsdb_handle *h= talloc_get_type(opaque, struct winsdb_handle);
345 bool a1_owned = false;
346 bool a2_owned = false;
349 * first the owned addresses with the newest to the oldest address
350 * then the replica addresses with the newest to the oldest address
352 if (a2->expire_time != a1->expire_time) {
353 return NUMERIC_CMP(a2->expire_time, a1->expire_time);
356 if (strcmp(a2->wins_owner, h->local_owner) == 0) {
357 a2_owned = true;
360 if (strcmp(a1->wins_owner, h->local_owner) == 0) {
361 a1_owned = true;
364 return NUMERIC_CMP(a2_owned, a1_owned);
367 struct winsdb_addr **winsdb_addr_list_add(struct winsdb_handle *h, const struct winsdb_record *rec,
368 struct winsdb_addr **addresses, const char *address,
369 const char *wins_owner, time_t expire_time,
370 bool is_name_registration)
372 struct winsdb_addr *old_addr = NULL;
373 size_t len = 0;
374 size_t i;
375 bool found_old_replica = false;
378 * count the addresses and maybe
379 * find an old entry for the new address
381 for (i=0; addresses[i]; i++) {
382 if (old_addr) continue;
383 if (strcmp(addresses[i]->address, address) == 0) {
384 old_addr = addresses[i];
387 len = i;
390 * the address is already there
391 * and we can replace it
393 if (old_addr) {
394 goto remove_old_addr;
398 * if we don't have 25 addresses already,
399 * we can just add the new address
401 if (len < 25) {
402 goto add_new_addr;
406 * if we haven't found the address,
407 * and we have already have 25 addresses
408 * if so then we need to do the following:
409 * - if it isn't a name registration, then just ignore the new address
410 * - if it is a name registration, then first search for
411 * the oldest replica and if there's no replica address
412 * search the oldest owned address
414 if (!is_name_registration) {
415 return addresses;
419 * find the oldest replica address, if there's no replica
420 * record at all, find the oldest owned address
422 for (i=0; addresses[i]; i++) {
423 bool cur_is_replica = false;
424 /* find out if the current address is a replica */
425 if (strcmp(addresses[i]->wins_owner, h->local_owner) != 0) {
426 cur_is_replica = true;
430 * if we already found a replica address and the current address
431 * is not a replica, then skip it
433 if (found_old_replica && !cur_is_replica) continue;
436 * if we found the first replica address, reset the address
437 * that would be replaced
439 if (!found_old_replica && cur_is_replica) {
440 found_old_replica = true;
441 old_addr = addresses[i];
442 continue;
446 * if the first address isn't a replica, just start with
447 * the first one
449 if (!old_addr) {
450 old_addr = addresses[i];
451 continue;
455 * see if we find an older address
457 if (addresses[i]->expire_time < old_addr->expire_time) {
458 old_addr = addresses[i];
459 continue;
463 remove_old_addr:
464 winsdb_addr_list_remove(addresses, old_addr->address);
465 len --;
467 add_new_addr:
468 addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
469 if (!addresses) return NULL;
471 addresses[len] = talloc(addresses, struct winsdb_addr);
472 if (!addresses[len]) {
473 talloc_free(addresses);
474 return NULL;
477 addresses[len]->address = talloc_strdup(addresses[len], address);
478 if (!addresses[len]->address) {
479 talloc_free(addresses);
480 return NULL;
483 addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
484 if (!addresses[len]->wins_owner) {
485 talloc_free(addresses);
486 return NULL;
489 addresses[len]->expire_time = expire_time;
491 addresses[len+1] = NULL;
493 LDB_TYPESAFE_QSORT(addresses, len+1, h, winsdb_addr_sort_list);
495 return addresses;
498 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
500 size_t i;
502 for (i=0; addresses[i]; i++) {
503 if (strcmp(addresses[i]->address, address) == 0) {
504 break;
508 for (; addresses[i]; i++) {
509 addresses[i] = addresses[i+1];
512 return;
515 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
517 size_t i;
519 for (i=0; addresses[i]; i++) {
520 if (strcmp(addresses[i]->address, address) == 0) {
521 return addresses[i];
525 return NULL;
528 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
530 size_t i;
531 for (i=0; addresses[i]; i++);
532 return i;
535 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
537 size_t len = winsdb_addr_list_length(addresses);
538 const char **str_list=NULL;
539 size_t i;
541 for (i=0; i < len; i++) {
542 str_list = str_list_add(str_list, addresses[i]->address);
543 if (!str_list[i]) {
544 return NULL;
547 talloc_steal(mem_ctx, str_list);
548 return str_list;
552 load a WINS entry from the database
554 NTSTATUS winsdb_lookup(struct winsdb_handle *h,
555 const struct nbt_name *name,
556 TALLOC_CTX *mem_ctx,
557 struct winsdb_record **_rec)
559 NTSTATUS status;
560 struct ldb_result *res = NULL;
561 int ret;
562 struct winsdb_record *rec;
563 struct ldb_context *wins_db = h->ldb;
564 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
565 time_t now = time(NULL);
567 /* find the record in the WINS database */
568 ret = ldb_search(wins_db, tmp_ctx, &res,
569 winsdb_dn(tmp_ctx, wins_db, name),
570 LDB_SCOPE_BASE, NULL, NULL);
572 if (ret != LDB_SUCCESS || res->count > 1) {
573 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
574 goto failed;
575 } else if (res->count== 0) {
576 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
577 goto failed;
580 status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
581 if (!NT_STATUS_IS_OK(status)) goto failed;
583 talloc_steal(mem_ctx, rec);
584 talloc_free(tmp_ctx);
585 *_rec = rec;
586 return NT_STATUS_OK;
588 failed:
589 talloc_free(tmp_ctx);
590 return status;
593 NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
595 NTSTATUS status;
596 struct winsdb_record *rec;
597 struct ldb_message_element *el;
598 struct nbt_name *name;
599 uint32_t i, j, num_values;
601 rec = talloc(mem_ctx, struct winsdb_record);
602 if (rec == NULL) {
603 status = NT_STATUS_NO_MEMORY;
604 goto failed;
607 status = winsdb_nbt_name(rec, msg->dn, &name);
608 if (!NT_STATUS_IS_OK(status)) goto failed;
610 if (strlen(name->name) > 15) {
611 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
612 goto failed;
614 if (name->scope && strlen(name->scope) > 238) {
615 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
616 goto failed;
619 /* parse it into a more convenient winsdb_record structure */
620 rec->name = name;
621 rec->type = ldb_msg_find_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
622 rec->state = ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
623 rec->node = ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
624 rec->is_static = ldb_msg_find_attr_as_int(msg, "isStatic", 0);
625 rec->expire_time = ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
626 rec->version = ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
627 rec->wins_owner = ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
628 rec->registered_by = ldb_msg_find_attr_as_string(msg, "registeredBy", NULL);
629 talloc_steal(rec, rec->wins_owner);
630 talloc_steal(rec, rec->registered_by);
632 if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
633 rec->wins_owner = h->local_owner;
636 el = ldb_msg_find_element(msg, "address");
637 if (el) {
638 num_values = el->num_values;
639 } else {
640 num_values = 0;
643 if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
644 if (num_values != 1) {
645 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
646 goto failed;
649 if (rec->state == WREPL_STATE_ACTIVE) {
650 if (num_values < 1) {
651 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
652 goto failed;
655 if (num_values > 25) {
656 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
657 goto failed;
660 rec->addresses = talloc_array(rec, struct winsdb_addr *, num_values+1);
661 if (rec->addresses == NULL) {
662 status = NT_STATUS_NO_MEMORY;
663 goto failed;
666 for (i=0,j=0;i<num_values;i++) {
667 bool we_are_owner = false;
669 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
670 if (!NT_STATUS_IS_OK(status)) goto failed;
672 if (strcmp(rec->addresses[j]->wins_owner, h->local_owner) == 0) {
673 we_are_owner = true;
677 * the record isn't static and is active
678 * then don't add the address if it's expired,
679 * but only if we're the owner of the address
681 * This is important for SGROUP records,
682 * because each server thinks he's the owner of the
683 * record and the record isn't replicated on a
684 * name_refresh. So addresses owned by another owner
685 * could expire, but we still need to return them
686 * (as windows does).
688 if (!rec->is_static &&
689 rec->addresses[j]->expire_time <= now &&
690 rec->state == WREPL_STATE_ACTIVE &&
691 we_are_owner) {
692 DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n",
693 rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
694 timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
695 talloc_free(rec->addresses[j]);
696 rec->addresses[j] = NULL;
697 continue;
699 j++;
701 rec->addresses[j] = NULL;
702 num_values = j;
704 if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
705 rec->expire_time = get_time_t_max();
706 for (i=0;rec->addresses[i];i++) {
707 rec->addresses[i]->expire_time = rec->expire_time;
711 if (rec->state == WREPL_STATE_ACTIVE) {
712 if (num_values < 1) {
713 DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n",
714 nbt_name_string(mem_ctx, rec->name)));
715 rec->state = WREPL_STATE_RELEASED;
719 *_rec = rec;
720 return NT_STATUS_OK;
721 failed:
722 if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
723 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
725 talloc_free(rec);
726 return status;
730 form a ldb_message from a winsdb_record
732 static struct ldb_message *winsdb_message(struct ldb_context *ldb,
733 struct winsdb_record *rec,
734 TALLOC_CTX *mem_ctx)
736 int i, ret;
737 size_t addr_count;
738 const char *expire_time;
739 struct ldb_message *msg = ldb_msg_new(mem_ctx);
740 if (msg == NULL) goto failed;
742 /* make sure we don't put in corrupted records */
743 addr_count = winsdb_addr_list_length(rec->addresses);
744 if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
745 rec->state = WREPL_STATE_RELEASED;
747 if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
748 rec->type = WREPL_TYPE_MHOMED;
751 expire_time = ldb_timestring(msg, rec->expire_time);
752 if (!expire_time) {
753 goto failed;
756 msg->dn = winsdb_dn(msg, ldb, rec->name);
757 if (msg->dn == NULL) goto failed;
758 ret = ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
759 if (rec->name->name && *rec->name->name) {
760 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
762 if (rec->name->scope && *rec->name->scope) {
763 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
765 ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
766 ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
767 ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
768 ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
769 ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
770 ret |= ldb_msg_add_empty(msg, "expireTime", 0, NULL);
771 if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
772 ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
774 ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
775 ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
776 ret |= ldb_msg_add_empty(msg, "address", 0, NULL);
777 for (i=0;rec->addresses[i];i++) {
778 ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
780 if (rec->registered_by) {
781 ret |= ldb_msg_append_string(msg, "registeredBy", rec->registered_by, 0);
783 if (ret != LDB_SUCCESS) goto failed;
784 return msg;
786 failed:
787 talloc_free(msg);
788 return NULL;
792 save a WINS record into the database
794 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
796 struct ldb_message *msg;
797 struct ldb_context *wins_db = h->ldb;
798 TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
799 int trans = -1;
800 int ret;
802 trans = ldb_transaction_start(wins_db);
803 if (trans != LDB_SUCCESS) goto failed;
805 if (flags & WINSDB_FLAG_ALLOC_VERSION) {
806 /* passing '0' means auto-allocate a new one */
807 rec->version = winsdb_set_maxVersion(h, 0);
808 if (rec->version == 0) goto failed;
810 if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
811 rec->wins_owner = h->local_owner;
814 msg = winsdb_message(wins_db, rec, tmp_ctx);
815 if (msg == NULL) goto failed;
816 ret = ldb_add(wins_db, msg);
817 if (ret != LDB_SUCCESS) goto failed;
819 trans = ldb_transaction_commit(wins_db);
820 if (trans != LDB_SUCCESS) goto failed;
822 wins_hook(h, rec, WINS_HOOK_ADD, h->hook_script);
824 talloc_free(tmp_ctx);
825 return NBT_RCODE_OK;
827 failed:
828 if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
829 talloc_free(tmp_ctx);
830 return NBT_RCODE_SVR;
835 modify a WINS record in the database
837 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
839 struct ldb_message *msg;
840 struct ldb_context *wins_db = h->ldb;
841 TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
842 int trans;
843 int ret;
844 unsigned int i;
846 trans = ldb_transaction_start(wins_db);
847 if (trans != LDB_SUCCESS) goto failed;
849 if (flags & WINSDB_FLAG_ALLOC_VERSION) {
850 /* passing '0' means auto-allocate a new one */
851 rec->version = winsdb_set_maxVersion(h, 0);
852 if (rec->version == 0) goto failed;
854 if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
855 rec->wins_owner = h->local_owner;
858 msg = winsdb_message(wins_db, rec, tmp_ctx);
859 if (msg == NULL) goto failed;
861 for (i=0;i<msg->num_elements;i++) {
862 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
865 ret = ldb_modify(wins_db, msg);
866 if (ret != LDB_SUCCESS) goto failed;
868 trans = ldb_transaction_commit(wins_db);
869 if (trans != LDB_SUCCESS) goto failed;
871 wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);
873 talloc_free(tmp_ctx);
874 return NBT_RCODE_OK;
876 failed:
877 if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
878 talloc_free(tmp_ctx);
879 return NBT_RCODE_SVR;
884 delete a WINS record from the database
886 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
888 struct ldb_context *wins_db = h->ldb;
889 TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
890 struct ldb_dn *dn;
891 int trans;
892 int ret;
894 trans = ldb_transaction_start(wins_db);
895 if (trans != LDB_SUCCESS) goto failed;
897 dn = winsdb_dn(tmp_ctx, wins_db, rec->name);
898 if (dn == NULL) goto failed;
900 ret = ldb_delete(wins_db, dn);
901 if (ret != LDB_SUCCESS) goto failed;
903 trans = ldb_transaction_commit(wins_db);
904 if (trans != LDB_SUCCESS) goto failed;
906 wins_hook(h, rec, WINS_HOOK_DELETE, h->hook_script);
908 talloc_free(tmp_ctx);
909 return NBT_RCODE_OK;
911 failed:
912 if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
913 talloc_free(tmp_ctx);
914 return NBT_RCODE_SVR;
917 static bool winsdb_check_or_add_module_list(struct tevent_context *ev_ctx,
918 struct loadparm_context *lp_ctx, struct winsdb_handle *h,
919 const char *wins_path)
921 int trans;
922 int ret;
923 struct ldb_dn *dn;
924 struct ldb_result *res = NULL;
925 struct ldb_message *msg = NULL;
926 TALLOC_CTX *tmp_ctx = talloc_new(h);
927 unsigned int flags = 0;
929 trans = ldb_transaction_start(h->ldb);
930 if (trans != LDB_SUCCESS) goto failed;
932 /* check if we have a special @MODULES record already */
933 dn = ldb_dn_new(tmp_ctx, h->ldb, "@MODULES");
934 if (!dn) goto failed;
936 /* find the record in the WINS database */
937 ret = ldb_search(h->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
938 if (ret != LDB_SUCCESS) goto failed;
940 if (res->count > 0) goto skip;
942 /* if there's no record, add one */
943 msg = ldb_msg_new(tmp_ctx);
944 if (!msg) goto failed;
945 msg->dn = dn;
947 ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
948 if (ret != LDB_SUCCESS) goto failed;
950 ret = ldb_add(h->ldb, msg);
951 if (ret != LDB_SUCCESS) goto failed;
953 trans = ldb_transaction_commit(h->ldb);
954 if (trans != LDB_SUCCESS) goto failed;
956 /* close and reopen the database, with the modules */
957 trans = LDB_ERR_OTHER;
958 talloc_free(h->ldb);
959 h->ldb = NULL;
961 if (lpcfg_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
962 flags |= LDB_FLG_NOSYNC;
965 h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, wins_path,
966 NULL, NULL, flags);
967 if (!h->ldb) goto failed;
969 talloc_free(tmp_ctx);
970 return true;
972 skip:
973 if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
974 talloc_free(tmp_ctx);
975 return true;
977 failed:
978 if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
979 talloc_free(tmp_ctx);
980 return false;
983 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx,
984 struct tevent_context *ev_ctx,
985 struct loadparm_context *lp_ctx,
986 const char *owner,
987 enum winsdb_handle_caller caller)
989 const struct loadparm_substitution *lp_sub =
990 lpcfg_noop_substitution();
991 struct winsdb_handle *h = NULL;
992 unsigned int flags = 0;
993 bool ret;
994 int ldb_err;
995 char *wins_path;
997 h = talloc_zero(mem_ctx, struct winsdb_handle);
998 if (!h) return NULL;
1000 wins_path = lpcfg_state_path(h, lp_ctx, "wins.ldb");
1002 if (lpcfg_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
1003 flags |= LDB_FLG_NOSYNC;
1006 h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, wins_path,
1007 NULL, NULL, flags);
1008 if (!h->ldb) goto failed;
1010 h->caller = caller;
1011 h->hook_script = lpcfg_wins_hook(lp_ctx, lp_sub, h);
1013 h->local_owner = talloc_strdup(h, owner);
1014 if (!h->local_owner) goto failed;
1016 /* make sure the module list is available and used */
1017 ret = winsdb_check_or_add_module_list(ev_ctx, lp_ctx, h, wins_path);
1018 if (!ret) goto failed;
1020 ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1021 if (ldb_err != LDB_SUCCESS) goto failed;
1023 return h;
1024 failed:
1025 talloc_free(h);
1026 return NULL;