lib: Add a few required includes
[samba4-gss.git] / source3 / utils / net_witness.c
blobaccff5b36e75b1b328ceb550df4b2418bf887ed5
1 /*
2 * Samba Unix/Linux client library
3 * net witness commands to manage smb witness registrations
4 * Copyright (C) 2023 Stefan Metzmacher
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "utils/net.h"
22 #include "messages.h"
23 #include "serverid.h"
24 #include "lib/util/util_tdb.h"
25 #include "source3/include/util_tdb.h"
26 #include "libcli/security/dom_sid.h"
27 #include "lib/dbwrap/dbwrap.h"
28 #include "lib/dbwrap/dbwrap_rbt.h"
29 #include "lib/dbwrap/dbwrap_open.h"
30 #include "lib/param/param.h"
31 #include "librpc/gen_ndr/ndr_rpcd_witness.h"
32 #include <regex.h>
34 struct json_object;
36 #ifdef HAVE_JANSSON
37 #include <jansson.h>
38 #include "audit_logging.h" /* various JSON helpers */
39 #endif /* HAVE_JANSSON */
41 #undef strcasecmp
43 static struct db_context *net_witness_open_registration_db(void)
45 static struct db_context *db;
46 char *global_path = NULL;
48 if (db != NULL) {
49 return db;
52 global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
53 if (global_path == NULL) {
54 return NULL;
57 db = db_open(NULL,
58 global_path,
59 0, /* hash_size */
60 TDB_DEFAULT |
61 TDB_CLEAR_IF_FIRST |
62 TDB_INCOMPATIBLE_HASH,
63 O_RDONLY,
64 0600,
65 DBWRAP_LOCK_ORDER_1,
66 DBWRAP_FLAG_NONE);
67 TALLOC_FREE(global_path);
68 if (db == NULL) {
69 return NULL;
72 return db;
75 struct net_witness_scan_registrations_action_state {
76 bool (*prepare_fn)(void *private_data);
77 bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg);
78 NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg);
79 void *private_data;
82 struct net_witness_scan_registrations_regex {
83 regex_t regex;
84 bool valid;
87 struct net_witness_scan_registrations_state {
88 struct net_context *c;
89 struct net_witness_scan_registrations_regex net_name;
90 struct net_witness_scan_registrations_regex share_name;
91 struct net_witness_scan_registrations_regex ip_address;
92 struct net_witness_scan_registrations_regex client_computer;
93 struct json_object *message_json;
94 #ifdef HAVE_JANSSON
95 struct json_object filters_json;
96 struct json_object registrations_json;
97 #endif
98 const struct net_witness_scan_registrations_action_state *action;
99 NTSTATUS error;
102 static bool net_witness_scan_registrations_regex_init(
103 struct net_witness_scan_registrations_state *state,
104 struct net_witness_scan_registrations_regex *r,
105 const char *option, const char *value);
106 static bool net_witness_scan_registrations_regex_match(
107 struct net_witness_scan_registrations_regex *r,
108 const char *name, const char *value);
109 static void net_witness_scan_registrations_regex_free(
110 struct net_witness_scan_registrations_regex *r);
112 static bool net_witness_scan_registrations_match(
113 struct net_witness_scan_registrations_state *state,
114 const struct rpcd_witness_registration *rg)
116 if (state->net_name.valid) {
117 bool match;
119 match = net_witness_scan_registrations_regex_match(
120 &state->net_name,
121 "net_name",
122 rg->net_name);
123 if (!match) {
124 return false;
128 if (state->share_name.valid) {
129 bool match;
131 match = net_witness_scan_registrations_regex_match(
132 &state->share_name,
133 "share_name",
134 rg->share_name);
135 if (!match) {
136 return false;
140 if (state->ip_address.valid) {
141 bool match;
143 match = net_witness_scan_registrations_regex_match(
144 &state->ip_address,
145 "ip_address",
146 rg->ip_address);
147 if (!match) {
148 return false;
152 if (state->client_computer.valid) {
153 bool match;
155 match = net_witness_scan_registrations_regex_match(
156 &state->client_computer,
157 "client_computer_name",
158 rg->client_computer_name);
159 if (!match) {
160 return false;
164 return true;
167 static bool net_witness_scan_registrations_regex_init(
168 struct net_witness_scan_registrations_state *state,
169 struct net_witness_scan_registrations_regex *r,
170 const char *option, const char *value)
172 #ifdef HAVE_JANSSON
173 struct net_context *c = state->c;
174 #endif /* HAVE_JANSSON */
175 int ret;
177 r->valid = false;
179 if (value == NULL) {
180 return true;
183 ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB);
184 if (ret != 0) {
185 fstring buf = { 0,};
186 regerror(ret, &r->regex, buf, sizeof(buf));
187 d_printf("regcomp(%s) failed for %s: "
188 "%d: %s\n", value, option, ret, buf);
189 return false;
192 #ifdef HAVE_JANSSON
193 if (c->opt_json) {
194 ret = json_add_string(&state->filters_json,
195 option,
196 value);
197 if (ret != 0) {
198 return false;
201 #endif /* HAVE_JANSSON */
203 r->valid = true;
204 return true;
207 static bool net_witness_scan_registrations_regex_match(
208 struct net_witness_scan_registrations_regex *r,
209 const char *name, const char *value)
211 int ret;
213 if (!r->valid) {
214 return false;
217 if (value == NULL) {
219 * without a share name,
220 * we match against an empty
221 * string.
223 value = "";
226 ret = regexec(&r->regex, value, 0, NULL, 0);
227 if (ret == REG_NOMATCH) {
228 return false;
231 return true;
234 static void net_witness_scan_registrations_regex_free(
235 struct net_witness_scan_registrations_regex *r)
237 if (r->valid) {
238 regfree(&r->regex);
239 r->valid = false;
243 static bool net_witness_scan_registrations_init(
244 struct net_witness_scan_registrations_state *state)
246 struct net_context *c = state->c;
247 bool ok;
249 if (c->opt_json) {
250 #ifdef HAVE_JANSSON
251 state->filters_json = json_new_object();
252 if (json_is_invalid(&state->filters_json)) {
253 return false;
256 if (c->opt_witness_registration != NULL) {
257 int ret;
259 ret = json_add_string(&state->filters_json,
260 "--witness-registration",
261 c->opt_witness_registration);
262 if (ret != 0) {
263 return false;
267 if (c->opt_witness_apply_to_all != 0) {
268 int ret;
270 ret = json_add_bool(&state->filters_json,
271 "--witness-apply-to-all",
272 c->opt_witness_apply_to_all != 0);
273 if (ret != 0) {
274 return false;
278 state->registrations_json = json_new_object();
279 if (json_is_invalid(&state->registrations_json)) {
280 return false;
282 #else /* not HAVE_JANSSON */
283 d_fprintf(stderr, _("JSON support not available\n"));
284 return false;
285 #endif /* not HAVE_JANSSON */
288 ok = net_witness_scan_registrations_regex_init(state,
289 &state->net_name,
290 "--witness-net-name",
291 c->opt_witness_net_name);
292 if (!ok) {
293 return false;
296 ok = net_witness_scan_registrations_regex_init(state,
297 &state->share_name,
298 "--witness-share-name",
299 c->opt_witness_share_name);
300 if (!ok) {
301 return false;
304 ok = net_witness_scan_registrations_regex_init(state,
305 &state->ip_address,
306 "--witness-ip-address",
307 c->opt_witness_ip_address);
308 if (!ok) {
309 return false;
312 ok = net_witness_scan_registrations_regex_init(state,
313 &state->client_computer,
314 "--witness-client-computer-name",
315 c->opt_witness_client_computer_name);
316 if (!ok) {
317 return false;
320 ok = state->action->prepare_fn(state->action->private_data);
321 if (!ok) {
322 return false;
325 if (!c->opt_json) {
326 d_printf("%-36s %-20s %-15s %-20s %s\n",
327 "Registration-UUID:",
328 "NetName",
329 "ShareName",
330 "IpAddress",
331 "ClientComputerName");
332 d_printf("%-36s-%-20s-%-15s-%-20s-%s\n",
333 "------------------------------------",
334 "--------------------",
335 "------------------",
336 "--------------------",
337 "------------------");
340 return true;
343 static bool net_witness_scan_registrations_finish(
344 struct net_witness_scan_registrations_state *state)
346 #ifdef HAVE_JANSSON
347 struct net_context *c = state->c;
348 struct json_object root_json = json_empty_object;
349 TALLOC_CTX *frame = NULL;
350 const char *json_str = NULL;
351 int ret;
353 if (!c->opt_json) {
354 return true;
357 frame = talloc_stackframe();
359 root_json = json_new_object();
360 if (json_is_invalid(&root_json)) {
361 TALLOC_FREE(frame);
362 return false;
365 ret = json_add_object(&root_json,
366 "filters",
367 &state->filters_json);
368 if (ret != 0) {
369 json_free(&root_json);
370 TALLOC_FREE(frame);
371 return false;
373 state->filters_json = json_empty_object;
375 if (state->message_json != NULL) {
376 ret = json_add_object(&root_json,
377 "message",
378 state->message_json);
379 if (ret != 0) {
380 json_free(&root_json);
381 TALLOC_FREE(frame);
382 return false;
384 *state->message_json = json_empty_object;
387 ret = json_add_object(&root_json,
388 "registrations",
389 &state->registrations_json);
390 if (ret != 0) {
391 json_free(&root_json);
392 TALLOC_FREE(frame);
393 return false;
395 state->registrations_json = json_empty_object;
397 json_str = json_to_string(frame, &root_json);
398 json_free(&root_json);
399 if (json_str == NULL) {
400 TALLOC_FREE(frame);
401 return false;
404 d_printf("%s\n", json_str);
405 TALLOC_FREE(frame);
406 return true;
407 #else /* not HAVE_JANSSON */
408 return true;
409 #endif /* not HAVE_JANSSON */
412 static void net_witness_scan_registrations_free(
413 struct net_witness_scan_registrations_state *state)
415 #ifdef HAVE_JANSSON
416 if (!json_is_invalid(&state->filters_json)) {
417 json_free(&state->filters_json);
419 if (!json_is_invalid(&state->registrations_json)) {
420 json_free(&state->registrations_json);
422 #endif /* HAVE_JANSSON */
424 net_witness_scan_registrations_regex_free(&state->net_name);
425 net_witness_scan_registrations_regex_free(&state->share_name);
426 net_witness_scan_registrations_regex_free(&state->ip_address);
427 net_witness_scan_registrations_regex_free(&state->client_computer);
430 #ifdef HAVE_JANSSON
431 static int dump_registration_json(struct json_object *registrations_json,
432 const char *key_str,
433 const struct rpcd_witness_registration *rg)
435 struct json_object jsobj = json_empty_object;
436 struct json_object flags_json = json_empty_object;
437 struct json_object context_json = json_empty_object;
438 struct json_object serverid_json = json_empty_object;
439 struct json_object auth_json = json_empty_object;
440 struct json_object connection_json = json_empty_object;
441 struct timeval tv;
442 struct dom_sid_buf sid_buf;
443 int ret = 0;
445 jsobj = json_new_object();
446 if (json_is_invalid(&jsobj)) {
447 d_fprintf(stderr, _("error setting up JSON value\n"));
448 goto failure;
451 ret = json_add_flags32(&jsobj, "version", rg->version);
452 if (ret != 0) {
453 goto failure;
456 ret = json_add_string(&jsobj, "net_name", rg->net_name);
457 if (ret != 0) {
458 goto failure;
461 ret = json_add_string(&jsobj, "share_name", rg->share_name);
462 if (ret != 0) {
463 goto failure;
466 ret = json_add_string(&jsobj, "ip_address", rg->ip_address);
467 if (ret != 0) {
468 goto failure;
471 ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name);
472 if (ret != 0) {
473 goto failure;
476 flags_json = json_new_object();
477 if (json_is_invalid(&flags_json)) {
478 goto failure;
481 ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION",
482 (rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ?
483 true : false);
484 if (ret != 0) {
485 goto failure;
488 ret = json_add_int(&flags_json, "int", rg->flags);
489 if (ret != 0) {
490 goto failure;
493 ret = json_add_flags32(&flags_json, "hex", rg->flags);
494 if (ret != 0) {
495 goto failure;
498 ret = json_add_object(&jsobj, "flags", &flags_json);
499 if (ret != 0) {
500 goto failure;
502 flags_json = json_empty_object;
504 ret = json_add_int(&jsobj, "timeout", rg->timeout);
505 if (ret != 0) {
506 goto failure;
509 context_json = json_new_object();
510 if (json_is_invalid(&context_json)) {
511 goto failure;
514 ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type);
515 if (ret != 0) {
516 goto failure;
519 ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid);
520 if (ret != 0) {
521 goto failure;
524 ret = json_add_object(&jsobj, "context_handle", &context_json);
525 if (ret != 0) {
526 goto failure;
528 context_json = json_empty_object;
530 serverid_json = json_new_object();
531 if (json_is_invalid(&serverid_json)) {
532 goto failure;
535 ret = json_add_int(&serverid_json, "pid", rg->server_id.pid);
536 if (ret != 0) {
537 goto failure;
540 ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id);
541 if (ret != 0) {
542 goto failure;
545 ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn);
546 if (ret != 0) {
547 goto failure;
550 ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id);
551 if (ret != 0) {
552 goto failure;
555 ret = json_add_object(&jsobj, "server_id", &serverid_json);
556 if (ret != 0) {
557 goto failure;
559 serverid_json = json_empty_object;
561 auth_json = json_new_object();
562 if (json_is_invalid(&auth_json)) {
563 goto failure;
566 ret = json_add_string(&auth_json, "account_name", rg->account_name);
567 if (ret != 0) {
568 goto failure;
571 ret = json_add_string(&auth_json, "domain_name", rg->domain_name);
572 if (ret != 0) {
573 goto failure;
576 ret = json_add_string(&auth_json,
577 "account_sid",
578 dom_sid_str_buf(&rg->account_sid, &sid_buf));
579 if (ret != 0) {
580 goto failure;
583 ret = json_add_object(&jsobj, "auth", &auth_json);
584 if (ret != 0) {
585 goto failure;
587 auth_json = json_empty_object;
589 connection_json = json_new_object();
590 if (json_is_invalid(&connection_json)) {
591 goto failure;
594 ret = json_add_string(&connection_json, "local_address", rg->local_address);
595 if (ret != 0) {
596 goto failure;
599 ret = json_add_string(&connection_json, "remote_address", rg->remote_address);
600 if (ret != 0) {
601 goto failure;
604 ret = json_add_object(&jsobj, "connection", &connection_json);
605 if (ret != 0) {
606 goto failure;
608 connection_json = json_empty_object;
610 nttime_to_timeval(&tv, rg->registration_time);
611 ret = json_add_time(&jsobj, "registration_time", tv);
612 if (ret != 0) {
613 goto failure;
616 ret = json_add_object(registrations_json, key_str, &jsobj);
617 if (ret != 0) {
618 goto failure;
620 jsobj = json_empty_object;
622 failure:
623 if (!json_is_invalid(&connection_json)) {
624 json_free(&connection_json);
626 if (!json_is_invalid(&auth_json)) {
627 json_free(&auth_json);
629 if (!json_is_invalid(&serverid_json)) {
630 json_free(&serverid_json);
632 if (!json_is_invalid(&context_json)) {
633 json_free(&context_json);
635 if (!json_is_invalid(&flags_json)) {
636 json_free(&flags_json);
638 if (!json_is_invalid(&jsobj)) {
639 json_free(&jsobj);
642 return ret;
644 #endif /* HAVE_JANSSON */
646 static NTSTATUS net_witness_scan_registrations_dump_rg(
647 struct net_witness_scan_registrations_state *state,
648 const struct rpcd_witness_registration *rg)
650 struct net_context *c = state->c;
651 struct GUID_txt_buf key_buf;
652 const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf);
654 if (c->opt_json) {
655 #ifdef HAVE_JANSSON
656 int ret;
658 ret = dump_registration_json(&state->registrations_json,
659 key_str,
660 rg);
661 if (ret != 0) {
662 d_fprintf(stderr, "dump_registration_json(%s) failed\n",
663 key_str);
664 return NT_STATUS_INTERNAL_ERROR;
666 #endif /* HAVE_JANSSON */
667 return NT_STATUS_OK;
670 d_printf("%-36s %-20s %-15s %-20s %s\n",
671 key_str,
672 rg->net_name,
673 rg->share_name ? rg->share_name : "''",
674 rg->ip_address,
675 rg->client_computer_name);
677 return NT_STATUS_OK;
680 static void net_witness_scan_registrations_parser(TDB_DATA key,
681 TDB_DATA val,
682 void *private_data)
684 struct net_witness_scan_registrations_state *state =
685 (struct net_witness_scan_registrations_state *)private_data;
686 DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize);
687 struct rpcd_witness_registration rg;
688 enum ndr_err_code ndr_err;
689 TALLOC_CTX *frame = NULL;
690 bool match = false;
692 if (val_blob.length == 0) {
693 return;
696 frame = talloc_stackframe();
698 ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg,
699 (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration);
700 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
701 DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:"
702 "key '%s' ndr_pull_struct_blob - %s\n",
703 tdb_data_dbg(key),
704 ndr_errstr(ndr_err));
705 state->error = ndr_map_error2ntstatus(ndr_err);
706 TALLOC_FREE(frame);
707 return;
710 if (!serverid_exists(&rg.server_id)) {
711 TALLOC_FREE(frame);
712 return;
715 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
716 NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
719 match = net_witness_scan_registrations_match(state, &rg);
720 if (!NT_STATUS_IS_OK(state->error)) {
721 TALLOC_FREE(frame);
722 return;
724 if (!match) {
725 TALLOC_FREE(frame);
726 return;
729 match = state->action->match_fn(state->action->private_data, &rg);
730 if (!match) {
731 TALLOC_FREE(frame);
732 return;
735 state->error = state->action->process_fn(state->action->private_data, &rg);
736 if (NT_STATUS_IS_OK(state->error)) {
737 state->error = net_witness_scan_registrations_dump_rg(state,
738 &rg);
740 TALLOC_FREE(frame);
743 static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data)
745 struct net_witness_scan_registrations_state *state =
746 (struct net_witness_scan_registrations_state *)private_data;
747 TDB_DATA key = dbwrap_record_get_key(rec);
748 TDB_DATA val = dbwrap_record_get_value(rec);
750 net_witness_scan_registrations_parser(key, val, private_data);
752 if (!NT_STATUS_IS_OK(state->error)) {
753 return -1;
756 return 0;
759 static int net_witness_scan_registrations(struct net_context *c,
760 struct json_object *message_json,
761 const struct net_witness_scan_registrations_action_state *action)
763 struct net_witness_scan_registrations_state state = {
764 .c = c,
765 .message_json = message_json,
766 .action = action,
768 struct db_context *db = NULL;
769 NTSTATUS status;
770 bool ok;
772 db = net_witness_open_registration_db();
773 if (db == NULL) {
774 d_printf("net_witness_open_registration_db() failed\n");
775 return -1;
778 ok = net_witness_scan_registrations_init(&state);
779 if (!ok) {
780 d_printf("net_witness_scan_registrations_init() failed\n");
781 return -1;
784 if (c->opt_witness_registration != NULL) {
785 const char *key_str = c->opt_witness_registration;
786 DATA_BLOB key_blob = data_blob_string_const(key_str);
787 TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length);
789 status = dbwrap_parse_record(db,
790 key,
791 net_witness_scan_registrations_parser,
792 &state);
793 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
794 status = NT_STATUS_OK;
796 if (!NT_STATUS_IS_OK(status)) {
797 d_printf("dbwrap_parse_record(%s) failed: %s\n",
798 key_str, nt_errstr(status));
799 net_witness_scan_registrations_free(&state);
800 return -1;
802 if (!NT_STATUS_IS_OK(state.error)) {
803 d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n",
804 key_str, nt_errstr(state.error));
805 net_witness_scan_registrations_free(&state);
806 return -1;
808 } else {
809 status = dbwrap_traverse_read(db,
810 net_witness_scan_registrations_traverse_cb,
811 &state,
812 NULL); /* count */
813 if (!NT_STATUS_IS_OK(status)) {
814 d_printf("dbwrap_traverse_read() failed\n");
815 net_witness_scan_registrations_free(&state);
816 return -1;
818 if (!NT_STATUS_IS_OK(state.error)) {
819 d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n",
820 nt_errstr(state.error));
821 net_witness_scan_registrations_free(&state);
822 return -1;
826 ok = net_witness_scan_registrations_finish(&state);
827 if (!ok) {
828 d_printf("net_witness_scan_registrations_finish() failed\n");
829 return -1;
832 net_witness_scan_registrations_free(&state);
833 return 0;
836 struct net_witness_list_state {
837 struct net_context *c;
840 static bool net_witness_list_prepare_fn(void *private_data)
842 return true;
845 static bool net_witness_list_match_fn(void *private_data,
846 const struct rpcd_witness_registration *rg)
848 return true;
851 static NTSTATUS net_witness_list_process_fn(void *private_data,
852 const struct rpcd_witness_registration *rg)
854 return NT_STATUS_OK;
857 static void net_witness_filter_usage(void)
859 d_printf(" Note: Only supported with clustering=yes!\n\n");
860 d_printf(" Machine readable output can be generated with "
861 "the following option:\n"
862 "\n"
863 " --json\n"
864 "\n");
865 d_printf(" The selection of registrations can be limited by "
866 "the following options:\n"
867 "\n"
868 " --witness-registration=REGISTRATION_UUID\n"
869 " This does a direct lookup for REGISTRATION_UUID\n"
870 " instead of doing a database traversal.\n"
871 "\n"
872 " The following options all take a "
873 "POSIX Extended Regular Expression,\n"
874 " which can further filter the selection of "
875 "registrations.\n"
876 " These options are applied as logical AND, "
877 "but each REGEX \n"
878 " allows specifying multiple strings using "
879 "the pipe symbol.\n"
880 "\n"
881 " --witness-net-name=REGEX\n"
882 " This specifies the 'server name' the client\n"
883 " registered for monitoring.\n"
884 "\n"
885 " --witness-share-name=REGEX\n"
886 " This specifies the 'share name' the client\n"
887 " registered for monitoring.\n"
888 " Note that the share name is optional in the\n"
889 " registration, otherwise an empty string is \n"
890 " matched.\n"
891 "\n"
892 " --witness-ip-address=REGEX\n"
893 " This specifies the ip address the client\n"
894 " registered for monitoring.\n"
895 "\n"
896 " --witness-client-computer-name=REGEX\n"
897 " This specifies the client computer name the client\n"
898 " specified in the registration.\n"
899 " Note it is just a string chosen by the "
900 "client itself.\n"
901 "\n");
904 static void net_witness_list_usage(void)
906 d_printf("%s\n"
907 "net witness list\n"
908 " %s\n\n",
909 _("Usage:"),
910 _("List witness registrations "
911 "from rpcd_witness_registration.tdb"));
912 net_witness_filter_usage();
915 static int net_witness_list(struct net_context *c, int argc, const char **argv)
917 TALLOC_CTX *frame = talloc_stackframe();
918 struct net_witness_list_state state = { .c = c, };
919 struct net_witness_scan_registrations_action_state action = {
920 .prepare_fn = net_witness_list_prepare_fn,
921 .match_fn = net_witness_list_match_fn,
922 .process_fn = net_witness_list_process_fn,
923 .private_data = &state,
925 int ret = -1;
927 if (c->display_usage) {
928 net_witness_list_usage();
929 goto out;
932 if (argc != 0) {
933 net_witness_list_usage();
934 goto out;
937 if (!lp_clustering()) {
938 d_printf("ERROR: Only supported with clustering=yes!\n\n");
939 goto out;
942 ret = net_witness_scan_registrations(c, NULL, &action);
943 if (ret != 0) {
944 d_printf("net_witness_scan_registrations() failed\n");
945 goto out;
948 ret = 0;
949 out:
950 TALLOC_FREE(frame);
951 return ret;
954 struct net_witness_client_move_state {
955 struct net_context *c;
956 struct rpcd_witness_registration_updateB m;
957 char *headline;
960 static bool net_witness_client_move_prepare_fn(void *private_data)
962 struct net_witness_client_move_state *state =
963 (struct net_witness_client_move_state *)private_data;
965 if (state->headline != NULL) {
966 d_printf("%s\n", state->headline);
967 TALLOC_FREE(state->headline);
970 return true;
973 static bool net_witness_client_move_match_fn(void *private_data,
974 const struct rpcd_witness_registration *rg)
976 return true;
979 static NTSTATUS net_witness_client_move_process_fn(void *private_data,
980 const struct rpcd_witness_registration *rg)
982 struct net_witness_client_move_state *state =
983 (struct net_witness_client_move_state *)private_data;
984 struct net_context *c = state->c;
985 struct rpcd_witness_registration_updateB update = {
986 .context_handle = rg->context_handle,
987 .type = state->m.type,
988 .update = state->m.update,
990 DATA_BLOB blob = { .length = 0, };
991 enum ndr_err_code ndr_err;
992 NTSTATUS status;
994 if (state->headline != NULL) {
995 d_printf("%s\n", state->headline);
996 TALLOC_FREE(state->headline);
999 SMB_ASSERT(update.type != 0);
1001 if (DEBUGLVL(DBGLVL_DEBUG)) {
1002 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1005 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
1006 (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
1007 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1008 status = ndr_map_error2ntstatus(ndr_err);
1009 DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
1010 return status;
1013 status = messaging_send(c->msg_ctx,
1014 rg->server_id,
1015 MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
1016 &blob);
1017 if (!NT_STATUS_IS_OK(status)) {
1018 DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
1019 return status;
1022 return NT_STATUS_OK;
1025 static void net_witness_update_usage(void)
1027 d_printf(" If the update should be applied to all registrations\n"
1028 " it needs to be explicitly specified:\n"
1029 "\n"
1030 " --witness-apply-to-all\n"
1031 " This selects all registrations.\n"
1032 " Note: This is mutual exclusive to "
1033 "the above options.\n"
1034 "\n");
1037 static bool net_witness_verify_update_options(struct net_context *c)
1039 if (c->opt_witness_registration == NULL &&
1040 c->opt_witness_net_name == NULL &&
1041 c->opt_witness_share_name == NULL &&
1042 c->opt_witness_ip_address == NULL &&
1043 c->opt_witness_client_computer_name == NULL &&
1044 c->opt_witness_apply_to_all == 0)
1046 d_printf("--witness-apply-to-all or "
1047 "at least one of following requires:\n"
1048 "--witness-registration\n"
1049 "--witness-net-name\n"
1050 "--witness-share-name\n"
1051 "--witness-ip-address\n"
1052 "--witness-client-computer-name\n");
1053 return false;
1056 if (c->opt_witness_apply_to_all == 0) {
1057 return true;
1060 if (c->opt_witness_registration != NULL ||
1061 c->opt_witness_net_name != NULL ||
1062 c->opt_witness_share_name != NULL ||
1063 c->opt_witness_ip_address != NULL ||
1064 c->opt_witness_client_computer_name != NULL)
1066 d_printf("--witness-apply-to-all not allowed "
1067 "together with the following options:\n"
1068 "--witness-registration\n"
1069 "--witness-net-name\n"
1070 "--witness-share-name\n"
1071 "--witness-ip-address\n"
1072 "--witness-client-computer-name\n");
1073 return false;
1076 return true;
1079 static void net_witness_move_usage(const char *name)
1081 d_printf(" The content of the %s notification contains ip addresses\n"
1082 " specified by (exactly one) of the following options:\n"
1083 "\n"
1084 " --witness-new-node=NODEID\n"
1085 " By specifying a NODEID all ip addresses\n"
1086 " currently available on the given node are\n"
1087 " included in the response.\n"
1088 " By specifying '-1' as NODEID all ip addresses\n"
1089 " of the cluster are included in the response.\n"
1090 "\n"
1091 " --witness-new-ip=IPADDRESS\n"
1092 " By specifying an IPADDRESS only the specified\n"
1093 " ip address is included in the response.\n"
1094 "\n",
1095 name);
1098 static bool net_witness_verify_move_options(struct net_context *c,
1099 uint32_t *new_node,
1100 bool *is_ipv4,
1101 bool *is_ipv6)
1103 bool ok;
1105 *new_node = NONCLUSTER_VNN;
1106 *is_ipv4 = false;
1107 *is_ipv6 = false;
1109 ok = net_witness_verify_update_options(c);
1110 if (!ok) {
1111 return false;
1114 if (c->opt_witness_new_ip != NULL &&
1115 c->opt_witness_new_node != -2)
1117 d_printf("--witness-new-ip and "
1118 "--witness-new-node are not allowed together\n");
1119 return false;
1122 if (c->opt_witness_new_ip == NULL &&
1123 c->opt_witness_new_node == -2)
1125 d_printf("--witness-new-ip or --witness-new-node required\n");
1126 return false;
1129 if (c->opt_witness_new_node != -2) {
1130 *new_node = c->opt_witness_new_node;
1131 return true;
1134 if (is_ipaddress_v4(c->opt_witness_new_ip)) {
1135 *is_ipv4 = true;
1136 return true;
1139 if (is_ipaddress_v6(c->opt_witness_new_ip)) {
1140 *is_ipv6 = true;
1141 return true;
1144 d_printf("Invalid ip address for --witness-new-ip=%s\n",
1145 c->opt_witness_new_ip);
1146 return false;
1149 #ifdef HAVE_JANSSON
1150 static bool net_witness_move_message_json(struct net_context *c,
1151 const char *msg_type,
1152 struct json_object *pmessage_json)
1154 struct json_object message_json = json_empty_object;
1155 int ret;
1157 message_json = json_new_object();
1158 if (json_is_invalid(&message_json)) {
1159 return false;
1162 ret = json_add_string(&message_json,
1163 "type",
1164 msg_type);
1165 if (ret != 0) {
1166 json_free(&message_json);
1167 return false;
1170 if (c->opt_witness_new_ip != NULL) {
1171 ret = json_add_string(&message_json,
1172 "new_ip",
1173 c->opt_witness_new_ip);
1174 if (ret != 0) {
1175 return false;
1177 } else if (c->opt_witness_new_node != -1) {
1178 ret = json_add_int(&message_json,
1179 "new_node",
1180 c->opt_witness_new_node);
1181 if (ret != 0) {
1182 return false;
1184 } else {
1185 ret = json_add_bool(&message_json,
1186 "all_nodes",
1187 true);
1188 if (ret != 0) {
1189 return false;
1193 *pmessage_json = message_json;
1194 return true;
1196 #endif /* HAVE_JANSSON */
1198 static void net_witness_client_move_usage(void)
1200 d_printf("%s\n"
1201 "net witness client-move\n"
1202 " %s\n\n",
1203 _("Usage:"),
1204 _("Generate client move notifications for "
1205 "witness registrations to a new ip or node"));
1206 net_witness_filter_usage();
1207 net_witness_update_usage();
1208 net_witness_move_usage("CLIENT_MOVE");
1211 static int net_witness_client_move(struct net_context *c, int argc, const char **argv)
1213 TALLOC_CTX *frame = talloc_stackframe();
1214 struct net_witness_client_move_state state = { .c = c, };
1215 struct rpcd_witness_registration_updateB *m = &state.m;
1216 #ifdef HAVE_JANSSON
1217 struct json_object _message_json = json_empty_object;
1218 #endif /* HAVE_JANSSON */
1219 struct json_object *message_json = NULL;
1220 struct net_witness_scan_registrations_action_state action = {
1221 .prepare_fn = net_witness_client_move_prepare_fn,
1222 .match_fn = net_witness_client_move_match_fn,
1223 .process_fn = net_witness_client_move_process_fn,
1224 .private_data = &state,
1226 int ret = -1;
1227 const char *msg_type = NULL;
1228 uint32_t new_node = NONCLUSTER_VNN;
1229 bool is_ipv4 = false;
1230 bool is_ipv6 = false;
1231 bool ok;
1233 if (c->display_usage) {
1234 net_witness_client_move_usage();
1235 goto out;
1238 if (argc != 0) {
1239 net_witness_client_move_usage();
1240 goto out;
1243 if (!lp_clustering()) {
1244 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1245 goto out;
1248 ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
1249 if (!ok) {
1250 goto out;
1253 if (is_ipv4) {
1254 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV4;
1255 m->update.client_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
1256 msg_type = "CLIENT_MOVE_TO_IPV4";
1257 state.headline = talloc_asprintf(frame,
1258 "CLIENT_MOVE_TO_IPV4: %s",
1259 c->opt_witness_new_ip);
1260 if (state.headline == NULL) {
1261 goto out;
1263 } else if (is_ipv6) {
1264 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV6;
1265 m->update.client_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
1266 msg_type = "CLIENT_MOVE_TO_IPV6";
1267 state.headline = talloc_asprintf(frame,
1268 "CLIENT_MOVE_TO_IPV6: %s",
1269 c->opt_witness_new_ip);
1270 if (state.headline == NULL) {
1271 goto out;
1273 } else if (new_node != NONCLUSTER_VNN) {
1274 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
1275 m->update.client_move_to_node.new_node = new_node;
1276 msg_type = "CLIENT_MOVE_TO_NODE";
1277 state.headline = talloc_asprintf(frame,
1278 "CLIENT_MOVE_TO_NODE: %u",
1279 new_node);
1280 if (state.headline == NULL) {
1281 goto out;
1283 } else {
1284 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
1285 m->update.client_move_to_node.new_node = NONCLUSTER_VNN;
1286 msg_type = "CLIENT_MOVE_TO_NODE";
1287 state.headline = talloc_asprintf(frame,
1288 "CLIENT_MOVE_TO_NODE: ALL");
1289 if (state.headline == NULL) {
1290 goto out;
1294 #ifdef HAVE_JANSSON
1295 if (c->opt_json) {
1296 TALLOC_FREE(state.headline);
1298 ok = net_witness_move_message_json(c,
1299 msg_type,
1300 &_message_json);
1301 if (!ok) {
1302 d_printf("net_witness_move_message_json(%s) failed\n",
1303 msg_type);
1304 goto out;
1307 message_json = &_message_json;
1309 #else /* not HAVE_JANSSON */
1310 (void)msg_type;
1311 #endif /* not HAVE_JANSSON */
1313 ret = net_witness_scan_registrations(c, message_json, &action);
1314 if (ret != 0) {
1315 d_printf("net_witness_scan_registrations() failed\n");
1316 goto out;
1319 ret = 0;
1320 out:
1321 #ifdef HAVE_JANSSON
1322 if (!json_is_invalid(&_message_json)) {
1323 json_free(&_message_json);
1325 #endif /* HAVE_JANSSON */
1326 TALLOC_FREE(frame);
1327 return ret;
1330 struct net_witness_share_move_state {
1331 struct net_context *c;
1332 struct rpcd_witness_registration_updateB m;
1333 char *headline;
1336 static bool net_witness_share_move_prepare_fn(void *private_data)
1338 struct net_witness_share_move_state *state =
1339 (struct net_witness_share_move_state *)private_data;
1341 if (state->headline != NULL) {
1342 d_printf("%s\n", state->headline);
1343 TALLOC_FREE(state->headline);
1346 return true;
1349 static bool net_witness_share_move_match_fn(void *private_data,
1350 const struct rpcd_witness_registration *rg)
1352 if (rg->share_name == NULL) {
1353 return false;
1356 return true;
1359 static NTSTATUS net_witness_share_move_process_fn(void *private_data,
1360 const struct rpcd_witness_registration *rg)
1362 struct net_witness_share_move_state *state =
1363 (struct net_witness_share_move_state *)private_data;
1364 struct net_context *c = state->c;
1365 struct rpcd_witness_registration_updateB update = {
1366 .context_handle = rg->context_handle,
1367 .type = state->m.type,
1368 .update = state->m.update,
1370 DATA_BLOB blob = { .length = 0, };
1371 enum ndr_err_code ndr_err;
1372 NTSTATUS status;
1374 SMB_ASSERT(update.type != 0);
1376 if (DEBUGLVL(DBGLVL_DEBUG)) {
1377 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1380 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
1381 (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
1382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1383 status = ndr_map_error2ntstatus(ndr_err);
1384 DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
1385 return status;
1388 status = messaging_send(c->msg_ctx,
1389 rg->server_id,
1390 MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
1391 &blob);
1392 if (!NT_STATUS_IS_OK(status)) {
1393 DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
1394 return status;
1397 return NT_STATUS_OK;
1400 static void net_witness_share_move_usage(void)
1402 d_printf("%s\n"
1403 "net witness share-move\n"
1404 " %s\n\n",
1405 _("Usage:"),
1406 _("Generate share move notifications for "
1407 "witness registrations to a new ip or node"));
1408 net_witness_filter_usage();
1409 net_witness_update_usage();
1410 d_printf(" Note: This only applies to registrations with "
1411 "a non empty share name!\n\n");
1412 net_witness_move_usage("SHARE_MOVE");
1415 static int net_witness_share_move(struct net_context *c, int argc, const char **argv)
1417 TALLOC_CTX *frame = talloc_stackframe();
1418 struct net_witness_share_move_state state = { .c = c, };
1419 struct rpcd_witness_registration_updateB *m = &state.m;
1420 #ifdef HAVE_JANSSON
1421 struct json_object _message_json = json_empty_object;
1422 #endif /* HAVE_JANSSON */
1423 struct json_object *message_json = NULL;
1424 struct net_witness_scan_registrations_action_state action = {
1425 .prepare_fn = net_witness_share_move_prepare_fn,
1426 .match_fn = net_witness_share_move_match_fn,
1427 .process_fn = net_witness_share_move_process_fn,
1428 .private_data = &state,
1430 int ret = -1;
1431 const char *msg_type = NULL;
1432 uint32_t new_node = NONCLUSTER_VNN;
1433 bool is_ipv4 = false;
1434 bool is_ipv6 = false;
1435 bool ok;
1437 if (c->display_usage) {
1438 net_witness_share_move_usage();
1439 goto out;
1442 if (argc != 0) {
1443 net_witness_share_move_usage();
1444 goto out;
1447 if (!lp_clustering()) {
1448 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1449 goto out;
1452 ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
1453 if (!ok) {
1454 goto out;
1457 if (is_ipv4) {
1458 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV4;
1459 m->update.share_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
1460 msg_type = "SHARE_MOVE_TO_IPV4";
1461 state.headline = talloc_asprintf(frame,
1462 "SHARE_MOVE_TO_IPV4: %s",
1463 c->opt_witness_new_ip);
1464 if (state.headline == NULL) {
1465 goto out;
1467 } else if (is_ipv6) {
1468 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV6;
1469 m->update.share_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
1470 msg_type = "SHARE_MOVE_TO_IPV6";
1471 state.headline = talloc_asprintf(frame,
1472 "SHARE_MOVE_TO_IPV6: %s",
1473 c->opt_witness_new_ip);
1474 if (state.headline == NULL) {
1475 goto out;
1477 } else if (new_node != NONCLUSTER_VNN) {
1478 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
1479 m->update.share_move_to_node.new_node = new_node;
1480 msg_type = "SHARE_MOVE_TO_NODE";
1481 state.headline = talloc_asprintf(frame,
1482 "SHARE_MOVE_TO_NODE: %u",
1483 new_node);
1484 if (state.headline == NULL) {
1485 goto out;
1487 } else {
1488 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
1489 m->update.share_move_to_node.new_node = NONCLUSTER_VNN;
1490 msg_type = "SHARE_MOVE_TO_NODE";
1491 state.headline = talloc_asprintf(frame,
1492 "SHARE_MOVE_TO_NODE: ALL");
1493 if (state.headline == NULL) {
1494 goto out;
1498 #ifdef HAVE_JANSSON
1499 if (c->opt_json) {
1500 TALLOC_FREE(state.headline);
1502 ok = net_witness_move_message_json(c,
1503 msg_type,
1504 &_message_json);
1505 if (!ok) {
1506 d_printf("net_witness_move_message_json(%s) failed\n",
1507 msg_type);
1508 goto out;
1511 message_json = &_message_json;
1513 #else /* not HAVE_JANSSON */
1514 (void)msg_type;
1515 #endif /* not HAVE_JANSSON */
1517 ret = net_witness_scan_registrations(c, message_json, &action);
1518 if (ret != 0) {
1519 d_printf("net_witness_scan_registrations() failed\n");
1520 goto out;
1523 ret = 0;
1524 out:
1525 #ifdef HAVE_JANSSON
1526 if (!json_is_invalid(&_message_json)) {
1527 json_free(&_message_json);
1529 #endif /* HAVE_JANSSON */
1530 TALLOC_FREE(frame);
1531 return ret;
1534 struct net_witness_force_unregister_state {
1535 struct net_context *c;
1536 struct rpcd_witness_registration_updateB m;
1537 char *headline;
1540 static bool net_witness_force_unregister_prepare_fn(void *private_data)
1542 struct net_witness_force_unregister_state *state =
1543 (struct net_witness_force_unregister_state *)private_data;
1545 if (state->headline != NULL) {
1546 d_printf("%s\n", state->headline);
1547 TALLOC_FREE(state->headline);
1550 return true;
1553 static bool net_witness_force_unregister_match_fn(void *private_data,
1554 const struct rpcd_witness_registration *rg)
1556 return true;
1559 static NTSTATUS net_witness_force_unregister_process_fn(void *private_data,
1560 const struct rpcd_witness_registration *rg)
1562 struct net_witness_force_unregister_state *state =
1563 (struct net_witness_force_unregister_state *)private_data;
1564 struct net_context *c = state->c;
1565 struct rpcd_witness_registration_updateB update = {
1566 .context_handle = rg->context_handle,
1567 .type = state->m.type,
1568 .update = state->m.update,
1570 DATA_BLOB blob = { .length = 0, };
1571 enum ndr_err_code ndr_err;
1572 NTSTATUS status;
1574 SMB_ASSERT(update.type != 0);
1576 if (DEBUGLVL(DBGLVL_DEBUG)) {
1577 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1580 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
1581 (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
1582 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1583 status = ndr_map_error2ntstatus(ndr_err);
1584 DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
1585 return status;
1588 status = messaging_send(c->msg_ctx,
1589 rg->server_id,
1590 MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
1591 &blob);
1592 if (!NT_STATUS_IS_OK(status)) {
1593 DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
1594 return status;
1597 return NT_STATUS_OK;
1600 static void net_witness_force_unregister_usage(void)
1602 d_printf("%s\n"
1603 "net witness force-unregister\n"
1604 " %s\n\n",
1605 _("Usage:"),
1606 _("Force unregistrations for witness registrations"));
1607 net_witness_filter_usage();
1608 net_witness_update_usage();
1609 d_printf(" The selected registrations are removed on "
1610 "the server and\n"
1611 " any pending AsyncNotify request will get "
1612 "a NOT_FOUND error.\n"
1613 "\n"
1614 " Typically this triggers a clean re-registration "
1615 "on the client.\n"
1616 "\n");
1619 static int net_witness_force_unregister(struct net_context *c, int argc, const char **argv)
1621 TALLOC_CTX *frame = talloc_stackframe();
1622 struct net_witness_force_unregister_state state = { .c = c, };
1623 struct rpcd_witness_registration_updateB *m = &state.m;
1624 #ifdef HAVE_JANSSON
1625 struct json_object _message_json = json_empty_object;
1626 #endif /* HAVE_JANSSON */
1627 struct json_object *message_json = NULL;
1628 struct net_witness_scan_registrations_action_state action = {
1629 .prepare_fn = net_witness_force_unregister_prepare_fn,
1630 .match_fn = net_witness_force_unregister_match_fn,
1631 .process_fn = net_witness_force_unregister_process_fn,
1632 .private_data = &state,
1634 int ret = -1;
1635 bool ok;
1637 if (c->display_usage) {
1638 net_witness_force_unregister_usage();
1639 goto out;
1642 if (argc != 0) {
1643 net_witness_force_unregister_usage();
1644 goto out;
1647 if (!lp_clustering()) {
1648 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1649 goto out;
1652 ok = net_witness_verify_update_options(c);
1653 if (!ok) {
1654 goto out;
1657 m->type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER;
1659 state.headline = talloc_asprintf(frame, "FORCE_UNREGISTER:");
1660 if (state.headline == NULL) {
1661 goto out;
1664 #ifdef HAVE_JANSSON
1665 if (c->opt_json) {
1666 TALLOC_FREE(state.headline);
1668 _message_json = json_new_object();
1669 if (json_is_invalid(&_message_json)) {
1670 goto out;
1673 ret = json_add_string(&_message_json,
1674 "type",
1675 "FORCE_UNREGISTER");
1676 if (ret != 0) {
1677 goto out;
1680 message_json = &_message_json;
1682 #endif /* HAVE_JANSSON */
1684 ret = net_witness_scan_registrations(c, message_json, &action);
1685 if (ret != 0) {
1686 d_printf("net_witness_scan_registrations() failed\n");
1687 goto out;
1690 ret = 0;
1691 out:
1692 #ifdef HAVE_JANSSON
1693 if (!json_is_invalid(&_message_json)) {
1694 json_free(&_message_json);
1696 #endif /* HAVE_JANSSON */
1697 TALLOC_FREE(frame);
1698 return ret;
1701 struct net_witness_force_response_state {
1702 struct net_context *c;
1703 struct rpcd_witness_registration_updateB m;
1704 #ifdef HAVE_JANSSON
1705 struct json_object json_root;
1706 #endif /* HAVE_JANSSON */
1707 char *headline;
1710 #ifdef HAVE_JANSSON
1711 static NTSTATUS net_witness_force_response_parse_rc(
1712 struct net_witness_force_response_state *state,
1713 json_t *jsmsg,
1714 TALLOC_CTX *mem_ctx,
1715 size_t mi,
1716 union witness_notifyResponse_message *message)
1718 struct witness_ResourceChange *rc = &message->resource_change;
1719 json_t *jsctype = NULL;
1720 json_int_t ctype;
1721 json_t *jscname = NULL;
1722 const char *cname = NULL;
1724 if (!json_is_object(jsmsg)) {
1725 DBG_ERR("'message[%zu]' needs to be an object\n", mi);
1726 return NT_STATUS_INVALID_PARAMETER;
1729 jsctype = json_object_get(jsmsg, "type");
1730 if (jsctype == NULL) {
1731 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1732 return NT_STATUS_INVALID_PARAMETER;
1734 if (!json_is_integer(jsctype)) {
1735 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1736 return NT_STATUS_INVALID_PARAMETER;
1738 ctype = json_integer_value(jsctype);
1740 jscname = json_object_get(jsmsg, "name");
1741 if (jscname == NULL) {
1742 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1743 return NT_STATUS_INVALID_PARAMETER;
1745 if (!json_is_string(jscname)) {
1746 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1747 return NT_STATUS_INVALID_PARAMETER;
1749 cname = json_string_value(jscname);
1751 rc->type = ctype;
1752 rc->name = talloc_strdup(mem_ctx, cname);
1753 if (rc->name == NULL) {
1754 return NT_STATUS_NO_MEMORY;
1757 return NT_STATUS_OK;
1760 static NTSTATUS net_witness_force_response_parse_ipl(
1761 struct net_witness_force_response_state *state,
1762 json_t *jsmsg,
1763 TALLOC_CTX *mem_ctx,
1764 size_t mi,
1765 union witness_notifyResponse_message *message)
1767 struct witness_IPaddrInfoList *ipl =
1768 &message->client_move;
1769 size_t ai, num_addrs = 0;
1770 struct witness_IPaddrInfo *addrs = NULL;
1772 if (!json_is_array(jsmsg)) {
1773 DBG_ERR("'messages[%zu]' needs to be an array\n", mi);
1774 return NT_STATUS_INVALID_PARAMETER;
1777 num_addrs = json_array_size(jsmsg);
1778 if (num_addrs > UINT32_MAX) {
1779 DBG_ERR("Too many elements in 'messages[%zu]': %zu\n",
1780 mi, num_addrs);
1781 return NT_STATUS_INVALID_PARAMETER;
1784 addrs = talloc_zero_array(mem_ctx,
1785 struct witness_IPaddrInfo,
1786 num_addrs);
1787 if (addrs == NULL) {
1788 return NT_STATUS_NO_MEMORY;
1791 for (ai = 0; ai < num_addrs; ai++) {
1792 struct witness_IPaddrInfo *info =
1793 &addrs[ai];
1794 json_t *jsaddr = json_array_get(jsmsg, ai);
1795 json_t *jsflags = NULL;
1796 json_int_t flags;
1797 json_t *jsipv4 = NULL;
1798 const char *ipv4 = NULL;
1799 json_t *jsipv6 = NULL;
1800 const char *ipv6 = NULL;
1802 if (!json_is_object(jsaddr)) {
1803 DBG_ERR("'messages[%zu][%zu]' needs to be an object\n",
1804 mi, ai);
1805 return NT_STATUS_INVALID_PARAMETER;
1808 jsflags = json_object_get(jsaddr, "flags");
1809 if (jsflags == NULL) {
1810 DBG_ERR("'messages[%zu][%zu]['flags']' missing\n",
1811 mi, ai);
1812 return NT_STATUS_INVALID_PARAMETER;
1814 if (!json_is_integer(jsflags)) {
1815 DBG_ERR("'messages[%zu][%zu]['flags']' "
1816 "needs to be an integer\n",
1817 mi, ai);
1818 return NT_STATUS_INVALID_PARAMETER;
1820 flags = json_integer_value(jsflags);
1822 jsipv4 = json_object_get(jsaddr, "ipv4");
1823 if (jsipv4 != NULL) {
1824 if (!json_is_string(jsipv4)) {
1825 DBG_ERR("'messages[%zu][%zu]['ipv4']' "
1826 "needs to be a string\n",
1827 mi, ai);
1828 return NT_STATUS_INVALID_PARAMETER;
1830 ipv4 = json_string_value(jsipv4);
1831 if (!is_ipaddress_v4(ipv4)) {
1832 DBG_ERR("'messages[%zu][%zu]['ipv4']' "
1833 "needs to be a valid ipv4 address\n",
1834 mi, ai);
1835 return NT_STATUS_INVALID_PARAMETER;
1837 } else {
1838 ipv4 = "0.0.0.0";
1841 jsipv6 = json_object_get(jsaddr, "ipv6");
1842 if (jsipv6 != NULL) {
1843 if (!json_is_string(jsipv6)) {
1844 DBG_ERR("'messages[%zu][%zu]['ipv6']' "
1845 "needs to be a string\n",
1846 mi, ai);
1847 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1848 return NT_STATUS_INVALID_PARAMETER;
1850 ipv6 = json_string_value(jsipv6);
1851 if (!is_ipaddress_v6(ipv6)) {
1852 DBG_ERR("'messages[%zu][%zu]['ipv4']' "
1853 "needs to be a valid ipv6 address\n",
1854 mi, ai);
1855 return NT_STATUS_INVALID_PARAMETER;
1857 } else {
1858 ipv6 = "::";
1861 info->flags = flags;
1862 info->ipv4 = talloc_strdup(addrs, ipv4);
1863 if (info->ipv4 == NULL) {
1864 return NT_STATUS_NO_MEMORY;
1866 info->ipv6 = talloc_strdup(addrs, ipv6);
1867 if (info->ipv6 == NULL) {
1868 return NT_STATUS_NO_MEMORY;
1872 ipl->num = num_addrs;
1873 ipl->addr = addrs;
1875 return NT_STATUS_OK;
1877 #endif /* HAVE_JANSSON */
1879 static NTSTATUS net_witness_force_response_parse(struct net_witness_force_response_state *state)
1881 #ifdef HAVE_JANSSON
1882 struct net_context *c = state->c;
1883 struct rpcd_witness_registration_update_force_response *force = NULL;
1884 struct witness_notifyResponse *response = NULL;
1885 size_t mi, num_messages = 0;
1886 union witness_notifyResponse_message *messages = NULL;
1887 json_t *jsroot = NULL;
1888 json_t *jsresult = NULL;
1889 json_t *jsresponse = NULL;
1890 json_t *jstype = NULL;
1891 json_t *jsmessages = NULL;
1893 if (c->opt_witness_forced_response != NULL) {
1894 const char *str = c->opt_witness_forced_response;
1895 size_t flags = JSON_REJECT_DUPLICATES;
1896 json_error_t jserror;
1898 jsroot = json_loads(str, flags, &jserror);
1899 if (jsroot == NULL) {
1900 DBG_ERR("Invalid JSON in "
1901 "--witness-forced-response='%s'\n",
1902 str);
1903 return NT_STATUS_INVALID_PARAMETER;
1905 state->json_root = (struct json_object) {
1906 .root = jsroot,
1907 .valid = true,
1911 state->m.type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_RESPONSE;
1912 force = &state->m.update.force_response;
1913 force->response = NULL;
1914 force->result = WERR_OK;
1916 if (jsroot == NULL) {
1917 return NT_STATUS_OK;
1920 jsresult = json_object_get(jsroot, "result");
1921 if (jsresult != NULL) {
1922 int val_type = json_typeof(jsresult);
1924 switch (val_type) {
1925 case JSON_INTEGER: {
1926 json_int_t val = json_integer_value(jsresult);
1928 if (val > UINT32_MAX) {
1929 DBG_ERR("Invalid 'result' value: %d\n",
1930 (int) val);
1931 return NT_STATUS_INVALID_PARAMETER;
1933 if (val < 0) {
1934 DBG_ERR("invalid 'result' value: %d\n",
1935 (int) val);
1936 return NT_STATUS_INVALID_PARAMETER;
1939 force->result = W_ERROR(val);
1940 }; break;
1941 default:
1942 DBG_ERR("Invalid json type for 'result' - needs integer\n");
1943 return NT_STATUS_INVALID_PARAMETER;
1947 jsresponse = json_object_get(jsroot, "response");
1948 if (jsresponse == NULL) {
1949 return NT_STATUS_OK;
1952 if (!json_is_object(jsresponse)) {
1953 DBG_ERR("Invalid json type 'response' needs object\n");
1954 return NT_STATUS_INVALID_PARAMETER;
1957 response = talloc_zero(talloc_tos(), struct witness_notifyResponse);
1958 if (response == NULL) {
1959 return NT_STATUS_NO_MEMORY;
1962 jstype = json_object_get(jsresponse, "type");
1963 if (jstype == NULL) {
1964 DBG_ERR("Missing 'type' element in 'response'\n");
1965 return NT_STATUS_INVALID_PARAMETER;
1968 int val_type = json_typeof(jstype);
1970 switch (val_type) {
1971 case JSON_INTEGER: {
1972 json_int_t val = json_integer_value(jstype);
1974 if (val > WITNESS_NOTIFY_IP_CHANGE) {
1975 DBG_ERR("invalid 'type' value in 'response': "
1976 "%d\n", (int) val);
1977 return NT_STATUS_INVALID_PARAMETER;
1979 if (val < WITNESS_NOTIFY_RESOURCE_CHANGE) {
1980 DBG_ERR("invalid 'type' value in 'response': "
1981 "%d\n", (int) val);
1982 return NT_STATUS_INVALID_PARAMETER;
1985 response->type = val;
1986 }; break;
1987 default:
1988 DBG_ERR("Invalid json type for 'type' in 'response' "
1989 "- needs integer\n");
1990 return NT_STATUS_INVALID_PARAMETER;
1994 force->response = response;
1996 jsmessages = json_object_get(jsresponse, "messages");
1997 if (jsmessages == NULL) {
1998 return NT_STATUS_OK;
2001 if (!json_is_array(jsmessages)) {
2002 DBG_ERR("'messages' in 'response' needs to be an array\n");
2003 return NT_STATUS_INVALID_PARAMETER;
2006 num_messages = json_array_size(jsmessages);
2007 if (num_messages > UINT32_MAX) {
2008 DBG_ERR("Too many elements in 'messages': %zu\n",
2009 num_messages);
2010 return NT_STATUS_INVALID_PARAMETER;
2013 messages = talloc_zero_array(response,
2014 union witness_notifyResponse_message,
2015 num_messages);
2016 if (messages == NULL) {
2017 return NT_STATUS_NO_MEMORY;
2020 for (mi = 0; mi < num_messages; mi++) {
2021 json_t *jsmsg = json_array_get(jsmessages, mi);
2022 union witness_notifyResponse_message *message = &messages[mi];
2023 NTSTATUS status;
2025 switch (response->type) {
2026 case WITNESS_NOTIFY_RESOURCE_CHANGE:
2027 status = net_witness_force_response_parse_rc(state,
2028 jsmsg,
2029 messages,
2031 message);
2032 if (!NT_STATUS_IS_OK(status)) {
2033 const char *fn =
2034 "net_witness_force_response_parse_rc";
2035 DBG_ERR("%s failed: %s\n",
2036 fn, nt_errstr(status));
2037 return status;
2040 break;
2041 case WITNESS_NOTIFY_CLIENT_MOVE:
2042 case WITNESS_NOTIFY_SHARE_MOVE:
2043 case WITNESS_NOTIFY_IP_CHANGE:
2044 status = net_witness_force_response_parse_ipl(state,
2045 jsmsg,
2046 messages,
2048 message);
2049 if (!NT_STATUS_IS_OK(status)) {
2050 const char *fn =
2051 "net_witness_force_response_parse_ipl";
2052 DBG_ERR("%s failed: %s\n",
2053 fn, nt_errstr(status));
2054 return status;
2057 break;
2061 response->num = num_messages;
2062 response->messages = messages;
2064 return NT_STATUS_OK;
2065 #else /* not HAVE_JANSSON */
2066 d_fprintf(stderr, _("JSON support not available\n"));
2067 return NT_STATUS_NOT_IMPLEMENTED;
2068 #endif /* not HAVE_JANSSON */
2071 static bool net_witness_force_response_prepare_fn(void *private_data)
2073 struct net_witness_force_response_state *state =
2074 (struct net_witness_force_response_state *)private_data;
2076 if (state->headline != NULL) {
2077 d_printf("%s\n", state->headline);
2078 TALLOC_FREE(state->headline);
2081 return true;
2084 static bool net_witness_force_response_match_fn(void *private_data,
2085 const struct rpcd_witness_registration *rg)
2087 return true;
2090 static NTSTATUS net_witness_force_response_process_fn(void *private_data,
2091 const struct rpcd_witness_registration *rg)
2093 struct net_witness_force_response_state *state =
2094 (struct net_witness_force_response_state *)private_data;
2095 struct net_context *c = state->c;
2096 struct rpcd_witness_registration_updateB update = {
2097 .context_handle = rg->context_handle,
2098 .type = state->m.type,
2099 .update = state->m.update,
2101 DATA_BLOB blob = { .length = 0, };
2102 enum ndr_err_code ndr_err;
2103 NTSTATUS status;
2105 SMB_ASSERT(update.type != 0);
2107 if (DEBUGLVL(DBGLVL_DEBUG)) {
2108 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
2111 ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
2112 (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
2113 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2114 status = ndr_map_error2ntstatus(ndr_err);
2115 DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
2116 return status;
2119 status = messaging_send(c->msg_ctx,
2120 rg->server_id,
2121 MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
2122 &blob);
2123 if (!NT_STATUS_IS_OK(status)) {
2124 DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
2125 return status;
2128 return NT_STATUS_OK;
2131 static void net_witness_force_response_usage(void)
2133 d_printf("%s\n"
2134 "net witness force-response\n"
2135 " %s\n\n",
2136 _("Usage:"),
2137 _("Force an AsyncNotify response based on "
2138 "json input (mostly for testing)"));
2139 net_witness_filter_usage();
2140 net_witness_update_usage();
2141 d_printf(" Note this is designed for testing and debugging!\n"
2142 "\n"
2143 " In short it is not designed to be used by "
2144 "administrators,\n"
2145 " but developers and automated tests.\n"
2146 "\n"
2147 " By default an empty response with WERR_OK is generated,\n"
2148 " but basically any valid response can be specified by a\n"
2149 " specifying a JSON string:\n"
2150 "\n"
2151 " --witness-forced-response=JSON\n"
2152 " This allows the generation of very complex\n"
2153 " witness_notifyResponse structures.\n"
2154 "\n"
2155 " As this is for developers, please read the code\n"
2156 " in order to understand all possible values\n"
2157 " of the JSON string format...\n"
2158 "\n"
2159 " Simple examples are:\n"
2160 "\n"
2161 "# Resource Change:\n%s\n"
2162 "\n"
2163 "# Client Move:\n%s\n"
2164 "\n"
2165 "# Share Move:\n%s\n"
2166 "\n"
2167 "# IP Change:\n%s\n"
2168 "\n",
2169 "'{ \"result\": 0, \"response\": { \"type\": 1, "
2170 "\"messages\": [ { "
2171 "\"type\": 255 , "
2172 "\"name\": \"some-resource-name\" "
2173 "} ]"
2174 "}}'",
2175 "'{ \"result\": 0, \"response\": { \"type\": 2, "
2176 "\"messages\": ["
2177 "[{ "
2178 "\"flags\": 9, "
2179 "\"ipv4\": \"10.0.10.1\" "
2180 "}]"
2182 "}}'",
2183 "'{ \"result\": 0, \"response\": { \"type\": 3, "
2184 "\"messages\": ["
2185 "[{ "
2186 "\"flags\": 9, "
2187 "\"ipv4\": \"10.0.10.1\" "
2188 "}]"
2190 "}}'",
2191 "'{ \"result\": 0, \"response\": { \"type\": 4, "
2192 "\"messages\": ["
2193 "[{ "
2194 "\"flags\": 9, "
2195 "\"ipv4\": \"10.0.10.1\" "
2196 "}]"
2198 "}}'");
2201 static int net_witness_force_response(struct net_context *c, int argc, const char **argv)
2203 TALLOC_CTX *frame = talloc_stackframe();
2204 struct net_witness_force_response_state state = { .c = c, };
2205 #ifdef HAVE_JANSSON
2206 struct json_object _message_json = json_empty_object;
2207 #endif /* HAVE_JANSSON */
2208 struct json_object *message_json = NULL;
2209 struct net_witness_scan_registrations_action_state action = {
2210 .prepare_fn = net_witness_force_response_prepare_fn,
2211 .match_fn = net_witness_force_response_match_fn,
2212 .process_fn = net_witness_force_response_process_fn,
2213 .private_data = &state,
2215 NTSTATUS status;
2216 int ret = -1;
2217 bool ok;
2219 if (c->display_usage) {
2220 net_witness_force_response_usage();
2221 goto out;
2224 if (argc != 0) {
2225 net_witness_force_response_usage();
2226 goto out;
2229 if (!lp_clustering()) {
2230 d_printf("ERROR: Only supported with clustering=yes!\n\n");
2231 goto out;
2234 ok = net_witness_verify_update_options(c);
2235 if (!ok) {
2236 goto out;
2239 status = net_witness_force_response_parse(&state);
2240 if (!NT_STATUS_IS_OK(status)) {
2241 d_printf("net_witness_force_response_parse failed: %s\n",
2242 nt_errstr(status));
2243 goto out;
2246 state.headline = talloc_asprintf(frame, "FORCE_RESPONSE:%s%s",
2247 c->opt_witness_forced_response != NULL ?
2248 " " : "",
2249 c->opt_witness_forced_response != NULL ?
2250 c->opt_witness_forced_response : "");
2252 if (state.headline == NULL) {
2253 goto out;
2256 #ifdef HAVE_JANSSON
2257 if (c->opt_json) {
2258 TALLOC_FREE(state.headline);
2260 _message_json = json_new_object();
2261 if (json_is_invalid(&_message_json)) {
2262 goto out;
2265 ret = json_add_string(&_message_json,
2266 "type",
2267 "FORCE_RESPONSE");
2268 if (ret != 0) {
2269 goto out;
2272 if (!json_is_invalid(&state.json_root)) {
2273 ret = json_add_object(&_message_json,
2274 "json",
2275 &state.json_root);
2276 if (ret != 0) {
2277 goto out;
2279 state.json_root = json_empty_object;
2281 message_json = &_message_json;
2283 #endif /* HAVE_JANSSON */
2285 ret = net_witness_scan_registrations(c, message_json, &action);
2286 if (ret != 0) {
2287 d_printf("net_witness_scan_registrations() failed\n");
2288 goto out;
2291 ret = 0;
2292 out:
2293 #ifdef HAVE_JANSSON
2294 if (!json_is_invalid(&_message_json)) {
2295 json_free(&_message_json);
2297 if (!json_is_invalid(&state.json_root)) {
2298 json_free(&state.json_root);
2300 #endif /* HAVE_JANSSON */
2301 TALLOC_FREE(frame);
2302 return ret;
2305 int net_witness(struct net_context *c, int argc, const char **argv)
2307 struct functable func[] = {
2309 "list",
2310 net_witness_list,
2311 NET_TRANSPORT_LOCAL,
2312 N_("List witness registrations "
2313 "from rpcd_witness_registration.tdb"),
2314 N_("net witness list\n"
2315 " List witness registrations "
2316 "from rpcd_witness_registration.tdb"),
2319 "client-move",
2320 net_witness_client_move,
2321 NET_TRANSPORT_LOCAL,
2322 N_("Generate client move notifications for "
2323 "witness registrations to a new ip or node"),
2324 N_("net witness client-move\n"
2325 " Generate client move notifications for "
2326 "witness registrations to a new ip or node"),
2329 "share-move",
2330 net_witness_share_move,
2331 NET_TRANSPORT_LOCAL,
2332 N_("Generate share move notifications for "
2333 "witness registrations to a new ip or node"),
2334 N_("net witness share-move\n"
2335 " Generate share move notifications for "
2336 "witness registrations to a new ip or node"),
2339 "force-unregister",
2340 net_witness_force_unregister,
2341 NET_TRANSPORT_LOCAL,
2342 N_("Force unregistrations for witness registrations"),
2343 N_("net witness force-unregister\n"
2344 " Force unregistrations for "
2345 "witness registrations"),
2348 "force-response",
2349 net_witness_force_response,
2350 NET_TRANSPORT_LOCAL,
2351 N_("Force an AsyncNotify response based on "
2352 "json input (mostly for testing)"),
2353 N_("net witness force-response\n"
2354 " Force an AsyncNotify response based on "
2355 "json input (mostly for testing)"),
2357 {NULL, NULL, 0, NULL, NULL}
2360 return net_run_function(c, argc, argv, "net witness", func);