Line data Source code
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 3 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, see <http://www.gnu.org/licenses/>.
21 : */
22 :
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"
36 :
37 : #undef strcasecmp
38 :
39 3703 : uint64_t winsdb_get_maxVersion(struct winsdb_handle *h)
40 : {
41 33 : int ret;
42 3703 : struct ldb_context *ldb = h->ldb;
43 33 : struct ldb_dn *dn;
44 3703 : struct ldb_result *res = NULL;
45 3703 : TALLOC_CTX *tmp_ctx = talloc_new(ldb);
46 3703 : uint64_t maxVersion = 0;
47 :
48 3703 : dn = ldb_dn_new(tmp_ctx, ldb, "CN=VERSION");
49 3703 : if (!dn) goto failed;
50 :
51 : /* find the record in the WINS database */
52 3703 : ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
53 3703 : if (ret != LDB_SUCCESS) goto failed;
54 3703 : if (res->count > 1) goto failed;
55 :
56 3703 : if (res->count == 1) {
57 113 : maxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
58 : }
59 :
60 3590 : failed:
61 3703 : talloc_free(tmp_ctx);
62 3703 : return maxVersion;
63 : }
64 :
65 : /*
66 : if newVersion == 0 return the old maxVersion + 1 and save it
67 : if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
68 : */
69 286 : uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
70 : {
71 0 : int trans;
72 0 : int ret;
73 0 : struct ldb_dn *dn;
74 286 : struct ldb_result *res = NULL;
75 286 : struct ldb_message *msg = NULL;
76 286 : struct ldb_context *wins_db = h->ldb;
77 286 : TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
78 286 : uint64_t oldMaxVersion = 0;
79 :
80 286 : trans = ldb_transaction_start(wins_db);
81 286 : if (trans != LDB_SUCCESS) goto failed;
82 :
83 286 : dn = ldb_dn_new(tmp_ctx, wins_db, "CN=VERSION");
84 286 : if (!dn) goto failed;
85 :
86 : /* find the record in the WINS database */
87 286 : ret = ldb_search(wins_db, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
88 286 : if (ret != LDB_SUCCESS) goto failed;
89 286 : if (res->count > 1) goto failed;
90 :
91 286 : if (res->count == 1) {
92 285 : oldMaxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
93 : }
94 :
95 286 : if (newMaxVersion == 0) {
96 286 : newMaxVersion = oldMaxVersion + 1;
97 : } else {
98 0 : newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
99 : }
100 :
101 286 : msg = ldb_msg_new(tmp_ctx);
102 286 : if (!msg) goto failed;
103 286 : msg->dn = dn;
104 :
105 :
106 286 : ret = ldb_msg_append_string(msg, "objectClass", "winsMaxVersion",
107 : LDB_FLAG_MOD_REPLACE);
108 286 : if (ret != LDB_SUCCESS) goto failed;
109 286 : ret = ldb_msg_append_fmt(msg, LDB_FLAG_MOD_REPLACE,
110 : "maxVersion", "%llu", (long long)newMaxVersion);
111 286 : if (ret != LDB_SUCCESS) goto failed;
112 :
113 286 : ret = ldb_modify(wins_db, msg);
114 286 : if (ret != LDB_SUCCESS) ret = ldb_add(wins_db, msg);
115 286 : if (ret != LDB_SUCCESS) goto failed;
116 :
117 286 : trans = ldb_transaction_commit(wins_db);
118 286 : if (trans != LDB_SUCCESS) goto failed;
119 :
120 286 : talloc_free(tmp_ctx);
121 286 : return newMaxVersion;
122 :
123 0 : failed:
124 0 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
125 0 : talloc_free(tmp_ctx);
126 0 : return 0;
127 : }
128 :
129 : /*
130 : return a DN for a nbt_name
131 : */
132 2243 : static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
133 : const struct nbt_name *name)
134 : {
135 0 : struct ldb_dn *dn;
136 :
137 2243 : dn = ldb_dn_new_fmt(mem_ctx, ldb, "type=0x%02X", name->type);
138 2243 : if (ldb_dn_is_valid(dn) && name->name && *name->name) {
139 2227 : ldb_dn_add_child_fmt(dn, "name=%s", name->name);
140 : }
141 2243 : if (ldb_dn_is_valid(dn) && name->scope && *name->scope) {
142 421 : ldb_dn_add_child_fmt(dn, "scope=%s", name->scope);
143 : }
144 2243 : return dn;
145 : }
146 :
147 1497 : static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
148 : {
149 0 : NTSTATUS status;
150 0 : struct nbt_name *name;
151 0 : unsigned int comp_num;
152 1497 : uint32_t cur = 0;
153 1497 : int error = 0;
154 :
155 1497 : name = talloc(mem_ctx, struct nbt_name);
156 1497 : if (!name) {
157 0 : status = NT_STATUS_NO_MEMORY;
158 0 : goto failed;
159 : }
160 :
161 1497 : comp_num = ldb_dn_get_comp_num(dn);
162 :
163 1497 : if (comp_num > 3) {
164 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
165 0 : goto failed;
166 : }
167 :
168 1497 : if (comp_num > cur && strcasecmp("scope", ldb_dn_get_component_name(dn, cur)) == 0) {
169 306 : name->scope = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
170 306 : cur++;
171 : } else {
172 1191 : name->scope = NULL;
173 : }
174 :
175 1497 : if (comp_num > cur && strcasecmp("name", ldb_dn_get_component_name(dn, cur)) == 0) {
176 1490 : name->name = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
177 1490 : cur++;
178 : } else {
179 7 : name->name = talloc_strdup(name, "");
180 7 : if (!name->name) {
181 0 : status = NT_STATUS_NO_MEMORY;
182 0 : goto failed;
183 : }
184 : }
185 :
186 1497 : if (comp_num > cur && strcasecmp("type", ldb_dn_get_component_name(dn, cur)) == 0) {
187 1497 : name->type =
188 2994 : smb_strtoul(
189 1497 : (char *)ldb_dn_get_component_val(dn, cur)->data,
190 : NULL,
191 : 0,
192 : &error,
193 : SMB_STR_STANDARD);
194 1497 : if (error != 0) {
195 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
196 0 : goto failed;
197 : }
198 1497 : cur++;
199 : } else {
200 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
201 0 : goto failed;
202 : }
203 :
204 1497 : *_name = name;
205 1497 : return NT_STATUS_OK;
206 0 : failed:
207 0 : talloc_free(name);
208 0 : return status;
209 : }
210 :
211 : /*
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
216 : */
217 1764 : 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)
219 : {
220 0 : NTSTATUS status;
221 0 : struct winsdb_addr *addr;
222 0 : const char *address;
223 0 : const char *wins_owner;
224 0 : const char *expire_time;
225 0 : char *p;
226 :
227 1764 : addr = talloc(mem_ctx, struct winsdb_addr);
228 1764 : if (!addr) {
229 0 : status = NT_STATUS_NO_MEMORY;
230 0 : goto failed;
231 : }
232 :
233 1764 : address = (char *)val->data;
234 :
235 1764 : p = strchr(address, ';');
236 1764 : if (!p) {
237 : /* support old entries, with only the address */
238 26 : addr->address = (const char *)talloc_steal(addr, val->data);
239 26 : addr->wins_owner = talloc_strdup(addr, rec->wins_owner);
240 26 : if (!addr->wins_owner) {
241 0 : status = NT_STATUS_NO_MEMORY;
242 0 : goto failed;
243 : }
244 26 : addr->expire_time = rec->expire_time;
245 26 : *_addr = addr;
246 26 : return NT_STATUS_OK;
247 : }
248 :
249 1738 : *p = '\0'; p++;
250 1738 : addr->address = talloc_strdup(addr, address);
251 1738 : if (!addr->address) {
252 0 : status = NT_STATUS_NO_MEMORY;
253 0 : goto failed;
254 : }
255 :
256 1738 : if (strncmp("winsOwner:", p, 10) != 0) {
257 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
258 0 : goto failed;
259 : }
260 1738 : wins_owner = p + 10;
261 1738 : p = strchr(wins_owner, ';');
262 1738 : if (!p) {
263 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
264 0 : goto failed;
265 : }
266 :
267 1738 : *p = '\0';p++;
268 1738 : if (strcmp(wins_owner, "0.0.0.0") == 0) {
269 0 : wins_owner = h->local_owner;
270 : }
271 1738 : addr->wins_owner = talloc_strdup(addr, wins_owner);
272 1738 : if (!addr->wins_owner) {
273 0 : status = NT_STATUS_NO_MEMORY;
274 0 : goto failed;
275 : }
276 :
277 1738 : if (strncmp("expireTime:", p, 11) != 0) {
278 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
279 0 : goto failed;
280 : }
281 :
282 1738 : expire_time = p + 11;
283 1738 : p = strchr(expire_time, ';');
284 1738 : if (!p) {
285 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
286 0 : goto failed;
287 : }
288 :
289 1738 : *p = '\0';p++;
290 1738 : addr->expire_time = ldb_string_to_time(expire_time);
291 :
292 1738 : *_addr = addr;
293 1738 : return NT_STATUS_OK;
294 0 : failed:
295 0 : talloc_free(addr);
296 0 : return status;
297 : }
298 :
299 : /*
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"
305 : */
306 1102 : static int ldb_msg_add_winsdb_addr(struct ldb_message *msg, struct winsdb_record *rec,
307 : const char *attr_name, struct winsdb_addr *addr)
308 : {
309 0 : const char *str;
310 :
311 1102 : if (rec->is_static) {
312 13 : str = talloc_strdup(msg, addr->address);
313 13 : if (!str) return LDB_ERR_OPERATIONS_ERROR;
314 : } else {
315 0 : char *expire_time;
316 1089 : expire_time = ldb_timestring(msg, addr->expire_time);
317 1089 : if (!expire_time) return LDB_ERR_OPERATIONS_ERROR;
318 1089 : str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s;",
319 : addr->address, addr->wins_owner,
320 : expire_time);
321 1089 : talloc_free(expire_time);
322 1089 : if (!str) return LDB_ERR_OPERATIONS_ERROR;
323 : }
324 :
325 1102 : return ldb_msg_add_string(msg, attr_name, str);
326 : }
327 :
328 761 : struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
329 : {
330 0 : struct winsdb_addr **addresses;
331 :
332 761 : addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
333 761 : if (!addresses) return NULL;
334 :
335 761 : addresses[0] = NULL;
336 :
337 761 : return addresses;
338 : }
339 :
340 473 : static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr **p2, void *opaque)
341 : {
342 473 : struct winsdb_addr *a1 = talloc_get_type(*p1, struct winsdb_addr);
343 473 : struct winsdb_addr *a2 = talloc_get_type(*p2, struct winsdb_addr);
344 473 : struct winsdb_handle *h= talloc_get_type(opaque, struct winsdb_handle);
345 473 : bool a1_owned = false;
346 473 : bool a2_owned = false;
347 :
348 : /*
349 : * first the owned addresses with the newest to the oldest address
350 : * then the replica addresses with the newest to the oldest address
351 : */
352 473 : if (a2->expire_time != a1->expire_time) {
353 13 : return NUMERIC_CMP(a2->expire_time, a1->expire_time);
354 : }
355 :
356 460 : if (strcmp(a2->wins_owner, h->local_owner) == 0) {
357 39 : a2_owned = true;
358 : }
359 :
360 460 : if (strcmp(a1->wins_owner, h->local_owner) == 0) {
361 39 : a1_owned = true;
362 : }
363 :
364 460 : return NUMERIC_CMP(a2_owned, a1_owned);
365 : }
366 :
367 950 : 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)
371 : {
372 950 : struct winsdb_addr *old_addr = NULL;
373 950 : size_t len = 0;
374 0 : size_t i;
375 950 : bool found_old_replica = false;
376 :
377 : /*
378 : * count the addresses and maybe
379 : * find an old entry for the new address
380 : */
381 1303 : for (i=0; addresses[i]; i++) {
382 353 : if (old_addr) continue;
383 338 : if (strcmp(addresses[i]->address, address) == 0) {
384 48 : old_addr = addresses[i];
385 : }
386 : }
387 950 : len = i;
388 :
389 : /*
390 : * the address is already there
391 : * and we can replace it
392 : */
393 950 : if (old_addr) {
394 48 : goto remove_old_addr;
395 : }
396 :
397 : /*
398 : * if we don't have 25 addresses already,
399 : * we can just add the new address
400 : */
401 902 : if (len < 25) {
402 902 : goto add_new_addr;
403 : }
404 :
405 : /*
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
413 : */
414 0 : if (!is_name_registration) {
415 0 : return addresses;
416 : }
417 :
418 : /*
419 : * find the oldest replica address, if there's no replica
420 : * record at all, find the oldest owned address
421 : */
422 0 : for (i=0; addresses[i]; i++) {
423 0 : bool cur_is_replica = false;
424 : /* find out if the current address is a replica */
425 0 : if (strcmp(addresses[i]->wins_owner, h->local_owner) != 0) {
426 0 : cur_is_replica = true;
427 : }
428 :
429 : /*
430 : * if we already found a replica address and the current address
431 : * is not a replica, then skip it
432 : */
433 0 : if (found_old_replica && !cur_is_replica) continue;
434 :
435 : /*
436 : * if we found the first replica address, reset the address
437 : * that would be replaced
438 : */
439 0 : if (!found_old_replica && cur_is_replica) {
440 0 : found_old_replica = true;
441 0 : old_addr = addresses[i];
442 0 : continue;
443 : }
444 :
445 : /*
446 : * if the first address isn't a replica, just start with
447 : * the first one
448 : */
449 0 : if (!old_addr) {
450 0 : old_addr = addresses[i];
451 0 : continue;
452 : }
453 :
454 : /*
455 : * see if we find an older address
456 : */
457 0 : if (addresses[i]->expire_time < old_addr->expire_time) {
458 0 : old_addr = addresses[i];
459 0 : continue;
460 : }
461 : }
462 :
463 0 : remove_old_addr:
464 48 : winsdb_addr_list_remove(addresses, old_addr->address);
465 48 : len --;
466 :
467 950 : add_new_addr:
468 950 : addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
469 950 : if (!addresses) return NULL;
470 :
471 950 : addresses[len] = talloc(addresses, struct winsdb_addr);
472 950 : if (!addresses[len]) {
473 0 : talloc_free(addresses);
474 0 : return NULL;
475 : }
476 :
477 950 : addresses[len]->address = talloc_strdup(addresses[len], address);
478 950 : if (!addresses[len]->address) {
479 0 : talloc_free(addresses);
480 0 : return NULL;
481 : }
482 :
483 950 : addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
484 950 : if (!addresses[len]->wins_owner) {
485 0 : talloc_free(addresses);
486 0 : return NULL;
487 : }
488 :
489 950 : addresses[len]->expire_time = expire_time;
490 :
491 950 : addresses[len+1] = NULL;
492 :
493 950 : LDB_TYPESAFE_QSORT(addresses, len+1, h, winsdb_addr_sort_list);
494 :
495 950 : return addresses;
496 : }
497 :
498 138 : void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
499 : {
500 0 : size_t i;
501 :
502 162 : for (i=0; addresses[i]; i++) {
503 162 : if (strcmp(addresses[i]->address, address) == 0) {
504 138 : break;
505 : }
506 : }
507 :
508 307 : for (; addresses[i]; i++) {
509 169 : addresses[i] = addresses[i+1];
510 : }
511 :
512 138 : return;
513 : }
514 :
515 214 : struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
516 : {
517 0 : size_t i;
518 :
519 246 : for (i=0; addresses[i]; i++) {
520 220 : if (strcmp(addresses[i]->address, address) == 0) {
521 188 : return addresses[i];
522 : }
523 : }
524 :
525 26 : return NULL;
526 : }
527 :
528 1638 : size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
529 : {
530 0 : size_t i;
531 3639 : for (i=0; addresses[i]; i++);
532 1638 : return i;
533 : }
534 :
535 60 : const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
536 : {
537 60 : size_t len = winsdb_addr_list_length(addresses);
538 60 : const char **str_list=NULL;
539 0 : size_t i;
540 :
541 126 : for (i=0; i < len; i++) {
542 66 : str_list = str_list_add(str_list, addresses[i]->address);
543 66 : if (!str_list[i]) {
544 0 : return NULL;
545 : }
546 : }
547 60 : talloc_steal(mem_ctx, str_list);
548 60 : return str_list;
549 : }
550 :
551 : /*
552 : load a WINS entry from the database
553 : */
554 1192 : NTSTATUS winsdb_lookup(struct winsdb_handle *h,
555 : const struct nbt_name *name,
556 : TALLOC_CTX *mem_ctx,
557 : struct winsdb_record **_rec)
558 : {
559 0 : NTSTATUS status;
560 1192 : struct ldb_result *res = NULL;
561 0 : int ret;
562 0 : struct winsdb_record *rec;
563 1192 : struct ldb_context *wins_db = h->ldb;
564 1192 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
565 1192 : time_t now = time(NULL);
566 :
567 : /* find the record in the WINS database */
568 1192 : ret = ldb_search(wins_db, tmp_ctx, &res,
569 : winsdb_dn(tmp_ctx, wins_db, name),
570 : LDB_SCOPE_BASE, NULL, NULL);
571 :
572 1192 : if (ret != LDB_SUCCESS || res->count > 1) {
573 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
574 0 : goto failed;
575 1192 : } else if (res->count== 0) {
576 222 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
577 222 : goto failed;
578 : }
579 :
580 970 : status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
581 970 : if (!NT_STATUS_IS_OK(status)) goto failed;
582 :
583 970 : talloc_steal(mem_ctx, rec);
584 970 : talloc_free(tmp_ctx);
585 970 : *_rec = rec;
586 970 : return NT_STATUS_OK;
587 :
588 222 : failed:
589 222 : talloc_free(tmp_ctx);
590 222 : return status;
591 : }
592 :
593 1497 : NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
594 : {
595 0 : NTSTATUS status;
596 0 : struct winsdb_record *rec;
597 0 : struct ldb_message_element *el;
598 0 : struct nbt_name *name;
599 0 : uint32_t i, j, num_values;
600 :
601 1497 : rec = talloc(mem_ctx, struct winsdb_record);
602 1497 : if (rec == NULL) {
603 0 : status = NT_STATUS_NO_MEMORY;
604 0 : goto failed;
605 : }
606 :
607 1497 : status = winsdb_nbt_name(rec, msg->dn, &name);
608 1497 : if (!NT_STATUS_IS_OK(status)) goto failed;
609 :
610 1497 : if (strlen(name->name) > 15) {
611 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
612 0 : goto failed;
613 : }
614 1497 : if (name->scope && strlen(name->scope) > 238) {
615 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
616 0 : goto failed;
617 : }
618 :
619 : /* parse it into a more convenient winsdb_record structure */
620 1497 : rec->name = name;
621 1497 : rec->type = ldb_msg_find_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
622 1497 : rec->state = ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
623 1497 : rec->node = ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
624 1497 : rec->is_static = ldb_msg_find_attr_as_int(msg, "isStatic", 0);
625 1497 : rec->expire_time = ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
626 1497 : rec->version = ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
627 1497 : rec->wins_owner = ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
628 1497 : rec->registered_by = ldb_msg_find_attr_as_string(msg, "registeredBy", NULL);
629 1497 : talloc_steal(rec, rec->wins_owner);
630 1497 : talloc_steal(rec, rec->registered_by);
631 :
632 1497 : if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
633 0 : rec->wins_owner = h->local_owner;
634 : }
635 :
636 1497 : el = ldb_msg_find_element(msg, "address");
637 1497 : if (el) {
638 1420 : num_values = el->num_values;
639 : } else {
640 77 : num_values = 0;
641 : }
642 :
643 1497 : if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
644 694 : if (num_values != 1) {
645 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
646 0 : goto failed;
647 : }
648 : }
649 1497 : if (rec->state == WREPL_STATE_ACTIVE) {
650 909 : if (num_values < 1) {
651 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
652 0 : goto failed;
653 : }
654 : }
655 1497 : if (num_values > 25) {
656 0 : status = NT_STATUS_INTERNAL_DB_CORRUPTION;
657 0 : goto failed;
658 : }
659 :
660 1497 : rec->addresses = talloc_array(rec, struct winsdb_addr *, num_values+1);
661 1497 : if (rec->addresses == NULL) {
662 0 : status = NT_STATUS_NO_MEMORY;
663 0 : goto failed;
664 : }
665 :
666 3261 : for (i=0,j=0;i<num_values;i++) {
667 1764 : bool we_are_owner = false;
668 :
669 1764 : status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
670 1764 : if (!NT_STATUS_IS_OK(status)) goto failed;
671 :
672 1764 : if (strcmp(rec->addresses[j]->wins_owner, h->local_owner) == 0) {
673 446 : we_are_owner = true;
674 : }
675 :
676 : /*
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
680 : *
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).
687 : */
688 1764 : if (!rec->is_static &&
689 1738 : rec->addresses[j]->expire_time <= now &&
690 0 : rec->state == WREPL_STATE_ACTIVE &&
691 : we_are_owner) {
692 0 : 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 0 : talloc_free(rec->addresses[j]);
696 0 : rec->addresses[j] = NULL;
697 0 : continue;
698 : }
699 1764 : j++;
700 : }
701 1497 : rec->addresses[j] = NULL;
702 1497 : num_values = j;
703 :
704 1497 : if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
705 26 : rec->expire_time = get_time_t_max();
706 52 : for (i=0;rec->addresses[i];i++) {
707 26 : rec->addresses[i]->expire_time = rec->expire_time;
708 : }
709 : }
710 :
711 1497 : if (rec->state == WREPL_STATE_ACTIVE) {
712 909 : if (num_values < 1) {
713 0 : DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n",
714 : nbt_name_string(mem_ctx, rec->name)));
715 0 : rec->state = WREPL_STATE_RELEASED;
716 : }
717 : }
718 :
719 1497 : *_rec = rec;
720 1497 : return NT_STATUS_OK;
721 0 : failed:
722 0 : if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
723 0 : DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
724 : }
725 0 : talloc_free(rec);
726 0 : return status;
727 : }
728 :
729 : /*
730 : form a ldb_message from a winsdb_record
731 : */
732 1025 : static struct ldb_message *winsdb_message(struct ldb_context *ldb,
733 : struct winsdb_record *rec,
734 : TALLOC_CTX *mem_ctx)
735 : {
736 0 : int i, ret;
737 0 : size_t addr_count;
738 0 : const char *expire_time;
739 1025 : struct ldb_message *msg = ldb_msg_new(mem_ctx);
740 1025 : if (msg == NULL) goto failed;
741 :
742 : /* make sure we don't put in corrupted records */
743 1025 : addr_count = winsdb_addr_list_length(rec->addresses);
744 1025 : if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
745 26 : rec->state = WREPL_STATE_RELEASED;
746 : }
747 1025 : if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
748 0 : rec->type = WREPL_TYPE_MHOMED;
749 : }
750 :
751 1025 : expire_time = ldb_timestring(msg, rec->expire_time);
752 1025 : if (!expire_time) {
753 0 : goto failed;
754 : }
755 :
756 1025 : msg->dn = winsdb_dn(msg, ldb, rec->name);
757 1025 : if (msg->dn == NULL) goto failed;
758 1025 : ret = ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
759 1025 : if (rec->name->name && *rec->name->name) {
760 1020 : ret |= ldb_msg_add_string(msg, "name", rec->name->name);
761 : }
762 1025 : if (rec->name->scope && *rec->name->scope) {
763 176 : ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
764 : }
765 1025 : ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
766 1025 : ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
767 1025 : ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
768 1025 : ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
769 1025 : ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
770 1025 : ret |= ldb_msg_add_empty(msg, "expireTime", 0, NULL);
771 1025 : if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
772 1012 : ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
773 : }
774 1025 : ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
775 1025 : ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
776 1025 : ret |= ldb_msg_add_empty(msg, "address", 0, NULL);
777 2127 : for (i=0;rec->addresses[i];i++) {
778 1102 : ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
779 : }
780 1025 : if (rec->registered_by) {
781 458 : ret |= ldb_msg_append_string(msg, "registeredBy", rec->registered_by, 0);
782 : }
783 1025 : if (ret != LDB_SUCCESS) goto failed;
784 1025 : return msg;
785 :
786 0 : failed:
787 0 : talloc_free(msg);
788 0 : return NULL;
789 : }
790 :
791 : /*
792 : save a WINS record into the database
793 : */
794 207 : uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
795 : {
796 0 : struct ldb_message *msg;
797 207 : struct ldb_context *wins_db = h->ldb;
798 207 : TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
799 207 : int trans = -1;
800 0 : int ret;
801 :
802 207 : trans = ldb_transaction_start(wins_db);
803 207 : if (trans != LDB_SUCCESS) goto failed;
804 :
805 207 : if (flags & WINSDB_FLAG_ALLOC_VERSION) {
806 : /* passing '0' means auto-allocate a new one */
807 194 : rec->version = winsdb_set_maxVersion(h, 0);
808 194 : if (rec->version == 0) goto failed;
809 : }
810 207 : if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
811 194 : rec->wins_owner = h->local_owner;
812 : }
813 :
814 207 : msg = winsdb_message(wins_db, rec, tmp_ctx);
815 207 : if (msg == NULL) goto failed;
816 207 : ret = ldb_add(wins_db, msg);
817 207 : if (ret != LDB_SUCCESS) goto failed;
818 :
819 207 : trans = ldb_transaction_commit(wins_db);
820 207 : if (trans != LDB_SUCCESS) goto failed;
821 :
822 207 : wins_hook(h, rec, WINS_HOOK_ADD, h->hook_script);
823 :
824 207 : talloc_free(tmp_ctx);
825 207 : return NBT_RCODE_OK;
826 :
827 0 : failed:
828 0 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
829 0 : talloc_free(tmp_ctx);
830 0 : return NBT_RCODE_SVR;
831 : }
832 :
833 :
834 : /*
835 : modify a WINS record in the database
836 : */
837 818 : uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
838 : {
839 0 : struct ldb_message *msg;
840 818 : struct ldb_context *wins_db = h->ldb;
841 818 : TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
842 0 : int trans;
843 0 : int ret;
844 0 : unsigned int i;
845 :
846 818 : trans = ldb_transaction_start(wins_db);
847 818 : if (trans != LDB_SUCCESS) goto failed;
848 :
849 818 : if (flags & WINSDB_FLAG_ALLOC_VERSION) {
850 : /* passing '0' means auto-allocate a new one */
851 92 : rec->version = winsdb_set_maxVersion(h, 0);
852 92 : if (rec->version == 0) goto failed;
853 : }
854 818 : if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
855 92 : rec->wins_owner = h->local_owner;
856 : }
857 :
858 818 : msg = winsdb_message(wins_db, rec, tmp_ctx);
859 818 : if (msg == NULL) goto failed;
860 :
861 10225 : for (i=0;i<msg->num_elements;i++) {
862 9407 : msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
863 : }
864 :
865 818 : ret = ldb_modify(wins_db, msg);
866 818 : if (ret != LDB_SUCCESS) goto failed;
867 :
868 818 : trans = ldb_transaction_commit(wins_db);
869 818 : if (trans != LDB_SUCCESS) goto failed;
870 :
871 818 : wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);
872 :
873 818 : talloc_free(tmp_ctx);
874 818 : return NBT_RCODE_OK;
875 :
876 0 : failed:
877 0 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
878 0 : talloc_free(tmp_ctx);
879 0 : return NBT_RCODE_SVR;
880 : }
881 :
882 :
883 : /*
884 : delete a WINS record from the database
885 : */
886 26 : uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
887 : {
888 26 : struct ldb_context *wins_db = h->ldb;
889 26 : TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
890 0 : struct ldb_dn *dn;
891 0 : int trans;
892 0 : int ret;
893 :
894 26 : trans = ldb_transaction_start(wins_db);
895 26 : if (trans != LDB_SUCCESS) goto failed;
896 :
897 26 : dn = winsdb_dn(tmp_ctx, wins_db, rec->name);
898 26 : if (dn == NULL) goto failed;
899 :
900 26 : ret = ldb_delete(wins_db, dn);
901 26 : if (ret != LDB_SUCCESS) goto failed;
902 :
903 26 : trans = ldb_transaction_commit(wins_db);
904 26 : if (trans != LDB_SUCCESS) goto failed;
905 :
906 26 : wins_hook(h, rec, WINS_HOOK_DELETE, h->hook_script);
907 :
908 26 : talloc_free(tmp_ctx);
909 26 : return NBT_RCODE_OK;
910 :
911 0 : failed:
912 0 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
913 0 : talloc_free(tmp_ctx);
914 0 : return NBT_RCODE_SVR;
915 : }
916 :
917 130 : 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)
920 : {
921 4 : int trans;
922 4 : int ret;
923 4 : struct ldb_dn *dn;
924 130 : struct ldb_result *res = NULL;
925 130 : struct ldb_message *msg = NULL;
926 130 : TALLOC_CTX *tmp_ctx = talloc_new(h);
927 130 : unsigned int flags = 0;
928 :
929 130 : trans = ldb_transaction_start(h->ldb);
930 130 : if (trans != LDB_SUCCESS) goto failed;
931 :
932 : /* check if we have a special @MODULES record already */
933 130 : dn = ldb_dn_new(tmp_ctx, h->ldb, "@MODULES");
934 130 : if (!dn) goto failed;
935 :
936 : /* find the record in the WINS database */
937 130 : ret = ldb_search(h->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
938 130 : if (ret != LDB_SUCCESS) goto failed;
939 :
940 130 : if (res->count > 0) goto skip;
941 :
942 : /* if there's no record, add one */
943 64 : msg = ldb_msg_new(tmp_ctx);
944 64 : if (!msg) goto failed;
945 64 : msg->dn = dn;
946 :
947 64 : ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
948 64 : if (ret != LDB_SUCCESS) goto failed;
949 :
950 64 : ret = ldb_add(h->ldb, msg);
951 64 : if (ret != LDB_SUCCESS) goto failed;
952 :
953 64 : trans = ldb_transaction_commit(h->ldb);
954 64 : if (trans != LDB_SUCCESS) goto failed;
955 :
956 : /* close and reopen the database, with the modules */
957 64 : trans = LDB_ERR_OTHER;
958 64 : talloc_free(h->ldb);
959 64 : h->ldb = NULL;
960 :
961 64 : if (lpcfg_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
962 0 : flags |= LDB_FLG_NOSYNC;
963 : }
964 :
965 64 : h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, wins_path,
966 : NULL, NULL, flags);
967 64 : if (!h->ldb) goto failed;
968 :
969 64 : talloc_free(tmp_ctx);
970 64 : return true;
971 :
972 66 : skip:
973 66 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
974 66 : talloc_free(tmp_ctx);
975 66 : return true;
976 :
977 0 : failed:
978 0 : if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
979 0 : talloc_free(tmp_ctx);
980 0 : return false;
981 : }
982 :
983 130 : 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)
988 : {
989 4 : const struct loadparm_substitution *lp_sub =
990 130 : lpcfg_noop_substitution();
991 130 : struct winsdb_handle *h = NULL;
992 130 : unsigned int flags = 0;
993 4 : bool ret;
994 4 : int ldb_err;
995 4 : char *wins_path;
996 :
997 130 : h = talloc_zero(mem_ctx, struct winsdb_handle);
998 130 : if (!h) return NULL;
999 :
1000 130 : wins_path = lpcfg_state_path(h, lp_ctx, "wins.ldb");
1001 :
1002 130 : if (lpcfg_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
1003 0 : flags |= LDB_FLG_NOSYNC;
1004 : }
1005 :
1006 130 : h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, wins_path,
1007 : NULL, NULL, flags);
1008 130 : if (!h->ldb) goto failed;
1009 :
1010 130 : h->caller = caller;
1011 130 : h->hook_script = lpcfg_wins_hook(lp_ctx, lp_sub, h);
1012 :
1013 130 : h->local_owner = talloc_strdup(h, owner);
1014 130 : if (!h->local_owner) goto failed;
1015 :
1016 : /* make sure the module list is available and used */
1017 130 : ret = winsdb_check_or_add_module_list(ev_ctx, lp_ctx, h, wins_path);
1018 130 : if (!ret) goto failed;
1019 :
1020 130 : ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
1021 130 : if (ldb_err != LDB_SUCCESS) goto failed;
1022 :
1023 126 : return h;
1024 0 : failed:
1025 0 : talloc_free(h);
1026 0 : return NULL;
1027 : }
1028 :
|