This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
+#include "dlinklist.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsdb.h"
#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
#include "system/time.h"
#include "libcli/composite/composite.h"
#include "smbd/service_task.h"
+#include "system/network.h"
#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+#include "lib/ldb/include/ldb.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
/*
work out the ttl we will use given a client requested ttl
return ttl;
}
-static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, BOOL mhomed)
+static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, bool mhomed)
{
/* this copes with the nasty hack that is the type 0x1c name */
if (name->type == NBT_NAME_LOGON) {
const struct socket_address *src,
enum wrepl_name_type type)
{
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
rec.type = type;
rec.state = WREPL_STATE_ACTIVE;
rec.node = node;
- rec.is_static = False;
+ rec.is_static = false;
rec.expire_time = time(NULL) + ttl;
rec.version = 0; /* will be allocated later */
rec.wins_owner = NULL; /* will be set later */
rec.addresses = winsdb_addr_list_make(packet);
if (rec.addresses == NULL) return NBT_RCODE_SVR;
- rec.addresses = winsdb_addr_list_add(rec.addresses,
+ rec.addresses = winsdb_addr_list_add(winssrv->wins_db,
+ &rec, rec.addresses,
address,
winssrv->wins_db->local_owner,
- rec.expire_time);
+ rec.expire_time,
+ true);
if (rec.addresses == NULL) return NBT_RCODE_SVR;
DEBUG(4,("WINS: accepted registration of %s with address %s\n",
struct winsdb_addr *winsdb_addr,
const struct socket_address *src)
{
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
rec->registered_by = src->addr;
if (winsdb_addr) {
- winsdb_addr->wins_owner = winssrv->wins_db->local_owner;
- winsdb_addr->expire_time = rec->expire_time;
+ rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
+ rec, rec->addresses,
+ winsdb_addr->address,
+ winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) return NBT_RCODE_SVR;
}
if (strcmp(winssrv->wins_db->local_owner, rec->wins_owner) != 0) {
const char *address,
const struct socket_address *src)
{
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
rec->expire_time = time(NULL) + ttl;
rec->registered_by = src->addr;
- rec->addresses = winsdb_addr_list_add(rec->addresses,
+ rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
+ rec, rec->addresses,
address,
winssrv->wins_db->local_owner,
- rec->expire_time);
+ rec->expire_time,
+ true);
if (rec->addresses == NULL) return NBT_RCODE_SVR;
DEBUG(5,("WINS: sgroup merge of %s at %s\n",
return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
}
-struct wack_state {
+struct nbtd_wins_wack_state {
+ struct nbtd_wins_wack_state *prev, *next;
struct wins_server *winssrv;
struct nbt_name_socket *nbtsock;
+ struct nbtd_interface *iface;
struct nbt_name_packet *request_packet;
struct winsdb_record *rec;
struct socket_address *src;
NTSTATUS status;
};
+static int nbtd_wins_wack_state_destructor(struct nbtd_wins_wack_state *s)
+{
+ DLIST_REMOVE(s->iface->wack_queue, s);
+ return 0;
+}
+
+static bool wins_check_wack_queue(struct nbtd_interface *iface,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_wins_wack_state *s;
+
+ for (s= iface->wack_queue; s; s = s->next) {
+ if (packet->name_trn_id != s->request_packet->name_trn_id) {
+ continue;
+ }
+ if (packet->operation != s->request_packet->operation) {
+ continue;
+ }
+ if (src->port != s->src->port) {
+ continue;
+ }
+ if (strcmp(src->addr, s->src->addr) != 0) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
/*
deny a registration request
*/
-static void wins_wack_deny(struct wack_state *s)
+static void wins_wack_deny(struct nbtd_wins_wack_state *s)
{
nbtd_name_registration_reply(s->nbtsock, s->request_packet,
s->src, NBT_RCODE_ACT);
/*
allow a registration request
*/
-static void wins_wack_allow(struct wack_state *s)
+static void wins_wack_allow(struct nbtd_wins_wack_state *s)
{
NTSTATUS status;
uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl);
if (!NT_STATUS_IS_OK(status) ||
rec2->version != rec->version ||
strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
- DEBUG(1,("WINS: record %s changed during WACK - failing registration\n",
+ DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
nbt_name_string(s, rec->name)));
wins_wack_deny(s);
return;
* and update the time stamp and owner for the ownes that are still there
*/
for (i=0; rec->addresses[i]; i++) {
- BOOL found = False;
+ bool found = false;
for (j=0; j < s->io.out.num_addresses; j++) {
if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue;
- found = True;
+ found = true;
break;
}
if (found) {
- rec->addresses[i]->wins_owner = s->winssrv->wins_db->local_owner;
- rec->addresses[i]->expire_time = rec->expire_time;
+ rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+ rec, rec->addresses,
+ s->reg_address,
+ s->winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) goto failed;
continue;
}
winsdb_addr_list_remove(rec->addresses, rec->addresses[i]->address);
}
- rec->addresses = winsdb_addr_list_add(rec->addresses,
+ rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+ rec, rec->addresses,
s->reg_address,
s->winssrv->wins_db->local_owner,
- rec->expire_time);
+ rec->expire_time,
+ true);
if (rec->addresses == NULL) goto failed;
/* if we have more than one address, this becomes implicit a MHOMED record */
*/
static void wack_wins_challenge_handler(struct composite_context *c_req)
{
- struct wack_state *s = talloc_get_type(c_req->async.private_data,
- struct wack_state);
- BOOL found;
+ struct nbtd_wins_wack_state *s = talloc_get_type(c_req->async.private_data,
+ struct nbtd_wins_wack_state);
+ bool found;
uint32_t i;
s->status = wins_challenge_recv(c_req, s, &s->io);
* with the address trying to be registered, then deny
* the registration
*/
- found = False;
+ found = false;
for (i=0; i < s->io.out.num_addresses; i++) {
if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue;
- found = True;
+ found = true;
break;
}
if (!found) {
struct socket_address *src,
enum wrepl_name_type new_type)
{
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
- struct wack_state *s;
+ struct nbtd_wins_wack_state *s;
struct composite_context *c_req;
uint32_t ttl;
- s = talloc_zero(nbtsock, struct wack_state);
+ s = talloc_zero(nbtsock, struct nbtd_wins_wack_state);
if (s == NULL) goto failed;
/* package up the state variables for this wack request */
s->winssrv = winssrv;
s->nbtsock = nbtsock;
+ s->iface = iface;
s->request_packet = talloc_steal(s, packet);
s->rec = talloc_steal(s, rec);
s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
if (talloc_reference(s, src) == NULL) goto failed;
s->io.in.nbtd_server = iface->nbtsrv;
+ s->io.in.nbt_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
s->io.in.event_ctx = iface->nbtsrv->task->event_ctx;
s->io.in.name = rec->name;
s->io.in.num_addresses = winsdb_addr_list_length(rec->addresses);
s->io.in.addresses = winsdb_addr_string_list(s, rec->addresses);
if (s->io.in.addresses == NULL) goto failed;
+ DLIST_ADD_END(iface->wack_queue, s, struct nbtd_wins_wack_state *);
+
+ talloc_set_destructor(s, nbtd_wins_wack_state_destructor);
+
/*
* send a WACK to the client, specifying the maximum time it could
* take to check with the owner, plus some slack
struct socket_address *src)
{
NTSTATUS status;
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
uint8_t rcode = NBT_RCODE_OK;
uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
- BOOL mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
+ bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
struct winsdb_addr *winsdb_addr = NULL;
+ bool duplicate_packet;
/*
* as a special case, the local master browser name is always accepted
goto done;
}
+ duplicate_packet = wins_check_wack_queue(iface, packet, src);
+ if (duplicate_packet) {
+ /* just ignore the packet */
+ DEBUG(5,("Ignoring duplicate packet while WACK is pending from %s:%d\n",
+ src->addr, src->port));
+ return;
+ }
+
status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
rcode = wins_register_new(nbtsock, packet, src, new_type);
/*
* if the registration is for an address that is currently active, then
* just update the expiry time of the record and the address
- * TODO: is this correct?
*/
winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
if (winsdb_addr) {
/*
* if the registration is for an address that is currently active, then
- * just update the expiry time
* just update the expiry time of the record and the address
- * TODO: is this correct?
*/
winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
if (winsdb_addr) {
nbtd_name_registration_reply(nbtsock, packet, src, rcode);
}
+static uint32_t ipv4_match_bits(struct in_addr ip1, struct in_addr ip2)
+{
+ uint32_t i, j, match=0;
+ uint8_t *p1, *p2;
+
+ p1 = (uint8_t *)&ip1.s_addr;
+ p2 = (uint8_t *)&ip2.s_addr;
+
+ for (i=0; i<4; i++) {
+ if (p1[i] != p2[i]) break;
+ match += 8;
+ }
+
+ if (i==4) return match;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ match++;
+ }
+
+ return match;
+}
+
+static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */
+ void *p2,/* (const char **) */
+ struct socket_address *src)
+{
+ const char *a1 = (const char *)*(const char **)p1;
+ const char *a2 = (const char *)*(const char **)p2;
+ uint32_t match_bits1;
+ uint32_t match_bits2;
+
+ match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr));
+ match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr));
+
+ return match_bits2 - match_bits1;
+}
+
+static void nbtd_wins_randomize1Clist(struct loadparm_context *lp_ctx,
+ const char **addresses, struct socket_address *src)
+{
+ const char *mask;
+ const char *tmp;
+ uint32_t num_addrs;
+ uint32_t idx, sidx;
+ int r;
+
+ for (num_addrs=0; addresses[num_addrs]; num_addrs++) { /* noop */ }
+
+ if (num_addrs <= 1) return; /* nothing to do */
+
+ /* first sort the addresses depending on the matching to the client */
+ ldb_qsort(addresses, num_addrs , sizeof(addresses[0]),
+ src, (ldb_qsort_cmp_fn_t)nbtd_wins_randomize1Clist_sort);
+
+ mask = lp_parm_string(lp_ctx, NULL, "nbtd", "wins_randomize1Clist_mask");
+ if (!mask) {
+ mask = "255.255.255.0";
+ }
+
+ /*
+ * choose a random address to be the first in the response to the client,
+ * preferr the addresses inside the nbtd:wins_randomize1Clist_mask netmask
+ */
+ r = random();
+ idx = sidx = r % num_addrs;
+
+ while (1) {
+ bool same;
+
+ /* if the current one is in the same subnet, use it */
+ same = iface_same_net(addresses[idx], src->addr, mask);
+ if (same) {
+ sidx = idx;
+ break;
+ }
+
+ /* we need to check for idx == 0, after checking for the same net */
+ if (idx == 0) break;
+ /*
+ * if we haven't found an address in the same subnet, search in ones
+ * which match the client more
+ *
+ * some notes:
+ *
+ * it's not "idx = idx % r" but "idx = r % idx"
+ * because in "a % b" b is the allowed range
+ * and b-1 is the maximum possible result, so it must be decreasing
+ * and the above idx == 0 check breaks the while(1) loop.
+ */
+ idx = r % idx;
+ }
+
+ /* note sidx == 0 is also valid here ... */
+ tmp = addresses[0];
+ addresses[0] = addresses[sidx];
+ addresses[sidx] = tmp;
+}
+
/*
query a name
*/
-static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
+static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
+ struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
NTSTATUS status;
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
/*
* w2k3 returns the first address of the 0x1B record as first address
* to a 0x1C query
+ *
+ * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+ *
+ * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Prepend1BTo1CQueries
+ * Typ: Daten REG_DWORD
+ * Value: 0 = deactivated, 1 = activated
*/
- if (name->type == NBT_NAME_LOGON) {
+ if (name->type == NBT_NAME_LOGON &&
+ lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
struct nbt_name name_1b;
name_1b = *name;
status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
if (!NT_STATUS_IS_OK(status)) {
- if (!lp_wins_dns_proxy()) {
+ if (!lp_wins_dns_proxy(lp_ctx)) {
goto notfound;
}
if (addresses_1b && addresses_1b[0]) {
const char **addresses_1c = addresses;
uint32_t i;
+ uint32_t num_addrs;
addresses = str_list_add(NULL, addresses_1b[0]);
if (!addresses) {
goto notfound;
}
talloc_steal(packet, addresses);
+ num_addrs = 1;
for (i=0; addresses_1c[i]; i++) {
if (strcmp(addresses_1b[0], addresses_1c[i]) == 0) continue;
+ /*
+ * stop when we already have 25 addresses
+ */
+ if (num_addrs >= 25) break;
+
+ num_addrs++;
addresses = str_list_add(addresses, addresses_1c[i]);
if (!addresses) {
goto notfound;
if (rec->type == WREPL_TYPE_SGROUP) {
nb_flags |= NBT_NM_GROUP;
} else {
- nb_flags |= (rec->node <<13);
+ nb_flags |= (rec->node <<13);
+ }
+
+ /*
+ * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+ *
+ * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Randomize1CList
+ * Typ: Daten REG_DWORD
+ * Value: 0 = deactivated, 1 = activated
+ */
+ if (name->type == NBT_NAME_LOGON &&
+ lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_randomize1Clist", false)) {
+ nbtd_wins_randomize1Clist(lp_ctx, addresses, src);
}
found:
struct socket_address *src)
{
NTSTATUS status;
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
int i;
DEBUG(4,("WINS: silently ignoring attempted name release on %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
- DEBUGADD(4, ("Registered Addressss: \n"));
+ DEBUGADD(4, ("Registered Addresses: \n"));
for (i=0; rec->addresses && rec->addresses[i]; i++) {
DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
}
break;
}
- if (rec->state == WREPL_STATE_RELEASED) {
+ if (rec->state == WREPL_STATE_ACTIVE) {
+ /*
+ * If the record is still active, we need to update the
+ * expire_time.
+ *
+ * if we're not the owner, we need to take the ownership.
+ */
+ rec->expire_time= time(NULL) + winssrv->config.max_renew_interval;
+ if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) != 0) {
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+ if (lp_parm_bool(iface->nbtsrv->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false)) {
+ /*
+ * We have an option to propagate every name release,
+ * this is off by default to match windows servers
+ */
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+ } else if (rec->state == WREPL_STATE_RELEASED) {
/*
* if we're not the owner, we need to take the owner ship
* and make the record tombstone, but expire after
struct nbt_name_packet *packet,
struct socket_address *src)
{
- struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
switch (packet->operation & NBT_OPCODE) {
case NBT_OPCODE_QUERY:
- nbtd_winsserver_query(nbtsock, packet, src);
+ nbtd_winsserver_query(iface->nbtsrv->task->lp_ctx, nbtsock, packet, src);
break;
case NBT_OPCODE_REGISTER:
NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
{
uint32_t tmp;
+ const char *owner;
- if (!lp_wins_support()) {
+ if (!lp_wins_support(nbtsrv->task->lp_ctx)) {
nbtsrv->winssrv = NULL;
return NT_STATUS_OK;
}
nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
- nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
- nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
- tmp = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
+ nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl(nbtsrv->task->lp_ctx);
+ nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl(nbtsrv->task->lp_ctx);
+ tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv", "tombstone_interval", 6*24*60*60);
nbtsrv->winssrv->config.tombstone_interval = tmp;
- tmp = lp_parm_int(-1,"wreplsrv","tombstone_timeout", 1*24*60*60);
+ tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
nbtsrv->winssrv->config.tombstone_timeout = tmp;
- nbtsrv->winssrv->wins_db = winsdb_connect(nbtsrv->winssrv, WINSDB_HANDLE_CALLER_NBTD);
+ owner = lp_parm_string(nbtsrv->task->lp_ctx, NULL, "winsdb", "local_owner");
+
+ if (owner == NULL) {
+ struct interface *ifaces;
+ load_interfaces(nbtsrv->task, lp_interfaces(nbtsrv->task->lp_ctx), &ifaces);
+ owner = iface_n_ip(ifaces, 0);
+ }
+
+ nbtsrv->winssrv->wins_db = winsdb_connect(nbtsrv->winssrv, nbtsrv->task->event_ctx,
+ nbtsrv->task->lp_ctx,
+ owner, WINSDB_HANDLE_CALLER_NBTD);
if (!nbtsrv->winssrv->wins_db) {
return NT_STATUS_INTERNAL_DB_ERROR;
}