wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
# NBTD server subsystem
#######################
# Start SUBSYSTEM WINSDB
[SUBSYSTEM::WINSDB]
OBJ_FILES = \
wins/winsdb.o \
wins/wins_hook.o
PRIVATE_PROTO_HEADER = wins/winsdb_proto.h
PUBLIC_DEPENDENCIES = \
ldb
# End SUBSYSTEM WINSDB
#######################
#######################
# Start MODULE ldb_wins_ldb
[MODULE::ldb_wins_ldb]
SUBSYSTEM = ldb
INIT_FUNCTION = wins_ldb_module_init
OBJ_FILES = \
wins/wins_ldb.o
PUBLIC_DEPENDENCIES = \
LIBNETIF
# End MODULE ldb_wins_ldb
#######################
#######################
# Start SUBSYSTEM NBTD_WINS
[SUBSYSTEM::NBTD_WINS]
OBJ_FILES = \
wins/winsserver.o \
wins/winsclient.o \
wins/winswack.o \
wins/wins_dns_proxy.o
PRIVATE_PROTO_HEADER = wins/winsserver_proto.h
PUBLIC_DEPENDENCIES = \
LIBCLI_NBT WINSDB
# End SUBSYSTEM NBTD_WINS
#######################
#######################
# Start SUBSYSTEM NBTD_DGRAM
[SUBSYSTEM::NBTD_DGRAM]
PRIVATE_PROTO_HEADER = dgram/proto.h
OBJ_FILES = \
dgram/request.o \
dgram/netlogon.o \
dgram/ntlogon.o \
dgram/browse.o
PUBLIC_DEPENDENCIES = \
LIBCLI_DGRAM
# End SUBSYSTEM NBTD_DGRAM
#######################
#######################
# Start SUBSYSTEM NBTD
[MODULE::NBTD]
INIT_FUNCTION = server_service_nbtd_init
SUBSYSTEM = service
OBJ_FILES = \
nbt_server.o \
interfaces.o \
register.o \
query.o \
nodestatus.o \
defense.o \
packet.o \
irpc.o
PRIVATE_PROTO_HEADER = nbt_server_proto.h
PUBLIC_DEPENDENCIES = \
LIBCLI_NBT NBTD_WINS NBTD_DGRAM process_model
# End SUBSYSTEM NBTD
#######################
+80
View File
@@ -0,0 +1,80 @@
/*
Unix SMB/CIFS implementation.
defend our names against name registration requests
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 "lib/util/dlinklist.h"
#include "system/network.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsserver.h"
#include "librpc/gen_ndr/ndr_nbt.h"
#include "lib/socket/socket.h"
/*
defend our registered names against registration or name refresh
requests
*/
void nbtd_request_defense(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
struct nbtd_iface_name *iname;
struct nbt_name *name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
/*
* if the packet comes from one of our interfaces
* it must be our winsclient trying to reach the winsserver
*/
if (nbtd_self_packet(nbtsock, packet, src)) {
nbtd_winsserver_request(nbtsock, packet, src);
return;
}
NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
NBTD_ASSERT_PACKET(packet, src, packet->arcount == 1);
NBTD_ASSERT_PACKET(packet, src,
packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
NBTD_ASSERT_PACKET(packet, src,
packet->questions[0].question_class == NBT_QCLASS_IP);
NBTD_ASSERT_PACKET(packet, src,
packet->additional[0].rr_type == NBT_QTYPE_NETBIOS);
NBTD_ASSERT_PACKET(packet, src,
packet->additional[0].rr_class == NBT_QCLASS_IP);
NBTD_ASSERT_PACKET(packet, src,
packet->additional[0].rdata.netbios.length == 6);
/* see if we have the requested name on this interface */
name = &packet->questions[0].name;
iname = nbtd_find_iname(iface, name, NBT_NM_ACTIVE);
if (iname != NULL &&
!(name->type == NBT_NAME_LOGON || iname->nb_flags & NBT_NM_GROUP)) {
DEBUG(2,("Defending name %s on %s against %s\n",
nbt_name_string(packet, name),
iface->bcast_address, src->addr));
nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_ACT);
} else {
nbtd_winsserver_request(nbtsock, packet, src);
}
}
+85
View File
@@ -0,0 +1,85 @@
/*
Unix SMB/CIFS implementation.
NBT datagram browse server
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 "lib/socket/socket.h"
#include "librpc/gen_ndr/ndr_nbt.h"
static const char *nbt_browse_opcode_string(enum nbt_browse_opcode r)
{
const char *val = NULL;
switch (r) {
case HostAnnouncement: val = "HostAnnouncement"; break;
case AnnouncementRequest: val = "AnnouncementRequest"; break;
case Election: val = "Election"; break;
case GetBackupListReq: val = "GetBackupListReq"; break;
case GetBackupListResp: val = "GetBackupListResp"; break;
case BecomeBackup: val = "BecomeBackup"; break;
case DomainAnnouncement: val = "DomainAnnouncement"; break;
case MasterAnnouncement: val = "MasterAnnouncement"; break;
case ResetBrowserState: val = "ResetBrowserState"; break;
case LocalMasterAnnouncement: val = "LocalMasterAnnouncement"; break;
}
return val;
}
/*
handle incoming browse mailslot requests
*/
void nbtd_mailslot_browse_handler(struct dgram_mailslot_handler *dgmslot,
struct nbt_dgram_packet *packet,
struct socket_address *src)
{
struct nbt_browse_packet *browse = talloc(dgmslot, struct nbt_browse_packet);
struct nbt_name *name = &packet->data.msg.dest_name;
NTSTATUS status;
if (browse == NULL) {
status = NT_STATUS_INVALID_PARAMETER;
goto failed;
}
status = dgram_mailslot_browse_parse(dgmslot, browse, packet, browse);
if (!NT_STATUS_IS_OK(status)) goto failed;
DEBUG(2,("Browse %s (Op %d) on '%s' '%s' from %s:%d\n",
nbt_browse_opcode_string(browse->opcode), browse->opcode,
nbt_name_string(browse, name), dgmslot->mailslot_name,
src->addr, src->port));
if (DEBUGLEVEL >= 10) {
NDR_PRINT_DEBUG(nbt_browse_packet, browse);
}
talloc_free(browse);
return;
failed:
DEBUG(2,("nbtd browse handler failed from %s:%d to %s - %s\n",
src->addr, src->port, nbt_name_string(browse, name),
nt_errstr(status)));
talloc_free(browse);
}
+268
View File
@@ -0,0 +1,268 @@
/*
Unix SMB/CIFS implementation.
NBT datagram netlogon server
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 "lib/socket/socket.h"
#include "lib/ldb/include/ldb.h"
#include "dsdb/samdb/samdb.h"
#include "auth/auth.h"
#include "db_wrap.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
reply to a GETDC request
*/
static void nbtd_netlogon_getdc(struct dgram_mailslot_handler *dgmslot,
struct nbtd_interface *iface,
struct nbt_dgram_packet *packet,
const struct socket_address *src,
struct nbt_netlogon_packet *netlogon)
{
struct nbt_name *name = &packet->data.msg.dest_name;
struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, False);
struct nbt_netlogon_packet reply;
struct nbt_netlogon_response_from_pdc *pdc;
const char *ref_attrs[] = {"nETBIOSName", NULL};
struct ldb_message **ref_res;
struct ldb_context *samctx;
struct ldb_dn *partitions_basedn;
int ret;
/* only answer getdc requests on the PDC or LOGON names */
if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
return;
}
samctx = samdb_connect(packet, anonymous_session(packet));
if (samctx == NULL) {
DEBUG(2,("Unable to open sam in getdc reply\n"));
return;
}
partitions_basedn = samdb_partitions_dn(samctx, samctx);
ret = gendb_search(samctx, samctx, partitions_basedn, &ref_res, ref_attrs,
"(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
name->name);
if (ret != 1) {
DEBUG(2,("Unable to find domain reference '%s' in sam\n", name->name));
return;
}
/* setup a GETDC reply */
ZERO_STRUCT(reply);
reply.command = NETLOGON_RESPONSE_FROM_PDC;
pdc = &reply.req.response;
pdc->pdc_name = lp_netbios_name();
pdc->unicode_pdc_name = pdc->pdc_name;
pdc->domain_name = samdb_result_string(ref_res[0], "nETBIOSName", name->name);;
pdc->nt_version = 1;
pdc->lmnt_token = 0xFFFF;
pdc->lm20_token = 0xFFFF;
packet->data.msg.dest_name.type = 0;
dgram_mailslot_netlogon_reply(reply_iface->dgmsock,
packet,
netlogon->req.pdc.mailslot_name,
&reply);
}
/*
reply to a ADS style GETDC request
*/
static void nbtd_netlogon_getdc2(struct dgram_mailslot_handler *dgmslot,
struct nbtd_interface *iface,
struct nbt_dgram_packet *packet,
const struct socket_address *src,
struct nbt_netlogon_packet *netlogon)
{
struct nbt_name *name = &packet->data.msg.dest_name;
struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, False);
struct nbt_netlogon_packet reply;
struct nbt_netlogon_response_from_pdc2 *pdc;
struct ldb_context *samctx;
const char *ref_attrs[] = {"nETBIOSName", "dnsRoot", "ncName", NULL};
const char *dom_attrs[] = {"objectGUID", NULL};
struct ldb_message **ref_res, **dom_res;
int ret;
const char **services = lp_server_services();
const char *my_ip = reply_iface->ip_address;
struct ldb_dn *partitions_basedn;
if (!my_ip) {
DEBUG(0, ("Could not obtain own IP address for datagram socket\n"));
return;
}
/* only answer getdc requests on the PDC or LOGON names */
if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
return;
}
samctx = samdb_connect(packet, anonymous_session(packet));
if (samctx == NULL) {
DEBUG(2,("Unable to open sam in getdc reply\n"));
return;
}
partitions_basedn = samdb_partitions_dn(samctx, samctx);
ret = gendb_search(samctx, samctx, partitions_basedn, &ref_res, ref_attrs,
"(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
name->name);
if (ret != 1) {
DEBUG(2,("Unable to find domain reference '%s' in sam\n", name->name));
return;
}
/* try and find the domain */
ret = gendb_search_dn(samctx, samctx,
samdb_result_dn(samctx, samctx, ref_res[0], "ncName", NULL),
&dom_res, dom_attrs);
if (ret != 1) {
DEBUG(2,("Unable to find domain from reference '%s' in sam\n",
ldb_dn_get_linearized(ref_res[0]->dn)));
return;
}
/* setup a GETDC reply */
ZERO_STRUCT(reply);
reply.command = NETLOGON_RESPONSE_FROM_PDC2;
#if 0
/* newer testing shows that the reply command type is not
changed based on whether a username is given in the
reply. This was what was causing the w2k join to be so
slow */
if (netlogon->req.pdc2.user_name[0]) {
reply.command = NETLOGON_RESPONSE_FROM_PDC_USER;
}
#endif
pdc = &reply.req.response2;
/* TODO: accurately depict which services we are running */
pdc->server_type =
NBT_SERVER_PDC | NBT_SERVER_GC |
NBT_SERVER_DS | NBT_SERVER_TIMESERV |
NBT_SERVER_CLOSEST | NBT_SERVER_WRITABLE |
NBT_SERVER_GOOD_TIMESERV;
/* hmm, probably a better way to do this */
if (str_list_check(services, "ldap")) {
pdc->server_type |= NBT_SERVER_LDAP;
}
if (str_list_check(services, "kdc")) {
pdc->server_type |= NBT_SERVER_KDC;
}
pdc->domain_uuid = samdb_result_guid(dom_res[0], "objectGUID");
pdc->forest = samdb_result_string(ref_res[0], "dnsRoot", lp_realm());
pdc->dns_domain = samdb_result_string(ref_res[0], "dnsRoot", lp_realm());
/* TODO: get our full DNS name from somewhere else */
pdc->pdc_dns_name = talloc_asprintf(packet, "%s.%s",
strlower_talloc(packet, lp_netbios_name()),
pdc->dns_domain);
pdc->domain = samdb_result_string(ref_res[0], "nETBIOSName", name->name);;
pdc->pdc_name = lp_netbios_name();
pdc->user_name = netlogon->req.pdc2.user_name;
/* TODO: we need to make sure these are in our DNS zone */
pdc->server_site = "Default-First-Site-Name";
pdc->client_site = "Default-First-Site-Name";
pdc->unknown = 0x10; /* what is this? */
pdc->unknown2 = 2; /* and this ... */
pdc->pdc_ip = my_ip;
pdc->nt_version = 13;
pdc->lmnt_token = 0xFFFF;
pdc->lm20_token = 0xFFFF;
packet->data.msg.dest_name.type = 0;
dgram_mailslot_netlogon_reply(reply_iface->dgmsock,
packet,
netlogon->req.pdc2.mailslot_name,
&reply);
}
/*
handle incoming netlogon mailslot requests
*/
void nbtd_mailslot_netlogon_handler(struct dgram_mailslot_handler *dgmslot,
struct nbt_dgram_packet *packet,
struct socket_address *src)
{
NTSTATUS status = NT_STATUS_NO_MEMORY;
struct nbtd_interface *iface =
talloc_get_type(dgmslot->private, struct nbtd_interface);
struct nbt_netlogon_packet *netlogon =
talloc(dgmslot, struct nbt_netlogon_packet);
struct nbtd_iface_name *iname;
struct nbt_name *name = &packet->data.msg.dest_name;
if (netlogon == NULL) goto failed;
/*
see if the we are listening on the destination netbios name
*/
iname = nbtd_find_iname(iface, name, 0);
if (iname == NULL) {
status = NT_STATUS_BAD_NETWORK_NAME;
goto failed;
}
DEBUG(2,("netlogon request to %s from %s:%d\n",
nbt_name_string(netlogon, name), src->addr, src->port));
status = dgram_mailslot_netlogon_parse(dgmslot, netlogon, packet, netlogon);
if (!NT_STATUS_IS_OK(status)) goto failed;
switch (netlogon->command) {
case NETLOGON_QUERY_FOR_PDC:
nbtd_netlogon_getdc(dgmslot, iface, packet, src, netlogon);
break;
case NETLOGON_QUERY_FOR_PDC2:
nbtd_netlogon_getdc2(dgmslot, iface, packet, src, netlogon);
break;
default:
DEBUG(2,("unknown netlogon op %d from %s:%d\n",
netlogon->command, src->addr, src->port));
NDR_PRINT_DEBUG(nbt_netlogon_packet, netlogon);
break;
}
talloc_free(netlogon);
return;
failed:
DEBUG(2,("nbtd netlogon handler failed from %s:%d to %s - %s\n",
src->addr, src->port, nbt_name_string(netlogon, name),
nt_errstr(status)));
talloc_free(netlogon);
}
+118
View File
@@ -0,0 +1,118 @@
/*
Unix SMB/CIFS implementation.
NBT datagram ntlogon server
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 "lib/socket/socket.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
reply to a SAM LOGON request
*/
static void nbtd_ntlogon_sam_logon(struct dgram_mailslot_handler *dgmslot,
struct nbtd_interface *iface,
struct nbt_dgram_packet *packet,
const struct socket_address *src,
struct nbt_ntlogon_packet *ntlogon)
{
struct nbt_name *name = &packet->data.msg.dest_name;
struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, False);
struct nbt_ntlogon_packet reply;
struct nbt_ntlogon_sam_logon_reply *logon;
/* only answer sam logon requests on the PDC or LOGON names */
if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
return;
}
/* setup a SAM LOGON reply */
ZERO_STRUCT(reply);
reply.command = NTLOGON_SAM_LOGON_REPLY;
logon = &reply.req.reply;
logon->server = talloc_asprintf(packet, "\\\\%s", lp_netbios_name());
logon->user_name = ntlogon->req.logon.user_name;
logon->domain = lp_workgroup();
logon->nt_version = 1;
logon->lmnt_token = 0xFFFF;
logon->lm20_token = 0xFFFF;
packet->data.msg.dest_name.type = 0;
dgram_mailslot_ntlogon_reply(reply_iface->dgmsock,
packet,
ntlogon->req.logon.mailslot_name,
&reply);
}
/*
handle incoming ntlogon mailslot requests
*/
void nbtd_mailslot_ntlogon_handler(struct dgram_mailslot_handler *dgmslot,
struct nbt_dgram_packet *packet,
struct socket_address *src)
{
NTSTATUS status = NT_STATUS_NO_MEMORY;
struct nbtd_interface *iface =
talloc_get_type(dgmslot->private, struct nbtd_interface);
struct nbt_ntlogon_packet *ntlogon =
talloc(dgmslot, struct nbt_ntlogon_packet);
struct nbtd_iface_name *iname;
struct nbt_name *name = &packet->data.msg.dest_name;
if (ntlogon == NULL) goto failed;
/*
see if the we are listening on the destination netbios name
*/
iname = nbtd_find_iname(iface, name, 0);
if (iname == NULL) {
status = NT_STATUS_BAD_NETWORK_NAME;
goto failed;
}
DEBUG(2,("ntlogon request to %s from %s:%d\n",
nbt_name_string(ntlogon, name), src->addr, src->port));
status = dgram_mailslot_ntlogon_parse(dgmslot, ntlogon, packet, ntlogon);
if (!NT_STATUS_IS_OK(status)) goto failed;
NDR_PRINT_DEBUG(nbt_ntlogon_packet, ntlogon);
switch (ntlogon->command) {
case NTLOGON_SAM_LOGON:
nbtd_ntlogon_sam_logon(dgmslot, iface, packet, src, ntlogon);
break;
default:
DEBUG(2,("unknown ntlogon op %d from %s:%d\n",
ntlogon->command, src->addr, src->port));
break;
}
talloc_free(ntlogon);
return;
failed:
DEBUG(2,("nbtd ntlogon handler failed from %s:%d to %s - %s\n",
src->addr, src->port, nbt_name_string(ntlogon, name),
nt_errstr(status)));
talloc_free(ntlogon);
}
+146
View File
@@ -0,0 +1,146 @@
/*
Unix SMB/CIFS implementation.
NBT datagram server
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 "smbd/service_task.h"
#include "lib/socket/socket.h"
#include "libcli/resolve/resolve.h"
#include "nbt_server/dgram/proto.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
a list of mailslots that we have static handlers for
*/
static const struct {
const char *mailslot_name;
dgram_mailslot_handler_t handler;
} mailslot_handlers[] = {
{ NBT_MAILSLOT_NETLOGON, nbtd_mailslot_netlogon_handler },
{ NBT_MAILSLOT_NTLOGON, nbtd_mailslot_ntlogon_handler },
{ NBT_MAILSLOT_BROWSE, nbtd_mailslot_browse_handler }
};
/*
receive an incoming dgram request. This is used for general datagram
requests. Mailslot requests for our listening mailslots
are handled in the specific mailslot handlers
*/
void dgram_request_handler(struct nbt_dgram_socket *dgmsock,
struct nbt_dgram_packet *packet,
struct socket_address *src)
{
DEBUG(0,("General datagram request from %s:%d\n", src->addr, src->port));
NDR_PRINT_DEBUG(nbt_dgram_packet, packet);
}
/*
setup the port 138 datagram listener for a given interface
*/
NTSTATUS nbtd_dgram_setup(struct nbtd_interface *iface, const char *bind_address)
{
struct nbt_dgram_socket *bcast_dgmsock = NULL;
struct nbtd_server *nbtsrv = iface->nbtsrv;
struct socket_address *bcast_addr, *bind_addr;
NTSTATUS status;
TALLOC_CTX *tmp_ctx = talloc_new(iface);
/* the list of mailslots that we are interested in */
int i;
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
}
if (strcmp("0.0.0.0", iface->netmask) != 0) {
/* listen for broadcasts on port 138 */
bcast_dgmsock = nbt_dgram_socket_init(iface, nbtsrv->task->event_ctx);
if (!bcast_dgmsock) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
bcast_addr = socket_address_from_strings(tmp_ctx, bcast_dgmsock->sock->backend_name,
iface->bcast_address, lp_dgram_port());
if (!bcast_addr) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
status = socket_listen(bcast_dgmsock->sock, bcast_addr, 0, 0);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
DEBUG(0,("Failed to bind to %s:%d - %s\n",
iface->bcast_address, lp_dgram_port(), nt_errstr(status)));
return status;
}
dgram_set_incoming_handler(bcast_dgmsock, dgram_request_handler, iface);
}
/* listen for unicasts on port 138 */
iface->dgmsock = nbt_dgram_socket_init(iface, nbtsrv->task->event_ctx);
if (!iface->dgmsock) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
bind_addr = socket_address_from_strings(tmp_ctx, iface->dgmsock->sock->backend_name,
bind_address, lp_dgram_port());
if (!bind_addr) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
status = socket_listen(iface->dgmsock->sock, bind_addr, 0, 0);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
DEBUG(0,("Failed to bind to %s:%d - %s\n",
bind_address, lp_dgram_port(), nt_errstr(status)));
return status;
}
dgram_set_incoming_handler(iface->dgmsock, dgram_request_handler, iface);
talloc_free(tmp_ctx);
for (i=0;i<ARRAY_SIZE(mailslot_handlers);i++) {
/* note that we don't need to keep the pointer
to the dgmslot around - the callback is all
we need */
struct dgram_mailslot_handler *dgmslot;
if (bcast_dgmsock) {
dgmslot = dgram_mailslot_listen(bcast_dgmsock,
mailslot_handlers[i].mailslot_name,
mailslot_handlers[i].handler, iface);
NT_STATUS_HAVE_NO_MEMORY(dgmslot);
}
dgmslot = dgram_mailslot_listen(iface->dgmsock,
mailslot_handlers[i].mailslot_name,
mailslot_handlers[i].handler, iface);
NT_STATUS_HAVE_NO_MEMORY(dgmslot);
}
return NT_STATUS_OK;
}
+349
View File
@@ -0,0 +1,349 @@
/*
Unix SMB/CIFS implementation.
NBT interface 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 "lib/util/dlinklist.h"
#include "nbt_server/nbt_server.h"
#include "smbd/service_task.h"
#include "lib/socket/socket.h"
#include "nbt_server/wins/winsserver.h"
#include "nbt_server/dgram/proto.h"
#include "system/network.h"
#include "lib/socket/netif.h"
/*
receive an incoming request and dispatch it to the right place
*/
static void nbtd_request_handler(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
nbtsrv->stats.total_received++;
/* see if its from one of our own interfaces - if so, then ignore it */
if (nbtd_self_packet_and_bcast(nbtsock, packet, src)) {
DEBUG(10,("Ignoring bcast self packet from %s:%d\n", src->addr, src->port));
return;
}
switch (packet->operation & NBT_OPCODE) {
case NBT_OPCODE_QUERY:
nbtsrv->stats.query_count++;
nbtd_request_query(nbtsock, packet, src);
break;
case NBT_OPCODE_REGISTER:
case NBT_OPCODE_REFRESH:
case NBT_OPCODE_REFRESH2:
nbtsrv->stats.register_count++;
nbtd_request_defense(nbtsock, packet, src);
break;
case NBT_OPCODE_RELEASE:
case NBT_OPCODE_MULTI_HOME_REG:
nbtsrv->stats.release_count++;
nbtd_winsserver_request(nbtsock, packet, src);
break;
default:
nbtd_bad_packet(packet, src, "Unexpected opcode");
break;
}
}
/*
find a registered name on an interface
*/
struct nbtd_iface_name *nbtd_find_iname(struct nbtd_interface *iface,
struct nbt_name *name,
uint16_t nb_flags)
{
struct nbtd_iface_name *iname;
for (iname=iface->names;iname;iname=iname->next) {
if (iname->name.type == name->type &&
strcmp(name->name, iname->name.name) == 0 &&
((iname->nb_flags & nb_flags) == nb_flags)) {
return iname;
}
}
return NULL;
}
/*
start listening on the given address
*/
static NTSTATUS nbtd_add_socket(struct nbtd_server *nbtsrv,
const char *bind_address,
const char *address,
const char *bcast,
const char *netmask)
{
struct nbtd_interface *iface;
NTSTATUS status;
struct socket_address *bcast_address;
struct socket_address *unicast_address;
/*
we actually create two sockets. One listens on the broadcast address
for the interface, and the other listens on our specific address. This
allows us to run with "bind interfaces only" while still receiving
broadcast addresses, and also simplifies matching incoming requests
to interfaces
*/
iface = talloc(nbtsrv, struct nbtd_interface);
NT_STATUS_HAVE_NO_MEMORY(iface);
iface->nbtsrv = nbtsrv;
iface->bcast_address = talloc_steal(iface, bcast);
iface->ip_address = talloc_steal(iface, address);
iface->netmask = talloc_steal(iface, netmask);
iface->names = NULL;
if (strcmp(netmask, "0.0.0.0") != 0) {
struct nbt_name_socket *bcast_nbtsock;
/* listen for broadcasts on port 137 */
bcast_nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
if (!bcast_nbtsock) {
talloc_free(iface);
return NT_STATUS_NO_MEMORY;
}
bcast_address = socket_address_from_strings(bcast_nbtsock, bcast_nbtsock->sock->backend_name,
bcast, lp_nbt_port());
if (!bcast_address) {
talloc_free(iface);
return NT_STATUS_NO_MEMORY;
}
status = socket_listen(bcast_nbtsock->sock, bcast_address, 0, 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%d - %s\n",
bcast, lp_nbt_port(), nt_errstr(status)));
talloc_free(iface);
return status;
}
talloc_free(bcast_address);
nbt_set_incoming_handler(bcast_nbtsock, nbtd_request_handler, iface);
}
/* listen for unicasts on port 137 */
iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
if (!iface->nbtsock) {
talloc_free(iface);
return NT_STATUS_NO_MEMORY;
}
unicast_address = socket_address_from_strings(iface->nbtsock, iface->nbtsock->sock->backend_name,
bind_address, lp_nbt_port());
status = socket_listen(iface->nbtsock->sock, unicast_address, 0, 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%d - %s\n",
bind_address, lp_nbt_port(), nt_errstr(status)));
talloc_free(iface);
return status;
}
talloc_free(unicast_address);
nbt_set_incoming_handler(iface->nbtsock, nbtd_request_handler, iface);
/* also setup the datagram listeners */
status = nbtd_dgram_setup(iface, bind_address);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to setup dgram listen on %s - %s\n",
bind_address, nt_errstr(status)));
talloc_free(iface);
return status;
}
if (strcmp(netmask, "0.0.0.0") == 0) {
DLIST_ADD(nbtsrv->bcast_interface, iface);
} else {
DLIST_ADD(nbtsrv->interfaces, iface);
}
return NT_STATUS_OK;
}
/*
setup a socket for talking to our WINS servers
*/
static NTSTATUS nbtd_add_wins_socket(struct nbtd_server *nbtsrv)
{
struct nbtd_interface *iface;
iface = talloc_zero(nbtsrv, struct nbtd_interface);
NT_STATUS_HAVE_NO_MEMORY(iface);
iface->nbtsrv = nbtsrv;
DLIST_ADD(nbtsrv->wins_interface, iface);
return NT_STATUS_OK;
}
/*
setup our listening sockets on the configured network interfaces
*/
NTSTATUS nbtd_startup_interfaces(struct nbtd_server *nbtsrv)
{
int num_interfaces = iface_count();
int i;
TALLOC_CTX *tmp_ctx = talloc_new(nbtsrv);
NTSTATUS status;
/* if we are allowing incoming packets from any address, then
we also need to bind to the wildcard address */
if (!lp_bind_interfaces_only()) {
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 = iface_n_ip(0);
} else {
primary_address = sys_inet_ntoa(interpret_addr2(
lp_netbios_name()));
}
primary_address = talloc_strdup(tmp_ctx, primary_address);
NT_STATUS_HAVE_NO_MEMORY(primary_address);
status = nbtd_add_socket(nbtsrv,
"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 *bcast = iface_n_bcast(i);
const char *address, *netmask;
/* we can't assume every interface is broadcast capable */
if (bcast == NULL) continue;
address = talloc_strdup(tmp_ctx, iface_n_ip(i));
bcast = talloc_strdup(tmp_ctx, bcast);
netmask = talloc_strdup(tmp_ctx, iface_n_netmask(i));
status = nbtd_add_socket(nbtsrv, address, address, bcast, netmask);
NT_STATUS_NOT_OK_RETURN(status);
}
if (lp_wins_server_list()) {
status = nbtd_add_wins_socket(nbtsrv);
NT_STATUS_NOT_OK_RETURN(status);
}
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
/*
form a list of addresses that we should use in name query replies
we always place the IP in the given interface first
*/
const char **nbtd_address_list(struct nbtd_interface *iface, TALLOC_CTX *mem_ctx)
{
struct nbtd_server *nbtsrv = iface->nbtsrv;
const char **ret = NULL;
struct nbtd_interface *iface2;
BOOL is_loopback = False;
if (iface->ip_address) {
is_loopback = iface_same_net(iface->ip_address, "127.0.0.1", "255.0.0.0");
ret = str_list_add(ret, iface->ip_address);
}
for (iface2=nbtsrv->interfaces;iface2;iface2=iface2->next) {
if (iface2 == iface) continue;
if (!iface2->ip_address) continue;
if (!is_loopback) {
if (iface_same_net(iface2->ip_address, "127.0.0.1", "255.0.0.0")) {
continue;
}
}
ret = str_list_add(ret, iface2->ip_address);
}
talloc_steal(mem_ctx, ret);
return ret;
}
/*
find the interface to use for sending a outgoing request
*/
struct nbtd_interface *nbtd_find_request_iface(struct nbtd_server *nbtd_server,
const char *address, BOOL allow_bcast_iface)
{
struct nbtd_interface *cur;
/* try to find a exact match */
for (cur=nbtd_server->interfaces;cur;cur=cur->next) {
if (iface_same_net(address, cur->ip_address, cur->netmask)) {
return cur;
}
}
/* no exact match, if we have the broadcast interface, use that */
if (allow_bcast_iface && nbtd_server->bcast_interface) {
return nbtd_server->bcast_interface;
}
/* fallback to first interface */
return nbtd_server->interfaces;
}
/*
* find the interface to use for sending a outgoing reply
*/
struct nbtd_interface *nbtd_find_reply_iface(struct nbtd_interface *iface,
const char *address, BOOL allow_bcast_iface)
{
struct nbtd_server *nbtd_server = iface->nbtsrv;
/* first try to use the given interfacel when it's not the broadcast one */
if (iface != nbtd_server->bcast_interface) {
return iface;
}
return nbtd_find_request_iface(nbtd_server, address, allow_bcast_iface);
}
+216
View File
@@ -0,0 +1,216 @@
/*
Unix SMB/CIFS implementation.
irpc services for the NBT server
Copyright (C) Andrew Tridgell 2005
Copyright (C) Volker Lendecke 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 "smbd/service_task.h"
#include "smbd/service.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsserver.h"
#include "librpc/gen_ndr/ndr_irpc.h"
#include "lib/socket/socket.h"
#include "libcli/resolve/resolve.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
serve out the nbt statistics
*/
static NTSTATUS nbtd_information(struct irpc_message *msg,
struct nbtd_information *r)
{
struct nbtd_server *server = talloc_get_type(msg->private, struct nbtd_server);
switch (r->in.level) {
case NBTD_INFO_STATISTICS:
r->out.info.stats = &server->stats;
break;
}
return NT_STATUS_OK;
}
/*
winbind needs to be able to do a getdc request, but some windows
servers always send the reply to port 138, regardless of the request
port. To cope with this we use a irpc request to the NBT server
which has port 138 open, and thus can receive the replies
*/
struct getdc_state {
struct irpc_message *msg;
struct nbtd_getdcname *req;
};
static void getdc_recv_ntlogon_reply(struct dgram_mailslot_handler *dgmslot,
struct nbt_dgram_packet *packet,
struct socket_address *src)
{
struct getdc_state *s =
talloc_get_type(dgmslot->private, struct getdc_state);
struct nbt_ntlogon_packet ntlogon;
NTSTATUS status;
status = dgram_mailslot_ntlogon_parse(dgmslot, packet, packet,
&ntlogon);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("dgram_mailslot_ntlogon_parse failed: %s\n",
nt_errstr(status)));
goto done;
}
status = NT_STATUS_NO_LOGON_SERVERS;
DEBUG(10, ("reply: command=%d\n", ntlogon.command));
switch (ntlogon.command) {
case NTLOGON_SAM_LOGON:
DEBUG(0, ("Huh -- got NTLOGON_SAM_LOGON as reply\n"));
break;
case NTLOGON_SAM_LOGON_REPLY:
case NTLOGON_SAM_LOGON_REPLY15: {
const char *p = ntlogon.req.reply.server;
DEBUG(10, ("NTLOGON_SAM_LOGON_REPLY: server: %s, user: %s, "
"domain: %s\n", p, ntlogon.req.reply.user_name,
ntlogon.req.reply.domain));
if (*p == '\\') p += 1;
if (*p == '\\') p += 1;
s->req->out.dcname = talloc_strdup(s->req, p);
if (s->req->out.dcname == NULL) {
DEBUG(0, ("talloc failed\n"));
status = NT_STATUS_NO_MEMORY;
goto done;
}
status = NT_STATUS_OK;
break;
}
default:
DEBUG(0, ("Got unknown packet: %d\n", ntlogon.command));
break;
}
done:
irpc_send_reply(s->msg, status);
}
static NTSTATUS nbtd_getdcname(struct irpc_message *msg,
struct nbtd_getdcname *req)
{
struct nbtd_server *server =
talloc_get_type(msg->private, struct nbtd_server);
struct nbtd_interface *iface = nbtd_find_request_iface(server, req->in.ip_address, True);
struct getdc_state *s;
struct nbt_ntlogon_packet p;
struct nbt_ntlogon_sam_logon *r;
struct nbt_name src, dst;
struct socket_address *dest;
struct dgram_mailslot_handler *handler;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
DEBUG(0, ("nbtd_getdcname called\n"));
s = talloc(msg, struct getdc_state);
NT_STATUS_HAVE_NO_MEMORY(s);
s->msg = msg;
s->req = req;
handler = dgram_mailslot_temp(iface->dgmsock, NBT_MAILSLOT_GETDC,
getdc_recv_ntlogon_reply, s);
NT_STATUS_HAVE_NO_MEMORY(handler);
ZERO_STRUCT(p);
p.command = NTLOGON_SAM_LOGON;
r = &p.req.logon;
r->request_count = 0;
r->computer_name = req->in.my_computername;
r->user_name = req->in.my_accountname;
r->mailslot_name = handler->mailslot_name;
r->acct_control = req->in.account_control;
r->sid = *req->in.domain_sid;
r->nt_version = 1;
r->lmnt_token = 0xffff;
r->lm20_token = 0xffff;
make_nbt_name_client(&src, req->in.my_computername);
make_nbt_name(&dst, req->in.domainname, 0x1c);
dest = socket_address_from_strings(msg, iface->dgmsock->sock->backend_name,
req->in.ip_address, 138);
NT_STATUS_HAVE_NO_MEMORY(dest);
status = dgram_mailslot_ntlogon_send(iface->dgmsock, DGRAM_DIRECT_GROUP,
&dst, dest,
&src, &p);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("dgram_mailslot_ntlogon_send failed: %s\n",
nt_errstr(status)));
return status;
}
msg->defer_reply = True;
return NT_STATUS_OK;
}
/*
register the irpc handlers for the nbt server
*/
void nbtd_register_irpc(struct nbtd_server *nbtsrv)
{
NTSTATUS status;
struct task_server *task = nbtsrv->task;
status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_INFORMATION,
nbtd_information, nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup monitoring");
return;
}
status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_GETDCNAME,
nbtd_getdcname, nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup getdcname "
"handler");
return;
}
status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_PROXY_WINS_CHALLENGE,
nbtd_proxy_wins_challenge, nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup wins challenge "
"handler");
return;
}
status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
nbtd_proxy_wins_release_demand, nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup wins release demand "
"handler");
return;
}
}
+95
View File
@@ -0,0 +1,95 @@
/*
Unix SMB/CIFS implementation.
NBT server task
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 "smbd/service_task.h"
#include "smbd/service.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsserver.h"
#include "system/network.h"
#include "lib/socket/netif.h"
/*
startup the nbtd task
*/
static void nbtd_task_init(struct task_server *task)
{
struct nbtd_server *nbtsrv;
NTSTATUS status;
if (iface_count() == 0) {
task_server_terminate(task, "nbtd: no network interfaces configured");
return;
}
task_server_set_title(task, "task[nbtd]");
nbtsrv = talloc(task, struct nbtd_server);
if (nbtsrv == NULL) {
task_server_terminate(task, "nbtd: out of memory");
return;
}
nbtsrv->task = task;
nbtsrv->interfaces = NULL;
nbtsrv->bcast_interface = NULL;
nbtsrv->wins_interface = NULL;
/* start listening on the configured network interfaces */
status = nbtd_startup_interfaces(nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup interfaces");
return;
}
/* start the WINS server, if appropriate */
status = nbtd_winsserver_init(nbtsrv);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to start WINS server");
return;
}
nbtd_register_irpc(nbtsrv);
/* start the process of registering our names on all interfaces */
nbtd_register_names(nbtsrv);
irpc_add_name(task->msg_ctx, "nbt_server");
}
/*
initialise the nbt server
*/
static NTSTATUS nbtd_init(struct event_context *event_ctx, const struct model_ops *model_ops)
{
return task_server_startup(event_ctx, model_ops, nbtd_task_init);
}
/*
register ourselves as a available server
*/
NTSTATUS server_service_nbtd_init(void)
{
return register_server_service("nbt", nbtd_init);
}
+90
View File
@@ -0,0 +1,90 @@
/*
Unix SMB/CIFS implementation.
NBT 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.
*/
#include "libcli/nbt/libnbt.h"
#include "libcli/wrepl/winsrepl.h"
#include "libcli/dgram/libdgram.h"
#include "librpc/gen_ndr/irpc.h"
#include "lib/messaging/irpc.h"
/*
a list of our registered names on each interface
*/
struct nbtd_iface_name {
struct nbtd_iface_name *next, *prev;
struct nbtd_interface *iface;
struct nbt_name name;
uint16_t nb_flags;
struct timeval registration_time;
uint32_t ttl;
/* if registered with a wins server, then this lists the server being
used */
const char *wins_server;
};
/* a list of network interfaces we are listening on */
struct nbtd_interface {
struct nbtd_interface *next, *prev;
struct nbtd_server *nbtsrv;
const char *ip_address;
const char *bcast_address;
const char *netmask;
struct nbt_name_socket *nbtsock;
struct nbt_dgram_socket *dgmsock;
struct nbtd_iface_name *names;
};
/*
top level context structure for the nbt server
*/
struct nbtd_server {
struct task_server *task;
/* the list of local network interfaces */
struct nbtd_interface *interfaces;
/* broadcast interface used for receiving packets only */
struct nbtd_interface *bcast_interface;
/* wins client interface - used for registering and refreshing
our names with a WINS server */
struct nbtd_interface *wins_interface;
struct wins_server *winssrv;
struct nbtd_statistics stats;
};
/* check a condition on an incoming packet */
#define NBTD_ASSERT_PACKET(packet, src, test) do { \
if (!(test)) { \
nbtd_bad_packet(packet, src, #test); \
return; \
} \
} while (0)
#include "nbt_server/nbt_server_proto.h"
+127
View File
@@ -0,0 +1,127 @@
/*
Unix SMB/CIFS implementation.
answer node status 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 "lib/util/dlinklist.h"
#include "system/network.h"
#include "nbt_server/nbt_server.h"
#include "lib/socket/socket.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
send a name status reply
*/
static void nbtd_node_status_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src,
struct nbt_name *name,
struct nbtd_interface *iface)
{
struct nbt_name_packet *packet;
uint32_t name_count;
struct nbtd_iface_name *iname;
struct nbtd_server *nbtsrv = iface->nbtsrv;
/* work out how many names to send */
name_count = 0;
for (iname=iface->names;iname;iname=iname->next) {
if ((iname->nb_flags & NBT_NM_ACTIVE) &&
strcmp(iname->name.name, "*") != 0) {
name_count++;
}
}
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_OPCODE_QUERY | NBT_FLAG_REPLY | NBT_FLAG_AUTHORITIVE;
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_STATUS;
packet->answers[0].rr_class = NBT_QCLASS_IP;
packet->answers[0].ttl = 0;
packet->answers[0].rdata.status.num_names = name_count;
packet->answers[0].rdata.status.names = talloc_array(packet->answers,
struct nbt_status_name, name_count);
if (packet->answers[0].rdata.status.names == NULL) goto failed;
name_count = 0;
for (iname=iface->names;iname;iname=iname->next) {
if ((iname->nb_flags & NBT_NM_ACTIVE) &&
strcmp(iname->name.name, "*") != 0) {
struct nbt_status_name *n = &packet->answers[0].rdata.status.names[name_count];
n->name = talloc_asprintf(packet->answers, "%-15s", iname->name.name);
if (n->name == NULL) goto failed;
n->type = iname->name.type;
n->nb_flags = iname->nb_flags;
name_count++;
}
}
/* we deliberately don't fill in the statistics structure as
it could lead to giving attackers too much information */
ZERO_STRUCT(packet->answers[0].rdata.status.statistics);
DEBUG(7,("Sending node status reply for %s to %s:%d\n",
nbt_name_string(packet, name), src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
/*
answer a node status query
*/
void nbtd_query_status(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
struct nbt_name *name;
struct nbtd_iface_name *iname;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
NBTD_ASSERT_PACKET(packet, src, packet->questions[0].question_type == NBT_QTYPE_STATUS);
NBTD_ASSERT_PACKET(packet, src, packet->questions[0].question_class == NBT_QCLASS_IP);
/* see if we have the requested name on this interface */
name = &packet->questions[0].name;
iname = nbtd_find_iname(iface, name, NBT_NM_ACTIVE);
if (iname == NULL) {
DEBUG(7,("Node status query for %s from %s - not found on %s\n",
nbt_name_string(packet, name), src->addr, iface->ip_address));
return;
}
nbtd_node_status_reply(nbtsock, packet, src,
&iname->name, iface);
}
+342
View File
@@ -0,0 +1,342 @@
/*
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 "nbt_server/nbt_server.h"
#include "lib/socket/socket.h"
#include "librpc/gen_ndr/ndr_nbt.h"
/*
we received a badly formed packet - log it
*/
void nbtd_bad_packet(struct nbt_name_packet *packet,
const struct socket_address *src, const char *reason)
{
DEBUG(2,("nbtd: bad packet '%s' from %s:%d\n", reason, src->addr, src->port));
if (DEBUGLVL(5)) {
NDR_PRINT_DEBUG(nbt_name_packet, packet);
}
}
/*
see if an incoming packet is a broadcast packet from one of our own
interfaces
*/
BOOL nbtd_self_packet_and_bcast(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
const struct socket_address *src)
{
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
/* if its not a broadcast then its not considered a self packet */
if (!(packet->operation & NBT_FLAG_BROADCAST)) {
return False;
}
/*
* this uses the fact that iface->nbtsock is the unicast listen address
* if the interface isn't the global bcast interface
*
* so if the request was directed to the unicast address it isn't a broadcast
* message
*/
if (iface->nbtsock == nbtsock &&
iface != iface->nbtsrv->bcast_interface) {
return False;
}
return nbtd_self_packet(nbtsock, packet, src);
}
BOOL nbtd_self_packet(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
const struct socket_address *src)
{
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
/* if its not from the nbt port, then it wasn't a broadcast from us */
if (src->port != lp_nbt_port()) {
return False;
}
/* we have to loop over our interface list, seeing if its from
one of our own interfaces */
for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
if (strcmp(src->addr, iface->ip_address) == 0) {
return True;
}
}
return False;
}
/*
send a name query reply
*/
void nbtd_name_query_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src,
struct nbt_name *name, uint32_t ttl,
uint16_t nb_flags, const char **addresses)
{
struct nbt_name_packet *packet;
size_t num_addresses = str_list_length(addresses);
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
int i;
if (num_addresses == 0) {
DEBUG(3,("No addresses in name query reply - failing\n"));
return;
}
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 = num_addresses*6;
packet->answers[0].rdata.netbios.addresses =
talloc_array(packet->answers, struct nbt_rdata_address, num_addresses);
if (packet->answers[0].rdata.netbios.addresses == NULL) goto failed;
for (i=0;i<num_addresses;i++) {
struct nbt_rdata_address *addr =
&packet->answers[0].rdata.netbios.addresses[i];
addr->nb_flags = nb_flags;
addr->ipaddr = talloc_strdup(packet->answers, addresses[i]);
if (addr->ipaddr == NULL) goto failed;
}
DEBUG(7,("Sending name query reply for %s at %s to %s:%d\n",
nbt_name_string(packet, name), addresses[0], src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
/*
send a negative name query reply
*/
void nbtd_negative_name_query_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src)
{
struct nbt_name_packet *packet;
struct nbt_name *name = &request_packet->questions[0].name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
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_RCODE_NAM;
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_NULL;
packet->answers[0].rr_class = NBT_QCLASS_IP;
packet->answers[0].ttl = 0;
ZERO_STRUCT(packet->answers[0].rdata);
DEBUG(7,("Sending negative name query reply for %s to %s:%d\n",
nbt_name_string(packet, name), src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
/*
send a name registration reply
*/
void nbtd_name_registration_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src,
uint8_t rcode)
{
struct nbt_name_packet *packet;
struct nbt_name *name = &request_packet->questions[0].name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
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_REGISTER |
NBT_FLAG_AUTHORITIVE |
NBT_FLAG_RECURSION_DESIRED |
NBT_FLAG_RECURSION_AVAIL |
rcode;
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 = request_packet->additional[0].ttl;
packet->answers[0].rdata = request_packet->additional[0].rdata;
DEBUG(7,("Sending %s name registration reply for %s to %s:%d\n",
rcode==0?"positive":"negative",
nbt_name_string(packet, name), src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
/*
send a name release reply
*/
void nbtd_name_release_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src,
uint8_t rcode)
{
struct nbt_name_packet *packet;
struct nbt_name *name = &request_packet->questions[0].name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
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_RELEASE |
NBT_FLAG_AUTHORITIVE |
rcode;
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 = request_packet->additional[0].ttl;
packet->answers[0].rdata = request_packet->additional[0].rdata;
DEBUG(7,("Sending %s name release reply for %s to %s:%d\n",
rcode==0?"positive":"negative",
nbt_name_string(packet, name), src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
/*
send a WACK reply
*/
void nbtd_wack_reply(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *request_packet,
struct socket_address *src,
uint32_t ttl)
{
struct nbt_name_packet *packet;
struct nbt_name *name = &request_packet->questions[0].name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct nbtd_server *nbtsrv = iface->nbtsrv;
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_WACK |
NBT_FLAG_AUTHORITIVE;
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.data.length = 2;
packet->answers[0].rdata.data.data = talloc_size(packet, 2);
if (packet->answers[0].rdata.data.data == NULL) goto failed;
RSSVAL(packet->answers[0].rdata.data.data, 0, request_packet->operation);
DEBUG(7,("Sending WACK reply for %s to %s:%d\n",
nbt_name_string(packet, name), src->addr, src->port));
nbtsrv->stats.total_sent++;
nbt_name_reply_send(nbtsock, src, packet);
failed:
talloc_free(packet);
}
+101
View File
@@ -0,0 +1,101 @@
/*
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 "lib/util/dlinklist.h"
#include "system/network.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsserver.h"
#include "librpc/gen_ndr/ndr_nbt.h"
#include "lib/socket/socket.h"
/*
answer a name query
*/
void nbtd_request_query(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
struct nbtd_iface_name *iname;
struct nbt_name *name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
/* see if its a node status query */
if (packet->qdcount == 1 &&
packet->questions[0].question_type == NBT_QTYPE_STATUS) {
nbtd_query_status(nbtsock, packet, src);
return;
}
NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
NBTD_ASSERT_PACKET(packet, src,
packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
NBTD_ASSERT_PACKET(packet, src,
packet->questions[0].question_class == NBT_QCLASS_IP);
/* see if we have the requested name on this interface */
name = &packet->questions[0].name;
iname = nbtd_find_iname(iface, name, 0);
if (iname == NULL) {
/* don't send negative replies to broadcast queries */
if (packet->operation & NBT_FLAG_BROADCAST) {
return;
}
if (packet->operation & NBT_FLAG_RECURSION_DESIRED) {
nbtd_winsserver_request(nbtsock, packet, src);
return;
}
/* otherwise send a negative reply */
nbtd_negative_name_query_reply(nbtsock, packet, src);
return;
}
/*
* normally we should forward all queries with the
* recursion desired flag to the wins server, but this
* breaks are winsclient code, when doing mhomed registrations
*/
if (!(packet->operation & NBT_FLAG_BROADCAST) &&
(packet->operation & NBT_FLAG_RECURSION_DESIRED) &&
(iname->nb_flags & NBT_NM_GROUP) &&
lp_wins_support()) {
nbtd_winsserver_request(nbtsock, packet, src);
return;
}
/* if the name is not yet active and its a broadcast query then
ignore it for now */
if (!(iname->nb_flags & NBT_NM_ACTIVE) &&
(packet->operation & NBT_FLAG_BROADCAST)) {
DEBUG(7,("Query for %s from %s - name not active yet on %s\n",
nbt_name_string(packet, name), src->addr, iface->ip_address));
return;
}
nbtd_name_query_reply(nbtsock, packet, src,
&iname->name, iname->ttl, iname->nb_flags,
nbtd_address_list(iface, packet));
}
+292
View File
@@ -0,0 +1,292 @@
/*
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 "lib/events/events.h"
#include "lib/util/dlinklist.h"
#include "nbt_server/nbt_server.h"
#include "smbd/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"
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,
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 event_context *ev, struct timed_event *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.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 = 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 = lp_parm_int(-1, "nbtd", "max_refresh_time", 7200);
refresh_time = MIN(max_refresh_time, iname->ttl/2);
event_add_timed(iname->iface->nbtsrv->task->event_ctx,
iname,
timeval_add(&iname->registration_time, refresh_time, 0),
name_refresh_handler, iname);
}
/*
a name registration has completed
*/
static void nbtd_register_handler(struct composite_context *creq)
{
struct nbtd_iface_name *iname = talloc_get_type(creq->async.private_data,
struct nbtd_iface_name);
NTSTATUS status;
TALLOC_CTX *tmp_ctx = talloc_new(iname);
status = nbt_name_register_bcast_recv(creq);
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(tmp_ctx, &iname->name),
iname->iface->ip_address, iname->iface->bcast_address));
iname->registration_time = timeval_current();
talloc_free(tmp_ctx);
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(tmp_ctx, &iname->name),
iname->iface->ip_address, iname->iface->bcast_address,
nt_errstr(status)));
talloc_free(tmp_ctx);
}
/*
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 = lp_netbios_scope();
struct nbt_name_register_bcast io;
struct composite_context *creq;
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 = lp_parm_int(-1, "nbtd", "bcast_ttl", 300000);
iname->registration_time = timeval_zero();
iname->wins_server = NULL;
DLIST_ADD_END(iface->names, iname, struct nbtd_iface_name *);
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;
}
/* 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.ttl = iname->ttl;
nbtsrv->stats.total_sent++;
creq = nbt_name_register_bcast_send(iface->nbtsock, &io);
if (creq == NULL) return;
creq->async.fn = nbtd_register_handler;
creq->async.private_data = iname;
}
/*
register one name on all our interfaces
*/
static 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, lp_netbios_name(), NBT_NAME_CLIENT, nb_flags);
nbtd_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_USER, nb_flags);
nbtd_register_name(nbtsrv, lp_netbios_name(), NBT_NAME_SERVER, nb_flags);
aliases = lp_netbios_aliases();
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++;
}
switch (lp_server_role()) {
case ROLE_DOMAIN_PDC:
nbtd_register_name(nbtsrv, lp_workgroup(), NBT_NAME_PDC, nb_flags);
nbtd_register_name(nbtsrv, lp_workgroup(), NBT_NAME_LOGON, nb_flags | NBT_NM_GROUP);
break;
case ROLE_DOMAIN_BDC:
nbtd_register_name(nbtsrv, lp_workgroup(), NBT_NAME_LOGON, nb_flags | NBT_NM_GROUP);
default:
break;
}
nb_flags |= NBT_NM_GROUP;
nbtd_register_name(nbtsrv, lp_workgroup(), 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);
}
@@ -0,0 +1,98 @@
/*
Unix SMB/CIFS implementation.
wins server dns proxy
Copyright (C) Stefan Metzmacher 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 "nbt_server/wins/winsserver.h"
#include "system/time.h"
#include "libcli/composite/composite.h"
#include "smbd/service_task.h"
#include "libcli/resolve/resolve.h"
struct wins_dns_proxy_state {
struct nbt_name_socket *nbtsock;
struct nbt_name_packet *packet;
struct socket_address *src;
};
static void nbtd_wins_dns_proxy_handler(struct composite_context *creq)
{
NTSTATUS status;
struct wins_dns_proxy_state *s = talloc_get_type(creq->async.private_data,
struct wins_dns_proxy_state);
struct nbt_name *name = &s->packet->questions[0].name;
const char *address;
const char **addresses;
uint16_t nb_flags = 0; /* TODO: ... */
status = resolve_name_recv(creq, s->packet, &address);
if (!NT_STATUS_IS_OK(status)) {
goto notfound;
}
addresses = str_list_add(NULL, address);
talloc_steal(s->packet, addresses);
if (!addresses) goto notfound;
nbtd_name_query_reply(s->nbtsock, s->packet, s->src, name,
0, nb_flags, addresses);
return;
notfound:
nbtd_negative_name_query_reply(s->nbtsock, s->packet, s->src);
}
/*
dns proxy query a name
*/
void nbtd_wins_dns_proxy_query(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
struct nbt_name *name = &packet->questions[0].name;
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct wins_dns_proxy_state *s;
struct composite_context *creq;
const char *methods[] = {
"host",
NULL
};
s = talloc(nbtsock, struct wins_dns_proxy_state);
if (!s) goto failed;
s->nbtsock = nbtsock;
s->packet = talloc_steal(s, packet);
s->src = src;
if (!talloc_reference(s, src)) {
goto failed;
}
creq = resolve_name_send(name, iface->nbtsrv->task->event_ctx, methods);
if (!creq) goto failed;
creq->async.fn = nbtd_wins_dns_proxy_handler;
creq->async.private_data= s;
return;
failed:
nbtd_negative_name_query_reply(nbtsock, packet, src);
}
+95
View File
@@ -0,0 +1,95 @@
/*
Unix SMB/CIFS implementation.
wins hook feature, we run a specified script
which can then do some custom actions
Copyright (C) Stefan Metzmacher 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/filesys.h"
static const char *wins_hook_action_string(enum wins_hook_action action)
{
switch (action) {
case WINS_HOOK_ADD: return "add";
case WINS_HOOK_MODIFY: return "refresh";
case WINS_HOOK_DELETE: return "delete";
}
return "unknown";
}
void wins_hook(struct winsdb_handle *h, const struct winsdb_record *rec, enum wins_hook_action action)
{
const char *script = lp_wins_hook();
uint32_t i, length;
int child;
char *cmd = NULL;
TALLOC_CTX *tmp_mem = NULL;
if (!script || !script[0]) return;
tmp_mem = talloc_new(h);
if (!tmp_mem) goto failed;
length = winsdb_addr_list_length(rec->addresses);
if (action == WINS_HOOK_MODIFY && length < 1) {
action = WINS_HOOK_DELETE;
}
cmd = talloc_asprintf(tmp_mem,
"%s %s %s %02x %ld",
script,
wins_hook_action_string(action),
rec->name->name,
rec->name->type,
rec->expire_time);
if (!cmd) goto failed;
for (i=0; rec->addresses[i]; i++) {
cmd = talloc_asprintf_append(cmd, " %s", rec->addresses[i]->address);
if (!cmd) goto failed;
}
DEBUG(10,("call wins hook '%s'\n", cmd));
/* signal handling in posix really sucks - doing this in a library
affects the whole app, but what else to do?? */
signal(SIGCHLD, SIG_IGN);
child = fork();
if (child == (pid_t)-1) {
goto failed;
}
if (child == 0) {
/* TODO: close file handles */
execl("/bin/sh", "sh", "-c", cmd, NULL);
_exit(0);
}
talloc_free(tmp_mem);
return;
failed:
talloc_free(tmp_mem);
DEBUG(0,("FAILED: calling wins hook '%s'\n", script));
}
+124
View File
@@ -0,0 +1,124 @@
/*
ldb database module
Copyright (C) Stefan Metzmacher 2006
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.
*/
/*
* Name: ldb
*
* Component: ldb winsdb module
*
* Description: verify winsdb records before they're written to disk
*
* Author: Stefan Metzmacher
*/
#include "includes.h"
#include "nbt_server/nbt_server.h"
#include "nbt_server/wins/winsdb.h"
#include "lib/ldb/include/ldb.h"
#include "lib/ldb/include/ldb_errors.h"
#include "lib/ldb/include/ldb_private.h"
#include "system/network.h"
#include "lib/socket/netif.h"
static int wins_ldb_verify(struct ldb_module *module, struct ldb_request *req)
{
struct winsdb_handle *h = talloc_get_type(ldb_get_opaque(module->ldb, "winsdb_handle"),
struct winsdb_handle);
const struct ldb_message *msg;
switch (req->operation) {
case LDB_ADD:
msg = req->op.add.message;
break;
case LDB_MODIFY:
msg = req->op.mod.message;
break;
default:
return ldb_next_request(module, req);
}
/* do not manipulate our control entries */
if (ldb_dn_is_special(msg->dn)) {
return ldb_next_request(module, req);
}
if (!h) {
ldb_debug_set(module->ldb, LDB_DEBUG_FATAL, "%s", "WINS_LDB: INTERNAL ERROR: no winsdb_handle present!");
return LDB_ERR_OTHER;
}
switch (h->caller) {
case WINSDB_HANDLE_CALLER_NBTD:
case WINSDB_HANDLE_CALLER_WREPL:
/* we trust our nbt and wrepl code ... */
return ldb_next_request(module, req);
case WINSDB_HANDLE_CALLER_ADMIN:
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "%s\n", "WINS_LDB: TODO verify add/modify for WINSDB_HANDLE_CALLER_ADMIN");
return ldb_next_request(module, req);
}
return LDB_ERR_OTHER;
}
static int wins_ldb_init(struct ldb_module *ctx)
{
struct winsdb_handle *h;
const char *owner;
ctx->private_data = NULL;
owner = lp_parm_string(-1, "winsdb", "local_owner");
if (!owner) {
owner = iface_n_ip(0);
if (!owner) {
owner = "0.0.0.0";
}
}
h = talloc(ctx, struct winsdb_handle);
if (!h) goto failed;
h->ldb = ctx->ldb;
h->caller = WINSDB_HANDLE_CALLER_ADMIN;
h->local_owner = talloc_strdup(h, owner);
if (!h->local_owner) goto failed;
return ldb_set_opaque(ctx->ldb, "winsdb_handle", h);
failed:
talloc_free(h);
return LDB_ERR_OTHER;
}
static const struct ldb_module_ops wins_ldb_ops = {
.name = "wins_ldb",
.add = wins_ldb_verify,
.modify = wins_ldb_verify,
.init_context = wins_ldb_init
};
/* the init function */
int wins_ldb_module_init(void)
{
return ldb_register_module(&wins_ldb_ops);
}
+257
View File
@@ -0,0 +1,257 @@
/*
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 "nbt_server/wins/winsserver.h"
#include "libcli/composite/composite.h"
#include "lib/events/events.h"
#include "librpc/gen_ndr/ndr_nbt.h"
#include "smbd/service_task.h"
static void nbtd_wins_refresh_handler(struct composite_context *c);
/* 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_data);
/*
retry a WINS name registration
*/
static void nbtd_wins_register_retry(struct event_context *ev, struct timed_event *te,
struct timeval t, void *private_data)
{
struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name);
nbtd_winsclient_register(iname);
}
/*
start a timer to refresh this name
*/
static void nbtd_wins_start_refresh_timer(struct nbtd_iface_name *iname)
{
uint32_t refresh_time;
uint32_t max_refresh_time = lp_parm_int(-1, "nbtd", "max_refresh_time", 7200);
refresh_time = MIN(max_refresh_time, iname->ttl/2);
event_add_timed(iname->iface->nbtsrv->task->event_ctx,
iname,
timeval_add(&iname->registration_time, refresh_time, 0),
nbtd_wins_refresh, 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_data,
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() would generate a warning,
* so steal it into the tmp context
*/
talloc_steal(tmp_ctx, iname->wins_server);
}
iname->wins_server = talloc_steal(iname, io.out.wins_server);
iname->registration_time = timeval_current();
nbtd_wins_start_refresh_timer(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_data)
{
struct nbtd_iface_name *iname = talloc_get_type(private_data, 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;
if (!io.in.addresses) {
talloc_free(tmp_ctx);
return;
}
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_data = 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_data,
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() would generate a warning,
* so steal it into the tmp context
*/
talloc_steal(tmp_ctx, iname->wins_server);
}
iname->wins_server = talloc_steal(iname, io.out.wins_server);
iname->registration_time = timeval_current();
nbtd_wins_start_refresh_timer(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;
if (!io.in.addresses) {
return;
}
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_data = iname;
}
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
/*
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.
*/
#define WINSDB_FLAG_ALLOC_VERSION (1<<0)
#define WINSDB_FLAG_TAKE_OWNERSHIP (1<<1)
struct winsdb_addr {
const char *address;
const char *wins_owner;
time_t expire_time;
};
/*
each record in the database contains the following information
*/
struct winsdb_record {
struct nbt_name *name;
enum wrepl_name_type type;
enum wrepl_name_state state;
enum wrepl_name_node node;
BOOL is_static;
time_t expire_time;
uint64_t version;
const char *wins_owner;
struct winsdb_addr **addresses;
/* only needed for debugging problems */
const char *registered_by;
};
enum winsdb_handle_caller {
WINSDB_HANDLE_CALLER_ADMIN = 0,
WINSDB_HANDLE_CALLER_NBTD = 1,
WINSDB_HANDLE_CALLER_WREPL = 2
};
struct winsdb_handle {
/* wins server database handle */
struct ldb_context *ldb;
/*
* the type of the caller, as we pass this to the
* 'wins_ldb' ldb module can decide if it needs to verify the
* the records before they're written to disk
*/
enum winsdb_handle_caller caller;
/* local owner address */
const char *local_owner;
};
enum wins_hook_action {
WINS_HOOK_ADD = 0,
WINS_HOOK_MODIFY = 1,
WINS_HOOK_DELETE = 2
};
struct ldb_message;
#include "nbt_server/wins/winsdb_proto.h"
+863
View File
@@ -0,0 +1,863 @@
/*
Unix SMB/CIFS implementation.
core wins server handling
Copyright (C) Andrew Tridgell 2005
Copyright (C) Stefan Metzmacher 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 "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 "lib/socket/socket.h"
#include "lib/ldb/include/ldb.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->config.max_renew_interval);
ttl = MAX(ttl, winssrv->config.min_renew_interval);
return ttl;
}
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) {
return WREPL_TYPE_SGROUP;
}
if (nb_flags & NBT_NM_GROUP) {
return WREPL_TYPE_GROUP;
}
if (mhomed) {
return WREPL_TYPE_MHOMED;
}
return WREPL_TYPE_UNIQUE;
}
/*
register a new name with WINS
*/
static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
const struct socket_address *src,
enum wrepl_name_type type)
{
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;
enum wrepl_name_node node;
#define WREPL_NODE_NBT_FLAGS(nb_flags) \
((nb_flags & NBT_NM_OWNER_TYPE)>>13)
node = WREPL_NODE_NBT_FLAGS(nb_flags);
rec.name = name;
rec.type = type;
rec.state = WREPL_STATE_ACTIVE;
rec.node = node;
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.registered_by = src->addr;
rec.addresses = winsdb_addr_list_make(packet);
if (rec.addresses == NULL) return NBT_RCODE_SVR;
rec.addresses = winsdb_addr_list_add(winssrv->wins_db,
&rec, rec.addresses,
address,
winssrv->wins_db->local_owner,
rec.expire_time,
True);
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]->address));
return winsdb_add(winssrv->wins_db, &rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
}
/*
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,
struct winsdb_addr *winsdb_addr,
const struct socket_address *src)
{
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;
uint32_t modify_flags = 0;
rec->expire_time = time(NULL) + ttl;
rec->registered_by = src->addr;
if (winsdb_addr) {
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) {
modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
}
DEBUG(5,("WINS: refreshed registration of %s at %s\n",
nbt_name_string(packet, rec->name), address));
return winsdb_modify(winssrv->wins_db, rec, modify_flags);
}
/*
do a sgroup merge
*/
static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct winsdb_record *rec,
const char *address,
const struct socket_address *src)
{
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);
rec->expire_time = time(NULL) + ttl;
rec->registered_by = src->addr;
rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
rec, rec->addresses,
address,
winssrv->wins_db->local_owner,
rec->expire_time,
True);
if (rec->addresses == NULL) return NBT_RCODE_SVR;
DEBUG(5,("WINS: sgroup merge of %s at %s\n",
nbt_name_string(packet, rec->name), address));
return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
}
struct wack_state {
struct wins_server *winssrv;
struct nbt_name_socket *nbtsock;
struct nbt_name_packet *request_packet;
struct winsdb_record *rec;
struct socket_address *src;
const char *reg_address;
enum wrepl_name_type new_type;
struct wins_challenge_io io;
NTSTATUS status;
};
/*
deny a registration request
*/
static void wins_wack_deny(struct wack_state *s)
{
nbtd_name_registration_reply(s->nbtsock, s->request_packet,
s->src, NBT_RCODE_ACT);
DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n",
nbt_name_string(s, s->rec->name), s->src->addr, s->src->port));
talloc_free(s);
}
/*
allow a registration request
*/
static void wins_wack_allow(struct wack_state *s)
{
NTSTATUS status;
uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl);
struct winsdb_record *rec = s->rec, *rec2;
uint32_t i,j;
status = winsdb_lookup(s->winssrv->wins_db, rec->name, s, &rec2);
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",
nbt_name_string(s, rec->name)));
wins_wack_deny(s);
return;
}
/*
* if the old name owner doesn't hold the name anymore
* handle the request as new registration for the new name owner
*/
if (!NT_STATUS_IS_OK(s->status)) {
uint8_t rcode;
winsdb_delete(s->winssrv->wins_db, rec);
rcode = wins_register_new(s->nbtsock, s->request_packet, s->src, s->new_type);
if (rcode != NBT_RCODE_OK) {
DEBUG(1,("WINS: record %s failed to register as new during WACK\n",
nbt_name_string(s, rec->name)));
wins_wack_deny(s);
return;
}
goto done;
}
rec->expire_time = time(NULL) + ttl;
rec->registered_by = s->src->addr;
/*
* now remove all addresses that're the client doesn't hold anymore
* and update the time stamp and owner for the ownes that are still there
*/
for (i=0; rec->addresses[i]; i++) {
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;
break;
}
if (found) {
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(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;
/* if we have more than one address, this becomes implicit a MHOMED record */
if (winsdb_addr_list_length(rec->addresses) > 1) {
rec->type = WREPL_TYPE_MHOMED;
}
winsdb_modify(s->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
DEBUG(4,("WINS: accepted registration of %s with address %s\n",
nbt_name_string(s, rec->name), s->reg_address));
done:
nbtd_name_registration_reply(s->nbtsock, s->request_packet,
s->src, NBT_RCODE_OK);
failed:
talloc_free(s);
}
/*
called when a name query to a current owner completes
*/
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;
uint32_t i;
s->status = wins_challenge_recv(c_req, s, &s->io);
/*
* if the owner denies it holds the name, then allow
* the registration
*/
if (!NT_STATUS_IS_OK(s->status)) {
wins_wack_allow(s);
return;
}
if (s->new_type == WREPL_TYPE_GROUP || s->new_type == WREPL_TYPE_SGROUP) {
DEBUG(1,("WINS: record %s failed to register as group type(%u) during WACK, it's still type(%u)\n",
nbt_name_string(s, s->rec->name), s->new_type, s->rec->type));
wins_wack_deny(s);
return;
}
/*
* if the owner still wants the name and doesn't reply
* with the address trying to be registered, then deny
* the registration
*/
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;
break;
}
if (!found) {
wins_wack_deny(s);
return;
}
wins_wack_allow(s);
return;
}
/*
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
*/
static void wins_register_wack(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct winsdb_record *rec,
struct socket_address *src,
enum wrepl_name_type new_type)
{
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct wack_state *s;
struct composite_context *c_req;
uint32_t ttl;
s = talloc_zero(nbtsock, struct wack_state);
if (s == NULL) goto failed;
/* package up the state variables for this wack request */
s->winssrv = winssrv;
s->nbtsock = nbtsock;
s->request_packet = talloc_steal(s, packet);
s->rec = talloc_steal(s, rec);
s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
s->new_type = new_type;
s->src = src;
if (talloc_reference(s, src) == NULL) goto failed;
s->io.in.nbtd_server = iface->nbtsrv;
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;
/*
* send a WACK to the client, specifying the maximum time it could
* take to check with the owner, plus some slack
*/
ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses);
nbtd_wack_reply(nbtsock, packet, src, ttl);
/*
* send the challenge to the old addresses
*/
c_req = wins_challenge_send(s, &s->io);
if (c_req == NULL) goto failed;
c_req->async.fn = wack_wins_challenge_handler;
c_req->async.private_data = s;
return;
failed:
talloc_free(s);
nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR);
}
/*
register a name
*/
static void nbtd_winsserver_register(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);
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;
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;
/*
* as a special case, the local master browser name is always accepted
* for registration, but never stored, but w2k3 stores it if it's registered
* as a group name, (but a query for the 0x1D name still returns not found!)
*/
if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
rcode = NBT_RCODE_OK;
goto done;
}
/* w2k3 refuses 0x1B names with marked as group */
if (name->type == NBT_NAME_PDC && (nb_flags & NBT_NM_GROUP)) {
rcode = NBT_RCODE_RFS;
goto done;
}
/* w2k3 refuses 0x1C names with out marked as group */
if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
rcode = NBT_RCODE_RFS;
goto done;
}
/* w2k3 refuses 0x1E names with out marked as group */
if (name->type == NBT_NAME_BROWSER && !(nb_flags & NBT_NM_GROUP)) {
rcode = NBT_RCODE_RFS;
goto done;
}
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);
goto done;
} else if (!NT_STATUS_IS_OK(status)) {
rcode = NBT_RCODE_SVR;
goto done;
} else if (rec->is_static) {
if (rec->type == WREPL_TYPE_GROUP || rec->type == WREPL_TYPE_SGROUP) {
rcode = NBT_RCODE_OK;
goto done;
}
rcode = NBT_RCODE_ACT;
goto done;
}
if (rec->type == WREPL_TYPE_GROUP) {
if (new_type != WREPL_TYPE_GROUP) {
DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
" while a normal group is already there\n",
nbt_name_string(packet, name), new_type));
rcode = NBT_RCODE_ACT;
goto done;
}
if (rec->state == WREPL_STATE_ACTIVE) {
/* TODO: is this correct? */
rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
goto done;
}
/* TODO: is this correct? */
winsdb_delete(winssrv->wins_db, rec);
rcode = wins_register_new(nbtsock, packet, src, new_type);
goto done;
}
if (rec->state != WREPL_STATE_ACTIVE) {
winsdb_delete(winssrv->wins_db, rec);
rcode = wins_register_new(nbtsock, packet, src, new_type);
goto done;
}
switch (rec->type) {
case WREPL_TYPE_UNIQUE:
case WREPL_TYPE_MHOMED:
/*
* if its an active unique name, and the registration is for a group, then
* see if the unique name owner still wants the name
* TODO: is this correct?
*/
if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
wins_register_wack(nbtsock, packet, rec, src, new_type);
return;
}
/*
* if the registration is for an address that is currently active, then
* just update the expiry time of the record and the address
*/
winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
if (winsdb_addr) {
rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
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, new_type);
return;
case WREPL_TYPE_GROUP:
/* this should not be reached as normal groups are handled above */
DEBUG(0,("BUG at %s\n",__location__));
rcode = NBT_RCODE_ACT;
goto done;
case WREPL_TYPE_SGROUP:
/* if the new record isn't also a special group, refuse the registration */
if (new_type != WREPL_TYPE_SGROUP) {
DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
" while a special group is already there\n",
nbt_name_string(packet, name), new_type));
rcode = NBT_RCODE_ACT;
goto done;
}
/*
* if the registration is for an address that is currently active, then
* just update the expiry time of the record and the address
*/
winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
if (winsdb_addr) {
rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
goto done;
}
rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
goto done;
}
done:
nbtd_name_registration_reply(nbtsock, packet, src, rcode);
}
/*
query a name
*/
static void nbtd_winsserver_query(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);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
struct winsdb_record *rec;
struct winsdb_record *rec_1b = NULL;
const char **addresses;
const char **addresses_1b = NULL;
uint16_t nb_flags = 0;
if (name->type == NBT_NAME_MASTER) {
goto notfound;
}
/*
* w2k3 returns the first address of the 0x1B record as first address
* to a 0x1C query
*/
if (name->type == NBT_NAME_LOGON) {
struct nbt_name name_1b;
name_1b = *name;
name_1b.type = NBT_NAME_PDC;
status = winsdb_lookup(winssrv->wins_db, &name_1b, packet, &rec_1b);
if (NT_STATUS_IS_OK(status)) {
addresses_1b = winsdb_addr_string_list(packet, rec_1b->addresses);
}
}
status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
if (!NT_STATUS_IS_OK(status)) {
if (!lp_wins_dns_proxy()) {
goto notfound;
}
if (name->type != NBT_NAME_CLIENT && name->type != NBT_NAME_SERVER) {
goto notfound;
}
nbtd_wins_dns_proxy_query(nbtsock, packet, src);
return;
}
/*
* for group's we always reply with
* 255.255.255.255 as address, even if
* the record is released or tombstoned
*/
if (rec->type == WREPL_TYPE_GROUP) {
addresses = str_list_add(NULL, "255.255.255.255");
talloc_steal(packet, addresses);
if (!addresses) {
goto notfound;
}
nb_flags |= NBT_NM_GROUP;
goto found;
}
if (rec->state != WREPL_STATE_ACTIVE) {
goto notfound;
}
addresses = winsdb_addr_string_list(packet, rec->addresses);
if (!addresses) {
goto notfound;
}
/*
* if addresses_1b isn't NULL, we have a 0x1C query and need to return the
* first 0x1B address as first address
*/
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);
}
found:
nbtd_name_query_reply(nbtsock, packet, src, name,
0, nb_flags, addresses);
return;
notfound:
nbtd_negative_name_query_reply(nbtsock, packet, src);
}
/*
release a name
*/
static void nbtd_winsserver_release(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);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
struct nbt_name *name = &packet->questions[0].name;
struct winsdb_record *rec;
uint32_t modify_flags = 0;
uint8_t ret;
if (name->type == NBT_NAME_MASTER) {
goto done;
}
status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
if (rec->is_static) {
if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
goto done;
}
nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
return;
}
if (rec->state != WREPL_STATE_ACTIVE) {
goto done;
}
/*
* TODO: do we need to check if
* src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
* here?
*/
/*
* we only allow releases from an owner - other releases are
* silently ignored
*/
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 Addresses: \n"));
for (i=0; rec->addresses && rec->addresses[i]; i++) {
DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
}
goto done;
}
DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
switch (rec->type) {
case WREPL_TYPE_UNIQUE:
rec->state = WREPL_STATE_RELEASED;
break;
case WREPL_TYPE_GROUP:
rec->state = WREPL_STATE_RELEASED;
break;
case WREPL_TYPE_SGROUP:
winsdb_addr_list_remove(rec->addresses, src->addr);
/* TODO: do we need to take the ownership here? */
if (winsdb_addr_list_length(rec->addresses) == 0) {
rec->state = WREPL_STATE_RELEASED;
}
break;
case WREPL_TYPE_MHOMED:
winsdb_addr_list_remove(rec->addresses, src->addr);
/* TODO: do we need to take the ownership here? */
if (winsdb_addr_list_length(rec->addresses) == 0) {
rec->state = WREPL_STATE_RELEASED;
}
break;
}
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
* tombstone_interval + tombstone_timeout and not only after tombstone_timeout
* like for normal tombstone records.
* This is to replicate the record directly to the original owner,
* where the record is still active
*/
if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) == 0) {
rec->expire_time= time(NULL) + winssrv->config.tombstone_interval;
} else {
rec->state = WREPL_STATE_TOMBSTONE;
rec->expire_time= time(NULL) +
winssrv->config.tombstone_interval +
winssrv->config.tombstone_timeout;
modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
}
}
ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
if (ret != NBT_RCODE_OK) {
DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
nbt_name_string(rec, rec->name), src->addr, ret));
}
done:
/* we match w2k3 by always giving a positive reply to name releases. */
nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
}
/*
answer a name query
*/
void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
struct nbt_name_packet *packet,
struct socket_address *src)
{
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);
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);
break;
case NBT_OPCODE_RELEASE:
nbtd_winsserver_release(nbtsock, packet, src);
break;
}
}
/*
startup the WINS server, if configured
*/
NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
{
uint32_t tmp;
if (!lp_wins_support()) {
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.tombstone_interval = tmp;
tmp = lp_parm_int(-1,"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);
if (!nbtsrv->winssrv->wins_db) {
return NT_STATUS_INTERNAL_DB_ERROR;
}
irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
return NT_STATUS_OK;
}
+66
View File
@@ -0,0 +1,66 @@
/*
Unix SMB/CIFS implementation.
wins server WACK processing
Copyright (C) Stefan Metzmacher 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.
*/
struct wins_server {
/* wins server database handle */
struct winsdb_handle *wins_db;
/* some configuration */
struct {
/*
* the interval (in secs) till an active record will be marked as RELEASED
*/
uint32_t min_renew_interval;
uint32_t max_renew_interval;
/*
* the interval (in secs) a record remains in RELEASED state,
* before it will be marked as TOMBSTONE
* (also known as extinction interval)
*/
uint32_t tombstone_interval;
/*
* the interval (in secs) a record remains in TOMBSTONE state,
* before it will be removed from the database.
* See also 'tombstone_extra_timeout'.
* (also known as extinction timeout)
*/
uint32_t tombstone_timeout;
} config;
};
struct wins_challenge_io {
struct {
struct nbtd_server *nbtd_server;
struct event_context *event_ctx;
struct nbt_name *name;
uint32_t num_addresses;
const char **addresses;
} in;
struct {
uint32_t num_addresses;
const char **addresses;
} out;
};
#include "nbt_server/wins/winsserver_proto.h"
+381
View File
@@ -0,0 +1,381 @@
/*
Unix SMB/CIFS implementation.
"secure" wins server WACK processing
Copyright (C) Andrew Tridgell 2005
Copyright (C) Stefan Metzmacher 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 "nbt_server/wins/winsserver.h"
#include "system/time.h"
#include "libcli/composite/composite.h"
struct wins_challenge_state {
struct wins_challenge_io *io;
uint32_t current_address;
struct nbt_name_query query;
};
static void wins_challenge_handler(struct nbt_name_request *req)
{
struct composite_context *ctx = talloc_get_type(req->async.private, struct composite_context);
struct wins_challenge_state *state = talloc_get_type(ctx->private_data, struct wins_challenge_state);
ctx->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(ctx->status, NT_STATUS_IO_TIMEOUT)) {
state->current_address++;
if (state->current_address < state->io->in.num_addresses) {
struct nbtd_interface *iface;
state->query.in.dest_addr = state->io->in.addresses[state->current_address];
iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->query.in.dest_addr, True);
if (!iface) {
composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
return;
}
ZERO_STRUCT(state->query.out);
req = nbt_name_query_send(iface->nbtsock, &state->query);
composite_continue_nbt(ctx, req, wins_challenge_handler, ctx);
return;
}
}
composite_done(ctx);
}
NTSTATUS wins_challenge_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, struct wins_challenge_io *io)
{
NTSTATUS status = ctx->status;
struct wins_challenge_state *state = talloc_get_type(ctx->private_data, struct wins_challenge_state);
if (NT_STATUS_IS_OK(status)) {
io->out.num_addresses = state->query.out.num_addrs;
io->out.addresses = state->query.out.reply_addrs;
talloc_steal(mem_ctx, io->out.addresses);
} else {
ZERO_STRUCT(io->out);
}
talloc_free(ctx);
return status;
}
struct composite_context *wins_challenge_send(TALLOC_CTX *mem_ctx, struct wins_challenge_io *io)
{
struct composite_context *result;
struct wins_challenge_state *state;
struct nbt_name_request *req;
struct nbtd_interface *iface;
result = talloc_zero(mem_ctx, struct composite_context);
if (result == NULL) return NULL;
result->state = COMPOSITE_STATE_IN_PROGRESS;
result->event_ctx = talloc_reference(result, io->in.event_ctx);
state = talloc_zero(result, struct wins_challenge_state);
if (state == NULL) goto failed;
result->private_data = state;
/* package up the state variables for this wack request */
state->io = io;
state->current_address = 0;
/* setup a name query to the first address */
state->query.in.name = *state->io->in.name;
state->query.in.dest_addr = state->io->in.addresses[state->current_address];
state->query.in.broadcast = False;
state->query.in.wins_lookup = True;
state->query.in.timeout = 1;
state->query.in.retries = 2;
ZERO_STRUCT(state->query.out);
iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->query.in.dest_addr, True);
if (!iface) {
goto failed;
}
req = nbt_name_query_send(iface->nbtsock, &state->query);
if (req == NULL) goto failed;
req->async.fn = wins_challenge_handler;
req->async.private = result;
return result;
failed:
talloc_free(result);
return NULL;
}
struct wins_release_demand_io {
struct {
struct nbtd_server *nbtd_server;
struct event_context *event_ctx;
struct nbt_name *name;
uint16_t nb_flags;
uint32_t num_addresses;
const char **addresses;
} in;
};
struct wins_release_demand_state {
struct wins_release_demand_io *io;
uint32_t current_address;
uint32_t addresses_left;
struct nbt_name_release release;
};
static void wins_release_demand_handler(struct nbt_name_request *req)
{
struct composite_context *ctx = talloc_get_type(req->async.private, struct composite_context);
struct wins_release_demand_state *state = talloc_get_type(ctx->private_data, struct wins_release_demand_state);
ctx->status = nbt_name_release_recv(req, state, &state->release);
/* if we timed out then try the next owner address, if any */
if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_IO_TIMEOUT)) {
state->current_address++;
state->addresses_left--;
if (state->current_address < state->io->in.num_addresses) {
struct nbtd_interface *iface;
state->release.in.dest_addr = state->io->in.addresses[state->current_address];
state->release.in.address = state->release.in.dest_addr;
state->release.in.timeout = (state->addresses_left > 1 ? 2 : 1);
state->release.in.retries = (state->addresses_left > 1 ? 0 : 2);
iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->release.in.dest_addr, True);
if (!iface) {
composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
return;
}
ZERO_STRUCT(state->release.out);
req = nbt_name_release_send(iface->nbtsock, &state->release);
composite_continue_nbt(ctx, req, wins_release_demand_handler, ctx);
return;
}
}
composite_done(ctx);
}
static NTSTATUS wins_release_demand_recv(struct composite_context *ctx,
TALLOC_CTX *mem_ctx,
struct wins_release_demand_io *io)
{
NTSTATUS status = ctx->status;
talloc_free(ctx);
return status;
}
static struct composite_context *wins_release_demand_send(TALLOC_CTX *mem_ctx, struct wins_release_demand_io *io)
{
struct composite_context *result;
struct wins_release_demand_state *state;
struct nbt_name_request *req;
struct nbtd_interface *iface;
result = talloc_zero(mem_ctx, struct composite_context);
if (result == NULL) return NULL;
result->state = COMPOSITE_STATE_IN_PROGRESS;
result->event_ctx = talloc_reference(result, io->in.event_ctx);
state = talloc_zero(result, struct wins_release_demand_state);
if (state == NULL) goto failed;
result->private_data = state;
/* package up the state variables for this wack request */
state->io = io;
state->current_address = 0;
state->addresses_left = state->io->in.num_addresses;
/*
* setup a name query to the first address
* - if we have more than one address try the first
* with 2 secs timeout and no retry
* - otherwise use 1 sec timeout (w2k3 uses 0.5 sec here)
* with 2 retries
*/
state->release.in.name = *state->io->in.name;
state->release.in.dest_addr = state->io->in.addresses[state->current_address];
state->release.in.address = state->release.in.dest_addr;
state->release.in.broadcast = False;
state->release.in.timeout = (state->addresses_left > 1 ? 2 : 1);
state->release.in.retries = (state->addresses_left > 1 ? 0 : 2);
ZERO_STRUCT(state->release.out);
iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->release.in.dest_addr, True);
if (!iface) {
goto failed;
}
req = nbt_name_release_send(iface->nbtsock, &state->release);
if (req == NULL) goto failed;
req->async.fn = wins_release_demand_handler;
req->async.private = result;
return result;
failed:
talloc_free(result);
return NULL;
}
/*
wrepl_server needs to be able to do a name query request, but some windows
servers always send the reply to port 137, regardless of the request
port. To cope with this we use a irpc request to the NBT server
which has port 137 open, and thus can receive the replies
*/
struct proxy_wins_challenge_state {
struct irpc_message *msg;
struct nbtd_proxy_wins_challenge *req;
struct wins_challenge_io io;
struct composite_context *c_req;
};
static void proxy_wins_challenge_handler(struct composite_context *c_req)
{
NTSTATUS status;
uint32_t i;
struct proxy_wins_challenge_state *s = talloc_get_type(c_req->async.private_data,
struct proxy_wins_challenge_state);
status = wins_challenge_recv(s->c_req, s, &s->io);
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCT(s->req->out);
irpc_send_reply(s->msg, status);
return;
}
s->req->out.num_addrs = s->io.out.num_addresses;
/* TODO: fix pidl to handle inline ipv4address arrays */
s->req->out.addrs = talloc_array(s->msg, struct nbtd_proxy_wins_addr,
s->io.out.num_addresses);
if (!s->req->out.addrs) {
ZERO_STRUCT(s->req->out);
irpc_send_reply(s->msg, NT_STATUS_NO_MEMORY);
return;
}
for (i=0; i < s->io.out.num_addresses; i++) {
s->req->out.addrs[i].addr = talloc_steal(s->req->out.addrs, s->io.out.addresses[i]);
}
irpc_send_reply(s->msg, status);
}
NTSTATUS nbtd_proxy_wins_challenge(struct irpc_message *msg,
struct nbtd_proxy_wins_challenge *req)
{
struct nbtd_server *nbtd_server =
talloc_get_type(msg->private, struct nbtd_server);
struct proxy_wins_challenge_state *s;
uint32_t i;
s = talloc(msg, struct proxy_wins_challenge_state);
NT_STATUS_HAVE_NO_MEMORY(s);
s->msg = msg;
s->req = req;
s->io.in.nbtd_server = nbtd_server;
s->io.in.event_ctx = msg->ev;
s->io.in.name = &req->in.name;
s->io.in.num_addresses = req->in.num_addrs;
s->io.in.addresses = talloc_array(s, const char *, req->in.num_addrs);
NT_STATUS_HAVE_NO_MEMORY(s->io.in.addresses);
/* TODO: fix pidl to handle inline ipv4address arrays */
for (i=0; i < req->in.num_addrs; i++) {
s->io.in.addresses[i] = talloc_steal(s->io.in.addresses, req->in.addrs[i].addr);
}
s->c_req = wins_challenge_send(s, &s->io);
NT_STATUS_HAVE_NO_MEMORY(s->c_req);
s->c_req->async.fn = proxy_wins_challenge_handler;
s->c_req->async.private_data = s;
msg->defer_reply = True;
return NT_STATUS_OK;
}
/*
wrepl_server needs to be able to do a name release demands, but some windows
servers always send the reply to port 137, regardless of the request
port. To cope with this we use a irpc request to the NBT server
which has port 137 open, and thus can receive the replies
*/
struct proxy_wins_release_demand_state {
struct irpc_message *msg;
struct nbtd_proxy_wins_release_demand *req;
struct wins_release_demand_io io;
struct composite_context *c_req;
};
static void proxy_wins_release_demand_handler(struct composite_context *c_req)
{
NTSTATUS status;
struct proxy_wins_release_demand_state *s = talloc_get_type(c_req->async.private_data,
struct proxy_wins_release_demand_state);
status = wins_release_demand_recv(s->c_req, s, &s->io);
irpc_send_reply(s->msg, status);
}
NTSTATUS nbtd_proxy_wins_release_demand(struct irpc_message *msg,
struct nbtd_proxy_wins_release_demand *req)
{
struct nbtd_server *nbtd_server =
talloc_get_type(msg->private, struct nbtd_server);
struct proxy_wins_release_demand_state *s;
uint32_t i;
s = talloc(msg, struct proxy_wins_release_demand_state);
NT_STATUS_HAVE_NO_MEMORY(s);
s->msg = msg;
s->req = req;
s->io.in.nbtd_server = nbtd_server;
s->io.in.event_ctx = msg->ev;
s->io.in.name = &req->in.name;
s->io.in.num_addresses = req->in.num_addrs;
s->io.in.addresses = talloc_array(s, const char *, req->in.num_addrs);
NT_STATUS_HAVE_NO_MEMORY(s->io.in.addresses);
/* TODO: fix pidl to handle inline ipv4address arrays */
for (i=0; i < req->in.num_addrs; i++) {
s->io.in.addresses[i] = talloc_steal(s->io.in.addresses, req->in.addrs[i].addr);
}
s->c_req = wins_release_demand_send(s, &s->io);
NT_STATUS_HAVE_NO_MEMORY(s->c_req);
s->c_req->async.fn = proxy_wins_release_demand_handler;
s->c_req->async.private_data = s;
msg->defer_reply = True;
return NT_STATUS_OK;
}