wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# WREPL server subsystem
|
||||
|
||||
#######################
|
||||
# Start SUBSYSTEM WREPL_SRV
|
||||
[MODULE::WREPL_SRV]
|
||||
INIT_FUNCTION = server_service_wrepl_init
|
||||
SUBSYSTEM = service
|
||||
OBJ_FILES = \
|
||||
wrepl_server.o \
|
||||
wrepl_in_connection.o \
|
||||
wrepl_in_call.o \
|
||||
wrepl_apply_records.o \
|
||||
wrepl_periodic.o \
|
||||
wrepl_scavenging.o \
|
||||
wrepl_out_pull.o \
|
||||
wrepl_out_push.o \
|
||||
wrepl_out_helpers.o
|
||||
PRIVATE_PROTO_HEADER = wrepl_server_proto.h
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_WREPL WINSDB process_model
|
||||
# End SUBSYSTEM WREPL_SRV
|
||||
#######################
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "libcli/wrepl/winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "nbt_server/wins/winsdb.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "system/time.h"
|
||||
|
||||
static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wrepl_start *start = &call->req_packet.message.start;
|
||||
struct wrepl_start *start_reply = &call->rep_packet.message.start_reply;
|
||||
|
||||
if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
|
||||
/*
|
||||
*if the assoc_ctx doesn't match ignore the packet
|
||||
*/
|
||||
if ((call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx)
|
||||
&& (call->req_packet.assoc_ctx != 0)) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* it seems that we don't know all details about the start_association
|
||||
* to support replication with NT4 (it sends 1.1 instead of 5.2)
|
||||
* we ignore the version numbers until we know all details
|
||||
*/
|
||||
#if 0
|
||||
if (start->minor_version != 2 || start->major_version != 5) {
|
||||
/* w2k terminate the connection if the versions doesn't match */
|
||||
return NT_STATUS_UNKNOWN_REVISION;
|
||||
}
|
||||
#endif
|
||||
|
||||
call->wreplconn->assoc_ctx.stopped = False;
|
||||
call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_VALID_ASSOC_CTX;
|
||||
call->wreplconn->assoc_ctx.peer_ctx = start->assoc_ctx;
|
||||
|
||||
call->rep_packet.mess_type = WREPL_START_ASSOCIATION_REPLY;
|
||||
start_reply->assoc_ctx = call->wreplconn->assoc_ctx.our_ctx;
|
||||
start_reply->minor_version = 2;
|
||||
start_reply->major_version = 5;
|
||||
|
||||
/*
|
||||
* nt4 uses 41 bytes for the start_association call
|
||||
* so do it the same and as we don't know the meanings of this bytes
|
||||
* we just send zeros and nt4, w2k and w2k3 seems to be happy with this
|
||||
*
|
||||
* if we don't do this nt4 uses an old version of the wins replication protocol
|
||||
* and that would break nt4 <-> samba replication
|
||||
*/
|
||||
call->rep_packet.padding = data_blob_talloc(call, NULL, 21);
|
||||
NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
|
||||
|
||||
memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wrepl_stop *stop_out = &call->rep_packet.message.stop;
|
||||
|
||||
call->wreplconn->assoc_ctx.stopped = True;
|
||||
|
||||
call->rep_packet.mess_type = WREPL_STOP_ASSOCIATION;
|
||||
stop_out->reason = 4;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
|
||||
{
|
||||
/*
|
||||
* w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
|
||||
*/
|
||||
if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
|
||||
/*
|
||||
*if the assoc_ctx doesn't match ignore the packet
|
||||
*/
|
||||
if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
/* when the opcode bits are set the connection should be directly terminated */
|
||||
return NT_STATUS_CONNECTION_RESET;
|
||||
}
|
||||
|
||||
if (call->wreplconn->assoc_ctx.stopped) {
|
||||
/* this causes the connection to be directly terminated */
|
||||
return NT_STATUS_CONNECTION_RESET;
|
||||
}
|
||||
|
||||
/* this will cause to not receive packets anymore and terminate the connection if the reply is send */
|
||||
call->terminate_after_send = True;
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wreplsrv_service *service = call->wreplconn->service;
|
||||
struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
|
||||
struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
|
||||
|
||||
repl_out->command = WREPL_REPL_TABLE_REPLY;
|
||||
|
||||
return wreplsrv_fill_wrepl_table(service, call, table_out,
|
||||
service->wins_db->local_owner, True);
|
||||
}
|
||||
|
||||
static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
|
||||
struct wrepl_wins_name *n2)
|
||||
{
|
||||
if (n1->id < n2->id) return -1;
|
||||
if (n1->id > n2->id) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
|
||||
struct wrepl_wins_name *name,
|
||||
struct winsdb_record *rec)
|
||||
{
|
||||
uint32_t num_ips, i;
|
||||
struct wrepl_ip *ips;
|
||||
|
||||
name->name = rec->name;
|
||||
talloc_steal(mem_ctx, rec->name);
|
||||
|
||||
name->id = rec->version;
|
||||
name->unknown = "255.255.255.255";
|
||||
|
||||
name->flags = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
|
||||
|
||||
switch (name->flags & 2) {
|
||||
case 0:
|
||||
name->addresses.ip = rec->addresses[0]->address;
|
||||
talloc_steal(mem_ctx, rec->addresses[0]->address);
|
||||
break;
|
||||
case 2:
|
||||
num_ips = winsdb_addr_list_length(rec->addresses);
|
||||
ips = talloc_array(mem_ctx, struct wrepl_ip, num_ips);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ips);
|
||||
|
||||
for (i = 0; i < num_ips; i++) {
|
||||
ips[i].owner = rec->addresses[i]->wins_owner;
|
||||
talloc_steal(ips, rec->addresses[i]->wins_owner);
|
||||
ips[i].ip = rec->addresses[i]->address;
|
||||
talloc_steal(ips, rec->addresses[i]->address);
|
||||
}
|
||||
|
||||
name->addresses.addresses.num_ips = num_ips;
|
||||
name->addresses.addresses.ips = ips;
|
||||
break;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wreplsrv_service *service = call->wreplconn->service;
|
||||
struct wrepl_wins_owner *owner_in = &call->req_packet.message.replication.info.owner;
|
||||
struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
|
||||
struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
|
||||
struct wreplsrv_owner *owner;
|
||||
const char *owner_filter;
|
||||
const char *filter;
|
||||
struct ldb_result *res = NULL;
|
||||
int ret;
|
||||
struct wrepl_wins_name *names;
|
||||
struct winsdb_record *rec;
|
||||
NTSTATUS status;
|
||||
uint32_t i, j;
|
||||
time_t now = time(NULL);
|
||||
|
||||
owner = wreplsrv_find_owner(service, service->table, owner_in->address);
|
||||
|
||||
repl_out->command = WREPL_REPL_SEND_REPLY;
|
||||
reply_out->num_names = 0;
|
||||
reply_out->names = NULL;
|
||||
|
||||
/*
|
||||
* if we didn't know this owner, must be a bug in the partners client code...
|
||||
* return an empty list.
|
||||
*/
|
||||
if (!owner) {
|
||||
DEBUG(2,("WINSREPL:reply [0] records unknown owner[%s] to partner[%s]\n",
|
||||
owner_in->address, call->wreplconn->partner->address));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* the client sends a max_version of 0, interpret it as
|
||||
* (uint64_t)-1
|
||||
*/
|
||||
if (owner_in->max_version == 0) {
|
||||
owner_in->max_version = (uint64_t)-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the partner ask for nothing, or give invalid ranges,
|
||||
* return an empty list.
|
||||
*/
|
||||
if (owner_in->min_version > owner_in->max_version) {
|
||||
DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
|
||||
owner_in->address,
|
||||
(long long)owner_in->min_version,
|
||||
(long long)owner_in->max_version,
|
||||
call->wreplconn->partner->address));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the partner has already all records for nothing, or give invalid ranges,
|
||||
* return an empty list.
|
||||
*/
|
||||
if (owner_in->min_version > owner->owner.max_version) {
|
||||
DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
|
||||
owner_in->address,
|
||||
(long long)owner_in->min_version,
|
||||
(long long)owner_in->max_version,
|
||||
call->wreplconn->partner->address));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
owner_filter = wreplsrv_owner_filter(service, call, owner->owner.address);
|
||||
NT_STATUS_HAVE_NO_MEMORY(owner_filter);
|
||||
filter = talloc_asprintf(call,
|
||||
"(&%s(objectClass=winsRecord)"
|
||||
"(|(recordState=%u)(recordState=%u))"
|
||||
"(versionID>=%llu)(versionID<=%llu))",
|
||||
owner_filter,
|
||||
WREPL_STATE_ACTIVE, WREPL_STATE_TOMBSTONE,
|
||||
(long long)owner_in->min_version,
|
||||
(long long)owner_in->max_version);
|
||||
NT_STATUS_HAVE_NO_MEMORY(filter);
|
||||
ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
|
||||
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
talloc_steal(call, res);
|
||||
DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
|
||||
|
||||
if (res->count == 0) {
|
||||
DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
|
||||
res->count, owner_in->address,
|
||||
(long long)owner_in->min_version,
|
||||
(long long)owner_in->max_version,
|
||||
call->wreplconn->partner->address));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
names = talloc_array(call, struct wrepl_wins_name, res->count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(names);
|
||||
|
||||
for (i=0, j=0; i < res->count; i++) {
|
||||
status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/*
|
||||
* it's possible that winsdb_record() made the record RELEASED
|
||||
* because it's expired, but in the database it's still stored
|
||||
* as ACTIVE...
|
||||
*
|
||||
* make sure we really only replicate ACTIVE and TOMBSTONE records
|
||||
*/
|
||||
if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
|
||||
status = wreplsrv_record2wins_name(names, &names[j], rec);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
j++;
|
||||
}
|
||||
|
||||
talloc_free(rec);
|
||||
talloc_free(res->msgs[i]);
|
||||
}
|
||||
|
||||
/* sort the names before we send them */
|
||||
qsort(names, j, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
|
||||
|
||||
DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
|
||||
j, owner_in->address,
|
||||
(long long)owner_in->min_version,
|
||||
(long long)owner_in->max_version,
|
||||
call->wreplconn->partner->address));
|
||||
|
||||
reply_out->num_names = j;
|
||||
reply_out->names = names;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct wreplsrv_in_update_state {
|
||||
struct wreplsrv_in_connection *wrepl_in;
|
||||
struct wreplsrv_out_connection *wrepl_out;
|
||||
struct composite_context *creq;
|
||||
struct wreplsrv_pull_cycle_io cycle_io;
|
||||
};
|
||||
|
||||
static void wreplsrv_in_update_handler(struct composite_context *creq)
|
||||
{
|
||||
struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
|
||||
struct wreplsrv_in_update_state);
|
||||
NTSTATUS status;
|
||||
|
||||
status = wreplsrv_pull_cycle_recv(creq);
|
||||
|
||||
talloc_free(update_state->wrepl_out);
|
||||
|
||||
wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
|
||||
struct wreplsrv_out_connection *wrepl_out;
|
||||
struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
|
||||
struct wreplsrv_in_update_state *update_state;
|
||||
uint16_t fde_flags;
|
||||
|
||||
DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
|
||||
call->wreplconn->partner->address,
|
||||
update_in->initiator, update_in->partner_count));
|
||||
|
||||
/*
|
||||
* we need to flip the connection into a client connection
|
||||
* and do a WREPL_REPL_SEND_REQUEST's on the that connection
|
||||
* and then stop this connection
|
||||
*/
|
||||
fde_flags = event_get_fd_flags(wrepl_in->conn->event.fde);
|
||||
talloc_free(wrepl_in->conn->event.fde);
|
||||
wrepl_in->conn->event.fde = NULL;
|
||||
|
||||
update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(update_state);
|
||||
|
||||
wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
|
||||
NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
|
||||
wrepl_out->service = wrepl_in->service;
|
||||
wrepl_out->partner = wrepl_in->partner;
|
||||
wrepl_out->assoc_ctx.our_ctx = wrepl_in->assoc_ctx.our_ctx;
|
||||
wrepl_out->assoc_ctx.peer_ctx = wrepl_in->assoc_ctx.peer_ctx;
|
||||
wrepl_out->sock = wrepl_socket_merge(wrepl_out,
|
||||
wrepl_in->conn->event.ctx,
|
||||
wrepl_in->conn->socket,
|
||||
wrepl_in->packet);
|
||||
NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
|
||||
|
||||
event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
|
||||
|
||||
update_state->wrepl_in = wrepl_in;
|
||||
update_state->wrepl_out = wrepl_out;
|
||||
update_state->cycle_io.in.partner = wrepl_out->partner;
|
||||
update_state->cycle_io.in.num_owners = update_in->partner_count;
|
||||
update_state->cycle_io.in.owners = update_in->partners;
|
||||
talloc_steal(update_state, update_in->partners);
|
||||
update_state->cycle_io.in.wreplconn = wrepl_out;
|
||||
update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
|
||||
if (!update_state->creq) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
update_state->creq->async.fn = wreplsrv_in_update_handler;
|
||||
update_state->creq->async.private_data = update_state;
|
||||
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
|
||||
{
|
||||
return wreplsrv_in_update(call);
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
|
||||
|
||||
DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
|
||||
call->wreplconn->partner->address,
|
||||
inform_in->initiator, inform_in->partner_count));
|
||||
|
||||
wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
|
||||
|
||||
/* we don't reply to WREPL_REPL_INFORM messages */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
|
||||
{
|
||||
return wreplsrv_in_inform(call);
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wrepl_replication *repl_in = &call->req_packet.message.replication;
|
||||
NTSTATUS status;
|
||||
|
||||
/*
|
||||
* w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
|
||||
*/
|
||||
if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
|
||||
/*
|
||||
*if the assoc_ctx doesn't match ignore the packet
|
||||
*/
|
||||
if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (!call->wreplconn->partner) {
|
||||
struct socket_address *partner_ip = socket_get_peer_addr(call->wreplconn->conn->socket, call);
|
||||
|
||||
call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, partner_ip->addr);
|
||||
if (!call->wreplconn->partner) {
|
||||
DEBUG(1,("Failing WINS replication from non-partner %s\n",
|
||||
partner_ip ? partner_ip->addr : NULL));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
}
|
||||
|
||||
switch (repl_in->command) {
|
||||
case WREPL_REPL_TABLE_QUERY:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
|
||||
DEBUG(2,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_table_query(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPL_TABLE_REPLY:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
case WREPL_REPL_SEND_REQUEST:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
|
||||
DEBUG(2,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_send_request(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPL_SEND_REPLY:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
case WREPL_REPL_UPDATE:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
|
||||
DEBUG(2,("Failing WINS replication UPDATE from non-pull-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_update(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPL_UPDATE2:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
|
||||
DEBUG(2,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_update2(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPL_INFORM:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
|
||||
DEBUG(2,("Failing WINS replication INFORM from non-pull-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_inform(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPL_INFORM2:
|
||||
if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
|
||||
DEBUG(2,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
|
||||
call->wreplconn->partner->address));
|
||||
return wreplsrv_in_stop_assoc_ctx(call);
|
||||
}
|
||||
status = wreplsrv_in_inform2(call);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
call->rep_packet.mess_type = WREPL_REPLICATION;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
|
||||
{
|
||||
struct wrepl_start *start = &call->rep_packet.message.start;
|
||||
|
||||
call->rep_packet.opcode = 0x00008583;
|
||||
call->rep_packet.assoc_ctx = 0;
|
||||
call->rep_packet.mess_type = WREPL_START_ASSOCIATION;
|
||||
|
||||
start->assoc_ctx = 0x0000000a;
|
||||
start->minor_version = 0x0001;
|
||||
start->major_version = 0x0000;
|
||||
|
||||
call->rep_packet.padding = data_blob_talloc(call, NULL, 4);
|
||||
memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
|
||||
&& (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
|
||||
return wreplsrv_in_invalid_assoc_ctx(call);
|
||||
}
|
||||
|
||||
switch (call->req_packet.mess_type) {
|
||||
case WREPL_START_ASSOCIATION:
|
||||
status = wreplsrv_in_start_association(call);
|
||||
break;
|
||||
case WREPL_START_ASSOCIATION_REPLY:
|
||||
/* this is not valid here, so we ignore it */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
case WREPL_STOP_ASSOCIATION:
|
||||
status = wreplsrv_in_stop_association(call);
|
||||
break;
|
||||
|
||||
case WREPL_REPLICATION:
|
||||
status = wreplsrv_in_replication(call);
|
||||
break;
|
||||
default:
|
||||
/* everythingelse is also not valid here, so we ignore it */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
|
||||
return wreplsrv_in_invalid_assoc_ctx(call);
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
/* let the backend to set some of the opcode bits, but always add the standards */
|
||||
call->rep_packet.opcode |= WREPL_OPCODE_BITS;
|
||||
call->rep_packet.assoc_ctx = call->wreplconn->assoc_ctx.peer_ctx;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "smbd/service.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "smbd/process_model.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/netif.h"
|
||||
|
||||
void wreplsrv_terminate_in_connection(struct wreplsrv_in_connection *wreplconn, const char *reason)
|
||||
{
|
||||
stream_terminate_connection(wreplconn->conn, reason);
|
||||
}
|
||||
|
||||
static int terminate_after_send_destructor(struct wreplsrv_in_connection **tas)
|
||||
{
|
||||
wreplsrv_terminate_in_connection(*tas, "wreplsrv_in_connection: terminate_after_send");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
receive some data on a WREPL connection
|
||||
*/
|
||||
static NTSTATUS wreplsrv_recv_request(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct wreplsrv_in_connection *wreplconn = talloc_get_type(private, struct wreplsrv_in_connection);
|
||||
struct wreplsrv_in_call *call;
|
||||
DATA_BLOB packet_in_blob;
|
||||
DATA_BLOB packet_out_blob;
|
||||
struct wrepl_wrap packet_out_wrap;
|
||||
NTSTATUS status;
|
||||
|
||||
call = talloc_zero(wreplconn, struct wreplsrv_in_call);
|
||||
NT_STATUS_HAVE_NO_MEMORY(call);
|
||||
call->wreplconn = wreplconn;
|
||||
talloc_steal(call, blob.data);
|
||||
|
||||
packet_in_blob.data = blob.data + 4;
|
||||
packet_in_blob.length = blob.length - 4;
|
||||
|
||||
status = ndr_pull_struct_blob(&packet_in_blob, call, &call->req_packet,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (DEBUGLVL(10)) {
|
||||
DEBUG(10,("Received WINS-Replication packet of length %u\n",
|
||||
(unsigned)packet_in_blob.length + 4));
|
||||
NDR_PRINT_DEBUG(wrepl_packet, &call->req_packet);
|
||||
}
|
||||
|
||||
status = wreplsrv_in_call(call);
|
||||
NT_STATUS_IS_ERR_RETURN(status);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/* w2k just ignores invalid packets, so we do */
|
||||
DEBUG(10,("Received WINS-Replication packet was invalid, we just ignore it\n"));
|
||||
talloc_free(call);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* and now encode the reply */
|
||||
packet_out_wrap.packet = call->rep_packet;
|
||||
status = ndr_push_struct_blob(&packet_out_blob, call, &packet_out_wrap,
|
||||
(ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (DEBUGLVL(10)) {
|
||||
DEBUG(10,("Sending WINS-Replication packet of length %d\n", (int)packet_out_blob.length));
|
||||
NDR_PRINT_DEBUG(wrepl_packet, &call->rep_packet);
|
||||
}
|
||||
|
||||
if (call->terminate_after_send) {
|
||||
struct wreplsrv_in_connection **tas;
|
||||
tas = talloc(packet_out_blob.data, struct wreplsrv_in_connection *);
|
||||
NT_STATUS_HAVE_NO_MEMORY(tas);
|
||||
*tas = wreplconn;
|
||||
talloc_set_destructor(tas, terminate_after_send_destructor);
|
||||
}
|
||||
|
||||
status = packet_send(wreplconn->packet, packet_out_blob);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
talloc_free(call);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
called when the socket becomes readable
|
||||
*/
|
||||
static void wreplsrv_recv(struct stream_connection *conn, uint16_t flags)
|
||||
{
|
||||
struct wreplsrv_in_connection *wreplconn = talloc_get_type(conn->private,
|
||||
struct wreplsrv_in_connection);
|
||||
|
||||
packet_recv(wreplconn->packet);
|
||||
}
|
||||
|
||||
/*
|
||||
called when the socket becomes writable
|
||||
*/
|
||||
static void wreplsrv_send(struct stream_connection *conn, uint16_t flags)
|
||||
{
|
||||
struct wreplsrv_in_connection *wreplconn = talloc_get_type(conn->private,
|
||||
struct wreplsrv_in_connection);
|
||||
packet_queue_run(wreplconn->packet);
|
||||
}
|
||||
|
||||
/*
|
||||
handle socket recv errors
|
||||
*/
|
||||
static void wreplsrv_recv_error(void *private, NTSTATUS status)
|
||||
{
|
||||
struct wreplsrv_in_connection *wreplconn = talloc_get_type(private,
|
||||
struct wreplsrv_in_connection);
|
||||
wreplsrv_terminate_in_connection(wreplconn, nt_errstr(status));
|
||||
}
|
||||
|
||||
/*
|
||||
called when we get a new connection
|
||||
*/
|
||||
static void wreplsrv_accept(struct stream_connection *conn)
|
||||
{
|
||||
struct wreplsrv_service *service = talloc_get_type(conn->private, struct wreplsrv_service);
|
||||
struct wreplsrv_in_connection *wreplconn;
|
||||
struct socket_address *peer_ip;
|
||||
|
||||
wreplconn = talloc_zero(conn, struct wreplsrv_in_connection);
|
||||
if (!wreplconn) {
|
||||
stream_terminate_connection(conn, "wreplsrv_accept: out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
wreplconn->packet = packet_init(wreplconn);
|
||||
if (!wreplconn->packet) {
|
||||
wreplsrv_terminate_in_connection(wreplconn, "wreplsrv_accept: out of memory");
|
||||
return;
|
||||
}
|
||||
packet_set_private(wreplconn->packet, wreplconn);
|
||||
packet_set_socket(wreplconn->packet, conn->socket);
|
||||
packet_set_callback(wreplconn->packet, wreplsrv_recv_request);
|
||||
packet_set_full_request(wreplconn->packet, packet_full_request_u32);
|
||||
packet_set_error_handler(wreplconn->packet, wreplsrv_recv_error);
|
||||
packet_set_event_context(wreplconn->packet, conn->event.ctx);
|
||||
packet_set_fde(wreplconn->packet, conn->event.fde);
|
||||
packet_set_serialise(wreplconn->packet);
|
||||
|
||||
wreplconn->conn = conn;
|
||||
wreplconn->service = service;
|
||||
|
||||
peer_ip = socket_get_peer_addr(conn->socket, wreplconn);
|
||||
if (!peer_ip) {
|
||||
wreplsrv_terminate_in_connection(wreplconn, "wreplsrv_accept: could not obtain peer IP from kernel");
|
||||
return;
|
||||
}
|
||||
|
||||
wreplconn->partner = wreplsrv_find_partner(service, peer_ip->addr);
|
||||
|
||||
conn->private = wreplconn;
|
||||
|
||||
irpc_add_name(conn->msg_ctx, "wreplsrv_connection");
|
||||
}
|
||||
|
||||
static const struct stream_server_ops wreplsrv_stream_ops = {
|
||||
.name = "wreplsrv",
|
||||
.accept_connection = wreplsrv_accept,
|
||||
.recv_handler = wreplsrv_recv,
|
||||
.send_handler = wreplsrv_send,
|
||||
};
|
||||
|
||||
/*
|
||||
called when we get a new connection
|
||||
*/
|
||||
NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
|
||||
struct socket_context *sock,
|
||||
struct packet_context *packet,
|
||||
struct wreplsrv_in_connection **_wrepl_in)
|
||||
{
|
||||
struct wreplsrv_service *service = partner->service;
|
||||
struct wreplsrv_in_connection *wrepl_in;
|
||||
const struct model_ops *model_ops;
|
||||
struct stream_connection *conn;
|
||||
NTSTATUS status;
|
||||
|
||||
/* within the wrepl task we want to be a single process, so
|
||||
ask for the single process model ops and pass these to the
|
||||
stream_setup_socket() call. */
|
||||
model_ops = process_model_byname("single");
|
||||
if (!model_ops) {
|
||||
DEBUG(0,("Can't find 'single' process model_ops"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
wrepl_in = talloc_zero(partner, struct wreplsrv_in_connection);
|
||||
NT_STATUS_HAVE_NO_MEMORY(wrepl_in);
|
||||
|
||||
wrepl_in->service = service;
|
||||
wrepl_in->partner = partner;
|
||||
|
||||
status = stream_new_connection_merge(service->task->event_ctx, model_ops,
|
||||
sock, &wreplsrv_stream_ops, service->task->msg_ctx,
|
||||
wrepl_in, &conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/*
|
||||
* make the wreplsrv_in_connection structure a child of the
|
||||
* stream_connection, to match the hierachie of wreplsrv_accept
|
||||
*/
|
||||
wrepl_in->conn = conn;
|
||||
talloc_steal(conn, wrepl_in);
|
||||
|
||||
/*
|
||||
* now update the packet handling callback,...
|
||||
*/
|
||||
wrepl_in->packet = talloc_steal(wrepl_in, packet);
|
||||
packet_set_private(wrepl_in->packet, wrepl_in);
|
||||
packet_set_socket(wrepl_in->packet, conn->socket);
|
||||
packet_set_callback(wrepl_in->packet, wreplsrv_recv_request);
|
||||
packet_set_full_request(wrepl_in->packet, packet_full_request_u32);
|
||||
packet_set_error_handler(wrepl_in->packet, wreplsrv_recv_error);
|
||||
packet_set_event_context(wrepl_in->packet, conn->event.ctx);
|
||||
packet_set_fde(wrepl_in->packet, conn->event.fde);
|
||||
packet_set_serialise(wrepl_in->packet);
|
||||
|
||||
*_wrepl_in = wrepl_in;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
startup the wrepl port 42 server sockets
|
||||
*/
|
||||
NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct task_server *task = service->task;
|
||||
const struct model_ops *model_ops;
|
||||
const char *address;
|
||||
uint16_t port = WINS_REPLICATION_PORT;
|
||||
|
||||
/* within the wrepl task we want to be a single process, so
|
||||
ask for the single process model ops and pass these to the
|
||||
stream_setup_socket() call. */
|
||||
model_ops = process_model_byname("single");
|
||||
if (!model_ops) {
|
||||
DEBUG(0,("Can't find 'single' process model_ops"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (lp_interfaces() && lp_bind_interfaces_only()) {
|
||||
int num_interfaces = iface_count();
|
||||
int i;
|
||||
|
||||
/* We have been given an interfaces line, and been
|
||||
told to only bind to those interfaces. Create a
|
||||
socket per interface and bind to only these.
|
||||
*/
|
||||
for(i = 0; i < num_interfaces; i++) {
|
||||
address = iface_n_ip(i);
|
||||
status = stream_setup_socket(task->event_ctx, model_ops, &wreplsrv_stream_ops,
|
||||
"ipv4", address, &port, service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
|
||||
address, port, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
address = lp_socket_address();
|
||||
status = stream_setup_socket(task->event_ctx, model_ops, &wreplsrv_stream_ops,
|
||||
"ipv4", address, &port, service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
|
||||
address, port, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 wreplsrv_pull_cycle_io {
|
||||
struct {
|
||||
struct wreplsrv_partner *partner;
|
||||
uint32_t num_owners;
|
||||
struct wrepl_wins_owner *owners;
|
||||
struct wreplsrv_out_connection *wreplconn;
|
||||
} in;
|
||||
};
|
||||
|
||||
struct wreplsrv_push_notify_io {
|
||||
struct {
|
||||
struct wreplsrv_partner *partner;
|
||||
BOOL inform;
|
||||
BOOL propagate;
|
||||
} in;
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "librpc/gen_ndr/winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
|
||||
static void wreplsrv_out_pull_reschedule(struct wreplsrv_partner *partner, uint32_t interval)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
partner->pull.next_run = timeval_current_ofs(interval, 0);
|
||||
status = wreplsrv_periodic_schedule(partner->service, interval);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("wreplsrv_periodic_schedule() failed\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void wreplsrv_pull_handler_creq(struct composite_context *creq)
|
||||
{
|
||||
struct wreplsrv_partner *partner = talloc_get_type(creq->async.private_data, struct wreplsrv_partner);
|
||||
struct wreplsrv_pull_cycle_io *old_cycle_io;
|
||||
struct wrepl_table inform_in;
|
||||
|
||||
partner->pull.last_status = wreplsrv_pull_cycle_recv(partner->pull.creq);
|
||||
partner->pull.creq = NULL;
|
||||
|
||||
old_cycle_io = partner->pull.cycle_io;
|
||||
partner->pull.cycle_io = NULL;
|
||||
|
||||
if (NT_STATUS_IS_OK(partner->pull.last_status)) {
|
||||
partner->pull.error_count = 0;
|
||||
DEBUG(2,("wreplsrv_pull_cycle(%s): %s\n",
|
||||
partner->address, nt_errstr(partner->pull.last_status)));
|
||||
goto done;
|
||||
}
|
||||
|
||||
partner->pull.error_count++;
|
||||
|
||||
if (partner->pull.error_count > 1) {
|
||||
uint32_t retry_interval;
|
||||
|
||||
retry_interval = partner->pull.error_count * partner->pull.retry_interval;
|
||||
retry_interval = MIN(retry_interval, partner->pull.interval);
|
||||
|
||||
DEBUG(1,("wreplsrv_pull_cycle(%s): %s: error_count: %u: reschedule(%u)\n",
|
||||
partner->address, nt_errstr(partner->pull.last_status),
|
||||
partner->pull.error_count, retry_interval));
|
||||
|
||||
wreplsrv_out_pull_reschedule(partner, retry_interval);
|
||||
goto done;
|
||||
}
|
||||
|
||||
DEBUG(1,("wreplsrv_pull_cycle(%s): %s: error_count:%u retry\n",
|
||||
partner->address, nt_errstr(partner->pull.last_status),
|
||||
partner->pull.error_count));
|
||||
inform_in.partner_count = old_cycle_io->in.num_owners;
|
||||
inform_in.partners = old_cycle_io->in.owners;
|
||||
wreplsrv_out_partner_pull(partner, &inform_in);
|
||||
done:
|
||||
talloc_free(old_cycle_io);
|
||||
}
|
||||
|
||||
void wreplsrv_out_partner_pull(struct wreplsrv_partner *partner, struct wrepl_table *inform_in)
|
||||
{
|
||||
/* there's already a pull in progress, so we're done */
|
||||
if (partner->pull.creq) return;
|
||||
|
||||
partner->pull.cycle_io = talloc(partner, struct wreplsrv_pull_cycle_io);
|
||||
if (!partner->pull.cycle_io) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
partner->pull.cycle_io->in.partner = partner;
|
||||
partner->pull.cycle_io->in.wreplconn = NULL;
|
||||
if (inform_in) {
|
||||
partner->pull.cycle_io->in.num_owners = inform_in->partner_count;
|
||||
partner->pull.cycle_io->in.owners = inform_in->partners;
|
||||
talloc_steal(partner->pull.cycle_io, inform_in->partners);
|
||||
} else {
|
||||
partner->pull.cycle_io->in.num_owners = 0;
|
||||
partner->pull.cycle_io->in.owners = NULL;
|
||||
}
|
||||
partner->pull.creq = wreplsrv_pull_cycle_send(partner->pull.cycle_io, partner->pull.cycle_io);
|
||||
if (!partner->pull.creq) {
|
||||
DEBUG(1,("wreplsrv_pull_cycle_send(%s) failed\n",
|
||||
partner->address));
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
partner->pull.creq->async.fn = wreplsrv_pull_handler_creq;
|
||||
partner->pull.creq->async.private_data = partner;
|
||||
|
||||
return;
|
||||
nomem:
|
||||
talloc_free(partner->pull.cycle_io);
|
||||
partner->pull.cycle_io = NULL;
|
||||
DEBUG(0,("wreplsrv_out_partner_pull(%s): no memory? (ignoring)\n",
|
||||
partner->address));
|
||||
return;
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_out_pull_run(struct wreplsrv_service *service)
|
||||
{
|
||||
struct wreplsrv_partner *partner;
|
||||
|
||||
for (partner = service->partners; partner; partner = partner->next) {
|
||||
/* if it's not a pull partner, go to the next partner */
|
||||
if (!(partner->type & WINSREPL_PARTNER_PULL)) continue;
|
||||
|
||||
/* if pulling is disabled for the partner, go to the next one */
|
||||
if (partner->pull.interval == 0) continue;
|
||||
|
||||
/* if the next timer isn't reached, go to the next partner */
|
||||
if (!timeval_expired(&partner->pull.next_run)) continue;
|
||||
|
||||
wreplsrv_out_pull_reschedule(partner, partner->pull.interval);
|
||||
|
||||
wreplsrv_out_partner_pull(partner, NULL);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "librpc/gen_ndr/winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "nbt_server/wins/winsdb.h"
|
||||
|
||||
static void wreplsrv_out_partner_push(struct wreplsrv_partner *partner, BOOL propagate);
|
||||
|
||||
static void wreplsrv_push_handler_creq(struct composite_context *creq)
|
||||
{
|
||||
struct wreplsrv_partner *partner = talloc_get_type(creq->async.private_data, struct wreplsrv_partner);
|
||||
struct wreplsrv_push_notify_io *old_notify_io;
|
||||
|
||||
partner->push.last_status = wreplsrv_push_notify_recv(partner->push.creq);
|
||||
partner->push.creq = NULL;
|
||||
|
||||
old_notify_io = partner->push.notify_io;
|
||||
partner->push.notify_io = NULL;
|
||||
|
||||
if (NT_STATUS_IS_OK(partner->push.last_status)) {
|
||||
partner->push.error_count = 0;
|
||||
DEBUG(2,("wreplsrv_push_notify(%s): %s\n",
|
||||
partner->address, nt_errstr(partner->push.last_status)));
|
||||
goto done;
|
||||
}
|
||||
|
||||
partner->push.error_count++;
|
||||
|
||||
if (partner->push.error_count > 1) {
|
||||
DEBUG(1,("wreplsrv_push_notify(%s): %s: error_count: %u: giving up\n",
|
||||
partner->address, nt_errstr(partner->push.last_status),
|
||||
partner->push.error_count));
|
||||
goto done;
|
||||
}
|
||||
|
||||
DEBUG(1,("wreplsrv_push_notify(%s): %s: error_count: %u: retry\n",
|
||||
partner->address, nt_errstr(partner->push.last_status),
|
||||
partner->push.error_count));
|
||||
wreplsrv_out_partner_push(partner, old_notify_io->in.propagate);
|
||||
done:
|
||||
talloc_free(old_notify_io);
|
||||
}
|
||||
|
||||
static void wreplsrv_out_partner_push(struct wreplsrv_partner *partner, BOOL propagate)
|
||||
{
|
||||
/* a push for this partner is currently in progress, so we're done */
|
||||
if (partner->push.creq) return;
|
||||
|
||||
/* now prepare the push notify */
|
||||
partner->push.notify_io = talloc(partner, struct wreplsrv_push_notify_io);
|
||||
if (!partner->push.notify_io) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
partner->push.notify_io->in.partner = partner;
|
||||
partner->push.notify_io->in.inform = partner->push.use_inform;
|
||||
partner->push.notify_io->in.propagate = propagate;
|
||||
partner->push.creq = wreplsrv_push_notify_send(partner->push.notify_io, partner->push.notify_io);
|
||||
if (!partner->push.creq) {
|
||||
DEBUG(1,("wreplsrv_push_notify_send(%s) failed nomem?\n",
|
||||
partner->address));
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
partner->push.creq->async.fn = wreplsrv_push_handler_creq;
|
||||
partner->push.creq->async.private_data = partner;
|
||||
|
||||
return;
|
||||
nomem:
|
||||
talloc_free(partner->push.notify_io);
|
||||
partner->push.notify_io = NULL;
|
||||
DEBUG(1,("wreplsrv_out_partner_push(%s,%u) failed nomem? (ignoring)\n",
|
||||
partner->address, propagate));
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t wreplsrv_calc_change_count(struct wreplsrv_partner *partner, uint64_t maxVersionID)
|
||||
{
|
||||
uint64_t tmp_diff = UINT32_MAX;
|
||||
|
||||
/* catch an overflow */
|
||||
if (partner->push.maxVersionID > maxVersionID) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
tmp_diff = maxVersionID - partner->push.maxVersionID;
|
||||
|
||||
if (tmp_diff > UINT32_MAX) {
|
||||
tmp_diff = UINT32_MAX;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
partner->push.maxVersionID = maxVersionID;
|
||||
return (uint32_t)(tmp_diff & UINT32_MAX);
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_out_push_run(struct wreplsrv_service *service)
|
||||
{
|
||||
struct wreplsrv_partner *partner;
|
||||
uint64_t seqnumber;
|
||||
uint32_t change_count;
|
||||
|
||||
seqnumber = winsdb_get_maxVersion(service->wins_db);
|
||||
|
||||
for (partner = service->partners; partner; partner = partner->next) {
|
||||
/* if it's not a push partner, go to the next partner */
|
||||
if (!(partner->type & WINSREPL_PARTNER_PUSH)) continue;
|
||||
|
||||
/* if push notifies are disabled for this partner, go to the next partner */
|
||||
if (partner->push.change_count == 0) continue;
|
||||
|
||||
/* get the actual change count for the partner */
|
||||
change_count = wreplsrv_calc_change_count(partner, seqnumber);
|
||||
|
||||
/* if the configured change count isn't reached, go to the next partner */
|
||||
if (change_count < partner->push.change_count) continue;
|
||||
|
||||
wreplsrv_out_partner_push(partner, False);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "lib/events/events.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service.h"
|
||||
#include "librpc/gen_ndr/winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
|
||||
static NTSTATUS wreplsrv_periodic_run(struct wreplsrv_service *service)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = wreplsrv_load_partners(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = wreplsrv_scavenging_run(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = wreplsrv_out_pull_run(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = wreplsrv_out_push_run(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void wreplsrv_periodic_handler_te(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *ptr)
|
||||
{
|
||||
struct wreplsrv_service *service = talloc_get_type(ptr, struct wreplsrv_service);
|
||||
NTSTATUS status;
|
||||
|
||||
service->periodic.te = NULL;
|
||||
|
||||
status = wreplsrv_periodic_schedule(service, service->config.periodic_interval);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(service->task, nt_errstr(status));
|
||||
return;
|
||||
}
|
||||
|
||||
status = wreplsrv_periodic_run(service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("wresrv_periodic_run() failed: %s\n", nt_errstr(status)));
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_periodic_schedule(struct wreplsrv_service *service, uint32_t next_interval)
|
||||
{
|
||||
TALLOC_CTX *tmp_mem;
|
||||
struct timed_event *new_te;
|
||||
struct timeval next_time;
|
||||
|
||||
/* prevent looping */
|
||||
if (next_interval == 0) next_interval = 1;
|
||||
|
||||
next_time = timeval_current_ofs(next_interval, 5000);
|
||||
|
||||
if (service->periodic.te) {
|
||||
/*
|
||||
* if the timestamp of the new event is higher,
|
||||
* as current next we don't need to reschedule
|
||||
*/
|
||||
if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the next scheduled timestamp */
|
||||
service->periodic.next_event = next_time;
|
||||
|
||||
new_te = event_add_timed(service->task->event_ctx, service,
|
||||
service->periodic.next_event,
|
||||
wreplsrv_periodic_handler_te, service);
|
||||
NT_STATUS_HAVE_NO_MEMORY(new_te);
|
||||
|
||||
tmp_mem = talloc_new(service);
|
||||
DEBUG(6,("wreplsrv_periodic_schedule(%u) %sscheduled for: %s\n",
|
||||
next_interval,
|
||||
(service->periodic.te?"re":""),
|
||||
nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
|
||||
talloc_free(tmp_mem);
|
||||
|
||||
talloc_free(service->periodic.te);
|
||||
service->periodic.te = new_te;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_setup_periodic(struct wreplsrv_service *service)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = wreplsrv_periodic_schedule(service, 0);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "librpc/gen_ndr/ndr_winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "nbt_server/wins/winsdb.h"
|
||||
#include "ldb/include/ldb.h"
|
||||
#include "ldb/include/ldb_errors.h"
|
||||
#include "system/time.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *wins_owner)
|
||||
{
|
||||
if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
|
||||
return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
|
||||
wins_owner);
|
||||
}
|
||||
|
||||
return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
|
||||
wins_owner);
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct winsdb_record *rec = NULL;
|
||||
struct ldb_result *res = NULL;
|
||||
const char *owner_filter;
|
||||
const char *filter;
|
||||
uint32_t i;
|
||||
int ret;
|
||||
time_t now = time(NULL);
|
||||
const char *now_timestr;
|
||||
const char *action;
|
||||
const char *old_state=NULL;
|
||||
const char *new_state=NULL;
|
||||
uint32_t modify_flags;
|
||||
BOOL modify_record;
|
||||
BOOL delete_record;
|
||||
BOOL delete_tombstones;
|
||||
struct timeval tombstone_extra_time;
|
||||
|
||||
now_timestr = ldb_timestring(tmp_mem, now);
|
||||
NT_STATUS_HAVE_NO_MEMORY(now_timestr);
|
||||
owner_filter = wreplsrv_owner_filter(service, tmp_mem,
|
||||
service->wins_db->local_owner);
|
||||
NT_STATUS_HAVE_NO_MEMORY(owner_filter);
|
||||
filter = talloc_asprintf(tmp_mem,
|
||||
"(&%s(objectClass=winsRecord)"
|
||||
"(expireTime<=%s))",
|
||||
owner_filter, now_timestr);
|
||||
NT_STATUS_HAVE_NO_MEMORY(filter);
|
||||
ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
|
||||
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
talloc_steal(tmp_mem, res);
|
||||
DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
|
||||
|
||||
tombstone_extra_time = timeval_add(&service->startup_time,
|
||||
service->config.tombstone_extra_timeout,
|
||||
0);
|
||||
delete_tombstones = timeval_expired(&tombstone_extra_time);
|
||||
|
||||
for (i=0; i < res->count; i++) {
|
||||
/*
|
||||
* we pass '0' as 'now' here,
|
||||
* because we want to get the raw timestamps which are in the DB
|
||||
*/
|
||||
status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
talloc_free(res->msgs[i]);
|
||||
|
||||
modify_flags = 0;
|
||||
modify_record = False;
|
||||
delete_record = False;
|
||||
|
||||
switch (rec->state) {
|
||||
case WREPL_STATE_ACTIVE:
|
||||
old_state = "active";
|
||||
new_state = "active";
|
||||
if (!rec->is_static) {
|
||||
new_state = "released";
|
||||
rec->state = WREPL_STATE_RELEASED;
|
||||
rec->expire_time= service->config.tombstone_interval + now;
|
||||
}
|
||||
modify_flags = 0;
|
||||
modify_record = True;
|
||||
break;
|
||||
|
||||
case WREPL_STATE_RELEASED:
|
||||
old_state = "released";
|
||||
new_state = "tombstone";
|
||||
rec->state = WREPL_STATE_TOMBSTONE;
|
||||
rec->expire_time= service->config.tombstone_timeout + now;
|
||||
modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
|
||||
modify_record = True;
|
||||
break;
|
||||
|
||||
case WREPL_STATE_TOMBSTONE:
|
||||
old_state = "tombstone";
|
||||
new_state = "tombstone";
|
||||
if (!delete_tombstones) break;
|
||||
new_state = "deleted";
|
||||
delete_record = True;
|
||||
break;
|
||||
|
||||
case WREPL_STATE_RESERVED:
|
||||
DEBUG(0,("%s: corrupted record: %s\n",
|
||||
__location__, nbt_name_string(rec, rec->name)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (modify_record) {
|
||||
action = "modify";
|
||||
ret = winsdb_modify(service->wins_db, rec, modify_flags);
|
||||
} else if (delete_record) {
|
||||
action = "delete";
|
||||
ret = winsdb_delete(service->wins_db, rec);
|
||||
} else {
|
||||
action = "skip";
|
||||
ret = NBT_RCODE_OK;
|
||||
}
|
||||
|
||||
if (ret != NBT_RCODE_OK) {
|
||||
DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
|
||||
} else {
|
||||
DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_state));
|
||||
}
|
||||
|
||||
talloc_free(rec);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct winsdb_record *rec = NULL;
|
||||
struct ldb_result *res = NULL;
|
||||
const char *owner_filter;
|
||||
const char *filter;
|
||||
uint32_t i;
|
||||
int ret;
|
||||
time_t now = time(NULL);
|
||||
const char *now_timestr;
|
||||
const char *action;
|
||||
const char *old_state=NULL;
|
||||
const char *new_state=NULL;
|
||||
uint32_t modify_flags;
|
||||
BOOL modify_record;
|
||||
BOOL delete_record;
|
||||
BOOL delete_tombstones;
|
||||
struct timeval tombstone_extra_time;
|
||||
|
||||
now_timestr = ldb_timestring(tmp_mem, now);
|
||||
NT_STATUS_HAVE_NO_MEMORY(now_timestr);
|
||||
owner_filter = wreplsrv_owner_filter(service, tmp_mem,
|
||||
service->wins_db->local_owner);
|
||||
NT_STATUS_HAVE_NO_MEMORY(owner_filter);
|
||||
filter = talloc_asprintf(tmp_mem,
|
||||
"(&(!%s)(objectClass=winsRecord)"
|
||||
"(!(recordState=%u))(expireTime<=%s))",
|
||||
owner_filter, WREPL_STATE_ACTIVE, now_timestr);
|
||||
NT_STATUS_HAVE_NO_MEMORY(filter);
|
||||
ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
|
||||
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
talloc_steal(tmp_mem, res);
|
||||
DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
|
||||
|
||||
tombstone_extra_time = timeval_add(&service->startup_time,
|
||||
service->config.tombstone_extra_timeout,
|
||||
0);
|
||||
delete_tombstones = timeval_expired(&tombstone_extra_time);
|
||||
|
||||
for (i=0; i < res->count; i++) {
|
||||
/*
|
||||
* we pass '0' as 'now' here,
|
||||
* because we want to get the raw timestamps which are in the DB
|
||||
*/
|
||||
status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
talloc_free(res->msgs[i]);
|
||||
|
||||
modify_flags = 0;
|
||||
modify_record = False;
|
||||
delete_record = False;
|
||||
|
||||
switch (rec->state) {
|
||||
case WREPL_STATE_ACTIVE:
|
||||
DEBUG(0,("%s: corrupted record: %s\n",
|
||||
__location__, nbt_name_string(rec, rec->name)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
|
||||
case WREPL_STATE_RELEASED:
|
||||
old_state = "released";
|
||||
new_state = "tombstone";
|
||||
rec->state = WREPL_STATE_TOMBSTONE;
|
||||
rec->expire_time= service->config.tombstone_timeout + now;
|
||||
modify_flags = 0;
|
||||
modify_record = True;
|
||||
break;
|
||||
|
||||
case WREPL_STATE_TOMBSTONE:
|
||||
old_state = "tombstone";
|
||||
new_state = "tombstone";
|
||||
if (!delete_tombstones) break;
|
||||
new_state = "deleted";
|
||||
delete_record = True;
|
||||
break;
|
||||
|
||||
case WREPL_STATE_RESERVED:
|
||||
DEBUG(0,("%s: corrupted record: %s\n",
|
||||
__location__, nbt_name_string(rec, rec->name)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (modify_record) {
|
||||
action = "modify";
|
||||
ret = winsdb_modify(service->wins_db, rec, modify_flags);
|
||||
} else if (delete_record) {
|
||||
action = "delete";
|
||||
ret = winsdb_delete(service->wins_db, rec);
|
||||
} else {
|
||||
action = "skip";
|
||||
ret = NBT_RCODE_OK;
|
||||
}
|
||||
|
||||
if (ret != NBT_RCODE_OK) {
|
||||
DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
|
||||
} else {
|
||||
DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_state));
|
||||
}
|
||||
|
||||
talloc_free(rec);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct verify_state {
|
||||
struct messaging_context *msg_ctx;
|
||||
struct wreplsrv_service *service;
|
||||
struct winsdb_record *rec;
|
||||
struct nbtd_proxy_wins_challenge r;
|
||||
};
|
||||
|
||||
static void verify_handler(struct irpc_request *ireq)
|
||||
{
|
||||
struct verify_state *s = talloc_get_type(ireq->async.private,
|
||||
struct verify_state);
|
||||
struct winsdb_record *rec = s->rec;
|
||||
const char *action;
|
||||
const char *old_state = "active";
|
||||
const char *new_state = "active";
|
||||
const char *new_owner = "replica";
|
||||
uint32_t modify_flags = 0;
|
||||
BOOL modify_record = False;
|
||||
BOOL delete_record = False;
|
||||
BOOL different = False;
|
||||
int ret;
|
||||
NTSTATUS status;
|
||||
uint32_t i, j;
|
||||
|
||||
/*
|
||||
* - if the name isn't present anymore remove our record
|
||||
* - if the name is found and not a normal group check if the addresses match,
|
||||
* - if they don't match remove the record
|
||||
* - if they match do nothing
|
||||
* - if an error happens do nothing
|
||||
*/
|
||||
status = irpc_call_recv(ireq);
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
|
||||
delete_record = True;
|
||||
new_state = "deleted";
|
||||
} else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
|
||||
for (i=0; i < s->r.out.num_addrs; i++) {
|
||||
BOOL found = False;
|
||||
for (j=0; rec->addresses[j]; j++) {
|
||||
if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
|
||||
found = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
different = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
|
||||
if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
|
||||
different = True;
|
||||
}
|
||||
}
|
||||
|
||||
if (different) {
|
||||
/*
|
||||
* if the reply from the owning wins server has different addresses
|
||||
* then take the ownership of the record and make it a tombstone
|
||||
* this will then hopefully replicated to the original owner of the record
|
||||
* which will then propagate it's own record, so that the current record will
|
||||
* be replicated to to us
|
||||
*/
|
||||
DEBUG(0,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
|
||||
nbt_name_string(rec, rec->name), rec->wins_owner));
|
||||
|
||||
rec->state = WREPL_STATE_TOMBSTONE;
|
||||
rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
|
||||
for (i=0; rec->addresses[i]; i++) {
|
||||
rec->addresses[i]->expire_time = rec->expire_time;
|
||||
}
|
||||
modify_record = True;
|
||||
modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
|
||||
new_state = "tombstone";
|
||||
new_owner = "owned";
|
||||
} else if (NT_STATUS_IS_OK(status)) {
|
||||
/* if the addresses are the same, just update the timestamps */
|
||||
rec->expire_time = time(NULL) + s->service->config.verify_interval;
|
||||
for (i=0; rec->addresses[i]; i++) {
|
||||
rec->addresses[i]->expire_time = rec->expire_time;
|
||||
}
|
||||
modify_record = True;
|
||||
modify_flags = 0;
|
||||
new_state = "active";
|
||||
}
|
||||
|
||||
if (modify_record) {
|
||||
action = "modify";
|
||||
ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
|
||||
} else if (delete_record) {
|
||||
action = "delete";
|
||||
ret = winsdb_delete(s->service->wins_db, rec);
|
||||
} else {
|
||||
action = "skip";
|
||||
ret = NBT_RCODE_OK;
|
||||
}
|
||||
|
||||
if (ret != NBT_RCODE_OK) {
|
||||
DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
|
||||
} else {
|
||||
DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
|
||||
action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
|
||||
rec->wins_owner, nt_errstr(status)));
|
||||
}
|
||||
|
||||
talloc_free(s);
|
||||
}
|
||||
|
||||
static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct winsdb_record *rec = NULL;
|
||||
struct ldb_result *res = NULL;
|
||||
const char *owner_filter;
|
||||
const char *filter;
|
||||
uint32_t i;
|
||||
int ret;
|
||||
time_t now = time(NULL);
|
||||
const char *now_timestr;
|
||||
struct irpc_request *ireq;
|
||||
struct verify_state *s;
|
||||
uint32_t *nbt_servers;
|
||||
|
||||
nbt_servers = irpc_servers_byname(service->task->msg_ctx, "nbt_server");
|
||||
if ((nbt_servers == NULL) || (nbt_servers[0] == 0)) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
now_timestr = ldb_timestring(tmp_mem, now);
|
||||
NT_STATUS_HAVE_NO_MEMORY(now_timestr);
|
||||
owner_filter = wreplsrv_owner_filter(service, tmp_mem,
|
||||
service->wins_db->local_owner);
|
||||
NT_STATUS_HAVE_NO_MEMORY(owner_filter);
|
||||
filter = talloc_asprintf(tmp_mem,
|
||||
"(&(!%s)(objectClass=winsRecord)"
|
||||
"(recordState=%u)(expireTime<=%s))",
|
||||
owner_filter, WREPL_STATE_ACTIVE, now_timestr);
|
||||
NT_STATUS_HAVE_NO_MEMORY(filter);
|
||||
ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
|
||||
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
talloc_steal(tmp_mem, res);
|
||||
DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
|
||||
|
||||
for (i=0; i < res->count; i++) {
|
||||
/*
|
||||
* we pass '0' as 'now' here,
|
||||
* because we want to get the raw timestamps which are in the DB
|
||||
*/
|
||||
status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
talloc_free(res->msgs[i]);
|
||||
|
||||
if (rec->state != WREPL_STATE_ACTIVE) {
|
||||
DEBUG(0,("%s: corrupted record: %s\n",
|
||||
__location__, nbt_name_string(rec, rec->name)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/*
|
||||
* ask the owning wins server if the record still exists,
|
||||
* if not delete the record
|
||||
*
|
||||
* TODO: NOTE: this is a simpliefied version, to verify that
|
||||
* a record still exist, I assume that w2k3 uses
|
||||
* DCERPC calls or some WINSREPL packets for this,
|
||||
* but we use a wins name query
|
||||
*/
|
||||
DEBUG(0,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
|
||||
rec->wins_owner, nbt_name_string(rec, rec->name),
|
||||
(unsigned long long)rec->version));
|
||||
|
||||
s = talloc_zero(tmp_mem, struct verify_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s);
|
||||
s->msg_ctx = service->task->msg_ctx;
|
||||
s->service = service;
|
||||
s->rec = talloc_steal(s, rec);
|
||||
|
||||
s->r.in.name = *rec->name;
|
||||
s->r.in.num_addrs = 1;
|
||||
s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
|
||||
/* TODO: fix pidl to handle inline ipv4address arrays */
|
||||
s->r.in.addrs[0].addr = rec->wins_owner;
|
||||
|
||||
ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
|
||||
irpc, NBTD_PROXY_WINS_CHALLENGE,
|
||||
&s->r, s);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ireq);
|
||||
|
||||
ireq->async.fn = verify_handler;
|
||||
ireq->async.private = s;
|
||||
|
||||
talloc_steal(service, s);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
|
||||
{
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_mem;
|
||||
BOOL skip_first_run = False;
|
||||
|
||||
if (!timeval_expired(&service->scavenging.next_run)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (timeval_is_zero(&service->scavenging.next_run)) {
|
||||
skip_first_run = True;
|
||||
}
|
||||
|
||||
service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
|
||||
status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/*
|
||||
* if it's the first time this functions is called (startup)
|
||||
* the next_run is zero, in this case we should not do scavenging
|
||||
*/
|
||||
if (skip_first_run) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (service->scavenging.processing) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
|
||||
|
||||
tmp_mem = talloc_new(service);
|
||||
service->scavenging.processing = True;
|
||||
status = wreplsrv_scavenging_owned_records(service,tmp_mem);
|
||||
service->scavenging.processing = False;
|
||||
talloc_free(tmp_mem);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
tmp_mem = talloc_new(service);
|
||||
service->scavenging.processing = True;
|
||||
status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
|
||||
service->scavenging.processing = False;
|
||||
talloc_free(tmp_mem);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
tmp_mem = talloc_new(service);
|
||||
service->scavenging.processing = True;
|
||||
status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
|
||||
service->scavenging.processing = False;
|
||||
talloc_free(tmp_mem);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 "lib/util/dlinklist.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/winsrepl.h"
|
||||
#include "wrepl_server/wrepl_server.h"
|
||||
#include "nbt_server/wins/winsdb.h"
|
||||
#include "ldb/include/ldb.h"
|
||||
#include "ldb/include/ldb_errors.h"
|
||||
#include "auth/auth.h"
|
||||
#include "db_wrap.h"
|
||||
|
||||
static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
return ldb_wrap_connect(mem_ctx, private_path(mem_ctx, lp_wins_config_url()),
|
||||
system_session(mem_ctx), NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static uint64_t wins_config_db_get_seqnumber(struct ldb_context *ldb)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_dn *dn;
|
||||
struct ldb_result *res = NULL;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(ldb);
|
||||
uint64_t seqnumber = 0;
|
||||
|
||||
dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO");
|
||||
if (!dn) goto failed;
|
||||
|
||||
/* find the record in the WINS database */
|
||||
ret = ldb_search(ldb, dn, LDB_SCOPE_BASE,
|
||||
NULL, NULL, &res);
|
||||
if (ret != LDB_SUCCESS) goto failed;
|
||||
talloc_steal(tmp_ctx, res);
|
||||
if (res->count > 1) goto failed;
|
||||
|
||||
if (res->count == 1) {
|
||||
seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0);
|
||||
}
|
||||
|
||||
failed:
|
||||
talloc_free(tmp_ctx);
|
||||
return seqnumber;
|
||||
}
|
||||
|
||||
/*
|
||||
open winsdb
|
||||
*/
|
||||
static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service)
|
||||
{
|
||||
service->wins_db = winsdb_connect(service, WINSDB_HANDLE_CALLER_WREPL);
|
||||
if (!service->wins_db) {
|
||||
return NT_STATUS_INTERNAL_DB_ERROR;
|
||||
}
|
||||
|
||||
service->config.ldb = wins_config_db_connect(service);
|
||||
if (!service->config.ldb) {
|
||||
return NT_STATUS_INTERNAL_DB_ERROR;
|
||||
}
|
||||
|
||||
/* the default renew interval is 6 days */
|
||||
service->config.renew_interval = lp_parm_int(-1,"wreplsrv","renew_interval", 6*24*60*60);
|
||||
|
||||
/* the default tombstone (extinction) interval is 6 days */
|
||||
service->config.tombstone_interval= lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
|
||||
|
||||
/* the default tombstone (extinction) timeout is 1 day */
|
||||
service->config.tombstone_timeout = lp_parm_int(-1,"wreplsrv","tombstone_timeout", 1*24*60*60);
|
||||
|
||||
/* the default tombstone extra timeout is 3 days */
|
||||
service->config.tombstone_extra_timeout = lp_parm_int(-1,"wreplsrv","tombstone_extra_timeout", 3*24*60*60);
|
||||
|
||||
/* the default verify interval is 24 days */
|
||||
service->config.verify_interval = lp_parm_int(-1,"wreplsrv","verify_interval", 24*24*60*60);
|
||||
|
||||
/* the default scavenging interval is 'renew_interval/2' */
|
||||
service->config.scavenging_interval=lp_parm_int(-1,"wreplsrv","scavenging_interval",
|
||||
service->config.renew_interval/2);
|
||||
|
||||
/* the maximun interval to the next periodic processing event */
|
||||
service->config.periodic_interval = lp_parm_int(-1,"wreplsrv","periodic_interval", 15);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, const char *peer_addr)
|
||||
{
|
||||
struct wreplsrv_partner *cur;
|
||||
|
||||
for (cur = service->partners; cur; cur = cur->next) {
|
||||
if (strcmp(cur->address, peer_addr) == 0) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
load our replication partners
|
||||
*/
|
||||
NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service)
|
||||
{
|
||||
struct wreplsrv_partner *partner;
|
||||
struct ldb_result *res = NULL;
|
||||
int ret;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(service);
|
||||
int i;
|
||||
uint64_t new_seqnumber;
|
||||
|
||||
new_seqnumber = wins_config_db_get_seqnumber(service->config.ldb);
|
||||
|
||||
/* if it's not the first run and nothing changed we're done */
|
||||
if (service->config.seqnumber != 0 && service->config.seqnumber == new_seqnumber) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
service->config.seqnumber = new_seqnumber;
|
||||
|
||||
/* find the record in the WINS database */
|
||||
ret = ldb_search(service->config.ldb, ldb_dn_new(tmp_ctx, service->config.ldb, "CN=PARTNERS"), LDB_SCOPE_SUBTREE,
|
||||
"(objectClass=wreplPartner)", NULL, &res);
|
||||
if (ret != LDB_SUCCESS) goto failed;
|
||||
talloc_steal(tmp_ctx, res);
|
||||
|
||||
/* first disable all existing partners */
|
||||
for (partner=service->partners; partner; partner = partner->next) {
|
||||
partner->type = WINSREPL_PARTNER_NONE;
|
||||
}
|
||||
|
||||
for (i=0; i < res->count; i++) {
|
||||
const char *address;
|
||||
|
||||
address = ldb_msg_find_attr_as_string(res->msgs[i], "address", NULL);
|
||||
if (!address) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
partner = wreplsrv_find_partner(service, address);
|
||||
if (partner) {
|
||||
if (partner->name != partner->address) {
|
||||
talloc_free(discard_const(partner->name));
|
||||
}
|
||||
partner->name = NULL;
|
||||
talloc_free(discard_const(partner->our_address));
|
||||
partner->our_address = NULL;
|
||||
|
||||
/* force rescheduling of pulling */
|
||||
partner->pull.next_run = timeval_zero();
|
||||
} else {
|
||||
partner = talloc_zero(service, struct wreplsrv_partner);
|
||||
if (partner == NULL) goto failed;
|
||||
|
||||
partner->service = service;
|
||||
partner->address = address;
|
||||
talloc_steal(partner, partner->address);
|
||||
|
||||
DLIST_ADD_END(service->partners, partner, struct wreplsrv_partner *);
|
||||
}
|
||||
|
||||
partner->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", partner->address);
|
||||
talloc_steal(partner, partner->name);
|
||||
partner->our_address = ldb_msg_find_attr_as_string(res->msgs[i], "ourAddress", NULL);
|
||||
talloc_steal(partner, partner->our_address);
|
||||
|
||||
partner->type = ldb_msg_find_attr_as_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH);
|
||||
partner->pull.interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullInterval",
|
||||
WINSREPL_DEFAULT_PULL_INTERVAL);
|
||||
partner->pull.retry_interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullRetryInterval",
|
||||
WINSREPL_DEFAULT_PULL_RETRY_INTERVAL);
|
||||
partner->push.change_count = ldb_msg_find_attr_as_uint(res->msgs[i], "pushChangeCount",
|
||||
WINSREPL_DEFAULT_PUSH_CHANGE_COUNT);
|
||||
partner->push.use_inform = ldb_msg_find_attr_as_uint(res->msgs[i], "pushUseInform", False);
|
||||
|
||||
DEBUG(3,("wreplsrv_load_partners: found partner: %s type: 0x%X\n",
|
||||
partner->address, partner->type));
|
||||
}
|
||||
|
||||
DEBUG(2,("wreplsrv_load_partners: %u partners found: wins_config_db seqnumber %llu\n",
|
||||
res->count, (unsigned long long)service->config.seqnumber));
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
failed:
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_FOOBAR;
|
||||
}
|
||||
|
||||
NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct wrepl_table *table_out,
|
||||
const char *initiator,
|
||||
BOOL full_table)
|
||||
{
|
||||
struct wreplsrv_owner *cur;
|
||||
uint32_t i = 0;
|
||||
|
||||
table_out->partner_count = 0;
|
||||
table_out->partners = NULL;
|
||||
table_out->initiator = initiator;
|
||||
|
||||
for (cur = service->table; cur; cur = cur->next) {
|
||||
if (full_table) {
|
||||
table_out->partner_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(initiator, cur->owner.address) != 0) continue;
|
||||
|
||||
table_out->partner_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(table_out->partners);
|
||||
|
||||
for (cur = service->table; cur && i < table_out->partner_count; cur = cur->next) {
|
||||
/*
|
||||
* if it's our local entry
|
||||
* update the max version
|
||||
*/
|
||||
if (cur == service->owner) {
|
||||
cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
|
||||
}
|
||||
|
||||
if (full_table) {
|
||||
table_out->partners[i] = cur->owner;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(initiator, cur->owner.address) != 0) continue;
|
||||
|
||||
table_out->partners[i] = cur->owner;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_service *service,
|
||||
struct wreplsrv_owner *table,
|
||||
const char *wins_owner)
|
||||
{
|
||||
struct wreplsrv_owner *cur;
|
||||
|
||||
for (cur = table; cur; cur = cur->next) {
|
||||
if (strcmp(cur->owner.address, wins_owner) == 0) {
|
||||
/*
|
||||
* if it's our local entry
|
||||
* update the max version
|
||||
*/
|
||||
if (cur == service->owner) {
|
||||
cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
update the wins_owner_table max_version, if the given version is the highest version
|
||||
if no entry for the wins_owner exists yet, create one
|
||||
*/
|
||||
NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service,
|
||||
TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table,
|
||||
const char *wins_owner, uint64_t version)
|
||||
{
|
||||
struct wreplsrv_owner *table = *_table;
|
||||
struct wreplsrv_owner *cur;
|
||||
|
||||
if (!wins_owner || strcmp(wins_owner, "0.0.0.0") == 0) {
|
||||
wins_owner = service->wins_db->local_owner;
|
||||
}
|
||||
|
||||
cur = wreplsrv_find_owner(service, table, wins_owner);
|
||||
|
||||
/* if it doesn't exists yet, create one */
|
||||
if (!cur) {
|
||||
cur = talloc_zero(mem_ctx, struct wreplsrv_owner);
|
||||
NT_STATUS_HAVE_NO_MEMORY(cur);
|
||||
|
||||
cur->owner.address = talloc_strdup(cur, wins_owner);
|
||||
NT_STATUS_HAVE_NO_MEMORY(cur->owner.address);
|
||||
cur->owner.min_version = 0;
|
||||
cur->owner.max_version = 0;
|
||||
cur->owner.type = 1; /* don't know why this is always 1 */
|
||||
|
||||
cur->partner = wreplsrv_find_partner(service, wins_owner);
|
||||
|
||||
DLIST_ADD_END(table, cur, struct wreplsrv_owner *);
|
||||
*_table = table;
|
||||
}
|
||||
|
||||
/* the min_version is always 0 here, and won't be updated */
|
||||
|
||||
/* if the given version is higher than the current max_version, update */
|
||||
if (cur->owner.max_version < version) {
|
||||
cur->owner.max_version = version;
|
||||
/* if it's for our local db, we need to update the wins.ldb too */
|
||||
if (cur == service->owner) {
|
||||
uint64_t ret;
|
||||
ret = winsdb_set_maxVersion(service->wins_db, cur->owner.max_version);
|
||||
if (ret != cur->owner.max_version) {
|
||||
DEBUG(0,("winsdb_set_maxVersion(%llu) failed: %llu\n",
|
||||
(unsigned long long)cur->owner.max_version,
|
||||
(unsigned long long)ret));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
load the partner table
|
||||
*/
|
||||
static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service)
|
||||
{
|
||||
struct ldb_result *res = NULL;
|
||||
int ret;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(service);
|
||||
struct ldb_context *ldb = service->wins_db->ldb;
|
||||
int i;
|
||||
struct wreplsrv_owner *local_owner;
|
||||
const char *wins_owner;
|
||||
uint64_t version;
|
||||
const char * const attrs[] = {
|
||||
"winsOwner",
|
||||
"versionID",
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* make sure we have our local entry in the list,
|
||||
* but we set service->owner when we're done
|
||||
* to avoid to many calls to wreplsrv_local_max_version()
|
||||
*/
|
||||
status = wreplsrv_add_table(service,
|
||||
service, &service->table,
|
||||
service->wins_db->local_owner, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
local_owner = wreplsrv_find_owner(service, service->table, service->wins_db->local_owner);
|
||||
if (!local_owner) {
|
||||
status = NT_STATUS_INTERNAL_ERROR;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* find the record in the WINS database */
|
||||
ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE,
|
||||
"(objectClass=winsRecord)", attrs, &res);
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
if (ret != LDB_SUCCESS) goto failed;
|
||||
talloc_steal(tmp_ctx, res);
|
||||
|
||||
for (i=0; i < res->count; i++) {
|
||||
wins_owner = ldb_msg_find_attr_as_string(res->msgs[i], "winsOwner", NULL);
|
||||
version = ldb_msg_find_attr_as_uint64(res->msgs[i], "versionID", 0);
|
||||
|
||||
status = wreplsrv_add_table(service,
|
||||
service, &service->table,
|
||||
wins_owner, version);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
talloc_free(res->msgs[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* this makes sure we call wreplsrv_local_max_version() before returning in
|
||||
* wreplsrv_find_owner()
|
||||
*/
|
||||
service->owner = local_owner;
|
||||
|
||||
/*
|
||||
* this makes sure the maxVersion in the database is updated,
|
||||
* with the highest version we found, if this is higher than the current stored one
|
||||
*/
|
||||
status = wreplsrv_add_table(service,
|
||||
service, &service->table,
|
||||
service->wins_db->local_owner, local_owner->owner.max_version);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
failed:
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
setup our replication partners
|
||||
*/
|
||||
static NTSTATUS wreplsrv_setup_partners(struct wreplsrv_service *service)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = wreplsrv_load_partners(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = wreplsrv_load_table(service);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
startup the wrepl task
|
||||
*/
|
||||
static void wreplsrv_task_init(struct task_server *task)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct wreplsrv_service *service;
|
||||
|
||||
task_server_set_title(task, "task[wreplsrv]");
|
||||
|
||||
service = talloc_zero(task, struct wreplsrv_service);
|
||||
if (!service) {
|
||||
task_server_terminate(task, "wreplsrv_task_init: out of memory");
|
||||
return;
|
||||
}
|
||||
service->task = task;
|
||||
service->startup_time = timeval_current();
|
||||
task->private = service;
|
||||
|
||||
/*
|
||||
* setup up all partners, and open the winsdb
|
||||
*/
|
||||
status = wreplsrv_open_winsdb(service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(task, "wreplsrv_task_init: wreplsrv_open_winsdb() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup timed events for each partner we want to pull from
|
||||
*/
|
||||
status = wreplsrv_setup_partners(service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_partners() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup listen sockets, so we can anwser requests from our partners,
|
||||
* which pull from us
|
||||
*/
|
||||
status = wreplsrv_setup_sockets(service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_sockets() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
status = wreplsrv_setup_periodic(service);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
irpc_add_name(task->msg_ctx, "wrepl_server");
|
||||
}
|
||||
|
||||
/*
|
||||
initialise the WREPL server
|
||||
*/
|
||||
static NTSTATUS wreplsrv_init(struct event_context *event_ctx, const struct model_ops *model_ops)
|
||||
{
|
||||
if (!lp_wins_support()) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return task_server_startup(event_ctx, model_ops, wreplsrv_task_init);
|
||||
}
|
||||
|
||||
/*
|
||||
register ourselves as a available server
|
||||
*/
|
||||
NTSTATUS server_service_wrepl_init(void)
|
||||
{
|
||||
return register_server_service("wrepl", wreplsrv_init);
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
WINS Replication server
|
||||
|
||||
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 wreplsrv_service;
|
||||
struct wreplsrv_in_connection;
|
||||
struct wreplsrv_out_connection;
|
||||
struct wreplsrv_partner;
|
||||
|
||||
#define WREPLSRV_VALID_ASSOC_CTX 0x12345678
|
||||
#define WREPLSRV_INVALID_ASSOC_CTX 0x0000000a
|
||||
|
||||
/*
|
||||
state of an incoming wrepl call
|
||||
*/
|
||||
struct wreplsrv_in_call {
|
||||
struct wreplsrv_in_connection *wreplconn;
|
||||
struct wrepl_packet req_packet;
|
||||
struct wrepl_packet rep_packet;
|
||||
BOOL terminate_after_send;
|
||||
};
|
||||
|
||||
/*
|
||||
state of an incoming wrepl connection
|
||||
*/
|
||||
struct wreplsrv_in_connection {
|
||||
struct wreplsrv_in_connection *prev,*next;
|
||||
struct stream_connection *conn;
|
||||
struct packet_context *packet;
|
||||
|
||||
/* our global service context */
|
||||
struct wreplsrv_service *service;
|
||||
|
||||
/*
|
||||
* the partner that connects us,
|
||||
* can be NULL, when we got a connection
|
||||
* from an unknown address
|
||||
*/
|
||||
struct wreplsrv_partner *partner;
|
||||
|
||||
/* keep track of the assoc_ctx's */
|
||||
struct {
|
||||
BOOL stopped;
|
||||
uint32_t our_ctx;
|
||||
uint32_t peer_ctx;
|
||||
} assoc_ctx;
|
||||
};
|
||||
|
||||
/*
|
||||
state of an outgoing wrepl connection
|
||||
*/
|
||||
struct wreplsrv_out_connection {
|
||||
/* our global service context */
|
||||
struct wreplsrv_service *service;
|
||||
|
||||
/*
|
||||
* the partner we connect
|
||||
*/
|
||||
struct wreplsrv_partner *partner;
|
||||
|
||||
/* keep track of the assoc_ctx's */
|
||||
struct {
|
||||
uint32_t our_ctx;
|
||||
uint32_t peer_ctx;
|
||||
} assoc_ctx;
|
||||
|
||||
/*
|
||||
* the client socket to the partner,
|
||||
* NULL if not yet connected
|
||||
*/
|
||||
struct wrepl_socket *sock;
|
||||
};
|
||||
|
||||
enum winsrepl_partner_type {
|
||||
WINSREPL_PARTNER_NONE = 0x0,
|
||||
WINSREPL_PARTNER_PULL = 0x1,
|
||||
WINSREPL_PARTNER_PUSH = 0x2,
|
||||
WINSREPL_PARTNER_BOTH = (WINSREPL_PARTNER_PULL | WINSREPL_PARTNER_PUSH)
|
||||
};
|
||||
|
||||
#define WINSREPL_DEFAULT_PULL_INTERVAL (30*60)
|
||||
#define WINSREPL_DEFAULT_PULL_RETRY_INTERVAL (30)
|
||||
|
||||
#define WINSREPL_DEFAULT_PUSH_CHANGE_COUNT (0)
|
||||
|
||||
/*
|
||||
this represents one of our configured partners
|
||||
*/
|
||||
struct wreplsrv_partner {
|
||||
struct wreplsrv_partner *prev,*next;
|
||||
|
||||
/* our global service context */
|
||||
struct wreplsrv_service *service;
|
||||
|
||||
/* the netbios name of the partner, mostly just for debugging */
|
||||
const char *name;
|
||||
|
||||
/* the ip-address of the partner */
|
||||
const char *address;
|
||||
|
||||
/*
|
||||
* as wins partners identified by ip-address, we need to use a specific source-ip
|
||||
* when we want to connect to the partner
|
||||
*/
|
||||
const char *our_address;
|
||||
|
||||
/* the type of the partner, pull, push or both */
|
||||
enum winsrepl_partner_type type;
|
||||
|
||||
/* pull specific options */
|
||||
struct {
|
||||
/* the interval between 2 pull replications to the partner */
|
||||
uint32_t interval;
|
||||
|
||||
/* the retry_interval if a pull cycle failed to the partner */
|
||||
uint32_t retry_interval;
|
||||
|
||||
/* the error count till the last success */
|
||||
uint32_t error_count;
|
||||
|
||||
/* the status of the last pull cycle */
|
||||
NTSTATUS last_status;
|
||||
|
||||
/* the timestamp of the next pull try */
|
||||
struct timeval next_run;
|
||||
|
||||
/* this is a list of each wins_owner the partner knows about */
|
||||
struct wreplsrv_owner *table;
|
||||
|
||||
/* the outgoing connection to the partner */
|
||||
struct wreplsrv_out_connection *wreplconn;
|
||||
|
||||
/* the current pending pull cycle request */
|
||||
struct composite_context *creq;
|
||||
|
||||
/* the pull cycle io params */
|
||||
struct wreplsrv_pull_cycle_io *cycle_io;
|
||||
|
||||
/* the current timed_event to the next pull cycle */
|
||||
struct timed_event *te;
|
||||
} pull;
|
||||
|
||||
/* push specific options */
|
||||
struct {
|
||||
/* change count till push notification */
|
||||
uint32_t change_count;
|
||||
|
||||
/* the last wins db maxVersion have reported to the partner */
|
||||
uint64_t maxVersionID;
|
||||
|
||||
/* we should use WREPL_REPL_INFORM* messages to this partner */
|
||||
BOOL use_inform;
|
||||
|
||||
/* the error count till the last success */
|
||||
uint32_t error_count;
|
||||
|
||||
/* the status of the last push cycle */
|
||||
NTSTATUS last_status;
|
||||
|
||||
/* the outgoing connection to the partner */
|
||||
struct wreplsrv_out_connection *wreplconn;
|
||||
|
||||
/* the current push notification */
|
||||
struct composite_context *creq;
|
||||
|
||||
/* the pull cycle io params */
|
||||
struct wreplsrv_push_notify_io *notify_io;
|
||||
} push;
|
||||
};
|
||||
|
||||
struct wreplsrv_owner {
|
||||
struct wreplsrv_owner *prev,*next;
|
||||
|
||||
/* this hold the owner_id (address), min_version, max_version and partner_type */
|
||||
struct wrepl_wins_owner owner;
|
||||
|
||||
/* can be NULL if this owner isn't a configure partner */
|
||||
struct wreplsrv_partner *partner;
|
||||
};
|
||||
|
||||
/*
|
||||
state of the whole wrepl service
|
||||
*/
|
||||
struct wreplsrv_service {
|
||||
/* the whole wrepl service is in one task */
|
||||
struct task_server *task;
|
||||
|
||||
/* the time the service was started */
|
||||
struct timeval startup_time;
|
||||
|
||||
/* the winsdb handle */
|
||||
struct winsdb_handle *wins_db;
|
||||
|
||||
/* some configuration */
|
||||
struct {
|
||||
/* the wins config db handle */
|
||||
struct ldb_context *ldb;
|
||||
|
||||
/* the last wins config db seqnumber we know about */
|
||||
uint64_t seqnumber;
|
||||
|
||||
/*
|
||||
* the interval (in secs) till an active record will be marked as RELEASED
|
||||
*/
|
||||
uint32_t 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;
|
||||
|
||||
/*
|
||||
* the interval (in secs) a record remains in TOMBSTONE state,
|
||||
* even after 'tombstone_timeout' passes the current timestamp.
|
||||
* this is the minimum uptime of the wrepl service, before
|
||||
* we start delete tombstones. This is to prevent deletion of
|
||||
* tombstones, without replacte them.
|
||||
*/
|
||||
uint32_t tombstone_extra_timeout;
|
||||
|
||||
/*
|
||||
* the interval (in secs) till a replica record will be verified
|
||||
* with the owning wins server
|
||||
*/
|
||||
uint32_t verify_interval;
|
||||
|
||||
/*
|
||||
* the interval (in secs) till a do a database cleanup
|
||||
*/
|
||||
uint32_t scavenging_interval;
|
||||
|
||||
/*
|
||||
* the interval (in secs) to the next periodic processing
|
||||
* (this is the maximun interval)
|
||||
*/
|
||||
uint32_t periodic_interval;
|
||||
} config;
|
||||
|
||||
/* all incoming connections */
|
||||
struct wreplsrv_in_connection *in_connections;
|
||||
|
||||
/* all partners (pull and push) */
|
||||
struct wreplsrv_partner *partners;
|
||||
|
||||
/*
|
||||
* this is our local wins_owner entry, this is also in the table list
|
||||
* but we need a pointer to it, because we need to update it on each
|
||||
* query to wreplsrv_find_owner(), as the local records can be added
|
||||
* to the wins.ldb from external tools and the winsserver
|
||||
*/
|
||||
struct wreplsrv_owner *owner;
|
||||
|
||||
/* this is a list of each wins_owner we know about in our database */
|
||||
struct wreplsrv_owner *table;
|
||||
|
||||
/* some stuff for periodic processing */
|
||||
struct {
|
||||
/*
|
||||
* the timestamp for the next event,
|
||||
* this is the timstamp passed to event_add_timed()
|
||||
*/
|
||||
struct timeval next_event;
|
||||
|
||||
/* here we have a reference to the timed event the schedules the periodic stuff */
|
||||
struct timed_event *te;
|
||||
} periodic;
|
||||
|
||||
/* some stuff for scavenging processing */
|
||||
struct {
|
||||
/*
|
||||
* the timestamp for the next scavenging run,
|
||||
* this is the timstamp passed to event_add_timed()
|
||||
*/
|
||||
struct timeval next_run;
|
||||
|
||||
/*
|
||||
* are we currently inside a scavenging run
|
||||
*/
|
||||
BOOL processing;
|
||||
} scavenging;
|
||||
};
|
||||
|
||||
struct socket_context;
|
||||
struct wrepl_name;
|
||||
#include "wrepl_server/wrepl_out_helpers.h"
|
||||
#include "wrepl_server/wrepl_server_proto.h"
|
||||
Reference in New Issue
Block a user