r12897: make it possible to use nosync transaction to speed things up,
[kai/samba-autobuild/.git] / source4 / nbt_server / wins / winsdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    WINS database routines
5
6    Copyright (C) Andrew Tridgell        2005
7    Copyright (C) Stefan Metzmacher      2005
8       
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "nbt_server/nbt_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "lib/ldb/include/ldb.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "system/time.h"
30 #include "auth/auth.h"
31
32 uint64_t winsdb_get_maxVersion(struct winsdb_handle *h)
33 {
34         int ret;
35         struct ldb_context *ldb = h->ldb;
36         struct ldb_dn *dn;
37         struct ldb_result *res = NULL;
38         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
39         uint64_t maxVersion = 0;
40
41         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
42         if (!dn) goto failed;
43
44         /* find the record in the WINS database */
45         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, 
46                          NULL, NULL, &res);
47         if (ret != LDB_SUCCESS) goto failed;
48         talloc_steal(tmp_ctx, res);
49         if (res->count > 1) goto failed;
50
51         if (res->count == 1) {
52                 maxVersion = ldb_msg_find_uint64(res->msgs[0], "maxVersion", 0);
53         }
54
55 failed:
56         talloc_free(tmp_ctx);
57         return maxVersion;
58 }
59
60 /*
61  if newVersion == 0 return the old maxVersion + 1 and save it
62  if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
63 */
64 uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
65 {
66         int trans;
67         int ret;
68         struct ldb_dn *dn;
69         struct ldb_result *res = NULL;
70         struct ldb_message *msg = NULL;
71         struct ldb_context *wins_db = h->ldb;
72         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
73         uint64_t oldMaxVersion = 0;
74
75         trans = ldb_transaction_start(wins_db);
76         if (trans != LDB_SUCCESS) goto failed;
77
78         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
79         if (!dn) goto failed;
80
81         /* find the record in the WINS database */
82         ret = ldb_search(wins_db, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
83
84         if (ret != LDB_SUCCESS) goto failed;
85         if (res->count > 1) goto failed;
86
87         talloc_steal(tmp_ctx, res);
88
89         if (res->count == 1) {
90                 oldMaxVersion = ldb_msg_find_uint64(res->msgs[0], "maxVersion", 0);
91         }
92
93         if (newMaxVersion == 0) {
94                 newMaxVersion = oldMaxVersion + 1;
95         } else {
96                 newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
97         }
98
99         msg = ldb_msg_new(tmp_ctx);
100         if (!msg) goto failed;
101         msg->dn = dn;
102
103
104         ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
105         if (ret != 0) goto failed;
106         ret = ldb_msg_add_string(msg, "objectClass", "winsMaxVersion");
107         if (ret != 0) goto failed;
108         ret = ldb_msg_add_empty(msg, "maxVersion", LDB_FLAG_MOD_REPLACE);
109         if (ret != 0) goto failed;
110         ret = ldb_msg_add_fmt(msg, "maxVersion", "%llu", (long long)newMaxVersion);
111         if (ret != 0) goto failed;
112
113         ret = ldb_modify(wins_db, msg);
114         if (ret != 0) ret = ldb_add(wins_db, msg);
115         if (ret != 0) goto failed;
116
117         trans = ldb_transaction_commit(wins_db);
118         if (trans != LDB_SUCCESS) goto failed;
119
120         talloc_free(tmp_ctx);
121         return newMaxVersion;
122
123 failed:
124         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
125         talloc_free(tmp_ctx);
126         return 0;
127 }
128
129 uint64_t winsdb_get_seqnumber(struct winsdb_handle *h)
130 {
131         int ret;
132         struct ldb_context *ldb = h->ldb;
133         struct ldb_dn *dn;
134         struct ldb_result *res = NULL;
135         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
136         uint64_t seqnumber = 0;
137
138         dn = ldb_dn_explode(tmp_ctx, "@BASEINFO");
139         if (!dn) goto failed;
140
141         /* find the record in the WINS database */
142         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, 
143                          NULL, NULL, &res);
144         if (ret != LDB_SUCCESS) goto failed;
145         talloc_steal(tmp_ctx, res);
146         if (res->count > 1) goto failed;
147
148         if (res->count == 1) {
149                 seqnumber = ldb_msg_find_uint64(res->msgs[0], "sequenceNumber", 0);
150         }
151
152 failed:
153         talloc_free(tmp_ctx);
154         return seqnumber;
155 }
156
157 /*
158   return a DN for a nbt_name
159 */
160 static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct nbt_name *name)
161 {
162         struct ldb_dn *dn;
163
164         dn = ldb_dn_string_compose(mem_ctx, NULL, "type=0x%02X", name->type);
165         if (dn && name->name && *name->name) {
166                 dn = ldb_dn_string_compose(mem_ctx, dn, "name=%s", name->name);
167         }
168         if (dn && name->scope && *name->scope) {
169                 dn = ldb_dn_string_compose(mem_ctx, dn, "scope=%s", name->scope);
170         }
171         return dn;
172 }
173
174 static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
175 {
176         NTSTATUS status;
177         struct nbt_name *name;
178         uint32_t cur = 0;
179
180         name = talloc(mem_ctx, struct nbt_name);
181         if (!name) {
182                 status = NT_STATUS_NO_MEMORY;
183                 goto failed;
184         }
185
186         if (dn->comp_num > 3) {
187                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
188                 goto failed;
189         }
190
191         if (dn->comp_num > cur && strcasecmp("scope", dn->components[cur].name) == 0) {
192                 name->scope     = talloc_steal(name, dn->components[cur].value.data);
193                 cur++;
194         } else {
195                 name->scope     = NULL;
196         }
197
198         if (dn->comp_num > cur && strcasecmp("name", dn->components[cur].name) == 0) {
199                 name->name      = talloc_steal(name, dn->components[cur].value.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 (dn->comp_num > cur && strcasecmp("type", dn->components[cur].name) == 0) {
210                 name->type      = strtoul((char *)dn->components[cur].value.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           = 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  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
315 */
316 static int ldb_msg_add_winsdb_addr(struct ldb_message *msg, 
317                                    const char *attr_name, struct winsdb_addr *addr)
318 {
319         struct ldb_val val;
320         const char *str;
321         char *expire_time;
322
323         expire_time = ldb_timestring(msg, addr->expire_time);
324         if (!expire_time) return -1;
325         str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s;",
326                               addr->address, addr->wins_owner,
327                               expire_time);
328         talloc_free(expire_time);
329         if (!str) return -1;
330
331         val.data = discard_const_p(uint8_t, str);
332         val.length = strlen(str);
333
334         return ldb_msg_add_value(msg, attr_name, &val);
335 }
336
337 struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
338 {
339         struct winsdb_addr **addresses;
340
341         addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
342         if (!addresses) return NULL;
343
344         addresses[0] = NULL;
345
346         return addresses;
347 }
348
349 struct winsdb_addr **winsdb_addr_list_add(struct winsdb_addr **addresses, const char *address,
350                                           const char *wins_owner, time_t expire_time)
351 {
352         size_t len = winsdb_addr_list_length(addresses);
353
354         addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
355         if (!addresses) return NULL;
356
357         addresses[len] = talloc(addresses, struct winsdb_addr);
358         if (!addresses[len]) {
359                 talloc_free(addresses);
360                 return NULL;
361         }
362
363         addresses[len]->address = talloc_strdup(addresses[len], address);
364         if (!addresses[len]->address) {
365                 talloc_free(addresses);
366                 return NULL;
367         }
368
369         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
370         if (!addresses[len]->wins_owner) {
371                 talloc_free(addresses);
372                 return NULL;
373         }
374
375         addresses[len]->expire_time = expire_time;
376
377         addresses[len+1] = NULL;
378
379         return addresses;
380 }
381
382 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
383 {
384         size_t i;
385
386         for (i=0; addresses[i]; i++) {
387                 if (strcmp(addresses[i]->address, address) == 0) {
388                         break;
389                 }
390         }
391         if (!addresses[i]) return;
392
393         for (; addresses[i]; i++) {
394                 addresses[i] = addresses[i+1];
395         }
396
397         return;
398 }
399
400 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
401 {
402         size_t i;
403
404         for (i=0; addresses[i]; i++) {
405                 if (strcmp(addresses[i]->address, address) == 0) {
406                         return addresses[i];
407                 }
408         }
409
410         return NULL;
411 }
412
413 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
414 {
415         size_t i;
416         for (i=0; addresses[i]; i++);
417         return i;
418 }
419
420 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
421 {
422         size_t len = winsdb_addr_list_length(addresses);
423         const char **str_list=NULL;
424         size_t i;
425
426         for (i=0; i < len; i++) {
427                 str_list = str_list_add(str_list, addresses[i]->address);
428                 if (!str_list[i]) {
429                         return NULL;
430                 }
431         }
432         talloc_steal(mem_ctx, str_list);
433         return str_list;
434 }
435
436 /*
437   load a WINS entry from the database
438 */
439 NTSTATUS winsdb_lookup(struct winsdb_handle *h, 
440                        struct nbt_name *name,
441                        TALLOC_CTX *mem_ctx,
442                        struct winsdb_record **_rec)
443 {
444         NTSTATUS status;
445         struct ldb_result *res = NULL;
446         int ret;
447         struct winsdb_record *rec;
448         struct ldb_context *wins_db = h->ldb;
449         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
450
451         /* find the record in the WINS database */
452         ret = ldb_search(wins_db, winsdb_dn(tmp_ctx, name), LDB_SCOPE_BASE, 
453                          NULL, NULL, &res);
454
455         if (ret != LDB_SUCCESS || res->count > 1) {
456                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
457                 goto failed;
458         } else if (res->count== 0) {
459                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
460                 goto failed;
461         }
462
463         talloc_steal(tmp_ctx, res);
464
465         status = winsdb_record(h, res->msgs[0], tmp_ctx, &rec);
466         if (!NT_STATUS_IS_OK(status)) goto failed;
467
468         /* see if it has already expired */
469         if (rec->state == WREPL_STATE_ACTIVE &&
470             rec->expire_time <= time(NULL)) {
471                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
472                          nbt_name_string(tmp_ctx, rec->name), timestring(tmp_ctx, rec->expire_time)));
473                 rec->state = WREPL_STATE_RELEASED;
474         }
475
476         talloc_steal(mem_ctx, rec);
477         talloc_free(tmp_ctx);
478         *_rec = rec;
479         return NT_STATUS_OK;
480
481 failed:
482         talloc_free(tmp_ctx);
483         return status;
484 }
485
486 NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, struct winsdb_record **_rec)
487 {
488         NTSTATUS status;
489         struct winsdb_record *rec;
490         struct ldb_message_element *el;
491         struct nbt_name *name;
492         uint32_t i, num_values;
493
494         rec = talloc(mem_ctx, struct winsdb_record);
495         if (rec == NULL) {
496                 status = NT_STATUS_NO_MEMORY;
497                 goto failed;
498         }
499
500         status = winsdb_nbt_name(rec, msg->dn, &name);
501         if (!NT_STATUS_IS_OK(status)) goto failed;
502
503         if (strlen(name->name) > 15) {
504                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
505                 goto failed;
506         }
507         if (name->scope && strlen(name->scope) > 238) {
508                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
509                 goto failed;
510         }
511
512         /* parse it into a more convenient winsdb_record structure */
513         rec->name               = name;
514         rec->type               = ldb_msg_find_int(msg, "recordType", WREPL_TYPE_UNIQUE);
515         rec->state              = ldb_msg_find_int(msg, "recordState", WREPL_STATE_RELEASED);
516         rec->node               = ldb_msg_find_int(msg, "nodeType", WREPL_NODE_B);
517         rec->is_static          = ldb_msg_find_int(msg, "isStatic", 0);
518         rec->expire_time        = ldb_string_to_time(ldb_msg_find_string(msg, "expireTime", NULL));
519         rec->version            = ldb_msg_find_uint64(msg, "versionID", 0);
520         rec->wins_owner         = ldb_msg_find_string(msg, "winsOwner", NULL);
521         rec->registered_by      = ldb_msg_find_string(msg, "registeredBy", NULL);
522         talloc_steal(rec, rec->wins_owner);
523         talloc_steal(rec, rec->registered_by);
524
525         if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
526                 rec->wins_owner = h->local_owner;
527         }
528
529         el = ldb_msg_find_element(msg, "address");
530         if (el) {
531                 num_values = el->num_values;
532         } else {
533                 num_values = 0;
534         }
535
536         if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
537                 if (num_values != 1) {
538                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
539                         goto failed;
540                 }
541         }
542         if (rec->state == WREPL_STATE_ACTIVE) {
543                 if (num_values < 1) {
544                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
545                         goto failed;
546                 }
547         }
548         if (num_values > 25) {
549                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
550                 goto failed;
551         }
552
553         rec->addresses     = talloc_array(rec, struct winsdb_addr *, num_values+1);
554         if (rec->addresses == NULL) {
555                 status = NT_STATUS_NO_MEMORY;
556                 goto failed;
557         }
558
559         for (i=0;i<num_values;i++) {
560                 status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[i]);
561                 if (!NT_STATUS_IS_OK(status)) goto failed;
562         }
563         rec->addresses[i] = NULL;
564
565         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
566                 rec->expire_time = get_time_t_max();
567                 for (i=0;rec->addresses[i];i++) {
568                         rec->addresses[i]->expire_time = rec->expire_time;
569                 }
570         }
571
572         *_rec = rec;
573         return NT_STATUS_OK;
574 failed:
575         if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
576                 DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_linearize(rec, msg->dn)));
577         }
578         talloc_free(rec);
579         return status;
580 }
581
582 /*
583   form a ldb_message from a winsdb_record
584 */
585 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
586                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
587 {
588         int i, ret=0;
589         size_t addr_count;
590         const char *expire_time;
591         struct ldb_message *msg = ldb_msg_new(mem_ctx);
592         if (msg == NULL) goto failed;
593
594         if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
595                 rec->expire_time = get_time_t_max();
596                 for (i=0;rec->addresses[i];i++) {
597                         rec->addresses[i]->expire_time = rec->expire_time;
598                 }
599         }
600
601         /* make sure we don't put in corrupted records */
602         addr_count = winsdb_addr_list_length(rec->addresses);
603         if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
604                 rec->state = WREPL_STATE_RELEASED;
605         }
606         if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
607                 rec->type = WREPL_TYPE_MHOMED;
608         }
609
610         expire_time = ldb_timestring(msg, rec->expire_time);
611         if (!expire_time) {
612                 goto failed;
613         }
614
615         msg->dn = winsdb_dn(msg, rec->name);
616         if (msg->dn == NULL) goto failed;
617         ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
618         if (rec->name->name && *rec->name->name) {
619                 ret |= ldb_msg_add_string(msg, "name", rec->name->name);
620         }
621         if (rec->name->scope && *rec->name->scope) {
622                 ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
623         }
624         ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
625         ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
626         ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
627         ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
628         ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
629         ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
630         ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
631         ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
632         ret |= ldb_msg_add_empty(msg, "address", 0);
633         for (i=0;rec->addresses[i];i++) {
634                 ret |= ldb_msg_add_winsdb_addr(msg, "address", rec->addresses[i]);
635         }
636         ret |= ldb_msg_add_empty(msg, "registeredBy", 0);
637         if (rec->registered_by) {
638                 ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
639                 if (ret != 0) goto failed;
640         }
641         return msg;
642
643 failed:
644         talloc_free(msg);
645         return NULL;
646 }
647
648 /*
649   save a WINS record into the database
650 */
651 uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
652 {
653         struct ldb_message *msg;
654         struct ldb_context *wins_db = h->ldb;
655         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
656         int trans = -1;
657         int ret = 0;
658
659         trans = ldb_transaction_start(wins_db);
660         if (trans != LDB_SUCCESS) goto failed;
661
662         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
663                 /* passing '0' means auto-allocate a new one */
664                 rec->version = winsdb_set_maxVersion(h, 0);
665                 if (rec->version == 0) goto failed;
666         }
667         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
668                 rec->wins_owner = h->local_owner;
669         }
670
671         msg = winsdb_message(wins_db, rec, tmp_ctx);
672         if (msg == NULL) goto failed;
673         ret = ldb_add(wins_db, msg);
674         if (ret != 0) goto failed;
675
676         trans = ldb_transaction_commit(wins_db);
677         if (trans != LDB_SUCCESS) goto failed;
678
679         talloc_free(tmp_ctx);
680         return NBT_RCODE_OK;
681
682 failed:
683         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
684         talloc_free(tmp_ctx);
685         return NBT_RCODE_SVR;
686 }
687
688
689 /*
690   modify a WINS record in the database
691 */
692 uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
693 {
694         struct ldb_message *msg;
695         struct ldb_context *wins_db = h->ldb;
696         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
697         int trans;
698         int ret;
699         int i;
700
701         trans = ldb_transaction_start(wins_db);
702         if (trans != LDB_SUCCESS) goto failed;
703
704         if (flags & WINSDB_FLAG_ALLOC_VERSION) {
705                 /* passing '0' means auto-allocate a new one */
706                 rec->version = winsdb_set_maxVersion(h, 0);
707                 if (rec->version == 0) goto failed;
708         }
709         if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
710                 rec->wins_owner = h->local_owner;
711         }
712
713         msg = winsdb_message(wins_db, rec, tmp_ctx);
714         if (msg == NULL) goto failed;
715
716         for (i=0;i<msg->num_elements;i++) {
717                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
718         }
719
720         ret = ldb_modify(wins_db, msg);
721         if (ret != 0) goto failed;
722
723         trans = ldb_transaction_commit(wins_db);
724         if (trans != LDB_SUCCESS) goto failed;
725
726         talloc_free(tmp_ctx);
727         return NBT_RCODE_OK;
728
729 failed:
730         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
731         talloc_free(tmp_ctx);
732         return NBT_RCODE_SVR;
733 }
734
735
736 /*
737   delete a WINS record from the database
738 */
739 uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
740 {
741         struct ldb_context *wins_db = h->ldb;
742         TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
743         const struct ldb_dn *dn;
744         int trans;
745         int ret;
746
747         trans = ldb_transaction_start(wins_db);
748         if (trans != LDB_SUCCESS) goto failed;
749
750         dn = winsdb_dn(tmp_ctx, rec->name);
751         if (dn == NULL) goto failed;
752
753         ret = ldb_delete(wins_db, dn);
754         if (ret != 0) goto failed;
755
756         trans = ldb_transaction_commit(wins_db);
757         if (trans != LDB_SUCCESS) goto failed;
758
759         talloc_free(tmp_ctx);
760         return NBT_RCODE_OK;
761
762 failed:
763         if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
764         talloc_free(tmp_ctx);
765         return NBT_RCODE_SVR;
766 }
767
768 struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx)
769 {
770         struct winsdb_handle *h = NULL;
771         const char *owner;
772         unsigned int flags = 0;
773
774         h = talloc(mem_ctx, struct winsdb_handle);
775         if (!h) return NULL;
776
777         if (lp_parm_bool(-1,"winsdb", "nosync", False)) {
778                 flags |= LDB_FLG_NOSYNC;
779         }
780
781         h->ldb = ldb_wrap_connect(h, lock_path(h, lp_wins_url()),
782                                   system_session(h), NULL, flags, NULL);
783         if (!h->ldb) goto failed;
784
785         owner = lp_parm_string(-1, "winsdb", "local_owner");
786         if (!owner) {
787                 owner = iface_n_ip(0);
788         }
789
790         h->local_owner = talloc_strdup(h, owner);
791         if (!h->local_owner) goto failed;
792
793         return h;
794 failed:
795         talloc_free(h);
796         return NULL;
797 }