mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
7a23429ed6
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15577 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Guenther Deschner <gd@samba.org>
2362 lines
58 KiB
C
2362 lines
58 KiB
C
/*
|
|
* Samba Unix/Linux client library
|
|
* net witness commands to manage smb witness registrations
|
|
* Copyright (C) 2023 Stefan Metzmacher
|
|
*
|
|
* 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 "utils/net.h"
|
|
#include "messages.h"
|
|
#include "serverid.h"
|
|
#include "lib/util/util_tdb.h"
|
|
#include "source3/include/util_tdb.h"
|
|
#include "libcli/security/dom_sid.h"
|
|
#include "lib/dbwrap/dbwrap.h"
|
|
#include "lib/dbwrap/dbwrap_rbt.h"
|
|
#include "lib/dbwrap/dbwrap_open.h"
|
|
#include "lib/param/param.h"
|
|
#include "librpc/gen_ndr/ndr_rpcd_witness.h"
|
|
#include <regex.h>
|
|
|
|
struct json_object;
|
|
|
|
#ifdef HAVE_JANSSON
|
|
#include <jansson.h>
|
|
#include "audit_logging.h" /* various JSON helpers */
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
#undef strcasecmp
|
|
|
|
static struct db_context *net_witness_open_registration_db(void)
|
|
{
|
|
static struct db_context *db;
|
|
char *global_path = NULL;
|
|
|
|
if (db != NULL) {
|
|
return db;
|
|
}
|
|
|
|
global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
|
|
if (global_path == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
db = db_open(NULL,
|
|
global_path,
|
|
0, /* hash_size */
|
|
TDB_DEFAULT |
|
|
TDB_CLEAR_IF_FIRST |
|
|
TDB_INCOMPATIBLE_HASH,
|
|
O_RDONLY,
|
|
0600,
|
|
DBWRAP_LOCK_ORDER_1,
|
|
DBWRAP_FLAG_NONE);
|
|
TALLOC_FREE(global_path);
|
|
if (db == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return db;
|
|
}
|
|
|
|
struct net_witness_scan_registrations_action_state {
|
|
bool (*prepare_fn)(void *private_data);
|
|
bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg);
|
|
NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg);
|
|
void *private_data;
|
|
};
|
|
|
|
struct net_witness_scan_registrations_regex {
|
|
regex_t regex;
|
|
bool valid;
|
|
};
|
|
|
|
struct net_witness_scan_registrations_state {
|
|
struct net_context *c;
|
|
struct net_witness_scan_registrations_regex net_name;
|
|
struct net_witness_scan_registrations_regex share_name;
|
|
struct net_witness_scan_registrations_regex ip_address;
|
|
struct net_witness_scan_registrations_regex client_computer;
|
|
struct json_object *message_json;
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object filters_json;
|
|
struct json_object registrations_json;
|
|
#endif
|
|
const struct net_witness_scan_registrations_action_state *action;
|
|
NTSTATUS error;
|
|
};
|
|
|
|
static bool net_witness_scan_registrations_regex_init(
|
|
struct net_witness_scan_registrations_state *state,
|
|
struct net_witness_scan_registrations_regex *r,
|
|
const char *option, const char *value);
|
|
static bool net_witness_scan_registrations_regex_match(
|
|
struct net_witness_scan_registrations_regex *r,
|
|
const char *name, const char *value);
|
|
static void net_witness_scan_registrations_regex_free(
|
|
struct net_witness_scan_registrations_regex *r);
|
|
|
|
static bool net_witness_scan_registrations_match(
|
|
struct net_witness_scan_registrations_state *state,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
if (state->net_name.valid) {
|
|
bool match;
|
|
|
|
match = net_witness_scan_registrations_regex_match(
|
|
&state->net_name,
|
|
"net_name",
|
|
rg->net_name);
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (state->share_name.valid) {
|
|
bool match;
|
|
|
|
match = net_witness_scan_registrations_regex_match(
|
|
&state->share_name,
|
|
"share_name",
|
|
rg->share_name);
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (state->ip_address.valid) {
|
|
bool match;
|
|
|
|
match = net_witness_scan_registrations_regex_match(
|
|
&state->ip_address,
|
|
"ip_address",
|
|
rg->ip_address);
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (state->client_computer.valid) {
|
|
bool match;
|
|
|
|
match = net_witness_scan_registrations_regex_match(
|
|
&state->client_computer,
|
|
"client_computer_name",
|
|
rg->client_computer_name);
|
|
if (!match) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_scan_registrations_regex_init(
|
|
struct net_witness_scan_registrations_state *state,
|
|
struct net_witness_scan_registrations_regex *r,
|
|
const char *option, const char *value)
|
|
{
|
|
#ifdef HAVE_JANSSON
|
|
struct net_context *c = state->c;
|
|
#endif /* HAVE_JANSSON */
|
|
int ret;
|
|
|
|
r->valid = false;
|
|
|
|
if (value == NULL) {
|
|
return true;
|
|
}
|
|
|
|
ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB);
|
|
if (ret != 0) {
|
|
fstring buf = { 0,};
|
|
regerror(ret, &r->regex, buf, sizeof(buf));
|
|
d_printf("regcomp(%s) failed for %s: "
|
|
"%d: %s\n", value, option, ret, buf);
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
if (c->opt_json) {
|
|
ret = json_add_string(&state->filters_json,
|
|
option,
|
|
value);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
r->valid = true;
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_scan_registrations_regex_match(
|
|
struct net_witness_scan_registrations_regex *r,
|
|
const char *name, const char *value)
|
|
{
|
|
int ret;
|
|
|
|
if (!r->valid) {
|
|
return false;
|
|
}
|
|
|
|
if (value == NULL) {
|
|
/*
|
|
* without a share name,
|
|
* we match against an empty
|
|
* string.
|
|
*/
|
|
value = "";
|
|
}
|
|
|
|
ret = regexec(&r->regex, value, 0, NULL, 0);
|
|
if (ret == REG_NOMATCH) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void net_witness_scan_registrations_regex_free(
|
|
struct net_witness_scan_registrations_regex *r)
|
|
{
|
|
if (r->valid) {
|
|
regfree(&r->regex);
|
|
r->valid = false;
|
|
}
|
|
}
|
|
|
|
static bool net_witness_scan_registrations_init(
|
|
struct net_witness_scan_registrations_state *state)
|
|
{
|
|
struct net_context *c = state->c;
|
|
bool ok;
|
|
|
|
if (c->opt_json) {
|
|
#ifdef HAVE_JANSSON
|
|
state->filters_json = json_new_object();
|
|
if (json_is_invalid(&state->filters_json)) {
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_registration != NULL) {
|
|
int ret;
|
|
|
|
ret = json_add_string(&state->filters_json,
|
|
"--witness-registration",
|
|
c->opt_witness_registration);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (c->opt_witness_apply_to_all != 0) {
|
|
int ret;
|
|
|
|
ret = json_add_bool(&state->filters_json,
|
|
"--witness-apply-to-all",
|
|
c->opt_witness_apply_to_all != 0);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
state->registrations_json = json_new_object();
|
|
if (json_is_invalid(&state->registrations_json)) {
|
|
return false;
|
|
}
|
|
#else /* not HAVE_JANSSON */
|
|
d_fprintf(stderr, _("JSON support not available\n"));
|
|
return false;
|
|
#endif /* not HAVE_JANSSON */
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_regex_init(state,
|
|
&state->net_name,
|
|
"--witness-net-name",
|
|
c->opt_witness_net_name);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_regex_init(state,
|
|
&state->share_name,
|
|
"--witness-share-name",
|
|
c->opt_witness_share_name);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_regex_init(state,
|
|
&state->ip_address,
|
|
"--witness-ip-address",
|
|
c->opt_witness_ip_address);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_regex_init(state,
|
|
&state->client_computer,
|
|
"--witness-client-computer-name",
|
|
c->opt_witness_client_computer_name);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
ok = state->action->prepare_fn(state->action->private_data);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
if (!c->opt_json) {
|
|
d_printf("%-36s %-20s %-15s %-20s %s\n",
|
|
"Registration-UUID:",
|
|
"NetName",
|
|
"ShareName",
|
|
"IpAddress",
|
|
"ClientComputerName");
|
|
d_printf("%-36s-%-20s-%-15s-%-20s-%s\n",
|
|
"------------------------------------",
|
|
"--------------------",
|
|
"------------------",
|
|
"--------------------",
|
|
"------------------");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_scan_registrations_finish(
|
|
struct net_witness_scan_registrations_state *state)
|
|
{
|
|
#ifdef HAVE_JANSSON
|
|
struct net_context *c = state->c;
|
|
struct json_object root_json = json_empty_object;
|
|
TALLOC_CTX *frame = NULL;
|
|
const char *json_str = NULL;
|
|
int ret;
|
|
|
|
if (!c->opt_json) {
|
|
return true;
|
|
}
|
|
|
|
frame = talloc_stackframe();
|
|
|
|
root_json = json_new_object();
|
|
if (json_is_invalid(&root_json)) {
|
|
TALLOC_FREE(frame);
|
|
return false;
|
|
}
|
|
|
|
ret = json_add_object(&root_json,
|
|
"filters",
|
|
&state->filters_json);
|
|
if (ret != 0) {
|
|
json_free(&root_json);
|
|
TALLOC_FREE(frame);
|
|
return false;
|
|
}
|
|
state->filters_json = json_empty_object;
|
|
|
|
if (state->message_json != NULL) {
|
|
ret = json_add_object(&root_json,
|
|
"message",
|
|
state->message_json);
|
|
if (ret != 0) {
|
|
json_free(&root_json);
|
|
TALLOC_FREE(frame);
|
|
return false;
|
|
}
|
|
*state->message_json = json_empty_object;
|
|
}
|
|
|
|
ret = json_add_object(&root_json,
|
|
"registrations",
|
|
&state->registrations_json);
|
|
if (ret != 0) {
|
|
json_free(&root_json);
|
|
TALLOC_FREE(frame);
|
|
return false;
|
|
}
|
|
state->registrations_json = json_empty_object;
|
|
|
|
json_str = json_to_string(frame, &root_json);
|
|
json_free(&root_json);
|
|
if (json_str == NULL) {
|
|
TALLOC_FREE(frame);
|
|
return false;
|
|
}
|
|
|
|
d_printf("%s\n", json_str);
|
|
TALLOC_FREE(frame);
|
|
return true;
|
|
#else /* not HAVE_JANSSON */
|
|
return true;
|
|
#endif /* not HAVE_JANSSON */
|
|
}
|
|
|
|
static void net_witness_scan_registrations_free(
|
|
struct net_witness_scan_registrations_state *state)
|
|
{
|
|
#ifdef HAVE_JANSSON
|
|
if (!json_is_invalid(&state->filters_json)) {
|
|
json_free(&state->filters_json);
|
|
}
|
|
if (!json_is_invalid(&state->registrations_json)) {
|
|
json_free(&state->registrations_json);
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
net_witness_scan_registrations_regex_free(&state->net_name);
|
|
net_witness_scan_registrations_regex_free(&state->share_name);
|
|
net_witness_scan_registrations_regex_free(&state->ip_address);
|
|
net_witness_scan_registrations_regex_free(&state->client_computer);
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
static int dump_registration_json(struct json_object *registrations_json,
|
|
const char *key_str,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct json_object jsobj = json_empty_object;
|
|
struct json_object flags_json = json_empty_object;
|
|
struct json_object context_json = json_empty_object;
|
|
struct json_object serverid_json = json_empty_object;
|
|
struct json_object auth_json = json_empty_object;
|
|
struct json_object connection_json = json_empty_object;
|
|
struct timeval tv;
|
|
struct dom_sid_buf sid_buf;
|
|
int ret = 0;
|
|
|
|
jsobj = json_new_object();
|
|
if (json_is_invalid(&jsobj)) {
|
|
d_fprintf(stderr, _("error setting up JSON value\n"));
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_flags32(&jsobj, "version", rg->version);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&jsobj, "net_name", rg->net_name);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&jsobj, "share_name", rg->share_name);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&jsobj, "ip_address", rg->ip_address);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
flags_json = json_new_object();
|
|
if (json_is_invalid(&flags_json)) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION",
|
|
(rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ?
|
|
true : false);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&flags_json, "int", rg->flags);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_flags32(&flags_json, "hex", rg->flags);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(&jsobj, "flags", &flags_json);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
flags_json = json_empty_object;
|
|
|
|
ret = json_add_int(&jsobj, "timeout", rg->timeout);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
context_json = json_new_object();
|
|
if (json_is_invalid(&context_json)) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(&jsobj, "context_handle", &context_json);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
context_json = json_empty_object;
|
|
|
|
serverid_json = json_new_object();
|
|
if (json_is_invalid(&serverid_json)) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&serverid_json, "pid", rg->server_id.pid);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(&jsobj, "server_id", &serverid_json);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
serverid_json = json_empty_object;
|
|
|
|
auth_json = json_new_object();
|
|
if (json_is_invalid(&auth_json)) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&auth_json, "account_name", rg->account_name);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&auth_json, "domain_name", rg->domain_name);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&auth_json,
|
|
"account_sid",
|
|
dom_sid_str_buf(&rg->account_sid, &sid_buf));
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(&jsobj, "auth", &auth_json);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
auth_json = json_empty_object;
|
|
|
|
connection_json = json_new_object();
|
|
if (json_is_invalid(&connection_json)) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&connection_json, "local_address", rg->local_address);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_string(&connection_json, "remote_address", rg->remote_address);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(&jsobj, "connection", &connection_json);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
connection_json = json_empty_object;
|
|
|
|
nttime_to_timeval(&tv, rg->registration_time);
|
|
ret = json_add_time(&jsobj, "registration_time", tv);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
|
|
ret = json_add_object(registrations_json, key_str, &jsobj);
|
|
if (ret != 0) {
|
|
goto failure;
|
|
}
|
|
jsobj = json_empty_object;
|
|
|
|
failure:
|
|
if (!json_is_invalid(&connection_json)) {
|
|
json_free(&connection_json);
|
|
}
|
|
if (!json_is_invalid(&auth_json)) {
|
|
json_free(&auth_json);
|
|
}
|
|
if (!json_is_invalid(&serverid_json)) {
|
|
json_free(&serverid_json);
|
|
}
|
|
if (!json_is_invalid(&context_json)) {
|
|
json_free(&context_json);
|
|
}
|
|
if (!json_is_invalid(&flags_json)) {
|
|
json_free(&flags_json);
|
|
}
|
|
if (!json_is_invalid(&jsobj)) {
|
|
json_free(&jsobj);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
static NTSTATUS net_witness_scan_registrations_dump_rg(
|
|
struct net_witness_scan_registrations_state *state,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct net_context *c = state->c;
|
|
struct GUID_txt_buf key_buf;
|
|
const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf);
|
|
|
|
if (c->opt_json) {
|
|
#ifdef HAVE_JANSSON
|
|
int ret;
|
|
|
|
ret = dump_registration_json(&state->registrations_json,
|
|
key_str,
|
|
rg);
|
|
if (ret != 0) {
|
|
d_fprintf(stderr, "dump_registration_json(%s) failed\n",
|
|
key_str);
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
d_printf("%-36s %-20s %-15s %-20s %s\n",
|
|
key_str,
|
|
rg->net_name,
|
|
rg->share_name ? rg->share_name : "''",
|
|
rg->ip_address,
|
|
rg->client_computer_name);
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_scan_registrations_parser(TDB_DATA key,
|
|
TDB_DATA val,
|
|
void *private_data)
|
|
{
|
|
struct net_witness_scan_registrations_state *state =
|
|
(struct net_witness_scan_registrations_state *)private_data;
|
|
DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize);
|
|
struct rpcd_witness_registration rg;
|
|
enum ndr_err_code ndr_err;
|
|
TALLOC_CTX *frame = NULL;
|
|
bool match = false;
|
|
|
|
if (val_blob.length == 0) {
|
|
return;
|
|
}
|
|
|
|
frame = talloc_stackframe();
|
|
|
|
ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg,
|
|
(ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:"
|
|
"key '%s' ndr_pull_struct_blob - %s\n",
|
|
tdb_data_dbg(key),
|
|
ndr_errstr(ndr_err));
|
|
state->error = ndr_map_error2ntstatus(ndr_err);
|
|
TALLOC_FREE(frame);
|
|
return;
|
|
}
|
|
|
|
if (!serverid_exists(&rg.server_id)) {
|
|
TALLOC_FREE(frame);
|
|
return;
|
|
}
|
|
|
|
if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
|
|
NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
|
|
}
|
|
|
|
match = net_witness_scan_registrations_match(state, &rg);
|
|
if (!NT_STATUS_IS_OK(state->error)) {
|
|
TALLOC_FREE(frame);
|
|
return;
|
|
}
|
|
if (!match) {
|
|
TALLOC_FREE(frame);
|
|
return;
|
|
}
|
|
|
|
match = state->action->match_fn(state->action->private_data, &rg);
|
|
if (!match) {
|
|
TALLOC_FREE(frame);
|
|
return;
|
|
}
|
|
|
|
state->error = state->action->process_fn(state->action->private_data, &rg);
|
|
if (NT_STATUS_IS_OK(state->error)) {
|
|
state->error = net_witness_scan_registrations_dump_rg(state,
|
|
&rg);
|
|
}
|
|
TALLOC_FREE(frame);
|
|
}
|
|
|
|
static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data)
|
|
{
|
|
struct net_witness_scan_registrations_state *state =
|
|
(struct net_witness_scan_registrations_state *)private_data;
|
|
TDB_DATA key = dbwrap_record_get_key(rec);
|
|
TDB_DATA val = dbwrap_record_get_value(rec);
|
|
|
|
net_witness_scan_registrations_parser(key, val, private_data);
|
|
|
|
if (!NT_STATUS_IS_OK(state->error)) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int net_witness_scan_registrations(struct net_context *c,
|
|
struct json_object *message_json,
|
|
const struct net_witness_scan_registrations_action_state *action)
|
|
{
|
|
struct net_witness_scan_registrations_state state = {
|
|
.c = c,
|
|
.message_json = message_json,
|
|
.action = action,
|
|
};
|
|
struct db_context *db = NULL;
|
|
NTSTATUS status;
|
|
bool ok;
|
|
|
|
db = net_witness_open_registration_db();
|
|
if (db == NULL) {
|
|
d_printf("net_witness_open_registration_db() failed\n");
|
|
return -1;
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_init(&state);
|
|
if (!ok) {
|
|
d_printf("net_witness_scan_registrations_init() failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (c->opt_witness_registration != NULL) {
|
|
const char *key_str = c->opt_witness_registration;
|
|
DATA_BLOB key_blob = data_blob_string_const(key_str);
|
|
TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length);
|
|
|
|
status = dbwrap_parse_record(db,
|
|
key,
|
|
net_witness_scan_registrations_parser,
|
|
&state);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
status = NT_STATUS_OK;
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
d_printf("dbwrap_parse_record(%s) failed: %s\n",
|
|
key_str, nt_errstr(status));
|
|
net_witness_scan_registrations_free(&state);
|
|
return -1;
|
|
}
|
|
if (!NT_STATUS_IS_OK(state.error)) {
|
|
d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n",
|
|
key_str, nt_errstr(state.error));
|
|
net_witness_scan_registrations_free(&state);
|
|
return -1;
|
|
}
|
|
} else {
|
|
status = dbwrap_traverse_read(db,
|
|
net_witness_scan_registrations_traverse_cb,
|
|
&state,
|
|
NULL); /* count */
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
d_printf("dbwrap_traverse_read() failed\n");
|
|
net_witness_scan_registrations_free(&state);
|
|
return -1;
|
|
}
|
|
if (!NT_STATUS_IS_OK(state.error)) {
|
|
d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n",
|
|
nt_errstr(state.error));
|
|
net_witness_scan_registrations_free(&state);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ok = net_witness_scan_registrations_finish(&state);
|
|
if (!ok) {
|
|
d_printf("net_witness_scan_registrations_finish() failed\n");
|
|
return -1;
|
|
}
|
|
|
|
net_witness_scan_registrations_free(&state);
|
|
return 0;
|
|
}
|
|
|
|
struct net_witness_list_state {
|
|
struct net_context *c;
|
|
};
|
|
|
|
static bool net_witness_list_prepare_fn(void *private_data)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_list_match_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS net_witness_list_process_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_filter_usage(void)
|
|
{
|
|
d_printf(" Note: Only supported with clustering=yes!\n\n");
|
|
d_printf(" Machine readable output can be generated with "
|
|
"the following option:\n"
|
|
"\n"
|
|
" --json\n"
|
|
"\n");
|
|
d_printf(" The selection of registrations can be limited by "
|
|
"the following options:\n"
|
|
"\n"
|
|
" --witness-registration=REGISTRATION_UUID\n"
|
|
" This does a direct lookup for REGISTRATION_UUID\n"
|
|
" instead of doing a database traversal.\n"
|
|
"\n"
|
|
" The following options all take a "
|
|
"POSIX Extended Regular Expression,\n"
|
|
" which can further filter the selection of "
|
|
"registrations.\n"
|
|
" These options are applied as logical AND, "
|
|
"but each REGEX \n"
|
|
" allows specifying multiple strings using "
|
|
"the pipe symbol.\n"
|
|
"\n"
|
|
" --witness-net-name=REGEX\n"
|
|
" This specifies the 'server name' the client\n"
|
|
" registered for monitoring.\n"
|
|
"\n"
|
|
" --witness-share-name=REGEX\n"
|
|
" This specifies the 'share name' the client\n"
|
|
" registered for monitoring.\n"
|
|
" Note that the share name is optional in the\n"
|
|
" registration, otherwise an empty string is \n"
|
|
" matched.\n"
|
|
"\n"
|
|
" --witness-ip-address=REGEX\n"
|
|
" This specifies the ip address the client\n"
|
|
" registered for monitoring.\n"
|
|
"\n"
|
|
" --witness-client-computer-name=REGEX\n"
|
|
" This specifies the client computer name the client\n"
|
|
" specified in the registration.\n"
|
|
" Note it is just a string chosen by the "
|
|
"client itself.\n"
|
|
"\n");
|
|
}
|
|
|
|
static void net_witness_list_usage(void)
|
|
{
|
|
d_printf("%s\n"
|
|
"net witness list\n"
|
|
" %s\n\n",
|
|
_("Usage:"),
|
|
_("List witness registrations "
|
|
"from rpcd_witness_registration.tdb"));
|
|
net_witness_filter_usage();
|
|
}
|
|
|
|
static int net_witness_list(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct net_witness_list_state state = { .c = c, };
|
|
struct net_witness_scan_registrations_action_state action = {
|
|
.prepare_fn = net_witness_list_prepare_fn,
|
|
.match_fn = net_witness_list_match_fn,
|
|
.process_fn = net_witness_list_process_fn,
|
|
.private_data = &state,
|
|
};
|
|
int ret = -1;
|
|
|
|
if (c->display_usage) {
|
|
net_witness_list_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (argc != 0) {
|
|
net_witness_list_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (!lp_clustering()) {
|
|
d_printf("ERROR: Only supported with clustering=yes!\n\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = net_witness_scan_registrations(c, NULL, &action);
|
|
if (ret != 0) {
|
|
d_printf("net_witness_scan_registrations() failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
struct net_witness_client_move_state {
|
|
struct net_context *c;
|
|
struct rpcd_witness_registration_updateB m;
|
|
char *headline;
|
|
};
|
|
|
|
static bool net_witness_client_move_prepare_fn(void *private_data)
|
|
{
|
|
struct net_witness_client_move_state *state =
|
|
(struct net_witness_client_move_state *)private_data;
|
|
|
|
if (state->headline != NULL) {
|
|
d_printf("%s\n", state->headline);
|
|
TALLOC_FREE(state->headline);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_client_move_match_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS net_witness_client_move_process_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct net_witness_client_move_state *state =
|
|
(struct net_witness_client_move_state *)private_data;
|
|
struct net_context *c = state->c;
|
|
struct rpcd_witness_registration_updateB update = {
|
|
.context_handle = rg->context_handle,
|
|
.type = state->m.type,
|
|
.update = state->m.update,
|
|
};
|
|
DATA_BLOB blob = { .length = 0, };
|
|
enum ndr_err_code ndr_err;
|
|
NTSTATUS status;
|
|
|
|
if (state->headline != NULL) {
|
|
d_printf("%s\n", state->headline);
|
|
TALLOC_FREE(state->headline);
|
|
}
|
|
|
|
SMB_ASSERT(update.type != 0);
|
|
|
|
if (DEBUGLVL(DBGLVL_DEBUG)) {
|
|
NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
|
|
}
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
|
|
(ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
status = messaging_send(c->msg_ctx,
|
|
rg->server_id,
|
|
MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
|
|
&blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_update_usage(void)
|
|
{
|
|
d_printf(" If the update should be applied to all registrations\n"
|
|
" it needs to be explicitly specified:\n"
|
|
"\n"
|
|
" --witness-apply-to-all\n"
|
|
" This selects all registrations.\n"
|
|
" Note: This is mutual exclusive to "
|
|
"the above options.\n"
|
|
"\n");
|
|
}
|
|
|
|
static bool net_witness_verify_update_options(struct net_context *c)
|
|
{
|
|
if (c->opt_witness_registration == NULL &&
|
|
c->opt_witness_net_name == NULL &&
|
|
c->opt_witness_share_name == NULL &&
|
|
c->opt_witness_ip_address == NULL &&
|
|
c->opt_witness_client_computer_name == NULL &&
|
|
c->opt_witness_apply_to_all == 0)
|
|
{
|
|
d_printf("--witness-apply-to-all or "
|
|
"at least one of following requires:\n"
|
|
"--witness-registration\n"
|
|
"--witness-net-name\n"
|
|
"--witness-share-name\n"
|
|
"--witness-ip-address\n"
|
|
"--witness-client-computer-name\n");
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_apply_to_all == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (c->opt_witness_registration != NULL ||
|
|
c->opt_witness_net_name != NULL ||
|
|
c->opt_witness_share_name != NULL ||
|
|
c->opt_witness_ip_address != NULL ||
|
|
c->opt_witness_client_computer_name != NULL)
|
|
{
|
|
d_printf("--witness-apply-to-all not allowed "
|
|
"together with the following options:\n"
|
|
"--witness-registration\n"
|
|
"--witness-net-name\n"
|
|
"--witness-share-name\n"
|
|
"--witness-ip-address\n"
|
|
"--witness-client-computer-name\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void net_witness_move_usage(const char *name)
|
|
{
|
|
d_printf(" The content of the %s notification contains ip addresses\n"
|
|
" specified by (exactly one) of the following options:\n"
|
|
"\n"
|
|
" --witness-new-node=NODEID\n"
|
|
" By specifying a NODEID all ip addresses\n"
|
|
" currently available on the given node are\n"
|
|
" included in the response.\n"
|
|
" By specifying '-1' as NODEID all ip addresses\n"
|
|
" of the cluster are included in the response.\n"
|
|
"\n"
|
|
" --witness-new-ip=IPADDRESS\n"
|
|
" By specifying an IPADDRESS only the specified\n"
|
|
" ip address is included in the response.\n"
|
|
"\n",
|
|
name);
|
|
}
|
|
|
|
static bool net_witness_verify_move_options(struct net_context *c,
|
|
uint32_t *new_node,
|
|
bool *is_ipv4,
|
|
bool *is_ipv6)
|
|
{
|
|
bool ok;
|
|
|
|
*new_node = NONCLUSTER_VNN;
|
|
*is_ipv4 = false;
|
|
*is_ipv6 = false;
|
|
|
|
ok = net_witness_verify_update_options(c);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_new_ip != NULL &&
|
|
c->opt_witness_new_node != -2)
|
|
{
|
|
d_printf("--witness-new-ip and "
|
|
"--witness-new-node are not allowed together\n");
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_new_ip == NULL &&
|
|
c->opt_witness_new_node == -2)
|
|
{
|
|
d_printf("--witness-new-ip or --witness-new-node required\n");
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_new_node != -2) {
|
|
*new_node = c->opt_witness_new_node;
|
|
return true;
|
|
}
|
|
|
|
if (is_ipaddress_v4(c->opt_witness_new_ip)) {
|
|
*is_ipv4 = true;
|
|
return true;
|
|
}
|
|
|
|
if (is_ipaddress_v6(c->opt_witness_new_ip)) {
|
|
*is_ipv6 = true;
|
|
return true;
|
|
}
|
|
|
|
d_printf("Invalid ip address for --witness-new-ip=%s\n",
|
|
c->opt_witness_new_ip);
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
static bool net_witness_move_message_json(struct net_context *c,
|
|
const char *msg_type,
|
|
struct json_object *pmessage_json)
|
|
{
|
|
struct json_object message_json = json_empty_object;
|
|
int ret;
|
|
|
|
message_json = json_new_object();
|
|
if (json_is_invalid(&message_json)) {
|
|
return false;
|
|
}
|
|
|
|
ret = json_add_string(&message_json,
|
|
"type",
|
|
msg_type);
|
|
if (ret != 0) {
|
|
json_free(&message_json);
|
|
return false;
|
|
}
|
|
|
|
if (c->opt_witness_new_ip != NULL) {
|
|
ret = json_add_string(&message_json,
|
|
"new_ip",
|
|
c->opt_witness_new_ip);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
} else if (c->opt_witness_new_node != -1) {
|
|
ret = json_add_int(&message_json,
|
|
"new_node",
|
|
c->opt_witness_new_node);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
} else {
|
|
ret = json_add_bool(&message_json,
|
|
"all_nodes",
|
|
true);
|
|
if (ret != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
*pmessage_json = message_json;
|
|
return true;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
static void net_witness_client_move_usage(void)
|
|
{
|
|
d_printf("%s\n"
|
|
"net witness client-move\n"
|
|
" %s\n\n",
|
|
_("Usage:"),
|
|
_("Generate client move notifications for "
|
|
"witness registrations to a new ip or node"));
|
|
net_witness_filter_usage();
|
|
net_witness_update_usage();
|
|
net_witness_move_usage("CLIENT_MOVE");
|
|
}
|
|
|
|
static int net_witness_client_move(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct net_witness_client_move_state state = { .c = c, };
|
|
struct rpcd_witness_registration_updateB *m = &state.m;
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object _message_json = json_empty_object;
|
|
#endif /* HAVE_JANSSON */
|
|
struct json_object *message_json = NULL;
|
|
struct net_witness_scan_registrations_action_state action = {
|
|
.prepare_fn = net_witness_client_move_prepare_fn,
|
|
.match_fn = net_witness_client_move_match_fn,
|
|
.process_fn = net_witness_client_move_process_fn,
|
|
.private_data = &state,
|
|
};
|
|
int ret = -1;
|
|
const char *msg_type = NULL;
|
|
uint32_t new_node = NONCLUSTER_VNN;
|
|
bool is_ipv4 = false;
|
|
bool is_ipv6 = false;
|
|
bool ok;
|
|
|
|
if (c->display_usage) {
|
|
net_witness_client_move_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (argc != 0) {
|
|
net_witness_client_move_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (!lp_clustering()) {
|
|
d_printf("ERROR: Only supported with clustering=yes!\n\n");
|
|
goto out;
|
|
}
|
|
|
|
ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
|
|
if (is_ipv4) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV4;
|
|
m->update.client_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
|
|
msg_type = "CLIENT_MOVE_TO_IPV4";
|
|
state.headline = talloc_asprintf(frame,
|
|
"CLIENT_MOVE_TO_IPV4: %s",
|
|
c->opt_witness_new_ip);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else if (is_ipv6) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV6;
|
|
m->update.client_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
|
|
msg_type = "CLIENT_MOVE_TO_IPV6";
|
|
state.headline = talloc_asprintf(frame,
|
|
"CLIENT_MOVE_TO_IPV6: %s",
|
|
c->opt_witness_new_ip);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else if (new_node != NONCLUSTER_VNN) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
|
|
m->update.client_move_to_node.new_node = new_node;
|
|
msg_type = "CLIENT_MOVE_TO_NODE";
|
|
state.headline = talloc_asprintf(frame,
|
|
"CLIENT_MOVE_TO_NODE: %u",
|
|
new_node);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
|
|
m->update.client_move_to_node.new_node = NONCLUSTER_VNN;
|
|
msg_type = "CLIENT_MOVE_TO_NODE";
|
|
state.headline = talloc_asprintf(frame,
|
|
"CLIENT_MOVE_TO_NODE: ALL");
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
if (c->opt_json) {
|
|
TALLOC_FREE(state.headline);
|
|
|
|
ok = net_witness_move_message_json(c,
|
|
msg_type,
|
|
&_message_json);
|
|
if (!ok) {
|
|
d_printf("net_witness_move_message_json(%s) failed\n",
|
|
msg_type);
|
|
goto out;
|
|
}
|
|
|
|
message_json = &_message_json;
|
|
}
|
|
#else /* not HAVE_JANSSON */
|
|
(void)msg_type;
|
|
#endif /* not HAVE_JANSSON */
|
|
|
|
ret = net_witness_scan_registrations(c, message_json, &action);
|
|
if (ret != 0) {
|
|
d_printf("net_witness_scan_registrations() failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
#ifdef HAVE_JANSSON
|
|
if (!json_is_invalid(&_message_json)) {
|
|
json_free(&_message_json);
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
struct net_witness_share_move_state {
|
|
struct net_context *c;
|
|
struct rpcd_witness_registration_updateB m;
|
|
char *headline;
|
|
};
|
|
|
|
static bool net_witness_share_move_prepare_fn(void *private_data)
|
|
{
|
|
struct net_witness_share_move_state *state =
|
|
(struct net_witness_share_move_state *)private_data;
|
|
|
|
if (state->headline != NULL) {
|
|
d_printf("%s\n", state->headline);
|
|
TALLOC_FREE(state->headline);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_share_move_match_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
if (rg->share_name == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS net_witness_share_move_process_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct net_witness_share_move_state *state =
|
|
(struct net_witness_share_move_state *)private_data;
|
|
struct net_context *c = state->c;
|
|
struct rpcd_witness_registration_updateB update = {
|
|
.context_handle = rg->context_handle,
|
|
.type = state->m.type,
|
|
.update = state->m.update,
|
|
};
|
|
DATA_BLOB blob = { .length = 0, };
|
|
enum ndr_err_code ndr_err;
|
|
NTSTATUS status;
|
|
|
|
SMB_ASSERT(update.type != 0);
|
|
|
|
if (DEBUGLVL(DBGLVL_DEBUG)) {
|
|
NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
|
|
}
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
|
|
(ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
status = messaging_send(c->msg_ctx,
|
|
rg->server_id,
|
|
MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
|
|
&blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_share_move_usage(void)
|
|
{
|
|
d_printf("%s\n"
|
|
"net witness share-move\n"
|
|
" %s\n\n",
|
|
_("Usage:"),
|
|
_("Generate share move notifications for "
|
|
"witness registrations to a new ip or node"));
|
|
net_witness_filter_usage();
|
|
net_witness_update_usage();
|
|
d_printf(" Note: This only applies to registrations with "
|
|
"a non empty share name!\n\n");
|
|
net_witness_move_usage("SHARE_MOVE");
|
|
}
|
|
|
|
static int net_witness_share_move(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct net_witness_share_move_state state = { .c = c, };
|
|
struct rpcd_witness_registration_updateB *m = &state.m;
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object _message_json = json_empty_object;
|
|
#endif /* HAVE_JANSSON */
|
|
struct json_object *message_json = NULL;
|
|
struct net_witness_scan_registrations_action_state action = {
|
|
.prepare_fn = net_witness_share_move_prepare_fn,
|
|
.match_fn = net_witness_share_move_match_fn,
|
|
.process_fn = net_witness_share_move_process_fn,
|
|
.private_data = &state,
|
|
};
|
|
int ret = -1;
|
|
const char *msg_type = NULL;
|
|
uint32_t new_node = NONCLUSTER_VNN;
|
|
bool is_ipv4 = false;
|
|
bool is_ipv6 = false;
|
|
bool ok;
|
|
|
|
if (c->display_usage) {
|
|
net_witness_share_move_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (argc != 0) {
|
|
net_witness_share_move_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (!lp_clustering()) {
|
|
d_printf("ERROR: Only supported with clustering=yes!\n\n");
|
|
goto out;
|
|
}
|
|
|
|
ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
|
|
if (is_ipv4) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV4;
|
|
m->update.share_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
|
|
msg_type = "SHARE_MOVE_TO_IPV4";
|
|
state.headline = talloc_asprintf(frame,
|
|
"SHARE_MOVE_TO_IPV4: %s",
|
|
c->opt_witness_new_ip);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else if (is_ipv6) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV6;
|
|
m->update.share_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
|
|
msg_type = "SHARE_MOVE_TO_IPV6";
|
|
state.headline = talloc_asprintf(frame,
|
|
"SHARE_MOVE_TO_IPV6: %s",
|
|
c->opt_witness_new_ip);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else if (new_node != NONCLUSTER_VNN) {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
|
|
m->update.share_move_to_node.new_node = new_node;
|
|
msg_type = "SHARE_MOVE_TO_NODE";
|
|
state.headline = talloc_asprintf(frame,
|
|
"SHARE_MOVE_TO_NODE: %u",
|
|
new_node);
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
} else {
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
|
|
m->update.share_move_to_node.new_node = NONCLUSTER_VNN;
|
|
msg_type = "SHARE_MOVE_TO_NODE";
|
|
state.headline = talloc_asprintf(frame,
|
|
"SHARE_MOVE_TO_NODE: ALL");
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
if (c->opt_json) {
|
|
TALLOC_FREE(state.headline);
|
|
|
|
ok = net_witness_move_message_json(c,
|
|
msg_type,
|
|
&_message_json);
|
|
if (!ok) {
|
|
d_printf("net_witness_move_message_json(%s) failed\n",
|
|
msg_type);
|
|
goto out;
|
|
}
|
|
|
|
message_json = &_message_json;
|
|
}
|
|
#else /* not HAVE_JANSSON */
|
|
(void)msg_type;
|
|
#endif /* not HAVE_JANSSON */
|
|
|
|
ret = net_witness_scan_registrations(c, message_json, &action);
|
|
if (ret != 0) {
|
|
d_printf("net_witness_scan_registrations() failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
#ifdef HAVE_JANSSON
|
|
if (!json_is_invalid(&_message_json)) {
|
|
json_free(&_message_json);
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
struct net_witness_force_unregister_state {
|
|
struct net_context *c;
|
|
struct rpcd_witness_registration_updateB m;
|
|
char *headline;
|
|
};
|
|
|
|
static bool net_witness_force_unregister_prepare_fn(void *private_data)
|
|
{
|
|
struct net_witness_force_unregister_state *state =
|
|
(struct net_witness_force_unregister_state *)private_data;
|
|
|
|
if (state->headline != NULL) {
|
|
d_printf("%s\n", state->headline);
|
|
TALLOC_FREE(state->headline);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_force_unregister_match_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS net_witness_force_unregister_process_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct net_witness_force_unregister_state *state =
|
|
(struct net_witness_force_unregister_state *)private_data;
|
|
struct net_context *c = state->c;
|
|
struct rpcd_witness_registration_updateB update = {
|
|
.context_handle = rg->context_handle,
|
|
.type = state->m.type,
|
|
.update = state->m.update,
|
|
};
|
|
DATA_BLOB blob = { .length = 0, };
|
|
enum ndr_err_code ndr_err;
|
|
NTSTATUS status;
|
|
|
|
SMB_ASSERT(update.type != 0);
|
|
|
|
if (DEBUGLVL(DBGLVL_DEBUG)) {
|
|
NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
|
|
}
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
|
|
(ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
status = messaging_send(c->msg_ctx,
|
|
rg->server_id,
|
|
MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
|
|
&blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_force_unregister_usage(void)
|
|
{
|
|
d_printf("%s\n"
|
|
"net witness force-unregister\n"
|
|
" %s\n\n",
|
|
_("Usage:"),
|
|
_("Force unregistrations for witness registrations"));
|
|
net_witness_filter_usage();
|
|
net_witness_update_usage();
|
|
d_printf(" The selected registrations are removed on "
|
|
"the server and\n"
|
|
" any pending AsyncNotify request will get "
|
|
"a NOT_FOUND error.\n"
|
|
"\n"
|
|
" Typically this triggers a clean re-registration "
|
|
"on the client.\n"
|
|
"\n");
|
|
}
|
|
|
|
static int net_witness_force_unregister(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct net_witness_force_unregister_state state = { .c = c, };
|
|
struct rpcd_witness_registration_updateB *m = &state.m;
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object _message_json = json_empty_object;
|
|
#endif /* HAVE_JANSSON */
|
|
struct json_object *message_json = NULL;
|
|
struct net_witness_scan_registrations_action_state action = {
|
|
.prepare_fn = net_witness_force_unregister_prepare_fn,
|
|
.match_fn = net_witness_force_unregister_match_fn,
|
|
.process_fn = net_witness_force_unregister_process_fn,
|
|
.private_data = &state,
|
|
};
|
|
int ret = -1;
|
|
bool ok;
|
|
|
|
if (c->display_usage) {
|
|
net_witness_force_unregister_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (argc != 0) {
|
|
net_witness_force_unregister_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (!lp_clustering()) {
|
|
d_printf("ERROR: Only supported with clustering=yes!\n\n");
|
|
goto out;
|
|
}
|
|
|
|
ok = net_witness_verify_update_options(c);
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
|
|
m->type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER;
|
|
|
|
state.headline = talloc_asprintf(frame, "FORCE_UNREGISTER:");
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
if (c->opt_json) {
|
|
TALLOC_FREE(state.headline);
|
|
|
|
_message_json = json_new_object();
|
|
if (json_is_invalid(&_message_json)) {
|
|
goto out;
|
|
}
|
|
|
|
ret = json_add_string(&_message_json,
|
|
"type",
|
|
"FORCE_UNREGISTER");
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
message_json = &_message_json;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
ret = net_witness_scan_registrations(c, message_json, &action);
|
|
if (ret != 0) {
|
|
d_printf("net_witness_scan_registrations() failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
#ifdef HAVE_JANSSON
|
|
if (!json_is_invalid(&_message_json)) {
|
|
json_free(&_message_json);
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
struct net_witness_force_response_state {
|
|
struct net_context *c;
|
|
struct rpcd_witness_registration_updateB m;
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object json_root;
|
|
#endif /* HAVE_JANSSON */
|
|
char *headline;
|
|
};
|
|
|
|
#ifdef HAVE_JANSSON
|
|
static NTSTATUS net_witness_force_response_parse_rc(
|
|
struct net_witness_force_response_state *state,
|
|
json_t *jsmsg,
|
|
TALLOC_CTX *mem_ctx,
|
|
size_t mi,
|
|
union witness_notifyResponse_message *message)
|
|
{
|
|
struct witness_ResourceChange *rc = &message->resource_change;
|
|
json_t *jsctype = NULL;
|
|
json_int_t ctype;
|
|
json_t *jscname = NULL;
|
|
const char *cname = NULL;
|
|
|
|
if (!json_is_object(jsmsg)) {
|
|
DBG_ERR("'message[%zu]' needs to be an object\n", mi);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
jsctype = json_object_get(jsmsg, "type");
|
|
if (jsctype == NULL) {
|
|
DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!json_is_integer(jsctype)) {
|
|
DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
ctype = json_integer_value(jsctype);
|
|
|
|
jscname = json_object_get(jsmsg, "name");
|
|
if (jscname == NULL) {
|
|
DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!json_is_string(jscname)) {
|
|
DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
cname = json_string_value(jscname);
|
|
|
|
rc->type = ctype;
|
|
rc->name = talloc_strdup(mem_ctx, cname);
|
|
if (rc->name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS net_witness_force_response_parse_ipl(
|
|
struct net_witness_force_response_state *state,
|
|
json_t *jsmsg,
|
|
TALLOC_CTX *mem_ctx,
|
|
size_t mi,
|
|
union witness_notifyResponse_message *message)
|
|
{
|
|
struct witness_IPaddrInfoList *ipl =
|
|
&message->client_move;
|
|
size_t ai, num_addrs = 0;
|
|
struct witness_IPaddrInfo *addrs = NULL;
|
|
|
|
if (!json_is_array(jsmsg)) {
|
|
DBG_ERR("'messages[%zu]' needs to be an array\n", mi);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
num_addrs = json_array_size(jsmsg);
|
|
if (num_addrs > UINT32_MAX) {
|
|
DBG_ERR("Too many elements in 'messages[%zu]': %zu\n",
|
|
mi, num_addrs);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
addrs = talloc_zero_array(mem_ctx,
|
|
struct witness_IPaddrInfo,
|
|
num_addrs);
|
|
if (addrs == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (ai = 0; ai < num_addrs; ai++) {
|
|
struct witness_IPaddrInfo *info =
|
|
&addrs[ai];
|
|
json_t *jsaddr = json_array_get(jsmsg, ai);
|
|
json_t *jsflags = NULL;
|
|
json_int_t flags;
|
|
json_t *jsipv4 = NULL;
|
|
const char *ipv4 = NULL;
|
|
json_t *jsipv6 = NULL;
|
|
const char *ipv6 = NULL;
|
|
|
|
if (!json_is_object(jsaddr)) {
|
|
DBG_ERR("'messages[%zu][%zu]' needs to be an object\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
jsflags = json_object_get(jsaddr, "flags");
|
|
if (jsflags == NULL) {
|
|
DBG_ERR("'messages[%zu][%zu]['flags']' missing\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!json_is_integer(jsflags)) {
|
|
DBG_ERR("'messages[%zu][%zu]['flags']' "
|
|
"needs to be an integer\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
flags = json_integer_value(jsflags);
|
|
|
|
jsipv4 = json_object_get(jsaddr, "ipv4");
|
|
if (jsipv4 != NULL) {
|
|
if (!json_is_string(jsipv4)) {
|
|
DBG_ERR("'messages[%zu][%zu]['ipv4']' "
|
|
"needs to be a string\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
ipv4 = json_string_value(jsipv4);
|
|
if (!is_ipaddress_v4(ipv4)) {
|
|
DBG_ERR("'messages[%zu][%zu]['ipv4']' "
|
|
"needs to be a valid ipv4 address\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
ipv4 = "0.0.0.0";
|
|
}
|
|
|
|
jsipv6 = json_object_get(jsaddr, "ipv6");
|
|
if (jsipv6 != NULL) {
|
|
if (!json_is_string(jsipv6)) {
|
|
DBG_ERR("'messages[%zu][%zu]['ipv6']' "
|
|
"needs to be a string\n",
|
|
mi, ai);
|
|
DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
ipv6 = json_string_value(jsipv6);
|
|
if (!is_ipaddress_v6(ipv6)) {
|
|
DBG_ERR("'messages[%zu][%zu]['ipv4']' "
|
|
"needs to be a valid ipv6 address\n",
|
|
mi, ai);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
ipv6 = "::";
|
|
}
|
|
|
|
info->flags = flags;
|
|
info->ipv4 = talloc_strdup(addrs, ipv4);
|
|
if (info->ipv4 == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
info->ipv6 = talloc_strdup(addrs, ipv6);
|
|
if (info->ipv6 == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
ipl->num = num_addrs;
|
|
ipl->addr = addrs;
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
static NTSTATUS net_witness_force_response_parse(struct net_witness_force_response_state *state)
|
|
{
|
|
#ifdef HAVE_JANSSON
|
|
struct net_context *c = state->c;
|
|
struct rpcd_witness_registration_update_force_response *force = NULL;
|
|
struct witness_notifyResponse *response = NULL;
|
|
size_t mi, num_messages = 0;
|
|
union witness_notifyResponse_message *messages = NULL;
|
|
json_t *jsroot = NULL;
|
|
json_t *jsresult = NULL;
|
|
json_t *jsresponse = NULL;
|
|
json_t *jstype = NULL;
|
|
json_t *jsmessages = NULL;
|
|
|
|
if (c->opt_witness_forced_response != NULL) {
|
|
const char *str = c->opt_witness_forced_response;
|
|
size_t flags = JSON_REJECT_DUPLICATES;
|
|
json_error_t jserror;
|
|
|
|
jsroot = json_loads(str, flags, &jserror);
|
|
if (jsroot == NULL) {
|
|
DBG_ERR("Invalid JSON in "
|
|
"--witness-forced-response='%s'\n",
|
|
str);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
state->json_root = (struct json_object) {
|
|
.root = jsroot,
|
|
.valid = true,
|
|
};
|
|
}
|
|
|
|
state->m.type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_RESPONSE;
|
|
force = &state->m.update.force_response;
|
|
force->response = NULL;
|
|
force->result = WERR_OK;
|
|
|
|
if (jsroot == NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
jsresult = json_object_get(jsroot, "result");
|
|
if (jsresult != NULL) {
|
|
int val_type = json_typeof(jsresult);
|
|
|
|
switch (val_type) {
|
|
case JSON_INTEGER: {
|
|
json_int_t val = json_integer_value(jsresult);
|
|
|
|
if (val > UINT32_MAX) {
|
|
DBG_ERR("Invalid 'result' value: %d\n",
|
|
(int) val);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (val < 0) {
|
|
DBG_ERR("invalid 'result' value: %d\n",
|
|
(int) val);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
force->result = W_ERROR(val);
|
|
}; break;
|
|
default:
|
|
DBG_ERR("Invalid json type for 'result' - needs integer\n");
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
jsresponse = json_object_get(jsroot, "response");
|
|
if (jsresponse == NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (!json_is_object(jsresponse)) {
|
|
DBG_ERR("Invalid json type 'response' needs object\n");
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
response = talloc_zero(talloc_tos(), struct witness_notifyResponse);
|
|
if (response == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
jstype = json_object_get(jsresponse, "type");
|
|
if (jstype == NULL) {
|
|
DBG_ERR("Missing 'type' element in 'response'\n");
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
{
|
|
int val_type = json_typeof(jstype);
|
|
|
|
switch (val_type) {
|
|
case JSON_INTEGER: {
|
|
json_int_t val = json_integer_value(jstype);
|
|
|
|
if (val > WITNESS_NOTIFY_IP_CHANGE) {
|
|
DBG_ERR("invalid 'type' value in 'response': "
|
|
"%d\n", (int) val);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (val < WITNESS_NOTIFY_RESOURCE_CHANGE) {
|
|
DBG_ERR("invalid 'type' value in 'response': "
|
|
"%d\n", (int) val);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
response->type = val;
|
|
}; break;
|
|
default:
|
|
DBG_ERR("Invalid json type for 'type' in 'response' "
|
|
"- needs integer\n");
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
force->response = response;
|
|
|
|
jsmessages = json_object_get(jsresponse, "messages");
|
|
if (jsmessages == NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (!json_is_array(jsmessages)) {
|
|
DBG_ERR("'messages' in 'response' needs to be an array\n");
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
num_messages = json_array_size(jsmessages);
|
|
if (num_messages > UINT32_MAX) {
|
|
DBG_ERR("Too many elements in 'messages': %zu\n",
|
|
num_messages);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
messages = talloc_zero_array(response,
|
|
union witness_notifyResponse_message,
|
|
num_messages);
|
|
if (messages == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (mi = 0; mi < num_messages; mi++) {
|
|
json_t *jsmsg = json_array_get(jsmessages, mi);
|
|
union witness_notifyResponse_message *message = &messages[mi];
|
|
NTSTATUS status;
|
|
|
|
switch (response->type) {
|
|
case WITNESS_NOTIFY_RESOURCE_CHANGE:
|
|
status = net_witness_force_response_parse_rc(state,
|
|
jsmsg,
|
|
messages,
|
|
mi,
|
|
message);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
const char *fn =
|
|
"net_witness_force_response_parse_rc";
|
|
DBG_ERR("%s failed: %s\n",
|
|
fn, nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
break;
|
|
case WITNESS_NOTIFY_CLIENT_MOVE:
|
|
case WITNESS_NOTIFY_SHARE_MOVE:
|
|
case WITNESS_NOTIFY_IP_CHANGE:
|
|
status = net_witness_force_response_parse_ipl(state,
|
|
jsmsg,
|
|
messages,
|
|
mi,
|
|
message);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
const char *fn =
|
|
"net_witness_force_response_parse_ipl";
|
|
DBG_ERR("%s failed: %s\n",
|
|
fn, nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
response->num = num_messages;
|
|
response->messages = messages;
|
|
|
|
return NT_STATUS_OK;
|
|
#else /* not HAVE_JANSSON */
|
|
d_fprintf(stderr, _("JSON support not available\n"));
|
|
return NT_STATUS_NOT_IMPLEMENTED;
|
|
#endif /* not HAVE_JANSSON */
|
|
}
|
|
|
|
static bool net_witness_force_response_prepare_fn(void *private_data)
|
|
{
|
|
struct net_witness_force_response_state *state =
|
|
(struct net_witness_force_response_state *)private_data;
|
|
|
|
if (state->headline != NULL) {
|
|
d_printf("%s\n", state->headline);
|
|
TALLOC_FREE(state->headline);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool net_witness_force_response_match_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS net_witness_force_response_process_fn(void *private_data,
|
|
const struct rpcd_witness_registration *rg)
|
|
{
|
|
struct net_witness_force_response_state *state =
|
|
(struct net_witness_force_response_state *)private_data;
|
|
struct net_context *c = state->c;
|
|
struct rpcd_witness_registration_updateB update = {
|
|
.context_handle = rg->context_handle,
|
|
.type = state->m.type,
|
|
.update = state->m.update,
|
|
};
|
|
DATA_BLOB blob = { .length = 0, };
|
|
enum ndr_err_code ndr_err;
|
|
NTSTATUS status;
|
|
|
|
SMB_ASSERT(update.type != 0);
|
|
|
|
if (DEBUGLVL(DBGLVL_DEBUG)) {
|
|
NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
|
|
}
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
|
|
(ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
status = messaging_send(c->msg_ctx,
|
|
rg->server_id,
|
|
MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
|
|
&blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static void net_witness_force_response_usage(void)
|
|
{
|
|
d_printf("%s\n"
|
|
"net witness force-response\n"
|
|
" %s\n\n",
|
|
_("Usage:"),
|
|
_("Force an AsyncNotify response based on "
|
|
"json input (mostly for testing)"));
|
|
net_witness_filter_usage();
|
|
net_witness_update_usage();
|
|
d_printf(" Note this is designed for testing and debugging!\n"
|
|
"\n"
|
|
" In short it is not designed to be used by "
|
|
"administrators,\n"
|
|
" but developers and automated tests.\n"
|
|
"\n"
|
|
" By default an empty response with WERR_OK is generated,\n"
|
|
" but basically any valid response can be specified by a\n"
|
|
" specifying a JSON string:\n"
|
|
"\n"
|
|
" --witness-forced-response=JSON\n"
|
|
" This allows the generation of very complex\n"
|
|
" witness_notifyResponse structures.\n"
|
|
"\n"
|
|
" As this is for developers, please read the code\n"
|
|
" in order to understand all possible values\n"
|
|
" of the JSON string format...\n"
|
|
"\n"
|
|
" Simple examples are:\n"
|
|
"\n"
|
|
"# Resource Change:\n%s\n"
|
|
"\n"
|
|
"# Client Move:\n%s\n"
|
|
"\n"
|
|
"# Share Move:\n%s\n"
|
|
"\n"
|
|
"# IP Change:\n%s\n"
|
|
"\n",
|
|
"'{ \"result\": 0, \"response\": { \"type\": 1, "
|
|
"\"messages\": [ { "
|
|
"\"type\": 255 , "
|
|
"\"name\": \"some-resource-name\" "
|
|
"} ]"
|
|
"}}'",
|
|
"'{ \"result\": 0, \"response\": { \"type\": 2, "
|
|
"\"messages\": ["
|
|
"[{ "
|
|
"\"flags\": 9, "
|
|
"\"ipv4\": \"10.0.10.1\" "
|
|
"}]"
|
|
"]"
|
|
"}}'",
|
|
"'{ \"result\": 0, \"response\": { \"type\": 3, "
|
|
"\"messages\": ["
|
|
"[{ "
|
|
"\"flags\": 9, "
|
|
"\"ipv4\": \"10.0.10.1\" "
|
|
"}]"
|
|
"]"
|
|
"}}'",
|
|
"'{ \"result\": 0, \"response\": { \"type\": 4, "
|
|
"\"messages\": ["
|
|
"[{ "
|
|
"\"flags\": 9, "
|
|
"\"ipv4\": \"10.0.10.1\" "
|
|
"}]"
|
|
"]"
|
|
"}}'");
|
|
}
|
|
|
|
static int net_witness_force_response(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct net_witness_force_response_state state = { .c = c, };
|
|
#ifdef HAVE_JANSSON
|
|
struct json_object _message_json = json_empty_object;
|
|
#endif /* HAVE_JANSSON */
|
|
struct json_object *message_json = NULL;
|
|
struct net_witness_scan_registrations_action_state action = {
|
|
.prepare_fn = net_witness_force_response_prepare_fn,
|
|
.match_fn = net_witness_force_response_match_fn,
|
|
.process_fn = net_witness_force_response_process_fn,
|
|
.private_data = &state,
|
|
};
|
|
NTSTATUS status;
|
|
int ret = -1;
|
|
bool ok;
|
|
|
|
if (c->display_usage) {
|
|
net_witness_force_response_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (argc != 0) {
|
|
net_witness_force_response_usage();
|
|
goto out;
|
|
}
|
|
|
|
if (!lp_clustering()) {
|
|
d_printf("ERROR: Only supported with clustering=yes!\n\n");
|
|
goto out;
|
|
}
|
|
|
|
ok = net_witness_verify_update_options(c);
|
|
if (!ok) {
|
|
goto out;
|
|
}
|
|
|
|
status = net_witness_force_response_parse(&state);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
d_printf("net_witness_force_response_parse failed: %s\n",
|
|
nt_errstr(status));
|
|
goto out;
|
|
}
|
|
|
|
state.headline = talloc_asprintf(frame, "FORCE_RESPONSE:%s%s",
|
|
c->opt_witness_forced_response != NULL ?
|
|
" " : "",
|
|
c->opt_witness_forced_response != NULL ?
|
|
c->opt_witness_forced_response : "");
|
|
|
|
if (state.headline == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
#ifdef HAVE_JANSSON
|
|
if (c->opt_json) {
|
|
TALLOC_FREE(state.headline);
|
|
|
|
_message_json = json_new_object();
|
|
if (json_is_invalid(&_message_json)) {
|
|
goto out;
|
|
}
|
|
|
|
ret = json_add_string(&_message_json,
|
|
"type",
|
|
"FORCE_RESPONSE");
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (!json_is_invalid(&state.json_root)) {
|
|
ret = json_add_object(&_message_json,
|
|
"json",
|
|
&state.json_root);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
state.json_root = json_empty_object;
|
|
}
|
|
message_json = &_message_json;
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
|
|
ret = net_witness_scan_registrations(c, message_json, &action);
|
|
if (ret != 0) {
|
|
d_printf("net_witness_scan_registrations() failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
#ifdef HAVE_JANSSON
|
|
if (!json_is_invalid(&_message_json)) {
|
|
json_free(&_message_json);
|
|
}
|
|
if (!json_is_invalid(&state.json_root)) {
|
|
json_free(&state.json_root);
|
|
}
|
|
#endif /* HAVE_JANSSON */
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
int net_witness(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
struct functable func[] = {
|
|
{
|
|
"list",
|
|
net_witness_list,
|
|
NET_TRANSPORT_LOCAL,
|
|
N_("List witness registrations "
|
|
"from rpcd_witness_registration.tdb"),
|
|
N_("net witness list\n"
|
|
" List witness registrations "
|
|
"from rpcd_witness_registration.tdb"),
|
|
},
|
|
{
|
|
"client-move",
|
|
net_witness_client_move,
|
|
NET_TRANSPORT_LOCAL,
|
|
N_("Generate client move notifications for "
|
|
"witness registrations to a new ip or node"),
|
|
N_("net witness client-move\n"
|
|
" Generate client move notifications for "
|
|
"witness registrations to a new ip or node"),
|
|
},
|
|
{
|
|
"share-move",
|
|
net_witness_share_move,
|
|
NET_TRANSPORT_LOCAL,
|
|
N_("Generate share move notifications for "
|
|
"witness registrations to a new ip or node"),
|
|
N_("net witness share-move\n"
|
|
" Generate share move notifications for "
|
|
"witness registrations to a new ip or node"),
|
|
},
|
|
{
|
|
"force-unregister",
|
|
net_witness_force_unregister,
|
|
NET_TRANSPORT_LOCAL,
|
|
N_("Force unregistrations for witness registrations"),
|
|
N_("net witness force-unregister\n"
|
|
" Force unregistrations for "
|
|
"witness registrations"),
|
|
},
|
|
{
|
|
"force-response",
|
|
net_witness_force_response,
|
|
NET_TRANSPORT_LOCAL,
|
|
N_("Force an AsyncNotify response based on "
|
|
"json input (mostly for testing)"),
|
|
N_("net witness force-response\n"
|
|
" Force an AsyncNotify response based on "
|
|
"json input (mostly for testing)"),
|
|
},
|
|
{NULL, NULL, 0, NULL, NULL}
|
|
};
|
|
|
|
return net_run_function(c, argc, argv, "net witness", func);
|
|
}
|