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