r11034: r10344@SERNOX: metze | 2005-09-20 11:35:54 +0200
[samba.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 /*
97  decode the winsdb_addr("address") attribute:
98  "172.31.1.1" or 
99  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
100  are valid records
101 */
102 static BOOL winsdb_remove_version(struct wins_server *winssrv, uint64_t version)
103 {
104         if (version == winssrv->min_version) {
105                 winssrv->min_version++;
106                 return winsdb_save_version(winssrv);
107         }
108
109         return True;
110 }
111
112 /*
113  encode the winsdb_addr("address") attribute like this:
114  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
115 */
116 static int ldb_msg_add_winsdb_addr(struct ldb_context *ldb, struct ldb_message *msg, 
117                                    const char *attr_name, struct winsdb_addr *addr)
118 {
119         struct ldb_val val;
120         const char *str;
121
122         dn = ldb_dn_string_compose(mem_ctx, NULL, "type=%02x", name->type);
123
124         addresses[len]->address = talloc_strdup(addresses[len], address);
125         if (!addresses[len]->address) {
126                 talloc_free(addresses);
127                 return NULL;
128         }
129
130         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
131         if (!addresses[len]->wins_owner) {
132                 talloc_free(addresses);
133                 return NULL;
134         }
135
136         addresses[len]->expire_time = expire_time;
137
138         addresses[len+1] = NULL;
139
140         return addresses;
141 }
142
143 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
144 {
145         size_t i;
146
147         for (i=0; addresses[i]; i++) {
148                 if (strcmp(addresses[i]->address, address) == 0) {
149                         break;
150                 }
151         }
152         if (!addresses[i]) return;
153
154         for (; addresses[i]; i++) {
155                 addresses[i] = addresses[i+1];
156         }
157
158         return;
159 }
160
161 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
162 {
163         size_t i;
164
165         for (i=0; addresses[i]; i++) {
166                 if (strcmp(addresses[i]->address, address) == 0) {
167                         return addresses[i];
168                 }
169         }
170
171         return NULL;
172 }
173
174 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
175 {
176         size_t i;
177         for (i=0; addresses[i]; i++);
178         return i;
179 }
180
181 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
182 {
183         size_t len = winsdb_addr_list_length(addresses);
184         const char **str_list;
185         size_t i;
186
187         str_list = talloc_array(mem_ctx, const char *, len + 1);
188         if (!str_list) return NULL;
189
190         for (i=0; i < len; i++) {
191                 str_list[i] = talloc_strdup(str_list, addresses[i]->address);
192                 if (!str_list[i]) {
193                         talloc_free(str_list);
194                         return NULL;
195                 }
196         }
197
198         str_list[len] = NULL;
199         return str_list;
200 }
201
202 /*
203  decode the winsdb_addr("address") attribute:
204  "172.31.1.1" or 
205  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
206  are valid records
207 */
208 static struct winsdb_addr *winsdb_addr_decode(struct winsdb_record *rec, TALLOC_CTX *mem_ctx, struct ldb_val *val)
209 {
210         struct winsdb_addr *addr;
211         char *address;
212         char *wins_owner;
213         char *expire_time;
214         char *p;
215
216         addr = talloc(mem_ctx, struct winsdb_addr);
217         if (!addr) return NULL;
218
219         address = (char *)val->data;
220
221         p = strchr(address, ';');
222         if (!p) {
223                 /* support old entries, with only the address */
224                 addr->address           = talloc_steal(addr, val->data);
225                 addr->wins_owner        = rec->wins_owner;
226                 addr->expire_time       = rec->expire_time;
227                 return addr;
228         }
229
230         *p = '\0';p++;
231         addr->address = talloc_strdup(addr, address);
232         if (!addr->address) {
233                 talloc_free(addr);
234                 return NULL;
235         }
236
237         if (strncmp("winsOwner:", p, 10) != 0) {
238                 /* invalid record */
239                 talloc_free(addr);
240                 return NULL;
241         }
242         wins_owner = p + 10;
243         p = strchr(wins_owner, ';');
244         if (!p) {
245                 /* invalid record */
246                 talloc_free(addr);
247                 return NULL;
248         }
249
250         *p = '\0';p++;
251         addr->wins_owner = talloc_strdup(addr, wins_owner);
252         if (!addr->wins_owner) {
253                 talloc_free(addr);
254                 return NULL;
255         }
256
257         if (strncmp("expireTime:", p, 11) != 0) {
258                 /* invalid record */
259                 talloc_free(addr);
260                 return NULL;
261         }
262
263         expire_time = p + 11;
264
265         addr->expire_time = ldap_string_to_time(expire_time);
266
267         return addr;
268 }
269
270 /*
271  encode the winsdb_addr("address") attribute like this:
272  "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z"
273 */
274 static int ldb_msg_add_winsdb_addr(struct ldb_context *ldb, struct ldb_message *msg, 
275                                    const char *attr_name, struct winsdb_addr *addr)
276 {
277         struct ldb_val val;
278         const char *str;
279
280         str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s",
281                               addr->address, addr->wins_owner,
282                               ldap_timestring(msg, addr->expire_time));
283         if (!str) return -1;
284
285         val.data = discard_const_p(uint8_t, str);
286         val.length = strlen(str);
287
288         return ldb_msg_add_value(ldb, msg, attr_name, &val);
289 }
290
291 struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
292 {
293         struct winsdb_addr **addresses;
294
295         addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
296         if (!addresses) return NULL;
297
298         addresses[0] = NULL;
299
300         return addresses;
301 }
302
303 struct winsdb_addr **winsdb_addr_list_add(struct winsdb_addr **addresses, const char *address,
304                                           const char *wins_owner, time_t expire_time)
305 {
306         size_t len = winsdb_addr_list_length(addresses);
307
308         addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
309         if (!addresses) return NULL;
310
311         addresses[len] = talloc(addresses, struct winsdb_addr);
312         if (!addresses[len]) {
313                 talloc_free(addresses);
314                 return NULL;
315         }
316
317         addresses[len]->address = talloc_strdup(addresses[len], address);
318         if (!addresses[len]->address) {
319                 talloc_free(addresses);
320                 return NULL;
321         }
322
323         addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
324         if (!addresses[len]->wins_owner) {
325                 talloc_free(addresses);
326                 return NULL;
327         }
328
329         addresses[len]->expire_time = expire_time;
330
331         addresses[len+1] = NULL;
332
333         return addresses;
334 }
335
336 void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
337 {
338         size_t i;
339
340         for (i=0; addresses[i]; i++) {
341                 if (strcmp(addresses[i]->address, address) == 0) {
342                         break;
343                 }
344         }
345         if (!addresses[i]) return;
346
347         for (; addresses[i]; i++) {
348                 addresses[i] = addresses[i+1];
349         }
350
351         return;
352 }
353
354 struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
355 {
356         size_t i;
357
358         for (i=0; addresses[i]; i++) {
359                 if (strcmp(addresses[i]->address, address) == 0) {
360                         return addresses[i];
361                 }
362         }
363
364         return NULL;
365 }
366
367 size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
368 {
369         size_t i;
370         for (i=0; addresses[i]; i++);
371         return i;
372 }
373
374 const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
375 {
376         size_t len = winsdb_addr_list_length(addresses);
377         const char **str_list;
378         size_t i;
379
380         str_list = talloc_array(mem_ctx, const char *, len + 1);
381         if (!str_list) return NULL;
382
383         for (i=0; i < len; i++) {
384                 str_list[i] = talloc_strdup(str_list, addresses[i]->address);
385                 if (!str_list[i]) {
386                         talloc_free(str_list);
387                         return NULL;
388                 }
389         }
390
391         str_list[len] = NULL;
392         return str_list;
393 }
394
395 /*
396   load a WINS entry from the database
397 */
398 struct winsdb_record *winsdb_load(struct wins_server *winssrv, 
399                                   struct nbt_name *name, TALLOC_CTX *mem_ctx)
400 {
401         struct ldb_message **res = NULL;
402         int ret;
403         struct winsdb_record *rec;
404         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
405
406         /* find the record in the WINS database */
407         ret = ldb_search(winssrv->wins_db, winsdb_dn(tmp_ctx, name), LDB_SCOPE_BASE, 
408                          NULL, NULL, &res);
409         if (res != NULL) {
410                 talloc_steal(tmp_ctx, res);
411         }
412         if (ret != 1) goto failed;
413
414         rec = winsdb_record(res[0], tmp_ctx);
415         if (rec == NULL) goto failed;
416         rec->name           = name;
417
418         /* see if it has already expired */
419         if (rec->state == WINS_REC_ACTIVE &&
420             rec->expire_time <= time(NULL)) {
421                 DEBUG(5,("WINS: expiring name %s (expired at %s)\n", 
422                          nbt_name_string(tmp_ctx, rec->name), timestring(tmp_ctx, rec->expire_time)));
423                 rec->state = WINS_REC_RELEASED;
424         }
425
426         talloc_steal(mem_ctx, rec);
427         talloc_free(tmp_ctx);
428         return rec;
429
430 failed:
431         talloc_free(tmp_ctx);
432         return NULL;
433 }
434
435 struct winsdb_record *winsdb_record(struct ldb_message *msg, TALLOC_CTX *mem_ctx)
436 {
437         struct winsdb_record *rec;
438         struct ldb_message_element *el;
439         uint32_t i;
440
441         rec = talloc(mem_ctx, struct winsdb_record);
442         if (rec == NULL) goto failed;
443
444         /* parse it into a more convenient winsdb_record structure */
445         rec->name           = name;
446         rec->state          = ldb_msg_find_int(res[0], "active", WINS_REC_RELEASED);
447         rec->nb_flags       = ldb_msg_find_int(res[0], "nbFlags", 0);
448         rec->expire_time    = ldb_string_to_time(ldb_msg_find_string(res[0], "expires", NULL));
449         rec->registered_by  = ldb_msg_find_string(res[0], "registeredBy", NULL);
450         rec->version        = ldb_msg_find_uint64(res[0], "version", 0);
451         talloc_steal(rec, rec->wins_owner);
452         talloc_steal(rec, rec->registered_by);
453
454         if (!rec->wins_owner) rec->wins_owner = WINSDB_OWNER_LOCAL;
455
456         el = ldb_msg_find_element(msg, "address");
457         if (el == NULL) goto failed;
458
459         rec->addresses     = talloc_array(rec, struct winsdb_addr *, el->num_values+1);
460         if (rec->addresses == NULL) goto failed;
461
462         for (i=0;i<el->num_values;i++) {
463                 rec->addresses[i] = winsdb_addr_decode(rec, rec->addresses, &el->values[i]);
464                 if (rec->addresses[i] == NULL) goto failed;
465         }
466         rec->addresses[i] = NULL;
467
468         return rec;
469 failed:
470         talloc_free(rec);
471         return NULL;
472 }
473
474 /*
475   form a ldb_message from a winsdb_record
476 */
477 struct ldb_message *winsdb_message(struct ldb_context *ldb, 
478                                    struct winsdb_record *rec, TALLOC_CTX *mem_ctx)
479 {
480         int i, ret=0;
481         struct ldb_message *msg = ldb_msg_new(mem_ctx);
482         if (msg == NULL) goto failed;
483
484         msg->dn = winsdb_dn(msg, rec->name);
485         if (msg->dn == NULL) goto failed;
486         ret |= ldb_msg_add_fmt(msg, "objectClass", "wins");
487         ret |= ldb_msg_add_fmt(msg, "active", "%u", rec->state);
488         ret |= ldb_msg_add_fmt(msg, "nbFlags", "0x%04x", rec->nb_flags);
489         ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
490         ret |= ldb_msg_add_string(msg, "expires", 
491                                   ldb_timestring(msg, rec->expire_time));
492         ret |= ldb_msg_add_fmt(msg, "version", "%llu", rec->version);
493         for (i=0;rec->addresses[i];i++) {
494                 ret |= ldb_msg_add_string(msg, "address", rec->addresses[i]);
495         }
496         if (ret != 0) goto failed;
497         return msg;
498
499 failed:
500         talloc_free(msg);
501         return NULL;
502 }
503
504 /*
505   save a WINS record into the database
506 */
507 uint8_t winsdb_add(struct wins_server *winssrv, struct winsdb_record *rec)
508 {
509         struct ldb_context *ldb = winssrv->wins_db;
510         struct ldb_message *msg;
511         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
512         int trans = -1;
513         int ret = 0;
514
515
516         trans = ldb_transaction_start(ldb);
517         if (trans != LDB_SUCCESS) goto failed;
518
519         rec->version = winsdb_allocate_version(winssrv);
520         if (rec->version == 0) goto failed;
521
522         msg = winsdb_message(winssrv->wins_db, rec, tmp_ctx);
523         if (msg == NULL) goto failed;
524         ret = ldb_add(ldb, msg);
525         if (ret != 0) goto failed;
526
527         trans = ldb_transaction_commit(ldb);
528         if (trans != LDB_SUCCESS) goto failed;
529
530         talloc_free(tmp_ctx);
531         return NBT_RCODE_OK;
532
533 failed:
534         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
535         talloc_free(tmp_ctx);
536         return NBT_RCODE_SVR;
537 }
538
539
540 /*
541   modify a WINS record in the database
542 */
543 uint8_t winsdb_modify(struct wins_server *winssrv, struct winsdb_record *rec)
544 {
545         struct ldb_context *ldb = winssrv->wins_db;
546         struct ldb_message *msg;
547         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
548         int trans;
549         int ret;
550         int i;
551
552         trans = ldb_transaction_start(ldb);
553         if (trans != LDB_SUCCESS) goto failed;
554
555         rec->version = winsdb_allocate_version(winssrv);
556         if (rec->version == 0) goto failed;
557         rec->wins_owner = WINSDB_OWNER_LOCAL;
558
559         msg = winsdb_message(winssrv->wins_db, rec, tmp_ctx);
560         if (msg == NULL) goto failed;
561
562         for (i=0;i<msg->num_elements;i++) {
563                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
564         }
565
566         ret = ldb_modify(ldb, msg);
567         if (ret != 0) goto failed;
568
569         trans = ldb_transaction_commit(ldb);
570         if (trans != LDB_SUCCESS) goto failed;
571
572         talloc_free(tmp_ctx);
573         return NBT_RCODE_OK;
574
575 failed:
576         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
577         talloc_free(tmp_ctx);
578         return NBT_RCODE_SVR;
579 }
580
581
582 /*
583   delete a WINS record from the database
584 */
585 uint8_t winsdb_delete(struct wins_server *winssrv, struct winsdb_record *rec)
586 {
587         struct ldb_context *ldb = winssrv->wins_db;
588         TALLOC_CTX *tmp_ctx = talloc_new(winssrv);
589         const struct ldb_dn *dn;
590         int trans;
591         int ret;
592
593         trans = ldb_transaction_start(ldb);
594         if (trans != LDB_SUCCESS) goto failed;
595
596         if(!winsdb_remove_version(winssrv, rec->version))
597                 goto failed;
598
599         dn = winsdb_dn(tmp_ctx, rec->name);
600         if (dn == NULL) goto failed;
601
602         ret = ldb_delete(ldb, dn);
603         if (ret != 0) goto failed;
604
605         trans = ldb_transaction_commit(ldb);
606         if (trans != LDB_SUCCESS) goto failed;
607
608         talloc_free(tmp_ctx);
609         return NBT_RCODE_OK;
610
611 failed:
612         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
613         talloc_free(tmp_ctx);
614         return NBT_RCODE_SVR;
615 }
616
617 struct ldb_context *winsdb_connect(TALLOC_CTX *mem_ctx)
618 {
619         return ldb_wrap_connect(mem_ctx, lp_wins_url(), 0, NULL);
620 }