summaryrefslogtreecommitdiff
path: root/source4/nbt_server
diff options
context:
space:
mode:
Diffstat (limited to 'source4/nbt_server')
-rw-r--r--source4/nbt_server/config.mk6
-rw-r--r--source4/nbt_server/interfaces.c97
-rw-r--r--source4/nbt_server/nbt_server.c18
-rw-r--r--source4/nbt_server/nbt_server.h22
-rw-r--r--source4/nbt_server/packet.c38
-rw-r--r--source4/nbt_server/query.c122
-rw-r--r--source4/nbt_server/register.c161
-rw-r--r--source4/nbt_server/winsserver.c35
8 files changed, 483 insertions, 16 deletions
diff --git a/source4/nbt_server/config.mk b/source4/nbt_server/config.mk
index 093cb61864..e3f52a64e6 100644
--- a/source4/nbt_server/config.mk
+++ b/source4/nbt_server/config.mk
@@ -6,7 +6,11 @@
INIT_OBJ_FILES = \
nbt_server/nbt_server.o
ADD_OBJ_FILES = \
- nbt_server/interfaces.o
+ nbt_server/interfaces.o \
+ nbt_server/register.o \
+ nbt_server/query.o \
+ nbt_server/winsserver.o \
+ nbt_server/packet.o
REQUIRED_SUBSYSTEMS = \
LIBCLI_NBT
# End SUBSYSTEM SMB
diff --git a/source4/nbt_server/interfaces.c b/source4/nbt_server/interfaces.c
index 18893e179b..303802e2cd 100644
--- a/source4/nbt_server/interfaces.c
+++ b/source4/nbt_server/interfaces.c
@@ -24,13 +24,72 @@
#include "dlinklist.h"
#include "nbt_server/nbt_server.h"
#include "smbd/service_task.h"
-#include "libcli/nbt/libnbt.h"
+
+/*
+ find a registered name on an interface
+*/
+struct nbt_iface_name *nbt_find_iname(struct nbt_interface *iface, struct nbt_name *name,
+ uint16_t nb_flags)
+{
+ struct nbt_iface_name *iname;
+ for (iname=iface->names;iname;iname=iname->next) {
+ if (iname->name.type == name->type &&
+ StrCaseCmp(name->name, iname->name.name) == 0 &&
+ ((iname->nb_flags & nb_flags) == nb_flags)) {
+ return iname;
+ }
+ }
+ return NULL;
+}
+
+/*
+ see if a src address matches an interface
+*/
+static BOOL nbt_iface_match(struct nbt_interface *iface, const char *src_address)
+{
+ struct ipv4_addr ip1, ip2, mask;
+ ip1 = interpret_addr2(iface->ip_address);
+ ip2 = interpret_addr2(src_address);
+ mask = interpret_addr2(iface->netmask);
+ return same_net(ip1, ip2, mask);
+}
+
+
+/*
+ find the appropriate interface for a incoming packet. If a local interface isn't
+ found then the general broadcast interface is used
+*/
+struct nbt_interface *nbt_iface_find(struct nbt_name_socket *nbtsock, const char *src_address)
+{
+ struct nbt_interface *iface = talloc_get_type(nbtsock->incoming.private, struct nbt_interface);
+ struct nbt_server *nbtsrv = iface->nbtsrv;
+
+ /* it might have been received by one of our specific bound
+ addresses */
+ if (iface != nbtsrv->bcast_interface) {
+ return iface;
+ }
+
+ /* it came in on our broadcast interface - try to find a match */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ if (nbt_iface_match(iface, src_address)) {
+ return iface;
+ }
+ }
+
+ /* it didn't match any specific interface - use our general broadcast interface */
+ return nbtsrv->bcast_interface;
+}
+
/*
start listening on the given address
*/
static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
- const char *address, const char *bcast)
+ const char *bind_address,
+ const char *address,
+ const char *bcast,
+ const char *netmask)
{
struct nbt_interface *iface;
NTSTATUS status;
@@ -38,14 +97,16 @@ static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
iface = talloc(nbtsrv, struct nbt_interface);
NT_STATUS_HAVE_NO_MEMORY(iface);
- iface->nbtsrv = nbtsrv;
+ iface->nbtsrv = nbtsrv;
iface->bcast_address = talloc_steal(iface, bcast);
- iface->ip_address = talloc_steal(iface, address);
+ iface->ip_address = talloc_steal(iface, address);
+ iface->netmask = talloc_steal(iface, netmask);
+ iface->names = NULL;
iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
NT_STATUS_HAVE_NO_MEMORY(iface->ip_address);
- status = socket_listen(iface->nbtsock->sock, address, lp_nbt_port(), 0, 0);
+ status = socket_listen(iface->nbtsock->sock, bind_address, lp_nbt_port(), 0, 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%d - %s\n",
address, lp_nbt_port(), nt_errstr(status)));
@@ -53,7 +114,13 @@ static NTSTATUS nbt_add_socket(struct nbt_server *nbtsrv,
return status;
}
- DLIST_ADD(nbtsrv->interfaces, iface);
+ socket_set_option(iface->nbtsock->sock, "SO_BROADCAST", "1");
+
+ if (strcmp(netmask, "0.0.0.0") == 0) {
+ DLIST_ADD(nbtsrv->bcast_interface, iface);
+ } else {
+ DLIST_ADD(nbtsrv->interfaces, iface);
+ }
return NT_STATUS_OK;
}
@@ -68,17 +135,29 @@ NTSTATUS nbt_startup_interfaces(struct nbt_server *nbtsrv)
int i;
TALLOC_CTX *tmp_ctx = talloc_new(nbtsrv);
NTSTATUS status;
+ const char *primary_address;
+
+ /* the primary address is the address we will return for non-WINS queries
+ not made on a specific interface */
+ if (num_interfaces > 0) {
+ primary_address = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_ip(0)));
+ } else {
+ primary_address = sys_inet_ntoa(interpret_addr2(lp_netbios_name()));
+ }
status = nbt_add_socket(nbtsrv,
- talloc_strdup(tmp_ctx, "0.0.0.0"),
- talloc_strdup(tmp_ctx, "255.255.255.255"));
+ "0.0.0.0",
+ primary_address,
+ talloc_strdup(tmp_ctx, "255.255.255.255"),
+ talloc_strdup(tmp_ctx, "0.0.0.0"));
NT_STATUS_NOT_OK_RETURN(status);
for (i=0; i<num_interfaces; i++) {
const char *address = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_ip(i)));
const char *bcast = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_bcast(i)));
+ const char *netmask = talloc_strdup(tmp_ctx, sys_inet_ntoa(*iface_n_netmask(i)));
- status = nbt_add_socket(nbtsrv, address, bcast);
+ status = nbt_add_socket(nbtsrv, address, address, bcast, netmask);
NT_STATUS_NOT_OK_RETURN(status);
}
diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c
index d05a31e421..603ec2a583 100644
--- a/source4/nbt_server/nbt_server.c
+++ b/source4/nbt_server/nbt_server.c
@@ -22,7 +22,6 @@
#include "includes.h"
#include "events.h"
-#include "libcli/nbt/libnbt.h"
#include "smbd/service_task.h"
#include "nbt_server/nbt_server.h"
@@ -36,9 +35,12 @@ static void nbt_request_handler(struct nbt_name_socket *nbtsock,
{
struct nbt_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbt_interface);
- DEBUG(0,("nbtd request from %s:%d\n", src_address, src_port));
- NDR_PRINT_DEBUG(nbt_name_packet, packet);
+ switch (packet->operation & NBT_OPCODE) {
+ case NBT_OPCODE_QUERY:
+ nbt_request_query(nbtsock, packet, src_address, src_port);
+ break;
+ }
}
@@ -57,8 +59,9 @@ static void nbtd_task_init(struct task_server *task)
return;
}
- nbtsrv->task = task;
- nbtsrv->interfaces = NULL;
+ nbtsrv->task = task;
+ nbtsrv->interfaces = NULL;
+ nbtsrv->bcast_interface = NULL;
/* start listening on the configured network interfaces */
status = nbt_startup_interfaces(nbtsrv);
@@ -71,6 +74,11 @@ static void nbtd_task_init(struct task_server *task)
for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
nbt_set_incoming_handler(iface->nbtsock, nbt_request_handler, iface);
}
+ nbt_set_incoming_handler(nbtsrv->bcast_interface->nbtsock, nbt_request_handler,
+ nbtsrv->bcast_interface);
+
+ /* start the process of registering our names on all interfaces */
+ nbt_register_names(nbtsrv);
}
diff --git a/source4/nbt_server/nbt_server.h b/source4/nbt_server/nbt_server.h
index 9ef510fbb2..49a07ca95e 100644
--- a/source4/nbt_server/nbt_server.h
+++ b/source4/nbt_server/nbt_server.h
@@ -20,14 +20,30 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "libcli/nbt/libnbt.h"
+
+/*
+ a list of our registered names on each interface
+*/
+struct nbt_iface_name {
+ struct nbt_iface_name *next, *prev;
+ struct nbt_interface *iface;
+ struct nbt_name name;
+ uint16_t nb_flags;
+ struct timeval registration_time;
+ uint32_t ttl;
+};
+
/* a list of network interfaces we are listening on */
struct nbt_interface {
struct nbt_interface *next, *prev;
+ struct nbt_server *nbtsrv;
const char *ip_address;
const char *bcast_address;
+ const char *netmask;
struct nbt_name_socket *nbtsock;
- struct nbt_server *nbtsrv;
+ struct nbt_iface_name *names;
};
@@ -37,7 +53,11 @@ struct nbt_interface {
struct nbt_server {
struct task_server *task;
+ /* the list of local network interfaces */
struct nbt_interface *interfaces;
+
+ /* broadcast interface used for receiving packets only */
+ struct nbt_interface *bcast_interface;
};
diff --git a/source4/nbt_server/packet.c b/source4/nbt_server/packet.c
new file mode 100644
index 0000000000..53ccc4d93f
--- /dev/null
+++ b/source4/nbt_server/packet.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ packet utility functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ we received a badly formed packet - log it
+*/
+void nbt_bad_packet(struct nbt_name_packet *packet,
+ const char *src_address, const char *reason)
+{
+ DEBUG(2,("nbtd: bad packet '%s' from %s\n", reason, src_address));
+ if (DEBUGLVL(5)) {
+ NDR_PRINT_DEBUG(nbt_name_packet, packet);
+ }
+}
+
diff --git a/source4/nbt_server/query.c b/source4/nbt_server/query.c
new file mode 100644
index 0000000000..c962a691c6
--- /dev/null
+++ b/source4/nbt_server/query.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ answer name queries
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "system/network.h"
+#include "nbt_server/nbt_server.h"
+
+/* check a condition on an incoming packet */
+#define NBT_ASSERT_PACKET(packet, src_address, test) do { \
+ if (!(test)) { \
+ nbt_bad_packet(packet, src_address, #test); \
+ return; \
+ } \
+} while (0)
+
+
+/*
+ send a name query reply
+*/
+static void nbt_name_query_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ const char *src_address, int src_port,
+ struct nbt_name *name, uint32_t ttl,
+ uint16_t nb_flags, const char *address)
+{
+ struct nbt_name_packet *packet;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITIVE |
+ NBT_FLAG_RECURSION_DESIRED |
+ NBT_FLAG_RECURSION_AVAIL;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = ttl;
+ packet->answers[0].rdata.netbios.length = 6;
+ packet->answers[0].rdata.netbios.addresses = talloc_array(packet->answers,
+ struct nbt_rdata_address, 1);
+ if (packet->answers[0].rdata.netbios.addresses == NULL) goto failed;
+ packet->answers[0].rdata.netbios.addresses[0].nb_flags = nb_flags;
+ packet->answers[0].rdata.netbios.addresses[0].ipaddr = htonl(inet_addr(address));
+
+ DEBUG(7,("Sending name query reply for %s<%02x> at %s to %s:%d\n",
+ name->name, name->type, src_address, address, src_port));
+
+ nbt_name_reply_send(nbtsock, src_address, src_port, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ answer a name query
+*/
+void nbt_request_query(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const char *src_address, int src_port)
+{
+ struct nbt_interface *iface;
+ struct nbt_iface_name *iname;
+ struct nbt_name *name;
+
+ /* if its a WINS query then direct to our WINS server */
+ if ((packet->operation & NBT_FLAG_RECURSION_DESIRED) &&
+ !(packet->operation & NBT_FLAG_BROADCAST)) {
+ nbt_query_wins(nbtsock, packet, src_address, src_port);
+ return;
+ }
+
+ /* find the interface for this query */
+ iface = nbt_iface_find(nbtsock, src_address);
+
+ NBT_ASSERT_PACKET(packet, src_address, packet->qdcount == 1);
+ NBT_ASSERT_PACKET(packet, src_address, packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
+ NBT_ASSERT_PACKET(packet, src_address, packet->questions[0].question_class == NBT_QCLASS_IP);
+
+ /* see if we have the requested name on this interface */
+ name = &packet->questions[0].name;
+
+ iname = nbt_find_iname(iface, name, NBT_NM_ACTIVE);
+ if (iname == NULL) {
+ DEBUG(7,("Query for %s<%02x> from %s - not found on %s\n",
+ name->name, name->type, src_address, iface->ip_address));
+ return;
+ }
+
+ nbt_name_query_reply(nbtsock, packet, src_address, src_port,
+ &iname->name, iname->ttl, iname->nb_flags,
+ iface->ip_address);
+}
diff --git a/source4/nbt_server/register.c b/source4/nbt_server/register.c
new file mode 100644
index 0000000000..9a416e37aa
--- /dev/null
+++ b/source4/nbt_server/register.c
@@ -0,0 +1,161 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ register our names
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ start a timer to refresh this name
+*/
+static void nbt_start_refresh_timer(struct nbt_iface_name *iname)
+{
+}
+
+
+/*
+ a name registration has completed
+*/
+static void nbt_register_handler(struct nbt_name_request *req)
+{
+ struct nbt_iface_name *iname = talloc_get_type(req->async.private, struct nbt_iface_name);
+ NTSTATUS status;
+ struct nbt_name_register io;
+
+ status = nbt_name_register_recv(req, iname, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* good - nobody complained about our registration */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ DEBUG(3,("Registered %s<%02x> on interface %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address));
+ nbt_start_refresh_timer(iname);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Error registering %s<%02x> on interface %s - %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address,
+ nt_errstr(status)));
+ return;
+ }
+
+ /* someone must have replied with an objection! */
+ iname->nb_flags |= NBT_NM_CONFLICT;
+
+ DEBUG(1,("Name conflict registering %s<%02x> on interface %s - rcode %d from %s for %s\n",
+ iname->name.name, iname->name.type, iname->iface->bcast_address,
+ io.out.rcode, io.out.reply_from, io.out.reply_addr));
+}
+
+
+/*
+ register a name on a network interface
+*/
+static void nbt_register_name_iface(struct nbt_interface *iface,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbt_iface_name *iname;
+ const char *scope = lp_netbios_scope();
+ struct nbt_name_register io;
+ struct nbt_name_request *req;
+
+ iname = talloc(iface, struct nbt_iface_name);
+ if (!iname) return;
+
+ iname->iface = iface;
+ iname->name.name = talloc_strdup(iname, name);
+ iname->name.type = type;
+ if (scope && *scope) {
+ iname->name.scope = talloc_strdup(iname, scope);
+ } else {
+ iname->name.scope = NULL;
+ }
+ iname->nb_flags = nb_flags;
+ iname->ttl = lp_parm_int(-1, "nbtd", "bcast_ttl", 300000);
+ iname->registration_time = timeval_zero();
+
+ DLIST_ADD(iface->names, iname);
+
+ if (nb_flags & NBT_NM_PERMANENT) {
+ /* permanent names are not announced and are immediately active */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ iname->ttl = 0;
+ return;
+ }
+
+ /* setup a broadcast name registration request */
+ io.in.name = iname->name;
+ io.in.dest_addr = iface->bcast_address;
+ io.in.address = iface->ip_address;
+ io.in.nb_flags = nb_flags;
+ io.in.register_demand = False;
+ io.in.broadcast = True;
+ io.in.ttl = iname->ttl;
+ io.in.timeout = 1;
+
+ req = nbt_name_register_send(iface->nbtsock, &io);
+ if (req == NULL) return;
+
+ req->async.fn = nbt_register_handler;
+ req->async.private = iname;
+}
+
+
+/*
+ register one name on all our interfaces
+*/
+static void nbt_register_name(struct nbt_server *nbtsrv,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbt_interface *iface;
+
+ /* register with all the local interfaces */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ nbt_register_name_iface(iface, name, type, nb_flags);
+ }
+
+ /* register on our general broadcast interface as a permanent name */
+ nbt_register_name_iface(nbtsrv->bcast_interface, name, type, nb_flags | NBT_NM_PERMANENT);
+
+ /* TODO: register with our WINS servers */
+}
+
+
+/*
+ register our names on all interfaces
+*/
+void nbt_register_names(struct nbt_server *nbtsrv)
+{
+ uint16_t nb_flags = NBT_NODE_M;
+
+ /* note that we don't initially mark the names "ACTIVE". They are
+ marked active once registration is successful */
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_CLIENT, nb_flags);
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_USER, nb_flags);
+ nbt_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_SERVER, nb_flags);
+ nbt_register_name(nbtsrv, lp_workgroup(), NBT_NAME_CLIENT, nb_flags | NBT_NM_GROUP);
+ nbt_register_name(nbtsrv, lp_workgroup(), NBT_NAME_SERVER, nb_flags | NBT_NM_GROUP);
+ nbt_register_name(nbtsrv, "__SAMBA__", NBT_NAME_CLIENT, nb_flags | NBT_NM_PERMANENT);
+ nbt_register_name(nbtsrv, "__SAMBA__", NBT_NAME_SERVER, nb_flags | NBT_NM_PERMANENT);
+}
diff --git a/source4/nbt_server/winsserver.c b/source4/nbt_server/winsserver.c
new file mode 100644
index 0000000000..a659772cb3
--- /dev/null
+++ b/source4/nbt_server/winsserver.c
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ core wins server handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+
+/*
+ answer a name query
+*/
+void nbt_query_wins(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const char *src_address, int src_port)
+{
+ DEBUG(0,("WINS query from %s\n", src_address));
+}