r11036: r10349@SERNOX: metze | 2005-09-20 15:38:31 +0200
[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    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 "db_wrap.h"
29 #include "system/time.h"
30
31 /*
32   return the new maxVersion and save it
33 */
34 static uint64_t winsdb_allocate_version(struct wins_server *winssrv)
35 {
36         int ret;
37         struct ldb_context *ldb = winssrv->wins_db;
38         struct ldb_dn *dn;
39         struct ldb_message **res = NULL;
40         struct ldb_message *msg = NULL;
41         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
42         uint64_t maxVersion = 0;
43
44         dn = ldb_dn_explode(tmp_ctx, "CN=VERSION");
45         if (!dn) goto failed;
46
47         ret |= ldb_msg_add_string(msg, "objectClass", "winsEntry");
48         ret |= ldb_msg_add_fmt(msg, "minVersion", "%llu", winssrv->min_version);
49         ret |= ldb_msg_add_fmt(msg, "maxVersion", "%llu", winssrv->max_version);
50         if (ret != 0) goto failed;
51
52         if (ret == 1) {
53                 maxVersion = ldb_msg_find_uint64(res[0], "maxVersion", 0);
54         }
55         maxVersion++;
56
57         msg = ldb_msg_new(tmp_ctx);
58         if (!msg) goto failed;
59         msg->dn = dn;
60
61
62         ret = ldb_msg_add_empty(ldb, msg, "maxVersion", LDB_FLAG_MOD_REPLACE);
63         if (ret != 0) goto failed;
64         ret = ldb_msg_add_fmt(ldb, msg, "maxVersion", "%llu", maxVersion);
65         if (ret != 0) goto failed;
66
67         ret = ldb_modify(ldb, msg);
68         if (ret != 0) ret = ldb_add(ldb, msg);
69         if (ret != 0) goto failed;
70
71         talloc_free(tmp_ctx);
72         return maxVersion;
73
74 failed:
75         talloc_free(tmp_ctx);
76         return 0;
77 }
78
79 /*
80   return a DN for a nbt_name
81 */
82 static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct nbt_name *name)
83 {
84         struct ldb_dn *dn;
85
86         dn = ldb_dn_string_compose(mem_ctx, NULL, "type=%02x", name->type);
87         if (dn && name->name && *name->name) {
88                 dn = ldb_dn_string_compose(mem_ctx, dn, "name=%s", name->name);
89         }
90         if (dn && name->scope && *name->scope) {
91                 dn = ldb_dn_string_compose(mem_ctx, dn, "scope=%s", name->scope);
92         }
93         return dn;
94 }
95
96 struct nbt_name *winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
97 {
98         struct nbt_name *name;
99         uint32_t cur = 0;
100
101         name = talloc(mem_ctx, struct nbt_name);
102         if (!name) goto failed;
103
104         if (dn->comp_num > 3) {
105                 goto failed;
106         }
107
108         if (dn->comp_num > cur && strcasecmp("scope", dn->components[cur].name) == 0) {
109                 name->scope     = talloc_steal(name, dn->components[cur].value.data);
110                 cur++;
111         } else {
112                 name->scope     = NULL;
113         }
114
115         if (dn->comp_num > cur && strcasecmp("name", dn->components[cur].name) == 0) {
116                 name->name      = talloc_steal(name, dn->components[cur].value.data);
117                 cur++;
118         } else {
119                 name->name      = talloc_strdup(name, "");
120                 if (!name->name) goto failed;
121         }
122
123         if (dn->comp_num > cur && strcasecmp("type", dn->components[cur].name) == 0) {
124                 name->type      = strtoul((char *)dn->components[cur].value.data, NULL, 16);
125                 cur++;
126         } else {
127                 goto failed;
128         }
129
130         return name;
131 failed:
132         talloc_free(name);
133         return NULL;
134 }
135
136 /*
137  decode the winsdb_addr("address") attribute:
138  "172.31.1.1" or 
139  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
140  are valid records
141 */
142 static BOOL winsdb_remove_version(struct wins_server *winssrv, uint64_t version)
143 {
144         if (version == winssrv->min_version) {
145                 winssrv->min_version++;
146                 return winsdb_save_version(winssrv);
147         }
148
149         return True;
150 }
151
152 /*
153  encode the winsdb_addr("address") attribute like this:
154  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
155 */
156 static int ldb_msg_add_winsdb_addr(struct ldb_context *ldb, struct ldb_message *msg, 
157                                    const char *attr_name, struct winsdb_addr *addr)
158 {
159         struct ldb_val val;
160         const char *str;
161
162         dn = ldb_dn_string_compose(mem_ctx, NULL, "type=%02x", name->type);
163
164         addresses[len]->address = talloc_strdup(addresses[len], address);
165         if (!addresses[len]->address) {
166                 talloc_free(addresses);
167                 return NULL;
168         }
169
170         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
171         if (!addresses[len]->wins_owner) {
172                 talloc_free(addresses);
173                 return NULL;
174         }
175
176         addresses[len]->expire_time = expire_time;
177
178         addresses[len+1] = NULL;
179
180         return addresses;
181 }
182
183 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
184 {
185         size_t i;
186
187         for (i=0; addresses[i]; i++) {
188                 if (strcmp(addresses[i]->address, address) == 0) {
189                         break;
190                 }
191         }
192         if (!addresses[i]) return;
193
194         for (; addresses[i]; i++) {
195                 addresses[i] = addresses[i+1];
196         }
197
198         return;
199 }
200
201 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
202 {
203         size_t i;
204
205         for (i=0; addresses[i]; i++) {
206                 if (strcmp(addresses[i]->address, address) == 0) {
207                         return addresses[i];
208                 }
209         }
210
211         return NULL;
212 }
213
214 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
215 {
216         size_t i;
217         for (i=0; addresses[i]; i++);
218         return i;
219 }
220
221 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
222 {
223         size_t len = winsdb_addr_list_length(addresses);
224         const char **str_list;
225         size_t i;
226
227         str_list = talloc_array(mem_ctx, const char *, len + 1);
228         if (!str_list) return NULL;
229
230         for (i=0; i < len; i++) {
231                 str_list[i] = talloc_strdup(str_list, addresses[i]->address);
232                 if (!str_list[i]) {
233                         talloc_free(str_list);
234                         return NULL;
235                 }
236         }
237
238         str_list[len] = NULL;
239         return str_list;
240 }
241
242 /*
243  decode the winsdb_addr("address") attribute:
244  "172.31.1.1" or 
245  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
246  are valid records
247 */
248 static struct winsdb_addr *winsdb_addr_decode(struct winsdb_record *rec, TALLOC_CTX *mem_ctx, struct ldb_val *val)
249 {
250         struct winsdb_addr *addr;
251         char *address;
252         char *wins_owner;
253         char *expire_time;
254         char *p;
255
256         addr = talloc(mem_ctx, struct winsdb_addr);
257         if (!addr) return NULL;
258
259         address = (char *)val->data;
260
261         p = strchr(address, ';');
262         if (!p) {
263                 /* support old entries, with only the address */
264                 addr->address           = talloc_steal(addr, val->data);
265                 addr->wins_owner        = rec->wins_owner;
266                 addr->expire_time       = rec->expire_time;
267                 return addr;
268         }
269
270         *p = '\0';p++;
271         addr->address = talloc_strdup(addr, address);
272         if (!addr->address) {
273                 talloc_free(addr);
274                 return NULL;
275         }
276
277         if (strncmp("winsOwner:", p, 10) != 0) {
278                 /* invalid record */
279                 talloc_free(addr);
280                 return NULL;
281         }
282         wins_owner = p + 10;
283         p = strchr(wins_owner, ';');
284         if (!p) {
285                 /* invalid record */
286                 talloc_free(addr);
287                 return NULL;
288         }
289
290         *p = '\0';p++;
291         addr->wins_owner = talloc_strdup(addr, wins_owner);
292         if (!addr->wins_owner) {
293                 talloc_free(addr);
294                 return NULL;
295         }
296
297         if (strncmp("expireTime:", p, 11) != 0) {
298                 /* invalid record */
299                 talloc_free(addr);
300                 return NULL;
301         }
302
303         expire_time = p + 11;
304
305         addr->expire_time = ldap_string_to_time(expire_time);
306
307         return addr;
308 }
309
310 /*
311  encode the winsdb_addr("address") attribute like this:
312  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
313 */
314 static int ldb_msg_add_winsdb_addr(struct ldb_context *ldb, struct ldb_message *msg, 
315                                    const char *attr_name, struct winsdb_addr *addr)
316 {
317         struct ldb_val val;
318         const char *str;
319
320         str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s",
321                               addr->address, addr->wins_owner,
322                               ldap_timestring(msg, addr->expire_time));
323         if (!str) return -1;
324
325         val.data = discard_const_p(uint8_t, str);
326         val.length = strlen(str);
327
328         return ldb_msg_add_value(ldb, msg, attr_name, &val);
329 }
330
331 struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
332 {
333         struct winsdb_addr **addresses;
334
335         addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
336         if (!addresses) return NULL;
337
338         addresses[0] = NULL;
339
340         return addresses;
341 }
342
343 struct winsdb_addr **winsdb_addr_list_add(struct winsdb_addr **addresses, const char *address,
344                                           const char *wins_owner, time_t expire_time)
345 {
346         size_t len = winsdb_addr_list_length(addresses);
347
348         addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
349         if (!addresses) return NULL;
350
351         addresses[len] = talloc(addresses, struct winsdb_addr);
352         if (!addresses[len]) {
353                 talloc_free(addresses);
354                 return NULL;
355         }
356
357         addresses[len]->address = talloc_strdup(addresses[len], address);
358         if (!addresses[len]->address) {
359                 talloc_free(addresses);
360                 return NULL;
361         }
362
363         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
364         if (!addresses[len]->wins_owner) {
365                 talloc_free(addresses);
366                 return NULL;
367         }
368
369         addresses[len]->expire_time = expire_time;
370
371         addresses[len+1] = NULL;
372
373         return addresses;
374 }
375
376 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
377 {
378         size_t i;
379
380         for (i=0; addresses[i]; i++) {
381                 if (strcmp(addresses[i]->address, address) == 0) {
382                         break;
383                 }
384         }
385         if (!addresses[i]) return;
386
387         for (; addresses[i]; i++) {
388                 addresses[i] = addresses[i+1];
389         }
390
391         return;
392 }
393
394 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
395 {
396         size_t i;
397
398         for (i=0; addresses[i]; i++) {
399                 if (strcmp(addresses[i]->address, address) == 0) {
400                         return addresses[i];
401                 }
402         }
403
404         return NULL;
405 }
406
407 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
408 {
409         size_t i;
410         for (i=0; addresses[i]; i++);
411         return i;
412 }
413
414 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
415 {
416         size_t len = winsdb_addr_list_length(addresses);
417         const char **str_list;
418         size_t i;
419
420         str_list = talloc_array(mem_ctx, const char *, len + 1);
421         if (!str_list) return NULL;
422
423         for (i=0; i < len; i++) {
424                 str_list[i] = talloc_strdup(str_list, addresses[i]->address);
425                 if (!str_list[i]) {
426                         talloc_free(str_list);
427                         return NULL;
428                 }
429         }
430
431         str_list[len] = NULL;
432         return str_list;
433 }
434
435 /*
436   load a WINS entry from the database
437 */
438 struct winsdb_record *winsdb_load(struct wins_server *winssrv, 
439                                   struct nbt_name *name, TALLOC_CTX *mem_ctx)
440 {
441         struct ldb_message **res = NULL;
442         int ret;
443         struct winsdb_record *rec;
444         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
445
446         /* find the record in the WINS database */
447         ret = ldb_search(winssrv->wins_db, winsdb_dn(tmp_ctx, name), LDB_SCOPE_BASE, 
448                          NULL, NULL, &res);
449         if (res != NULL) {
450                 talloc_steal(tmp_ctx, res);
451         }
452         if (ret != 1) goto failed;
453
454         rec = winsdb_record(res[0], tmp_ctx);
455         if (rec == NULL) goto failed;
456         rec->name           = name;
457
458         /* see if it has already expired */
459         if (rec->state == WINS_REC_ACTIVE &&
460             rec->expire_time <= time(NULL)) {
461                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
462                          nbt_name_string(tmp_ctx, rec->name), timestring(tmp_ctx, rec->expire_time)));
463                 rec->state = WINS_REC_RELEASED;
464         }
465
466         talloc_steal(mem_ctx, rec);
467         talloc_free(tmp_ctx);
468         return rec;
469
470 failed:
471         talloc_free(tmp_ctx);
472         return NULL;
473 }
474
475 struct winsdb_record *winsdb_record(struct ldb_message *msg, TALLOC_CTX *mem_ctx)
476 {
477         struct winsdb_record *rec;
478         struct ldb_message_element *el;
479         uint32_t i;
480
481         rec = talloc(mem_ctx, struct winsdb_record);
482         if (rec == NULL) goto failed;
483
484         /* parse it into a more convenient winsdb_record structure */
485         rec->name           = name;
486         rec->state          = ldb_msg_find_int(res[0], "active", WINS_REC_RELEASED);
487         rec->nb_flags       = ldb_msg_find_int(res[0], "nbFlags", 0);
488         rec->expire_time    = ldb_string_to_time(ldb_msg_find_string(res[0], "expires", NULL));
489         rec->registered_by  = ldb_msg_find_string(res[0], "registeredBy", NULL);
490         rec->version        = ldb_msg_find_uint64(res[0], "version", 0);
491         talloc_steal(rec, rec->wins_owner);
492         talloc_steal(rec, rec->registered_by);
493
494         if (!rec->wins_owner) rec->wins_owner = WINSDB_OWNER_LOCAL;
495
496         el = ldb_msg_find_element(msg, "address");
497         if (el == NULL) goto failed;
498
499         rec->addresses     = talloc_array(rec, struct winsdb_addr *, el->num_values+1);
500         if (rec->addresses == NULL) goto failed;
501
502         for (i=0;i<el->num_values;i++) {
503                 rec->addresses[i] = winsdb_addr_decode(rec, rec->addresses, &el->values[i]);
504                 if (rec->addresses[i] == NULL) goto failed;
505         }
506         rec->addresses[i] = NULL;
507
508         return rec;
509 failed:
510         talloc_free(rec);
511         return NULL;
512 }
513
514 /*
515   form a ldb_message from a winsdb_record
516 */
517 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
518                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
519 {
520         int i, ret=0;
521         struct ldb_message *msg = ldb_msg_new(mem_ctx);
522         if (msg == NULL) goto failed;
523
524         msg->dn = winsdb_dn(msg, rec->name);
525         if (msg->dn == NULL) goto failed;
526         ret |= ldb_msg_add_fmt(msg, "objectClass", "wins");
527         ret |= ldb_msg_add_fmt(msg, "active", "%u", rec->state);
528         ret |= ldb_msg_add_fmt(msg, "nbFlags", "0x%04x", rec->nb_flags);
529         ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
530         ret |= ldb_msg_add_string(msg, "expires", 
531                                   ldb_timestring(msg, rec->expire_time));
532         ret |= ldb_msg_add_fmt(msg, "version", "%llu", rec->version);
533         for (i=0;rec->addresses[i];i++) {
534                 ret |= ldb_msg_add_string(msg, "address", rec->addresses[i]);
535         }
536         if (ret != 0) goto failed;
537         return msg;
538
539 failed:
540         talloc_free(msg);
541         return NULL;
542 }
543
544 /*
545   save a WINS record into the database
546 */
547 uint8_t winsdb_add(struct wins_server *winssrv, struct winsdb_record *rec)
548 {
549         struct ldb_context *ldb = winssrv->wins_db;
550         struct ldb_message *msg;
551         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
552         int trans = -1;
553         int ret = 0;
554
555
556         trans = ldb_transaction_start(ldb);
557         if (trans != LDB_SUCCESS) goto failed;
558
559         rec->version = winsdb_allocate_version(winssrv);
560         if (rec->version == 0) goto failed;
561
562         msg = winsdb_message(winssrv->wins_db, rec, tmp_ctx);
563         if (msg == NULL) goto failed;
564         ret = ldb_add(ldb, msg);
565         if (ret != 0) goto failed;
566
567         trans = ldb_transaction_commit(ldb);
568         if (trans != LDB_SUCCESS) goto failed;
569
570         talloc_free(tmp_ctx);
571         return NBT_RCODE_OK;
572
573 failed:
574         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
575         talloc_free(tmp_ctx);
576         return NBT_RCODE_SVR;
577 }
578
579
580 /*
581   modify a WINS record in the database
582 */
583 uint8_t winsdb_modify(struct wins_server *winssrv, struct winsdb_record *rec)
584 {
585         struct ldb_context *ldb = winssrv->wins_db;
586         struct ldb_message *msg;
587         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
588         int trans;
589         int ret;
590         int i;
591
592         trans = ldb_transaction_start(ldb);
593         if (trans != LDB_SUCCESS) goto failed;
594
595         rec->version = winsdb_allocate_version(winssrv);
596         if (rec->version == 0) goto failed;
597         rec->wins_owner = WINSDB_OWNER_LOCAL;
598
599         msg = winsdb_message(winssrv->wins_db, rec, tmp_ctx);
600         if (msg == NULL) goto failed;
601
602         for (i=0;i<msg->num_elements;i++) {
603                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
604         }
605
606         ret = ldb_modify(ldb, msg);
607         if (ret != 0) goto failed;
608
609         trans = ldb_transaction_commit(ldb);
610         if (trans != LDB_SUCCESS) goto failed;
611
612         talloc_free(tmp_ctx);
613         return NBT_RCODE_OK;
614
615 failed:
616         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
617         talloc_free(tmp_ctx);
618         return NBT_RCODE_SVR;
619 }
620
621
622 /*
623   delete a WINS record from the database
624 */
625 uint8_t winsdb_delete(struct wins_server *winssrv, struct winsdb_record *rec)
626 {
627         struct ldb_context *ldb = winssrv->wins_db;
628         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
629         const struct ldb_dn *dn;
630         int trans;
631         int ret;
632
633         trans = ldb_transaction_start(ldb);
634         if (trans != LDB_SUCCESS) goto failed;
635
636         if(!winsdb_remove_version(winssrv, rec->version))
637                 goto failed;
638
639         dn = winsdb_dn(tmp_ctx, rec->name);
640         if (dn == NULL) goto failed;
641
642         ret = ldb_delete(ldb, dn);
643         if (ret != 0) goto failed;
644
645         trans = ldb_transaction_commit(ldb);
646         if (trans != LDB_SUCCESS) goto failed;
647
648         talloc_free(tmp_ctx);
649         return NBT_RCODE_OK;
650
651 failed:
652         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
653         talloc_free(tmp_ctx);
654         return NBT_RCODE_SVR;
655 }
656
657 struct ldb_context *winsdb_connect(TALLOC_CTX *mem_ctx)
658 {
659         return ldb_wrap_connect(mem_ctx, lp_wins_url(), 0, NULL);
660 }