diff options
author | Andrew Tridgell <tridge@samba.org> | 2005-02-18 23:53:52 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:10:48 -0500 |
commit | b0fc1bfbcbacc61350fdd6e96306fe49d7fc0d6f (patch) | |
tree | 08ab0e6ae2db12c696fa4d3e5c564850b041fd15 /source4/nbt_server/wins | |
parent | 77701f6ca1b1e39d0f39612c5125d2f58d7630e1 (diff) | |
download | samba-b0fc1bfbcbacc61350fdd6e96306fe49d7fc0d6f.tar.gz samba-b0fc1bfbcbacc61350fdd6e96306fe49d7fc0d6f.tar.bz2 samba-b0fc1bfbcbacc61350fdd6e96306fe49d7fc0d6f.zip |
r5454: moved the WINS server code into its own directory
(This used to be commit 0bb997127fe6c49361d9f1eaeda5d9321601a52a)
Diffstat (limited to 'source4/nbt_server/wins')
-rw-r--r-- | source4/nbt_server/wins/winsclient.c | 231 | ||||
-rw-r--r-- | source4/nbt_server/wins/winsdb.c | 279 | ||||
-rw-r--r-- | source4/nbt_server/wins/winsdb.h | 52 | ||||
-rw-r--r-- | source4/nbt_server/wins/winsserver.c | 283 | ||||
-rw-r--r-- | source4/nbt_server/wins/winswack.c | 213 |
5 files changed, 1058 insertions, 0 deletions
diff --git a/source4/nbt_server/wins/winsclient.c b/source4/nbt_server/wins/winsclient.c new file mode 100644 index 0000000000..cfb68a3aaf --- /dev/null +++ b/source4/nbt_server/wins/winsclient.c @@ -0,0 +1,231 @@ +/* + Unix SMB/CIFS implementation. + + wins client name registration and refresh + + 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 "nbt_server/nbt_server.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/composite/composite.h" +#include "lib/events/events.h" +#include "smbd/service_task.h" + + +/* we send WINS client requests using our primary network interface +*/ +static struct nbt_name_socket *wins_socket(struct nbtd_interface *iface) +{ + struct nbtd_server *nbtsrv = iface->nbtsrv; + return nbtsrv->interfaces->nbtsock; +} + + +static void nbtd_wins_refresh(struct event_context *ev, struct timed_event *te, + struct timeval t, void *private); + +/* + retry a WINS name registration +*/ +static void nbtd_wins_register_retry(struct event_context *ev, struct timed_event *te, + struct timeval t, void *private) +{ + struct nbtd_iface_name *iname = talloc_get_type(private, struct nbtd_iface_name); + nbtd_winsclient_register(iname); +} + + +/* + called when a wins name refresh has completed +*/ +static void nbtd_wins_refresh_handler(struct composite_context *c) +{ + NTSTATUS status; + struct nbt_name_refresh_wins io; + struct nbtd_iface_name *iname = talloc_get_type(c->async.private, + struct nbtd_iface_name); + TALLOC_CTX *tmp_ctx = talloc_new(iname); + + status = nbt_name_refresh_wins_recv(c, tmp_ctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* our WINS server is dead - start registration over + from scratch */ + DEBUG(2,("Failed to refresh %s with WINS server %s\n", + nbt_name_string(tmp_ctx, &iname->name), iname->wins_server)); + talloc_free(tmp_ctx); + nbtd_winsclient_register(iname); + return; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("Name refresh failure with WINS for %s - %s\n", + nbt_name_string(tmp_ctx, &iname->name), nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + if (io.out.rcode != 0) { + DEBUG(1,("WINS server %s rejected name refresh of %s - %s\n", + io.out.wins_server, nbt_name_string(tmp_ctx, &iname->name), + nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode)))); + iname->nb_flags |= NBT_NM_CONFLICT; + talloc_free(tmp_ctx); + return; + } + + DEBUG(4,("Refreshed name %s with WINS server %s\n", + nbt_name_string(tmp_ctx, &iname->name), iname->wins_server)); + /* success - start a periodic name refresh */ + iname->nb_flags |= NBT_NM_ACTIVE; + if (iname->wins_server) { + talloc_free(iname->wins_server); + } + iname->wins_server = talloc_steal(iname, io.out.wins_server); + + iname->registration_time = timeval_current(); + event_add_timed(iname->iface->nbtsrv->task->event_ctx, + iname, + timeval_add(&iname->registration_time, iname->ttl/2, 0), + nbtd_wins_refresh, + iname); + + talloc_free(tmp_ctx); +} + + +/* + refresh a WINS name registration +*/ +static void nbtd_wins_refresh(struct event_context *ev, struct timed_event *te, + struct timeval t, void *private) +{ + struct nbtd_iface_name *iname = talloc_get_type(private, struct nbtd_iface_name); + struct nbtd_interface *iface = iname->iface; + struct nbt_name_refresh_wins io; + struct composite_context *c; + TALLOC_CTX *tmp_ctx = talloc_new(iname); + + /* setup a wins name refresh request */ + io.in.name = iname->name; + io.in.wins_servers = str_list_make(tmp_ctx, iname->wins_server, NULL); + io.in.addresses = nbtd_address_list(iface, tmp_ctx); + io.in.nb_flags = iname->nb_flags; + io.in.ttl = iname->ttl; + + c = nbt_name_refresh_wins_send(wins_socket(iface), &io); + if (c == NULL) { + talloc_free(tmp_ctx); + return; + } + talloc_steal(c, io.in.addresses); + + c->async.fn = nbtd_wins_refresh_handler; + c->async.private = iname; + + talloc_free(tmp_ctx); +} + + +/* + called when a wins name register has completed +*/ +static void nbtd_wins_register_handler(struct composite_context *c) +{ + NTSTATUS status; + struct nbt_name_register_wins io; + struct nbtd_iface_name *iname = talloc_get_type(c->async.private, + struct nbtd_iface_name); + TALLOC_CTX *tmp_ctx = talloc_new(iname); + + status = nbt_name_register_wins_recv(c, tmp_ctx, &io); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* none of the WINS servers responded - try again + periodically */ + int wins_retry_time = lp_parm_int(-1, "nbtd", "wins_retry", 300); + event_add_timed(iname->iface->nbtsrv->task->event_ctx, + iname, + timeval_current_ofs(wins_retry_time, 0), + nbtd_wins_register_retry, + iname); + talloc_free(tmp_ctx); + return; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("Name register failure with WINS for %s - %s\n", + nbt_name_string(tmp_ctx, &iname->name), nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + if (io.out.rcode != 0) { + DEBUG(1,("WINS server %s rejected name register of %s - %s\n", + io.out.wins_server, nbt_name_string(tmp_ctx, &iname->name), + nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode)))); + iname->nb_flags |= NBT_NM_CONFLICT; + talloc_free(tmp_ctx); + return; + } + + /* success - start a periodic name refresh */ + iname->nb_flags |= NBT_NM_ACTIVE; + if (iname->wins_server) { + talloc_free(iname->wins_server); + } + iname->wins_server = talloc_steal(iname, io.out.wins_server); + + iname->registration_time = timeval_current(); + event_add_timed(iname->iface->nbtsrv->task->event_ctx, + iname, + timeval_add(&iname->registration_time, iname->ttl/2, 0), + nbtd_wins_refresh, + iname); + + DEBUG(3,("Registered %s with WINS server %s\n", + nbt_name_string(tmp_ctx, &iname->name), iname->wins_server)); + + talloc_free(tmp_ctx); +} + +/* + register a name with our WINS servers +*/ +void nbtd_winsclient_register(struct nbtd_iface_name *iname) +{ + struct nbtd_interface *iface = iname->iface; + struct nbt_name_register_wins io; + struct composite_context *c; + + /* setup a wins name register request */ + io.in.name = iname->name; + io.in.wins_servers = lp_wins_server_list(); + io.in.addresses = nbtd_address_list(iface, iname); + io.in.nb_flags = iname->nb_flags; + io.in.ttl = iname->ttl; + + c = nbt_name_register_wins_send(wins_socket(iface), &io); + if (c == NULL) { + talloc_free(io.in.addresses); + return; + } + talloc_steal(c, io.in.addresses); + + c->async.fn = nbtd_wins_register_handler; + c->async.private = iname; +} diff --git a/source4/nbt_server/wins/winsdb.c b/source4/nbt_server/wins/winsdb.c new file mode 100644 index 0000000000..4b547bc644 --- /dev/null +++ b/source4/nbt_server/wins/winsdb.c @@ -0,0 +1,279 @@ +/* + Unix SMB/CIFS implementation. + + WINS database routines + + 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 "nbt_server/nbt_server.h" +#include "nbt_server/wins/winsdb.h" +#include "lib/ldb/include/ldb.h" +#include "db_wrap.h" +#include "system/time.h" + +/* + save the min/max version IDs for the database +*/ +static BOOL winsdb_save_version(struct wins_server *winssrv) +{ + int i, ret = 0; + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg = ldb_msg_new(winssrv); + if (msg == NULL) goto failed; + + msg->dn = talloc_strdup(msg, "CN=VERSION"); + if (msg->dn == NULL) goto failed; + + ret |= ldb_msg_add_fmt(ldb, msg, "minVersion", "%llu", winssrv->min_version); + ret |= ldb_msg_add_fmt(ldb, msg, "maxVersion", "%llu", winssrv->max_version); + if (ret != 0) goto failed; + + for (i=0;i<msg->num_elements;i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + ret = ldb_modify(ldb, msg); + if (ret != 0) ret = ldb_add(ldb, msg); + if (ret != 0) goto failed; + + talloc_free(msg); + return True; + +failed: + talloc_free(msg); + return False; +} + +/* + allocate a new version id for a record +*/ +static uint64_t winsdb_allocate_version(struct wins_server *winssrv) +{ + winssrv->max_version++; + if (!winsdb_save_version(winssrv)) { + return 0; + } + return winssrv->max_version; +} + +/* + allocate a new version id for a record +*/ +static void winsdb_remove_version(struct wins_server *winssrv, uint64_t version) +{ + if (version == winssrv->min_version) { + winssrv->min_version++; + winsdb_save_version(winssrv); + } +} + +/* + load a WINS entry from the database +*/ +struct winsdb_record *winsdb_load(struct wins_server *winssrv, + struct nbt_name *name, TALLOC_CTX *mem_ctx) +{ + struct ldb_message **res = NULL; + int ret; + struct winsdb_record *rec; + struct ldb_message_element *el; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + const char *expr; + int i; + + expr = talloc_asprintf(tmp_ctx, "dn=NAME=%s", nbt_name_string(tmp_ctx, name)); + if (expr == NULL) goto failed; + + /* find the record in the WINS database */ + ret = ldb_search(winssrv->wins_db->ldb, NULL, LDB_SCOPE_ONELEVEL, expr, NULL, &res); + if (res != NULL) { + talloc_steal(tmp_ctx, res); + } + if (ret != 1) goto failed; + + rec = talloc(tmp_ctx, struct winsdb_record); + if (rec == NULL) goto failed; + + /* parse it into a more convenient winsdb_record structure */ + rec->name = name; + rec->state = ldb_msg_find_int(res[0], "active", WINS_REC_RELEASED); + rec->nb_flags = ldb_msg_find_int(res[0], "nbFlags", 0); + rec->expire_time = ldap_string_to_time(ldb_msg_find_string(res[0], "expires", NULL)); + rec->registered_by = ldb_msg_find_string(res[0], "registeredBy", NULL); + rec->version = ldb_msg_find_uint64(res[0], "version", 0); + talloc_steal(rec, rec->registered_by); + + el = ldb_msg_find_element(res[0], "address"); + if (el == NULL) goto failed; + + rec->addresses = talloc_array(rec, const char *, el->num_values+1); + if (rec->addresses == NULL) goto failed; + + for (i=0;i<el->num_values;i++) { + rec->addresses[i] = talloc_steal(rec->addresses, el->values[i].data); + } + rec->addresses[i] = NULL; + + /* see if it has already expired */ + if (rec->state == WINS_REC_ACTIVE && + rec->expire_time <= time(NULL)) { + DEBUG(5,("WINS: expiring name %s (expired at %s)\n", + nbt_name_string(tmp_ctx, rec->name), timestring(tmp_ctx, rec->expire_time))); + rec->state = WINS_REC_RELEASED; + } + + talloc_steal(mem_ctx, rec); + talloc_free(tmp_ctx); + return rec; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + + +/* + form a ldb_message from a winsdb_record +*/ +static struct ldb_message *winsdb_message(struct wins_server *winssrv, + struct winsdb_record *rec, TALLOC_CTX *mem_ctx) +{ + int i, ret=0; + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg = ldb_msg_new(mem_ctx); + if (msg == NULL) goto failed; + + msg->dn = talloc_asprintf(msg, "NAME=%s", nbt_name_string(msg, rec->name)); + if (msg->dn == NULL) goto failed; + ret |= ldb_msg_add_fmt(ldb, msg, "active", "%u", rec->state); + ret |= ldb_msg_add_fmt(ldb, msg, "nbFlags", "0x%04x", rec->nb_flags); + ret |= ldb_msg_add_string(ldb, msg, "registeredBy", rec->registered_by); + ret |= ldb_msg_add_string(ldb, msg, "expires", + ldap_timestring(msg, rec->expire_time)); + ret |= ldb_msg_add_fmt(ldb, msg, "version", "%llu", rec->version); + for (i=0;rec->addresses[i];i++) { + ret |= ldb_msg_add_string(ldb, msg, "address", rec->addresses[i]); + } + if (ret != 0) goto failed; + return msg; + +failed: + talloc_free(msg); + return NULL; +} + +/* + save a WINS record into the database +*/ +uint8_t winsdb_add(struct wins_server *winssrv, struct winsdb_record *rec) +{ + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg; + TALLOC_CTX *tmp_ctx = talloc_new(winssrv); + int ret; + + rec->version = winsdb_allocate_version(winssrv); + if (rec->version == 0) goto failed; + + msg = winsdb_message(winssrv, rec, tmp_ctx); + if (msg == NULL) goto failed; + ret = ldb_add(ldb, msg); + if (ret != 0) goto failed; + + talloc_free(tmp_ctx); + return NBT_RCODE_OK; + +failed: + talloc_free(tmp_ctx); + return NBT_RCODE_SVR; +} + + +/* + modify a WINS record in the database +*/ +uint8_t winsdb_modify(struct wins_server *winssrv, struct winsdb_record *rec) +{ + struct ldb_context *ldb = winssrv->wins_db->ldb; + struct ldb_message *msg; + TALLOC_CTX *tmp_ctx = talloc_new(winssrv); + int ret; + int i; + + rec->version = winsdb_allocate_version(winssrv); + if (rec->version == 0) goto failed; + + msg = winsdb_message(winssrv, rec, tmp_ctx); + if (msg == NULL) goto failed; + + for (i=0;i<msg->num_elements;i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + ret = ldb_modify(ldb, msg); + if (ret != 0) goto failed; + + talloc_free(tmp_ctx); + return NBT_RCODE_OK; + +failed: + talloc_free(tmp_ctx); + return NBT_RCODE_SVR; +} + + +/* + delete a WINS record from the database +*/ +uint8_t winsdb_delete(struct wins_server *winssrv, struct winsdb_record *rec) +{ + struct ldb_context *ldb = winssrv->wins_db->ldb; + TALLOC_CTX *tmp_ctx = talloc_new(winssrv); + int ret; + const char *dn; + + winsdb_remove_version(winssrv, rec->version); + + dn = talloc_asprintf(tmp_ctx, "NAME=%s", nbt_name_string(tmp_ctx, rec->name)); + if (dn == NULL) goto failed; + + ret = ldb_delete(ldb, dn); + if (ret != 0) goto failed; + + talloc_free(tmp_ctx); + return NBT_RCODE_OK; + +failed: + talloc_free(tmp_ctx); + return NBT_RCODE_SVR; +} + + +/* + connect to the WINS database +*/ +NTSTATUS winsdb_init(struct wins_server *winssrv) +{ + winssrv->wins_db = ldb_wrap_connect(winssrv, lp_wins_url(), 0, NULL); + if (winssrv->wins_db == NULL) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + return NT_STATUS_OK; +} diff --git a/source4/nbt_server/wins/winsdb.h b/source4/nbt_server/wins/winsdb.h new file mode 100644 index 0000000000..6d395461c6 --- /dev/null +++ b/source4/nbt_server/wins/winsdb.h @@ -0,0 +1,52 @@ +/* + Unix SMB/CIFS implementation. + + WINS server structures + + 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. +*/ + +enum wins_record_state { + WINS_REC_RELEASED =0, + WINS_REC_ACTIVE =1 +}; + +/* + each record in the database contains the following information +*/ +struct winsdb_record { + struct nbt_name *name; + uint16_t nb_flags; + enum wins_record_state state; + time_t expire_time; + const char *registered_by; + const char **addresses; + uint64_t version; +}; + +struct wins_server { + /* wins server database handle */ + struct ldb_wrap *wins_db; + + uint32_t min_ttl; + uint32_t max_ttl; + + /* these are the minimum and maximum record version IDs in the + database. They are needed for replication */ + uint64_t min_version; + uint64_t max_version; +}; diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c new file mode 100644 index 0000000000..2212748777 --- /dev/null +++ b/source4/nbt_server/wins/winsserver.c @@ -0,0 +1,283 @@ +/* + 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 "nbt_server/nbt_server.h" +#include "nbt_server/wins/winsdb.h" +#include "system/time.h" + +/* + work out the ttl we will use given a client requested ttl +*/ +uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl) +{ + ttl = MIN(ttl, winssrv->max_ttl); + ttl = MAX(ttl, winssrv->min_ttl); + return ttl; +} + +/* + register a new name with WINS +*/ +static uint8_t wins_register_new(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl); + uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags; + const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; + struct winsdb_record rec; + + rec.name = name; + rec.nb_flags = nb_flags; + rec.state = WINS_REC_ACTIVE; + rec.expire_time = time(NULL) + ttl; + rec.registered_by = src_address; + if (IS_GROUP_NAME(name, nb_flags)) { + rec.addresses = str_list_make(packet, "255.255.255.255", NULL); + } else { + rec.addresses = str_list_make(packet, address, NULL); + } + if (rec.addresses == NULL) return NBT_RCODE_SVR; + + DEBUG(4,("WINS: accepted registration of %s with address %s\n", + nbt_name_string(packet, name), rec.addresses[0])); + + return winsdb_add(winssrv, &rec); +} + + +/* + update the ttl on an existing record +*/ +static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + struct winsdb_record *rec, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl); + const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; + time_t now = time(NULL); + + if (now + ttl > rec->expire_time) { + rec->expire_time = now + ttl; + } + rec->registered_by = src_address; + + DEBUG(5,("WINS: refreshed registration of %s at %s\n", + nbt_name_string(packet, rec->name), address)); + + return winsdb_modify(winssrv, rec); +} + +/* + register a name +*/ +static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + 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; + + /* as a special case, the local master browser name is always accepted + for registration, but never stored */ + if (name->type == NBT_NAME_MASTER) { + goto done; + } + + rec = winsdb_load(winssrv, name, packet); + if (rec == NULL) { + rcode = wins_register_new(nbtsock, packet, src_address, src_port); + goto done; + } else if (rec->state != WINS_REC_ACTIVE) { + winsdb_delete(winssrv, rec); + rcode = wins_register_new(nbtsock, packet, src_address, src_port); + goto done; + } + + /* its an active name - first see if the registration is of the right type */ + if ((rec->nb_flags & NBT_NM_GROUP) && !(nb_flags & NBT_NM_GROUP)) { + DEBUG(2,("WINS: Attempt to register unique name %s when group name is active\n", + nbt_name_string(packet, name))); + rcode = NBT_RCODE_ACT; + goto done; + } + + /* if its an active unique name, and the registration is for a group, then + see if the unique name owner still wants the name */ + if (!(rec->nb_flags & NBT_NM_GROUP) && (nb_flags & NBT_NM_GROUP)) { + wins_register_wack(nbtsock, packet, rec, src_address, src_port); + return; + } + + /* if the registration is for a group, then just update the expiry time + and we are done */ + if (IS_GROUP_NAME(name, nb_flags)) { + wins_update_ttl(nbtsock, packet, rec, src_address, src_port); + goto done; + } + + /* if the registration is for an address that is currently active, then + just update the expiry time */ + if (str_list_check(rec->addresses, address)) { + wins_update_ttl(nbtsock, packet, rec, src_address, src_port); + goto done; + } + + /* we have to do a WACK to see if the current owner is willing + to give up its claim */ + wins_register_wack(nbtsock, packet, rec, src_address, src_port); + return; + +done: + nbtd_name_registration_reply(nbtsock, packet, src_address, src_port, rcode); +} + + + +/* + query a name +*/ +static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + + rec = winsdb_load(winssrv, name, packet); + if (rec == NULL || rec->state != WINS_REC_ACTIVE) { + nbtd_negative_name_query_reply(nbtsock, packet, src_address, src_port); + return; + } + + nbtd_name_query_reply(nbtsock, packet, src_address, src_port, name, + 0, rec->nb_flags, rec->addresses); +} + +/* + release a name +*/ +static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct nbt_name *name = &packet->questions[0].name; + struct winsdb_record *rec; + + rec = winsdb_load(winssrv, name, packet); + if (rec == NULL || + rec->state != WINS_REC_ACTIVE || + IS_GROUP_NAME(name, rec->nb_flags)) { + goto done; + } + + /* we only allow releases from an owner - other releases are + silently ignored */ + if (str_list_check(rec->addresses, src_address)) { + const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; + + DEBUG(4,("WINS: released name %s at %s\n", nbt_name_string(rec, rec->name), address)); + str_list_remove(rec->addresses, address); + if (rec->addresses[0] == NULL) { + rec->state = WINS_REC_RELEASED; + } + winsdb_modify(winssrv, rec); + } + +done: + /* we match w2k3 by always giving a positive reply to name releases. */ + nbtd_name_release_reply(nbtsock, packet, src_address, src_port, NBT_RCODE_OK); +} + + +/* + answer a name query +*/ +void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) { + return; + } + + switch (packet->operation & NBT_OPCODE) { + case NBT_OPCODE_QUERY: + nbtd_winsserver_query(nbtsock, packet, src_address, src_port); + break; + + case NBT_OPCODE_REGISTER: + case NBT_OPCODE_REFRESH: + case NBT_OPCODE_REFRESH2: + case NBT_OPCODE_MULTI_HOME_REG: + nbtd_winsserver_register(nbtsock, packet, src_address, src_port); + break; + + case NBT_OPCODE_RELEASE: + nbtd_winsserver_release(nbtsock, packet, src_address, src_port); + break; + } + +} + +/* + startup the WINS server, if configured +*/ +NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv) +{ + if (!lp_wins_support()) { + nbtsrv->winssrv = NULL; + return NT_STATUS_OK; + } + + nbtsrv->winssrv = talloc(nbtsrv, struct wins_server); + NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv); + + nbtsrv->winssrv->max_ttl = lp_max_wins_ttl(); + nbtsrv->winssrv->min_ttl = lp_min_wins_ttl(); + + return winsdb_init(nbtsrv->winssrv); +} diff --git a/source4/nbt_server/wins/winswack.c b/source4/nbt_server/wins/winswack.c new file mode 100644 index 0000000000..8c7841d095 --- /dev/null +++ b/source4/nbt_server/wins/winswack.c @@ -0,0 +1,213 @@ +/* + Unix SMB/CIFS implementation. + + "secure" wins server WACK processing + + 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 "nbt_server/nbt_server.h" +#include "nbt_server/wins/winsdb.h" +#include "system/time.h" + +struct wack_state { + struct wins_server *winssrv; + struct nbt_name_socket *nbtsock; + struct nbt_name_packet *request_packet; + struct winsdb_record *rec; + const char *src_address; + int src_port; + const char **owner_addresses; + const char *reg_address; + struct nbt_name_query query; +}; + + +/* + deny a registration request +*/ +static void wins_wack_deny(struct wack_state *state) +{ + nbtd_name_registration_reply(state->nbtsock, state->request_packet, + state->src_address, state->src_port, NBT_RCODE_ACT); + DEBUG(4,("WINS: denied name registration request for %s from %s\n", + nbt_name_string(state, state->rec->name), state->src_address)); + talloc_free(state); +} + +/* + allow a registration request +*/ +static void wins_wack_allow(struct wack_state *state) +{ + uint32_t ttl; + time_t now = time(NULL); + struct winsdb_record *rec = state->rec, *rec2; + + rec2 = winsdb_load(state->winssrv, rec->name, state); + if (rec2 == NULL || rec2->version != rec->version) { + DEBUG(1,("WINS: record %s changed during WACK - failing registration\n", + nbt_name_string(state, rec->name))); + wins_wack_deny(state); + return; + } + + nbtd_name_registration_reply(state->nbtsock, state->request_packet, + state->src_address, state->src_port, NBT_RCODE_OK); + + rec->addresses = str_list_add(rec->addresses, state->reg_address); + if (rec->addresses == NULL) goto failed; + + ttl = wins_server_ttl(state->winssrv, state->request_packet->additional[0].ttl); + if (now + ttl > rec->expire_time) { + rec->expire_time = now + ttl; + } + rec->registered_by = state->src_address; + + winsdb_modify(state->winssrv, rec); + + DEBUG(4,("WINS: accepted registration of %s with address %s\n", + nbt_name_string(state, rec->name), state->reg_address)); + +failed: + talloc_free(state); +} + +/* + called when a name query to a current owner completes +*/ +static void wins_wack_handler(struct nbt_name_request *req) +{ + struct wack_state *state = talloc_get_type(req->async.private, struct wack_state); + NTSTATUS status; + int i; + struct winsdb_record *rec = state->rec; + + status = nbt_name_query_recv(req, state, &state->query); + + /* if we timed out then try the next owner address, if any */ + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + state->owner_addresses++; + if (state->owner_addresses[0] == NULL) { + wins_wack_allow(state); + return; + } + state->query.in.dest_addr = state->owner_addresses[0]; + + req = nbt_name_query_send(state->nbtsock, &state->query); + if (req == NULL) goto failed; + + req->async.fn = wins_wack_handler; + req->async.private = state; + return; + } + + /* if the owner denies it holds the name, then allow + the registration */ + if (!NT_STATUS_IS_OK(status)) { + wins_wack_allow(state); + return; + } + + /* if the owner still wants the name and doesn't reply + with the address trying to be registered, then deny + the registration */ + if (!str_list_check(state->query.out.reply_addrs, state->reg_address)) { + wins_wack_deny(state); + return; + } + + /* we are going to allow the registration, but first remove any addresses + from the record that aren't in the reply from the client */ + for (i=0;rec->addresses[i];) { + if (!str_list_check(state->query.out.reply_addrs, rec->addresses[i])) { + str_list_remove(rec->addresses, rec->addresses[i]); + } else { + i++; + } + } + + wins_wack_allow(state); + return; + +failed: + talloc_free(state); +} + + +/* + a client has asked to register a unique name that someone else owns. We + need to ask each of the current owners if they still want it. If they do + then reject the registration, otherwise allow it +*/ +void wins_register_wack(struct nbt_name_socket *nbtsock, + struct nbt_name_packet *packet, + struct winsdb_record *rec, + const char *src_address, int src_port) +{ + struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, + struct nbtd_interface); + struct wins_server *winssrv = iface->nbtsrv->winssrv; + struct wack_state *state; + struct nbt_name_request *req; + uint32_t ttl; + + state = talloc(nbtsock, struct wack_state); + if (state == NULL) goto failed; + + /* package up the state variables for this wack request */ + state->winssrv = winssrv; + state->nbtsock = nbtsock; + state->request_packet = talloc_steal(state, packet); + state->rec = talloc_steal(state, rec); + state->src_port = src_port; + state->owner_addresses = rec->addresses; + state->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; + state->src_address = talloc_strdup(state, src_address); + if (state->src_address == NULL) goto failed; + + /* setup a name query to the first address */ + state->query.in.name = *rec->name; + state->query.in.dest_addr = state->owner_addresses[0]; + state->query.in.broadcast = False; + state->query.in.wins_lookup = True; + state->query.in.timeout = 1; + state->query.in.retries = 2; + + /* the LOGON type is a nasty hack */ + if (rec->name->type == NBT_NAME_LOGON) { + wins_wack_allow(state); + return; + } + + /* send a WACK to the client, specifying the maximum time it could + take to check with the owner, plus some slack */ + ttl = 5 + 4 * str_list_length(rec->addresses); + nbtd_wack_reply(nbtsock, packet, src_address, src_port, ttl); + + req = nbt_name_query_send(nbtsock, &state->query); + if (req == NULL) goto failed; + + req->async.fn = wins_wack_handler; + req->async.private = state; + return; + +failed: + talloc_free(state); + nbtd_name_registration_reply(nbtsock, packet, src_address, src_port, NBT_RCODE_SVR); +} |