summaryrefslogtreecommitdiffstats
path: root/source4/nbt_server/register.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/nbt_server/register.c')
-rw-r--r--source4/nbt_server/register.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/source4/nbt_server/register.c b/source4/nbt_server/register.c
new file mode 100644
index 0000000..4d10e9b
--- /dev/null
+++ b/source4/nbt_server/register.c
@@ -0,0 +1,310 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "nbt_server/nbt_server.h"
+#include "samba/service_task.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+#include "libds/common/roles.h"
+
+static void nbtd_start_refresh_timer(struct nbtd_iface_name *iname);
+
+/*
+ a name refresh request has completed
+*/
+static void refresh_completion_handler(struct nbt_name_request *req)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(req->async.private_data,
+ struct nbtd_iface_name);
+ NTSTATUS status;
+ struct nbt_name_refresh io;
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ status = nbt_name_refresh_recv(req, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ DEBUG(4,("Refreshed name %s with %s on interface %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address));
+ iname->registration_time = timeval_current();
+ nbtd_start_refresh_timer(iname);
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ iname->nb_flags |= NBT_NM_CONFLICT;
+ iname->nb_flags &= ~NBT_NM_ACTIVE;
+
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Name conflict from %s refreshing name %s with %s on interface %s - %s\n",
+ io.out.reply_addr, nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode))));
+ } else {
+ DEBUG(1,("Error refreshing name %s with %s on interface %s - %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(status)));
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ handle name refresh timer events
+*/
+static void name_refresh_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name);
+ struct nbtd_interface *iface = iname->iface;
+ struct nbt_name_register io;
+ struct nbt_name_request *req;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ /* setup a single name register request. Notice that we don't
+ use a name refresh request, as Windows and Samba3 do not
+ defend against broadcast name refresh packets. So for this
+ to be of any use at all, we need to refresh using name
+ registration packets */
+ io.in.name = iname->name;
+ io.in.dest_addr = iface->bcast_address;
+ io.in.dest_port = lpcfg_nbt_port(iface->nbtsrv->task->lp_ctx);
+ io.in.address = iface->ip_address;
+ io.in.nb_flags = iname->nb_flags;
+ io.in.ttl = iname->ttl;
+ io.in.register_demand = false;
+ io.in.broadcast = true;
+ io.in.multi_homed = false;
+ io.in.timeout = 3;
+ io.in.retries = 0;
+
+ nbtsrv->stats.total_sent++;
+ req = nbt_name_register_send(iface->nbtsock, &io);
+ if (req == NULL) return;
+
+ req->async.fn = refresh_completion_handler;
+ req->async.private_data = iname;
+}
+
+
+/*
+ start a timer to refresh this name
+*/
+static void nbtd_start_refresh_timer(struct nbtd_iface_name *iname)
+{
+ uint32_t refresh_time;
+ uint32_t max_refresh_time = lpcfg_parm_int(iname->iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "max_refresh_time", 7200);
+
+ refresh_time = MIN(max_refresh_time, iname->ttl/2);
+
+ tevent_add_timer(iname->iface->nbtsrv->task->event_ctx,
+ iname,
+ timeval_add(&iname->registration_time, refresh_time, 0),
+ name_refresh_handler, iname);
+}
+
+struct nbtd_register_name_state {
+ struct nbtd_iface_name *iname;
+ struct nbt_name_register_bcast io;
+};
+
+/*
+ a name registration has completed
+*/
+static void nbtd_register_name_handler(struct tevent_req *subreq)
+{
+ struct nbtd_register_name_state *state =
+ tevent_req_callback_data(subreq,
+ struct nbtd_register_name_state);
+ struct nbtd_iface_name *iname = state->iname;
+ NTSTATUS status;
+
+ status = nbt_name_register_bcast_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_OK(status)) {
+ /* good - nobody complained about our registration */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ DEBUG(3,("Registered %s with %s on interface %s\n",
+ nbt_name_string(state, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address));
+ iname->registration_time = timeval_current();
+ talloc_free(state);
+ nbtd_start_refresh_timer(iname);
+ return;
+ }
+
+ /* someone must have replied with an objection! */
+ iname->nb_flags |= NBT_NM_CONFLICT;
+
+ DEBUG(1,("Error registering %s with %s on interface %s - %s\n",
+ nbt_name_string(state, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(status)));
+ talloc_free(state);
+}
+
+
+/*
+ register a name on a network interface
+*/
+static void nbtd_register_name_iface(struct nbtd_interface *iface,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbtd_iface_name *iname;
+ const char *scope = lpcfg_netbios_scope(iface->nbtsrv->task->lp_ctx);
+ struct nbtd_register_name_state *state;
+ struct tevent_req *subreq;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ iname = talloc(iface, struct nbtd_iface_name);
+ if (!iname) return;
+
+ iname->iface = iface;
+ iname->name.name = strupper_talloc(iname, name);
+ iname->name.type = type;
+ if (scope && *scope) {
+ iname->name.scope = strupper_talloc(iname, scope);
+ } else {
+ iname->name.scope = NULL;
+ }
+ iname->nb_flags = nb_flags;
+ iname->ttl = lpcfg_parm_int(iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "bcast_ttl", 300000);
+ iname->registration_time = timeval_zero();
+ iname->wins_server = NULL;
+
+ DLIST_ADD_END(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;
+ }
+
+ /* if this is the wins interface, then we need to do a special
+ wins name registration */
+ if (iface == iface->nbtsrv->wins_interface) {
+ nbtd_winsclient_register(iname);
+ return;
+ }
+
+ state = talloc_zero(iname, struct nbtd_register_name_state);
+ if (state == NULL) {
+ return;
+ }
+
+ state->iname = iname;
+
+ /* setup a broadcast name registration request */
+ state->io.in.name = iname->name;
+ state->io.in.dest_addr = iface->bcast_address;
+ state->io.in.dest_port = lpcfg_nbt_port(iface->nbtsrv->task->lp_ctx);
+ state->io.in.address = iface->ip_address;
+ state->io.in.nb_flags = nb_flags;
+ state->io.in.ttl = iname->ttl;
+
+ nbtsrv->stats.total_sent++;
+
+ subreq = nbt_name_register_bcast_send(state, nbtsrv->task->event_ctx,
+ iface->nbtsock, &state->io);
+ if (subreq == NULL) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, nbtd_register_name_handler, state);
+}
+
+
+/*
+ register one name on all our interfaces
+*/
+void nbtd_register_name(struct nbtd_server *nbtsrv,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbtd_interface *iface;
+
+ /* register with all the local interfaces */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ nbtd_register_name_iface(iface, name, type, nb_flags);
+ }
+
+ /* register on our general broadcast interface as a permanent name */
+ if (nbtsrv->bcast_interface) {
+ nbtd_register_name_iface(nbtsrv->bcast_interface, name, type,
+ nb_flags | NBT_NM_PERMANENT);
+ }
+
+ /* register with our WINS servers */
+ if (nbtsrv->wins_interface) {
+ nbtd_register_name_iface(nbtsrv->wins_interface, name, type, nb_flags);
+ }
+}
+
+
+/*
+ register our names on all interfaces
+*/
+void nbtd_register_names(struct nbtd_server *nbtsrv)
+{
+ uint16_t nb_flags = NBT_NODE_M;
+ const char **aliases;
+
+ /* note that we don't initially mark the names "ACTIVE". They are
+ marked active once registration is successful */
+ nbtd_register_name(nbtsrv, lpcfg_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, lpcfg_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_USER, nb_flags);
+ nbtd_register_name(nbtsrv, lpcfg_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_SERVER, nb_flags);
+
+ aliases = lpcfg_netbios_aliases(nbtsrv->task->lp_ctx);
+ while (aliases && aliases[0]) {
+ nbtd_register_name(nbtsrv, aliases[0], NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, aliases[0], NBT_NAME_SERVER, nb_flags);
+ aliases++;
+ }
+
+ if (lpcfg_server_role(nbtsrv->task->lp_ctx) == ROLE_ACTIVE_DIRECTORY_DC) {
+ bool is_pdc = samdb_is_pdc(nbtsrv->sam_ctx);
+ if (is_pdc) {
+ nbtd_register_name(nbtsrv, lpcfg_workgroup(nbtsrv->task->lp_ctx),
+ NBT_NAME_PDC, nb_flags);
+ }
+ nbtd_register_name(nbtsrv, lpcfg_workgroup(nbtsrv->task->lp_ctx),
+ NBT_NAME_LOGON, nb_flags | NBT_NM_GROUP);
+ }
+
+ nb_flags |= NBT_NM_GROUP;
+ nbtd_register_name(nbtsrv, lpcfg_workgroup(nbtsrv->task->lp_ctx), NBT_NAME_CLIENT, nb_flags);
+
+ nb_flags |= NBT_NM_PERMANENT;
+ nbtd_register_name(nbtsrv, "__SAMBA__", NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, "__SAMBA__", NBT_NAME_SERVER, nb_flags);
+ nbtd_register_name(nbtsrv, "*", NBT_NAME_CLIENT, nb_flags);
+}