source: branches/samba-3.5.x/source4/nbt_server/wins/winsserver.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 30.2 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 core wins server handling
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 "lib/util/dlinklist.h"
25#include "nbt_server/nbt_server.h"
26#include "nbt_server/wins/winsdb.h"
27#include "nbt_server/wins/winsserver.h"
28#include "librpc/gen_ndr/ndr_nbt.h"
29#include "system/time.h"
30#include "libcli/composite/composite.h"
31#include "smbd/service_task.h"
32#include "system/network.h"
33#include "lib/socket/socket.h"
34#include "lib/socket/netif.h"
35#include "lib/ldb/include/ldb.h"
36#include "param/param.h"
37#include "libcli/resolve/resolve.h"
38
39/*
40 work out the ttl we will use given a client requested ttl
41*/
42uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
43{
44 ttl = MIN(ttl, winssrv->config.max_renew_interval);
45 ttl = MAX(ttl, winssrv->config.min_renew_interval);
46 return ttl;
47}
48
49static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, bool mhomed)
50{
51 /* this copes with the nasty hack that is the type 0x1c name */
52 if (name->type == NBT_NAME_LOGON) {
53 return WREPL_TYPE_SGROUP;
54 }
55 if (nb_flags & NBT_NM_GROUP) {
56 return WREPL_TYPE_GROUP;
57 }
58 if (mhomed) {
59 return WREPL_TYPE_MHOMED;
60 }
61 return WREPL_TYPE_UNIQUE;
62}
63
64/*
65 register a new name with WINS
66*/
67static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
68 struct nbt_name_packet *packet,
69 const struct socket_address *src,
70 enum wrepl_name_type type)
71{
72 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
73 struct nbtd_interface);
74 struct wins_server *winssrv = iface->nbtsrv->winssrv;
75 struct nbt_name *name = &packet->questions[0].name;
76 uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
77 uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
78 const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
79 struct winsdb_record rec;
80 enum wrepl_name_node node;
81
82#define WREPL_NODE_NBT_FLAGS(nb_flags) \
83 ((nb_flags & NBT_NM_OWNER_TYPE)>>13)
84
85 node = WREPL_NODE_NBT_FLAGS(nb_flags);
86
87 rec.name = name;
88 rec.type = type;
89 rec.state = WREPL_STATE_ACTIVE;
90 rec.node = node;
91 rec.is_static = false;
92 rec.expire_time = time(NULL) + ttl;
93 rec.version = 0; /* will be allocated later */
94 rec.wins_owner = NULL; /* will be set later */
95 rec.registered_by = src->addr;
96 rec.addresses = winsdb_addr_list_make(packet);
97 if (rec.addresses == NULL) return NBT_RCODE_SVR;
98
99 rec.addresses = winsdb_addr_list_add(winssrv->wins_db,
100 &rec, rec.addresses,
101 address,
102 winssrv->wins_db->local_owner,
103 rec.expire_time,
104 true);
105 if (rec.addresses == NULL) return NBT_RCODE_SVR;
106
107 DEBUG(4,("WINS: accepted registration of %s with address %s\n",
108 nbt_name_string(packet, name), rec.addresses[0]->address));
109
110 return winsdb_add(winssrv->wins_db, &rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
111}
112
113
114/*
115 update the ttl on an existing record
116*/
117static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
118 struct nbt_name_packet *packet,
119 struct winsdb_record *rec,
120 struct winsdb_addr *winsdb_addr,
121 const struct socket_address *src)
122{
123 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
124 struct nbtd_interface);
125 struct wins_server *winssrv = iface->nbtsrv->winssrv;
126 uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
127 const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
128 uint32_t modify_flags = 0;
129
130 rec->expire_time = time(NULL) + ttl;
131 rec->registered_by = src->addr;
132
133 if (winsdb_addr) {
134 rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
135 rec, rec->addresses,
136 winsdb_addr->address,
137 winssrv->wins_db->local_owner,
138 rec->expire_time,
139 true);
140 if (rec->addresses == NULL) return NBT_RCODE_SVR;
141 }
142
143 if (strcmp(winssrv->wins_db->local_owner, rec->wins_owner) != 0) {
144 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
145 }
146
147 DEBUG(5,("WINS: refreshed registration of %s at %s\n",
148 nbt_name_string(packet, rec->name), address));
149
150 return winsdb_modify(winssrv->wins_db, rec, modify_flags);
151}
152
153/*
154 do a sgroup merge
155*/
156static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
157 struct nbt_name_packet *packet,
158 struct winsdb_record *rec,
159 const char *address,
160 const struct socket_address *src)
161{
162 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
163 struct nbtd_interface);
164 struct wins_server *winssrv = iface->nbtsrv->winssrv;
165 uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
166
167 rec->expire_time = time(NULL) + ttl;
168 rec->registered_by = src->addr;
169
170 rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
171 rec, rec->addresses,
172 address,
173 winssrv->wins_db->local_owner,
174 rec->expire_time,
175 true);
176 if (rec->addresses == NULL) return NBT_RCODE_SVR;
177
178 DEBUG(5,("WINS: sgroup merge of %s at %s\n",
179 nbt_name_string(packet, rec->name), address));
180
181 return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
182}
183
184struct nbtd_wins_wack_state {
185 struct nbtd_wins_wack_state *prev, *next;
186 struct wins_server *winssrv;
187 struct nbt_name_socket *nbtsock;
188 struct nbtd_interface *iface;
189 struct nbt_name_packet *request_packet;
190 struct winsdb_record *rec;
191 struct socket_address *src;
192 const char *reg_address;
193 enum wrepl_name_type new_type;
194 struct wins_challenge_io io;
195 NTSTATUS status;
196};
197
198static int nbtd_wins_wack_state_destructor(struct nbtd_wins_wack_state *s)
199{
200 DLIST_REMOVE(s->iface->wack_queue, s);
201 return 0;
202}
203
204static bool wins_check_wack_queue(struct nbtd_interface *iface,
205 struct nbt_name_packet *packet,
206 struct socket_address *src)
207{
208 struct nbtd_wins_wack_state *s;
209
210 for (s= iface->wack_queue; s; s = s->next) {
211 if (packet->name_trn_id != s->request_packet->name_trn_id) {
212 continue;
213 }
214 if (packet->operation != s->request_packet->operation) {
215 continue;
216 }
217 if (src->port != s->src->port) {
218 continue;
219 }
220 if (strcmp(src->addr, s->src->addr) != 0) {
221 continue;
222 }
223
224 return true;
225 }
226
227 return false;
228}
229
230/*
231 deny a registration request
232*/
233static void wins_wack_deny(struct nbtd_wins_wack_state *s)
234{
235 nbtd_name_registration_reply(s->nbtsock, s->request_packet,
236 s->src, NBT_RCODE_ACT);
237 DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n",
238 nbt_name_string(s, s->rec->name), s->src->addr, s->src->port));
239 talloc_free(s);
240}
241
242/*
243 allow a registration request
244*/
245static void wins_wack_allow(struct nbtd_wins_wack_state *s)
246{
247 NTSTATUS status;
248 uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl);
249 struct winsdb_record *rec = s->rec, *rec2;
250 uint32_t i,j;
251
252 status = winsdb_lookup(s->winssrv->wins_db, rec->name, s, &rec2);
253 if (!NT_STATUS_IS_OK(status) ||
254 rec2->version != rec->version ||
255 strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
256 DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
257 nbt_name_string(s, rec->name)));
258 wins_wack_deny(s);
259 return;
260 }
261
262 /*
263 * if the old name owner doesn't hold the name anymore
264 * handle the request as new registration for the new name owner
265 */
266 if (!NT_STATUS_IS_OK(s->status)) {
267 uint8_t rcode;
268
269 winsdb_delete(s->winssrv->wins_db, rec);
270 rcode = wins_register_new(s->nbtsock, s->request_packet, s->src, s->new_type);
271 if (rcode != NBT_RCODE_OK) {
272 DEBUG(1,("WINS: record %s failed to register as new during WACK\n",
273 nbt_name_string(s, rec->name)));
274 wins_wack_deny(s);
275 return;
276 }
277 goto done;
278 }
279
280 rec->expire_time = time(NULL) + ttl;
281 rec->registered_by = s->src->addr;
282
283 /*
284 * now remove all addresses that're the client doesn't hold anymore
285 * and update the time stamp and owner for the ownes that are still there
286 */
287 for (i=0; rec->addresses[i]; i++) {
288 bool found = false;
289 for (j=0; j < s->io.out.num_addresses; j++) {
290 if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue;
291
292 found = true;
293 break;
294 }
295 if (found) {
296 rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
297 rec, rec->addresses,
298 s->reg_address,
299 s->winssrv->wins_db->local_owner,
300 rec->expire_time,
301 true);
302 if (rec->addresses == NULL) goto failed;
303 continue;
304 }
305
306 winsdb_addr_list_remove(rec->addresses, rec->addresses[i]->address);
307 }
308
309 rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
310 rec, rec->addresses,
311 s->reg_address,
312 s->winssrv->wins_db->local_owner,
313 rec->expire_time,
314 true);
315 if (rec->addresses == NULL) goto failed;
316
317 /* if we have more than one address, this becomes implicit a MHOMED record */
318 if (winsdb_addr_list_length(rec->addresses) > 1) {
319 rec->type = WREPL_TYPE_MHOMED;
320 }
321
322 winsdb_modify(s->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
323
324 DEBUG(4,("WINS: accepted registration of %s with address %s\n",
325 nbt_name_string(s, rec->name), s->reg_address));
326
327done:
328 nbtd_name_registration_reply(s->nbtsock, s->request_packet,
329 s->src, NBT_RCODE_OK);
330failed:
331 talloc_free(s);
332}
333
334/*
335 called when a name query to a current owner completes
336*/
337static void wack_wins_challenge_handler(struct composite_context *c_req)
338{
339 struct nbtd_wins_wack_state *s = talloc_get_type(c_req->async.private_data,
340 struct nbtd_wins_wack_state);
341 bool found;
342 uint32_t i;
343
344 s->status = wins_challenge_recv(c_req, s, &s->io);
345
346 /*
347 * if the owner denies it holds the name, then allow
348 * the registration
349 */
350 if (!NT_STATUS_IS_OK(s->status)) {
351 wins_wack_allow(s);
352 return;
353 }
354
355 if (s->new_type == WREPL_TYPE_GROUP || s->new_type == WREPL_TYPE_SGROUP) {
356 DEBUG(1,("WINS: record %s failed to register as group type(%u) during WACK, it's still type(%u)\n",
357 nbt_name_string(s, s->rec->name), s->new_type, s->rec->type));
358 wins_wack_deny(s);
359 return;
360 }
361
362 /*
363 * if the owner still wants the name and doesn't reply
364 * with the address trying to be registered, then deny
365 * the registration
366 */
367 found = false;
368 for (i=0; i < s->io.out.num_addresses; i++) {
369 if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue;
370
371 found = true;
372 break;
373 }
374 if (!found) {
375 wins_wack_deny(s);
376 return;
377 }
378
379 wins_wack_allow(s);
380 return;
381}
382
383
384/*
385 a client has asked to register a unique name that someone else owns. We
386 need to ask each of the current owners if they still want it. If they do
387 then reject the registration, otherwise allow it
388*/
389static void wins_register_wack(struct nbt_name_socket *nbtsock,
390 struct nbt_name_packet *packet,
391 struct winsdb_record *rec,
392 struct socket_address *src,
393 enum wrepl_name_type new_type)
394{
395 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
396 struct nbtd_interface);
397 struct wins_server *winssrv = iface->nbtsrv->winssrv;
398 struct nbtd_wins_wack_state *s;
399 struct composite_context *c_req;
400 uint32_t ttl;
401
402 s = talloc_zero(nbtsock, struct nbtd_wins_wack_state);
403 if (s == NULL) goto failed;
404
405 /* package up the state variables for this wack request */
406 s->winssrv = winssrv;
407 s->nbtsock = nbtsock;
408 s->iface = iface;
409 s->request_packet = talloc_steal(s, packet);
410 s->rec = talloc_steal(s, rec);
411 s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
412 s->new_type = new_type;
413 s->src = src;
414 if (talloc_reference(s, src) == NULL) goto failed;
415
416 s->io.in.nbtd_server = iface->nbtsrv;
417 s->io.in.nbt_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
418 s->io.in.event_ctx = iface->nbtsrv->task->event_ctx;
419 s->io.in.name = rec->name;
420 s->io.in.num_addresses = winsdb_addr_list_length(rec->addresses);
421 s->io.in.addresses = winsdb_addr_string_list(s, rec->addresses);
422 if (s->io.in.addresses == NULL) goto failed;
423
424 DLIST_ADD_END(iface->wack_queue, s, struct nbtd_wins_wack_state *);
425
426 talloc_set_destructor(s, nbtd_wins_wack_state_destructor);
427
428 /*
429 * send a WACK to the client, specifying the maximum time it could
430 * take to check with the owner, plus some slack
431 */
432 ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses);
433 nbtd_wack_reply(nbtsock, packet, src, ttl);
434
435 /*
436 * send the challenge to the old addresses
437 */
438 c_req = wins_challenge_send(s, &s->io);
439 if (c_req == NULL) goto failed;
440
441 c_req->async.fn = wack_wins_challenge_handler;
442 c_req->async.private_data = s;
443 return;
444
445failed:
446 talloc_free(s);
447 nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR);
448}
449
450/*
451 register a name
452*/
453static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
454 struct nbt_name_packet *packet,
455 struct socket_address *src)
456{
457 NTSTATUS status;
458 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
459 struct nbtd_interface);
460 struct wins_server *winssrv = iface->nbtsrv->winssrv;
461 struct nbt_name *name = &packet->questions[0].name;
462 struct winsdb_record *rec;
463 uint8_t rcode = NBT_RCODE_OK;
464 uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
465 const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
466 bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
467 enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
468 struct winsdb_addr *winsdb_addr = NULL;
469 bool duplicate_packet;
470
471 /*
472 * as a special case, the local master browser name is always accepted
473 * for registration, but never stored, but w2k3 stores it if it's registered
474 * as a group name, (but a query for the 0x1D name still returns not found!)
475 */
476 if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
477 rcode = NBT_RCODE_OK;
478 goto done;
479 }
480
481 /* w2k3 refuses 0x1B names with marked as group */
482 if (name->type == NBT_NAME_PDC && (nb_flags & NBT_NM_GROUP)) {
483 rcode = NBT_RCODE_RFS;
484 goto done;
485 }
486
487 /* w2k3 refuses 0x1C names with out marked as group */
488 if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
489 rcode = NBT_RCODE_RFS;
490 goto done;
491 }
492
493 /* w2k3 refuses 0x1E names with out marked as group */
494 if (name->type == NBT_NAME_BROWSER && !(nb_flags & NBT_NM_GROUP)) {
495 rcode = NBT_RCODE_RFS;
496 goto done;
497 }
498
499 duplicate_packet = wins_check_wack_queue(iface, packet, src);
500 if (duplicate_packet) {
501 /* just ignore the packet */
502 DEBUG(5,("Ignoring duplicate packet while WACK is pending from %s:%d\n",
503 src->addr, src->port));
504 return;
505 }
506
507 status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
508 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
509 rcode = wins_register_new(nbtsock, packet, src, new_type);
510 goto done;
511 } else if (!NT_STATUS_IS_OK(status)) {
512 rcode = NBT_RCODE_SVR;
513 goto done;
514 } else if (rec->is_static) {
515 if (rec->type == WREPL_TYPE_GROUP || rec->type == WREPL_TYPE_SGROUP) {
516 rcode = NBT_RCODE_OK;
517 goto done;
518 }
519 rcode = NBT_RCODE_ACT;
520 goto done;
521 }
522
523 if (rec->type == WREPL_TYPE_GROUP) {
524 if (new_type != WREPL_TYPE_GROUP) {
525 DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
526 " while a normal group is already there\n",
527 nbt_name_string(packet, name), new_type));
528 rcode = NBT_RCODE_ACT;
529 goto done;
530 }
531
532 if (rec->state == WREPL_STATE_ACTIVE) {
533 /* TODO: is this correct? */
534 rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
535 goto done;
536 }
537
538 /* TODO: is this correct? */
539 winsdb_delete(winssrv->wins_db, rec);
540 rcode = wins_register_new(nbtsock, packet, src, new_type);
541 goto done;
542 }
543
544 if (rec->state != WREPL_STATE_ACTIVE) {
545 winsdb_delete(winssrv->wins_db, rec);
546 rcode = wins_register_new(nbtsock, packet, src, new_type);
547 goto done;
548 }
549
550 switch (rec->type) {
551 case WREPL_TYPE_UNIQUE:
552 case WREPL_TYPE_MHOMED:
553 /*
554 * if its an active unique name, and the registration is for a group, then
555 * see if the unique name owner still wants the name
556 * TODO: is this correct?
557 */
558 if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
559 wins_register_wack(nbtsock, packet, rec, src, new_type);
560 return;
561 }
562
563 /*
564 * if the registration is for an address that is currently active, then
565 * just update the expiry time of the record and the address
566 */
567 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
568 if (winsdb_addr) {
569 rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
570 goto done;
571 }
572
573 /*
574 * we have to do a WACK to see if the current owner is willing
575 * to give up its claim
576 */
577 wins_register_wack(nbtsock, packet, rec, src, new_type);
578 return;
579
580 case WREPL_TYPE_GROUP:
581 /* this should not be reached as normal groups are handled above */
582 DEBUG(0,("BUG at %s\n",__location__));
583 rcode = NBT_RCODE_ACT;
584 goto done;
585
586 case WREPL_TYPE_SGROUP:
587 /* if the new record isn't also a special group, refuse the registration */
588 if (new_type != WREPL_TYPE_SGROUP) {
589 DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
590 " while a special group is already there\n",
591 nbt_name_string(packet, name), new_type));
592 rcode = NBT_RCODE_ACT;
593 goto done;
594 }
595
596 /*
597 * if the registration is for an address that is currently active, then
598 * just update the expiry time of the record and the address
599 */
600 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
601 if (winsdb_addr) {
602 rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
603 goto done;
604 }
605
606 rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
607 goto done;
608 }
609
610done:
611 nbtd_name_registration_reply(nbtsock, packet, src, rcode);
612}
613
614static uint32_t ipv4_match_bits(struct in_addr ip1, struct in_addr ip2)
615{
616 uint32_t i, j, match=0;
617 uint8_t *p1, *p2;
618
619 p1 = (uint8_t *)&ip1.s_addr;
620 p2 = (uint8_t *)&ip2.s_addr;
621
622 for (i=0; i<4; i++) {
623 if (p1[i] != p2[i]) break;
624 match += 8;
625 }
626
627 if (i==4) return match;
628
629 for (j=0; j<8; j++) {
630 if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
631 break;
632 match++;
633 }
634
635 return match;
636}
637
638static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */
639 void *p2,/* (const char **) */
640 struct socket_address *src)
641{
642 const char *a1 = (const char *)*(const char **)p1;
643 const char *a2 = (const char *)*(const char **)p2;
644 uint32_t match_bits1;
645 uint32_t match_bits2;
646
647 match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr));
648 match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr));
649
650 return match_bits2 - match_bits1;
651}
652
653static void nbtd_wins_randomize1Clist(struct loadparm_context *lp_ctx,
654 const char **addresses, struct socket_address *src)
655{
656 const char *mask;
657 const char *tmp;
658 uint32_t num_addrs;
659 uint32_t idx, sidx;
660 int r;
661
662 for (num_addrs=0; addresses[num_addrs]; num_addrs++) { /* noop */ }
663
664 if (num_addrs <= 1) return; /* nothing to do */
665
666 /* first sort the addresses depending on the matching to the client */
667 ldb_qsort(addresses, num_addrs , sizeof(addresses[0]),
668 src, (ldb_qsort_cmp_fn_t)nbtd_wins_randomize1Clist_sort);
669
670 mask = lp_parm_string(lp_ctx, NULL, "nbtd", "wins_randomize1Clist_mask");
671 if (!mask) {
672 mask = "255.255.255.0";
673 }
674
675 /*
676 * choose a random address to be the first in the response to the client,
677 * preferr the addresses inside the nbtd:wins_randomize1Clist_mask netmask
678 */
679 r = random();
680 idx = sidx = r % num_addrs;
681
682 while (1) {
683 bool same;
684
685 /* if the current one is in the same subnet, use it */
686 same = iface_same_net(addresses[idx], src->addr, mask);
687 if (same) {
688 sidx = idx;
689 break;
690 }
691
692 /* we need to check for idx == 0, after checking for the same net */
693 if (idx == 0) break;
694 /*
695 * if we haven't found an address in the same subnet, search in ones
696 * which match the client more
697 *
698 * some notes:
699 *
700 * it's not "idx = idx % r" but "idx = r % idx"
701 * because in "a % b" b is the allowed range
702 * and b-1 is the maximum possible result, so it must be decreasing
703 * and the above idx == 0 check breaks the while(1) loop.
704 */
705 idx = r % idx;
706 }
707
708 /* note sidx == 0 is also valid here ... */
709 tmp = addresses[0];
710 addresses[0] = addresses[sidx];
711 addresses[sidx] = tmp;
712}
713
714/*
715 query a name
716*/
717static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
718 struct nbt_name_socket *nbtsock,
719 struct nbt_name_packet *packet,
720 struct socket_address *src)
721{
722 NTSTATUS status;
723 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
724 struct nbtd_interface);
725 struct wins_server *winssrv = iface->nbtsrv->winssrv;
726 struct nbt_name *name = &packet->questions[0].name;
727 struct winsdb_record *rec;
728 struct winsdb_record *rec_1b = NULL;
729 const char **addresses;
730 const char **addresses_1b = NULL;
731 uint16_t nb_flags = 0;
732
733 if (name->type == NBT_NAME_MASTER) {
734 goto notfound;
735 }
736
737 /*
738 * w2k3 returns the first address of the 0x1B record as first address
739 * to a 0x1C query
740 *
741 * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
742 *
743 * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Prepend1BTo1CQueries
744 * Typ: Daten REG_DWORD
745 * Value: 0 = deactivated, 1 = activated
746 */
747 if (name->type == NBT_NAME_LOGON &&
748 lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
749 struct nbt_name name_1b;
750
751 name_1b = *name;
752 name_1b.type = NBT_NAME_PDC;
753
754 status = winsdb_lookup(winssrv->wins_db, &name_1b, packet, &rec_1b);
755 if (NT_STATUS_IS_OK(status)) {
756 addresses_1b = winsdb_addr_string_list(packet, rec_1b->addresses);
757 }
758 }
759
760 status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
761 if (!NT_STATUS_IS_OK(status)) {
762 if (!lp_wins_dns_proxy(lp_ctx)) {
763 goto notfound;
764 }
765
766 if (name->type != NBT_NAME_CLIENT && name->type != NBT_NAME_SERVER) {
767 goto notfound;
768 }
769
770 nbtd_wins_dns_proxy_query(nbtsock, packet, src);
771 return;
772 }
773
774 /*
775 * for group's we always reply with
776 * 255.255.255.255 as address, even if
777 * the record is released or tombstoned
778 */
779 if (rec->type == WREPL_TYPE_GROUP) {
780 addresses = str_list_add(NULL, "255.255.255.255");
781 talloc_steal(packet, addresses);
782 if (!addresses) {
783 goto notfound;
784 }
785 nb_flags |= NBT_NM_GROUP;
786 goto found;
787 }
788
789 if (rec->state != WREPL_STATE_ACTIVE) {
790 goto notfound;
791 }
792
793 addresses = winsdb_addr_string_list(packet, rec->addresses);
794 if (!addresses) {
795 goto notfound;
796 }
797
798 /*
799 * if addresses_1b isn't NULL, we have a 0x1C query and need to return the
800 * first 0x1B address as first address
801 */
802 if (addresses_1b && addresses_1b[0]) {
803 const char **addresses_1c = addresses;
804 uint32_t i;
805 uint32_t num_addrs;
806
807 addresses = str_list_add(NULL, addresses_1b[0]);
808 if (!addresses) {
809 goto notfound;
810 }
811 talloc_steal(packet, addresses);
812 num_addrs = 1;
813
814 for (i=0; addresses_1c[i]; i++) {
815 if (strcmp(addresses_1b[0], addresses_1c[i]) == 0) continue;
816
817 /*
818 * stop when we already have 25 addresses
819 */
820 if (num_addrs >= 25) break;
821
822 num_addrs++;
823 addresses = str_list_add(addresses, addresses_1c[i]);
824 if (!addresses) {
825 goto notfound;
826 }
827 }
828 }
829
830 if (rec->type == WREPL_TYPE_SGROUP) {
831 nb_flags |= NBT_NM_GROUP;
832 } else {
833 nb_flags |= (rec->node <<13);
834 }
835
836 /*
837 * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
838 *
839 * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Randomize1CList
840 * Typ: Daten REG_DWORD
841 * Value: 0 = deactivated, 1 = activated
842 */
843 if (name->type == NBT_NAME_LOGON &&
844 lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_randomize1Clist", false)) {
845 nbtd_wins_randomize1Clist(lp_ctx, addresses, src);
846 }
847
848found:
849 nbtd_name_query_reply(nbtsock, packet, src, name,
850 0, nb_flags, addresses);
851 return;
852
853notfound:
854 nbtd_negative_name_query_reply(nbtsock, packet, src);
855}
856
857/*
858 release a name
859*/
860static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
861 struct nbt_name_packet *packet,
862 struct socket_address *src)
863{
864 NTSTATUS status;
865 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
866 struct nbtd_interface);
867 struct wins_server *winssrv = iface->nbtsrv->winssrv;
868 struct nbt_name *name = &packet->questions[0].name;
869 struct winsdb_record *rec;
870 uint32_t modify_flags = 0;
871 uint8_t ret;
872
873 if (name->type == NBT_NAME_MASTER) {
874 goto done;
875 }
876
877 status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
878 if (!NT_STATUS_IS_OK(status)) {
879 goto done;
880 }
881
882 if (rec->is_static) {
883 if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
884 goto done;
885 }
886 nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
887 return;
888 }
889
890 if (rec->state != WREPL_STATE_ACTIVE) {
891 goto done;
892 }
893
894 /*
895 * TODO: do we need to check if
896 * src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
897 * here?
898 */
899
900 /*
901 * we only allow releases from an owner - other releases are
902 * silently ignored
903 */
904 if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
905 int i;
906 DEBUG(4,("WINS: silently ignoring attempted name release on %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
907 DEBUGADD(4, ("Registered Addresses: \n"));
908 for (i=0; rec->addresses && rec->addresses[i]; i++) {
909 DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
910 }
911 goto done;
912 }
913
914 DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
915
916 switch (rec->type) {
917 case WREPL_TYPE_UNIQUE:
918 rec->state = WREPL_STATE_RELEASED;
919 break;
920
921 case WREPL_TYPE_GROUP:
922 rec->state = WREPL_STATE_RELEASED;
923 break;
924
925 case WREPL_TYPE_SGROUP:
926 winsdb_addr_list_remove(rec->addresses, src->addr);
927 /* TODO: do we need to take the ownership here? */
928 if (winsdb_addr_list_length(rec->addresses) == 0) {
929 rec->state = WREPL_STATE_RELEASED;
930 }
931 break;
932
933 case WREPL_TYPE_MHOMED:
934 winsdb_addr_list_remove(rec->addresses, src->addr);
935 /* TODO: do we need to take the ownership here? */
936 if (winsdb_addr_list_length(rec->addresses) == 0) {
937 rec->state = WREPL_STATE_RELEASED;
938 }
939 break;
940 }
941
942 if (rec->state == WREPL_STATE_ACTIVE) {
943 /*
944 * If the record is still active, we need to update the
945 * expire_time.
946 *
947 * if we're not the owner, we need to take the ownership.
948 */
949 rec->expire_time= time(NULL) + winssrv->config.max_renew_interval;
950 if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) != 0) {
951 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
952 }
953 if (lp_parm_bool(iface->nbtsrv->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false)) {
954 /*
955 * We have an option to propagate every name release,
956 * this is off by default to match windows servers
957 */
958 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
959 }
960 } else if (rec->state == WREPL_STATE_RELEASED) {
961 /*
962 * if we're not the owner, we need to take the owner ship
963 * and make the record tombstone, but expire after
964 * tombstone_interval + tombstone_timeout and not only after tombstone_timeout
965 * like for normal tombstone records.
966 * This is to replicate the record directly to the original owner,
967 * where the record is still active
968 */
969 if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) == 0) {
970 rec->expire_time= time(NULL) + winssrv->config.tombstone_interval;
971 } else {
972 rec->state = WREPL_STATE_TOMBSTONE;
973 rec->expire_time= time(NULL) +
974 winssrv->config.tombstone_interval +
975 winssrv->config.tombstone_timeout;
976 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
977 }
978 }
979
980 ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
981 if (ret != NBT_RCODE_OK) {
982 DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
983 nbt_name_string(rec, rec->name), src->addr, ret));
984 }
985done:
986 /* we match w2k3 by always giving a positive reply to name releases. */
987 nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
988}
989
990
991/*
992 answer a name query
993*/
994void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
995 struct nbt_name_packet *packet,
996 struct socket_address *src)
997{
998 struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
999 struct nbtd_interface);
1000 struct wins_server *winssrv = iface->nbtsrv->winssrv;
1001 if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
1002 return;
1003 }
1004
1005 switch (packet->operation & NBT_OPCODE) {
1006 case NBT_OPCODE_QUERY:
1007 nbtd_winsserver_query(iface->nbtsrv->task->lp_ctx, nbtsock, packet, src);
1008 break;
1009
1010 case NBT_OPCODE_REGISTER:
1011 case NBT_OPCODE_REFRESH:
1012 case NBT_OPCODE_REFRESH2:
1013 case NBT_OPCODE_MULTI_HOME_REG:
1014 nbtd_winsserver_register(nbtsock, packet, src);
1015 break;
1016
1017 case NBT_OPCODE_RELEASE:
1018 nbtd_winsserver_release(nbtsock, packet, src);
1019 break;
1020 }
1021
1022}
1023
1024/*
1025 startup the WINS server, if configured
1026*/
1027NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
1028{
1029 uint32_t tmp;
1030 const char *owner;
1031
1032 if (!lp_wins_support(nbtsrv->task->lp_ctx)) {
1033 nbtsrv->winssrv = NULL;
1034 return NT_STATUS_OK;
1035 }
1036
1037 nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
1038 NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
1039
1040 nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl(nbtsrv->task->lp_ctx);
1041 nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl(nbtsrv->task->lp_ctx);
1042 tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv", "tombstone_interval", 6*24*60*60);
1043 nbtsrv->winssrv->config.tombstone_interval = tmp;
1044 tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
1045 nbtsrv->winssrv->config.tombstone_timeout = tmp;
1046
1047 owner = lp_parm_string(nbtsrv->task->lp_ctx, NULL, "winsdb", "local_owner");
1048
1049 if (owner == NULL) {
1050 struct interface *ifaces;
1051 load_interfaces(nbtsrv->task, lp_interfaces(nbtsrv->task->lp_ctx), &ifaces);
1052 owner = iface_n_ip(ifaces, 0);
1053 }
1054
1055 nbtsrv->winssrv->wins_db = winsdb_connect(nbtsrv->winssrv, nbtsrv->task->event_ctx,
1056 nbtsrv->task->lp_ctx,
1057 owner, WINSDB_HANDLE_CALLER_NBTD);
1058 if (!nbtsrv->winssrv->wins_db) {
1059 return NT_STATUS_INTERNAL_DB_ERROR;
1060 }
1061
1062 irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
1063
1064 return NT_STATUS_OK;
1065}
Note: See TracBrowser for help on using the repository browser.