mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
4142bde7e5
Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Autobuild-User(master): Ralph Böhme <slow@samba.org> Autobuild-Date(master): Fri Nov 27 10:07:18 UTC 2020 on sn-devel-184
590 lines
18 KiB
C
590 lines
18 KiB
C
/*
|
|
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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "lib/events/events.h"
|
|
#include "lib/tsocket/tsocket.h"
|
|
#include "samba/service_task.h"
|
|
#include "samba/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 <ldb.h>
|
|
#include <ldb_errors.h>
|
|
#include "system/time.h"
|
|
#include "lib/util/tsort.h"
|
|
#include "param/param.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;
|
|
unsigned int 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, call, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
|
|
if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
|
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 */
|
|
TYPESAFE_QSORT(names, j, 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;
|
|
NTSTATUS status;
|
|
|
|
DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
|
|
call->wreplconn->partner->address,
|
|
update_in->initiator, update_in->partner_count));
|
|
|
|
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_init(wrepl_out,
|
|
wrepl_in->conn->event.ctx);
|
|
|
|
if (wrepl_out->sock == NULL) {
|
|
TALLOC_FREE(update_state);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
TALLOC_FREE(wrepl_in->send_queue);
|
|
|
|
status = wrepl_socket_donate_stream(wrepl_out->sock, &wrepl_in->tstream);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(update_state);
|
|
return status;
|
|
}
|
|
|
|
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) {
|
|
talloc_free(update_state);
|
|
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 tsocket_address *peer_addr = call->wreplconn->conn->remote_address;
|
|
char *peer_ip;
|
|
|
|
if (!tsocket_address_is_inet(peer_addr, "ipv4")) {
|
|
DEBUG(0,("wreplsrv_in_replication: non ipv4 peer addr '%s'\n",
|
|
tsocket_address_string(peer_addr, call)));
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
peer_ip = tsocket_address_inet_addr_string(peer_addr, call);
|
|
if (peer_ip == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, peer_ip);
|
|
if (!call->wreplconn->partner) {
|
|
DEBUG(1,("Failing WINS replication from non-partner %s\n", peer_ip));
|
|
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(0,("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(0,("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(0,("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(0,("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(0,("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(0,("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;
|
|
}
|