s3:utils: fix help string for 'net witness force-response'
[metze/samba/wip.git] / source3 / utils / net_witness.c
1 /*
2  * Samba Unix/Linux client library
3  * net witness commands to manage smb witness registrations
4  * Copyright (C) 2023 Stefan Metzmacher
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
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>
33
34 struct json_object;
35
36 #ifdef HAVE_JANSSON
37 #include <jansson.h>
38 #include "audit_logging.h" /* various JSON helpers */
39 #endif /* HAVE_JANSSON */
40
41 #undef strcasecmp
42
43 static struct db_context *net_witness_open_registration_db(void)
44 {
45         static struct db_context *db;
46         char *global_path = NULL;
47
48         if (db != NULL) {
49                 return db;
50         }
51
52         global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
53         if (global_path == NULL) {
54                 return NULL;
55         }
56
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;
70         }
71
72         return db;
73 }
74
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;
80 };
81
82 struct net_witness_scan_registrations_regex {
83         regex_t regex;
84         bool valid;
85 };
86
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;
100 };
101
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);
111
112 static bool net_witness_scan_registrations_match(
113         struct net_witness_scan_registrations_state *state,
114         const struct rpcd_witness_registration *rg)
115 {
116         if (state->net_name.valid) {
117                 bool match;
118
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;
125                 }
126         }
127
128         if (state->share_name.valid) {
129                 bool match;
130
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;
137                 }
138         }
139
140         if (state->ip_address.valid) {
141                 bool match;
142
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;
149                 }
150         }
151
152         if (state->client_computer.valid) {
153                 bool match;
154
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;
161                 }
162         }
163
164         return true;
165 }
166
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)
171 {
172 #ifdef HAVE_JANSSON
173         struct net_context *c = state->c;
174 #endif /* HAVE_JANSSON */
175         int ret;
176
177         r->valid = false;
178
179         if (value == NULL) {
180                 return true;
181         }
182
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;
190         }
191
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;
199                 }
200         }
201 #endif /* HAVE_JANSSON */
202
203         r->valid = true;
204         return true;
205 }
206
207 static bool net_witness_scan_registrations_regex_match(
208         struct net_witness_scan_registrations_regex *r,
209         const char *name, const char *value)
210 {
211         int ret;
212
213         if (!r->valid) {
214                 return false;
215         }
216
217         if (value == NULL) {
218                 /*
219                  * without a share name,
220                  * we match against an empty
221                  * string.
222                  */
223                 value = "";
224         }
225
226         ret = regexec(&r->regex, value, 0, NULL, 0);
227         if (ret == REG_NOMATCH) {
228                 return false;
229         }
230
231         return true;
232 }
233
234 static void net_witness_scan_registrations_regex_free(
235         struct net_witness_scan_registrations_regex *r)
236 {
237         if (r->valid) {
238                 regfree(&r->regex);
239                 r->valid = false;
240         }
241 }
242
243 static bool net_witness_scan_registrations_init(
244         struct net_witness_scan_registrations_state *state)
245 {
246         struct net_context *c = state->c;
247         bool ok;
248
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;
254                 }
255
256                 if (c->opt_witness_registration != NULL) {
257                         int ret;
258
259                         ret = json_add_string(&state->filters_json,
260                                               "--witness-registration",
261                                               c->opt_witness_registration);
262                         if (ret != 0) {
263                                 return false;
264                         }
265                 }
266
267                 if (c->opt_witness_apply_to_all != 0) {
268                         int ret;
269
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;
275                         }
276                 }
277
278                 state->registrations_json = json_new_object();
279                 if (json_is_invalid(&state->registrations_json)) {
280                         return false;
281                 }
282 #else /* not HAVE_JANSSON */
283                 d_fprintf(stderr, _("JSON support not available\n"));
284                 return false;
285 #endif /* not HAVE_JANSSON */
286         }
287
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;
294         }
295
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;
302         }
303
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;
310         }
311
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;
318         }
319
320         ok = state->action->prepare_fn(state->action->private_data);
321         if (!ok) {
322                 return false;
323         }
324
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                          "------------------");
338         }
339
340         return true;
341 }
342
343 static bool net_witness_scan_registrations_finish(
344         struct net_witness_scan_registrations_state *state)
345 {
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;
352
353         if (!c->opt_json) {
354                 return true;
355         }
356
357         frame = talloc_stackframe();
358
359         root_json = json_new_object();
360         if (json_is_invalid(&root_json)) {
361                 TALLOC_FREE(frame);
362                 return false;
363         }
364
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;
372         }
373         state->filters_json = json_empty_object;
374
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;
383                 }
384                 *state->message_json = json_empty_object;
385         }
386
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;
394         }
395         state->registrations_json = json_empty_object;
396
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;
402         }
403
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 */
410 }
411
412 static void net_witness_scan_registrations_free(
413         struct net_witness_scan_registrations_state *state)
414 {
415 #ifdef HAVE_JANSSON
416         if (!json_is_invalid(&state->filters_json)) {
417                 json_free(&state->filters_json);
418         }
419         if (!json_is_invalid(&state->registrations_json)) {
420                 json_free(&state->registrations_json);
421         }
422 #endif /* HAVE_JANSSON */
423
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);
428 }
429
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)
434 {
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;
444
445         jsobj = json_new_object();
446         if (json_is_invalid(&jsobj)) {
447                 d_fprintf(stderr, _("error setting up JSON value\n"));
448                 goto failure;
449         }
450
451         ret = json_add_flags32(&jsobj, "version", rg->version);
452         if (ret != 0) {
453                 goto failure;
454         }
455
456         ret = json_add_string(&jsobj, "net_name", rg->net_name);
457         if (ret != 0) {
458                 goto failure;
459         }
460
461         ret = json_add_string(&jsobj, "share_name", rg->share_name);
462         if (ret != 0) {
463                 goto failure;
464         }
465
466         ret = json_add_string(&jsobj, "ip_address", rg->ip_address);
467         if (ret != 0) {
468                 goto failure;
469         }
470
471         ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name);
472         if (ret != 0) {
473                 goto failure;
474         }
475
476         flags_json = json_new_object();
477         if (json_is_invalid(&flags_json)) {
478                 goto failure;
479         }
480
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;
486         }
487
488         ret = json_add_int(&flags_json, "int", rg->flags);
489         if (ret != 0) {
490                 goto failure;
491         }
492
493         ret = json_add_flags32(&flags_json, "hex", rg->flags);
494         if (ret != 0) {
495                 goto failure;
496         }
497
498         ret = json_add_object(&jsobj, "flags", &flags_json);
499         if (ret != 0) {
500                 goto failure;
501         }
502         flags_json = json_empty_object;
503
504         ret = json_add_int(&jsobj, "timeout", rg->timeout);
505         if (ret != 0) {
506                 goto failure;
507         }
508
509         context_json = json_new_object();
510         if (json_is_invalid(&context_json)) {
511                 goto failure;
512         }
513
514         ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type);
515         if (ret != 0) {
516                 goto failure;
517         }
518
519         ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid);
520         if (ret != 0) {
521                 goto failure;
522         }
523
524         ret = json_add_object(&jsobj, "context_handle", &context_json);
525         if (ret != 0) {
526                 goto failure;
527         }
528         context_json = json_empty_object;
529
530         serverid_json = json_new_object();
531         if (json_is_invalid(&serverid_json)) {
532                 goto failure;
533         }
534
535         ret = json_add_int(&serverid_json, "pid", rg->server_id.pid);
536         if (ret != 0) {
537                 goto failure;
538         }
539
540         ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id);
541         if (ret != 0) {
542                 goto failure;
543         }
544
545         ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn);
546         if (ret != 0) {
547                 goto failure;
548         }
549
550         ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id);
551         if (ret != 0) {
552                 goto failure;
553         }
554
555         ret = json_add_object(&jsobj, "server_id", &serverid_json);
556         if (ret != 0) {
557                 goto failure;
558         }
559         serverid_json = json_empty_object;
560
561         auth_json = json_new_object();
562         if (json_is_invalid(&auth_json)) {
563                 goto failure;
564         }
565
566         ret = json_add_string(&auth_json, "account_name", rg->account_name);
567         if (ret != 0) {
568                 goto failure;
569         }
570
571         ret = json_add_string(&auth_json, "domain_name", rg->domain_name);
572         if (ret != 0) {
573                 goto failure;
574         }
575
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;
581         }
582
583         ret = json_add_object(&jsobj, "auth", &auth_json);
584         if (ret != 0) {
585                 goto failure;
586         }
587         auth_json = json_empty_object;
588
589         connection_json = json_new_object();
590         if (json_is_invalid(&connection_json)) {
591                 goto failure;
592         }
593
594         ret = json_add_string(&connection_json, "local_address", rg->local_address);
595         if (ret != 0) {
596                 goto failure;
597         }
598
599         ret = json_add_string(&connection_json, "remote_address", rg->remote_address);
600         if (ret != 0) {
601                 goto failure;
602         }
603
604         ret = json_add_object(&jsobj, "connection", &connection_json);
605         if (ret != 0) {
606                 goto failure;
607         }
608         connection_json = json_empty_object;
609
610         nttime_to_timeval(&tv, rg->registration_time);
611         ret = json_add_time(&jsobj, "registration_time", tv);
612         if (ret != 0) {
613                 goto failure;
614         }
615
616         ret = json_add_object(registrations_json, key_str, &jsobj);
617         if (ret != 0) {
618                 goto failure;
619         }
620         jsobj = json_empty_object;
621
622 failure:
623         if (!json_is_invalid(&connection_json)) {
624                 json_free(&connection_json);
625         }
626         if (!json_is_invalid(&auth_json)) {
627                 json_free(&auth_json);
628         }
629         if (!json_is_invalid(&serverid_json)) {
630                 json_free(&serverid_json);
631         }
632         if (!json_is_invalid(&context_json)) {
633                 json_free(&context_json);
634         }
635         if (!json_is_invalid(&flags_json)) {
636                 json_free(&flags_json);
637         }
638         if (!json_is_invalid(&jsobj)) {
639                 json_free(&jsobj);
640         }
641
642         return ret;
643 }
644 #endif /* HAVE_JANSSON */
645
646 static NTSTATUS net_witness_scan_registrations_dump_rg(
647                         struct net_witness_scan_registrations_state *state,
648                         const struct rpcd_witness_registration *rg)
649 {
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);
653
654         if (c->opt_json) {
655 #ifdef HAVE_JANSSON
656                 int ret;
657
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;
665                 }
666 #endif /* HAVE_JANSSON */
667                 return NT_STATUS_OK;
668         }
669
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);
676
677         return NT_STATUS_OK;
678 }
679
680 static void net_witness_scan_registrations_parser(TDB_DATA key,
681                                                   TDB_DATA val,
682                                                   void *private_data)
683 {
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;
691
692         if (val_blob.length == 0) {
693                 return;
694         }
695
696         frame = talloc_stackframe();
697
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;
708         }
709
710         if (!serverid_exists(&rg.server_id)) {
711                 TALLOC_FREE(frame);
712                 return;
713         }
714
715         if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
716                 NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
717         }
718
719         match = net_witness_scan_registrations_match(state, &rg);
720         if (!NT_STATUS_IS_OK(state->error)) {
721                 TALLOC_FREE(frame);
722                 return;
723         }
724         if (!match) {
725                 TALLOC_FREE(frame);
726                 return;
727         }
728
729         match = state->action->match_fn(state->action->private_data, &rg);
730         if (!match) {
731                 TALLOC_FREE(frame);
732                 return;
733         }
734
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);
739         }
740         TALLOC_FREE(frame);
741 }
742
743 static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data)
744 {
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);
749
750         net_witness_scan_registrations_parser(key, val, private_data);
751
752         if (!NT_STATUS_IS_OK(state->error)) {
753                 return -1;
754         }
755
756         return 0;
757 }
758
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)
762 {
763         struct net_witness_scan_registrations_state state = {
764                 .c = c,
765                 .message_json = message_json,
766                 .action = action,
767         };
768         struct db_context *db = NULL;
769         NTSTATUS status;
770         bool ok;
771
772         db = net_witness_open_registration_db();
773         if (db == NULL) {
774                 d_printf("net_witness_open_registration_db() failed\n");
775                 return -1;
776         }
777
778         ok = net_witness_scan_registrations_init(&state);
779         if (!ok) {
780                 d_printf("net_witness_scan_registrations_init() failed\n");
781                 return -1;
782         }
783
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);
788
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;
795                 }
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;
801                 }
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;
807                 }
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;
817                 }
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;
823                 }
824         }
825
826         ok = net_witness_scan_registrations_finish(&state);
827         if (!ok) {
828                 d_printf("net_witness_scan_registrations_finish() failed\n");
829                 return -1;
830         }
831
832         net_witness_scan_registrations_free(&state);
833         return 0;
834 }
835
836 struct net_witness_list_state {
837         struct net_context *c;
838 };
839
840 static bool net_witness_list_prepare_fn(void *private_data)
841 {
842         return true;
843 }
844
845 static bool net_witness_list_match_fn(void *private_data,
846                         const struct rpcd_witness_registration *rg)
847 {
848         return true;
849 }
850
851 static NTSTATUS net_witness_list_process_fn(void *private_data,
852                         const struct rpcd_witness_registration *rg)
853 {
854         return NT_STATUS_OK;
855 }
856
857 static void net_witness_filter_usage(void)
858 {
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");
902 }
903
904 static void net_witness_list_usage(void)
905 {
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();
913 }
914
915 static int net_witness_list(struct net_context *c, int argc, const char **argv)
916 {
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,
924         };
925         int ret = -1;
926
927         if (c->display_usage) {
928                 net_witness_list_usage();
929                 goto out;
930         }
931
932         if (argc != 0) {
933                 net_witness_list_usage();
934                 goto out;
935         }
936
937         if (!lp_clustering()) {
938                 d_printf("ERROR: Only supported with clustering=yes!\n\n");
939                 goto out;
940         }
941
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;
946         }
947
948         ret = 0;
949 out:
950         TALLOC_FREE(frame);
951         return ret;
952 }
953
954 struct net_witness_client_move_state {
955         struct net_context *c;
956         struct rpcd_witness_registration_updateB m;
957         char *headline;
958 };
959
960 static bool net_witness_client_move_prepare_fn(void *private_data)
961 {
962         struct net_witness_client_move_state *state =
963                 (struct net_witness_client_move_state *)private_data;
964
965         if (state->headline != NULL) {
966                 d_printf("%s\n", state->headline);
967                 TALLOC_FREE(state->headline);
968         }
969
970         return true;
971 }
972
973 static bool net_witness_client_move_match_fn(void *private_data,
974                         const struct rpcd_witness_registration *rg)
975 {
976         return true;
977 }
978
979 static NTSTATUS net_witness_client_move_process_fn(void *private_data,
980                         const struct rpcd_witness_registration *rg)
981 {
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,
989         };
990         DATA_BLOB blob = { .length = 0, };
991         enum ndr_err_code ndr_err;
992         NTSTATUS status;
993
994         if (state->headline != NULL) {
995                 d_printf("%s\n", state->headline);
996                 TALLOC_FREE(state->headline);
997         }
998
999         SMB_ASSERT(update.type != 0);
1000
1001         if (DEBUGLVL(DBGLVL_DEBUG)) {
1002                 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1003         }
1004
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;
1011         }
1012
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;
1020         }
1021
1022         return NT_STATUS_OK;
1023 }
1024
1025 static void net_witness_update_usage(void)
1026 {
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");
1035 }
1036
1037 static bool net_witness_verify_update_options(struct net_context *c)
1038 {
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)
1045         {
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;
1054         }
1055
1056         if (c->opt_witness_apply_to_all == 0) {
1057                 return true;
1058         }
1059
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)
1065         {
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;
1074         }
1075
1076         return true;
1077 }
1078
1079 static void net_witness_move_usage(const char *name)
1080 {
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);
1096 }
1097
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)
1102 {
1103         bool ok;
1104
1105         *new_node = NONCLUSTER_VNN;
1106         *is_ipv4 = false;
1107         *is_ipv6 = false;
1108
1109         ok = net_witness_verify_update_options(c);
1110         if (!ok) {
1111                 return false;
1112         }
1113
1114         if (c->opt_witness_new_ip != NULL &&
1115             c->opt_witness_new_node != -2)
1116         {
1117                 d_printf("--witness-new-ip and "
1118                          "--witness-new-node are not allowed together\n");
1119                 return false;
1120         }
1121
1122         if (c->opt_witness_new_ip == NULL &&
1123             c->opt_witness_new_node == -2)
1124         {
1125                 d_printf("--witness-new-ip or --witness-new-node required\n");
1126                 return false;
1127         }
1128
1129         if (c->opt_witness_new_node != -2) {
1130                 *new_node = c->opt_witness_new_node;
1131                 return true;
1132         }
1133
1134         if (is_ipaddress_v4(c->opt_witness_new_ip)) {
1135                 *is_ipv4 = true;
1136                 return true;
1137         }
1138
1139         if (is_ipaddress_v6(c->opt_witness_new_ip)) {
1140                 *is_ipv6 = true;
1141                 return true;
1142         }
1143
1144         d_printf("Invalid ip address for --witness-new-ip=%s\n",
1145                  c->opt_witness_new_ip);
1146         return false;
1147 }
1148
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)
1153 {
1154         struct json_object message_json = json_empty_object;
1155         int ret;
1156
1157         message_json = json_new_object();
1158         if (json_is_invalid(&message_json)) {
1159                 return false;
1160         }
1161
1162         ret = json_add_string(&message_json,
1163                               "type",
1164                               msg_type);
1165         if (ret != 0) {
1166                 json_free(&message_json);
1167                 return false;
1168         }
1169
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;
1176                 }
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;
1183                 }
1184         } else {
1185                 ret = json_add_bool(&message_json,
1186                                     "all_nodes",
1187                                     true);
1188                 if (ret != 0) {
1189                         return false;
1190                 }
1191         }
1192
1193         *pmessage_json = message_json;
1194         return true;
1195 }
1196 #endif /* HAVE_JANSSON */
1197
1198 static void net_witness_client_move_usage(void)
1199 {
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");
1209 }
1210
1211 static int net_witness_client_move(struct net_context *c, int argc, const char **argv)
1212 {
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,
1225         };
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;
1232
1233         if (c->display_usage) {
1234                 net_witness_client_move_usage();
1235                 goto out;
1236         }
1237
1238         if (argc != 0) {
1239                 net_witness_client_move_usage();
1240                 goto out;
1241         }
1242
1243         if (!lp_clustering()) {
1244                 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1245                 goto out;
1246         }
1247
1248         ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
1249         if (!ok) {
1250                 goto out;
1251         }
1252
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;
1262                 }
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;
1272                 }
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;
1282                 }
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;
1291                 }
1292         }
1293
1294 #ifdef HAVE_JANSSON
1295         if (c->opt_json) {
1296                 TALLOC_FREE(state.headline);
1297
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;
1305                 }
1306
1307                 message_json = &_message_json;
1308         }
1309 #else /* not HAVE_JANSSON */
1310         (void)msg_type;
1311 #endif /* not HAVE_JANSSON */
1312
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;
1317         }
1318
1319         ret = 0;
1320 out:
1321 #ifdef HAVE_JANSSON
1322         if (!json_is_invalid(&_message_json)) {
1323                 json_free(&_message_json);
1324         }
1325 #endif /* HAVE_JANSSON */
1326         TALLOC_FREE(frame);
1327         return ret;
1328 }
1329
1330 struct net_witness_share_move_state {
1331         struct net_context *c;
1332         struct rpcd_witness_registration_updateB m;
1333         char *headline;
1334 };
1335
1336 static bool net_witness_share_move_prepare_fn(void *private_data)
1337 {
1338         struct net_witness_share_move_state *state =
1339                 (struct net_witness_share_move_state *)private_data;
1340
1341         if (state->headline != NULL) {
1342                 d_printf("%s\n", state->headline);
1343                 TALLOC_FREE(state->headline);
1344         }
1345
1346         return true;
1347 }
1348
1349 static bool net_witness_share_move_match_fn(void *private_data,
1350                         const struct rpcd_witness_registration *rg)
1351 {
1352         if (rg->share_name == NULL) {
1353                 return false;
1354         }
1355
1356         return true;
1357 }
1358
1359 static NTSTATUS net_witness_share_move_process_fn(void *private_data,
1360                         const struct rpcd_witness_registration *rg)
1361 {
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,
1369         };
1370         DATA_BLOB blob = { .length = 0, };
1371         enum ndr_err_code ndr_err;
1372         NTSTATUS status;
1373
1374         SMB_ASSERT(update.type != 0);
1375
1376         if (DEBUGLVL(DBGLVL_DEBUG)) {
1377                 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1378         }
1379
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;
1386         }
1387
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;
1395         }
1396
1397         return NT_STATUS_OK;
1398 }
1399
1400 static void net_witness_share_move_usage(void)
1401 {
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");
1413 }
1414
1415 static int net_witness_share_move(struct net_context *c, int argc, const char **argv)
1416 {
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,
1429         };
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;
1436
1437         if (c->display_usage) {
1438                 net_witness_share_move_usage();
1439                 goto out;
1440         }
1441
1442         if (argc != 0) {
1443                 net_witness_share_move_usage();
1444                 goto out;
1445         }
1446
1447         if (!lp_clustering()) {
1448                 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1449                 goto out;
1450         }
1451
1452         ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
1453         if (!ok) {
1454                 goto out;
1455         }
1456
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;
1466                 }
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;
1476                 }
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;
1486                 }
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;
1495                 }
1496         }
1497
1498 #ifdef HAVE_JANSSON
1499         if (c->opt_json) {
1500                 TALLOC_FREE(state.headline);
1501
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;
1509                 }
1510
1511                 message_json = &_message_json;
1512         }
1513 #else /* not HAVE_JANSSON */
1514         (void)msg_type;
1515 #endif /* not HAVE_JANSSON */
1516
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;
1521         }
1522
1523         ret = 0;
1524 out:
1525 #ifdef HAVE_JANSSON
1526         if (!json_is_invalid(&_message_json)) {
1527                 json_free(&_message_json);
1528         }
1529 #endif /* HAVE_JANSSON */
1530         TALLOC_FREE(frame);
1531         return ret;
1532 }
1533
1534 struct net_witness_force_unregister_state {
1535         struct net_context *c;
1536         struct rpcd_witness_registration_updateB m;
1537         char *headline;
1538 };
1539
1540 static bool net_witness_force_unregister_prepare_fn(void *private_data)
1541 {
1542         struct net_witness_force_unregister_state *state =
1543                 (struct net_witness_force_unregister_state *)private_data;
1544
1545         if (state->headline != NULL) {
1546                 d_printf("%s\n", state->headline);
1547                 TALLOC_FREE(state->headline);
1548         }
1549
1550         return true;
1551 }
1552
1553 static bool net_witness_force_unregister_match_fn(void *private_data,
1554                         const struct rpcd_witness_registration *rg)
1555 {
1556         return true;
1557 }
1558
1559 static NTSTATUS net_witness_force_unregister_process_fn(void *private_data,
1560                         const struct rpcd_witness_registration *rg)
1561 {
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,
1569         };
1570         DATA_BLOB blob = { .length = 0, };
1571         enum ndr_err_code ndr_err;
1572         NTSTATUS status;
1573
1574         SMB_ASSERT(update.type != 0);
1575
1576         if (DEBUGLVL(DBGLVL_DEBUG)) {
1577                 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
1578         }
1579
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;
1586         }
1587
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;
1595         }
1596
1597         return NT_STATUS_OK;
1598 }
1599
1600 static void net_witness_force_unregister_usage(void)
1601 {
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");
1617 }
1618
1619 static int net_witness_force_unregister(struct net_context *c, int argc, const char **argv)
1620 {
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,
1633         };
1634         int ret = -1;
1635         bool ok;
1636
1637         if (c->display_usage) {
1638                 net_witness_force_unregister_usage();
1639                 goto out;
1640         }
1641
1642         if (argc != 0) {
1643                 net_witness_force_unregister_usage();
1644                 goto out;
1645         }
1646
1647         if (!lp_clustering()) {
1648                 d_printf("ERROR: Only supported with clustering=yes!\n\n");
1649                 goto out;
1650         }
1651
1652         ok = net_witness_verify_update_options(c);
1653         if (!ok) {
1654                 goto out;
1655         }
1656
1657         m->type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER;
1658
1659         state.headline = talloc_asprintf(frame, "FORCE_UNREGISTER:");
1660         if (state.headline == NULL) {
1661                 goto out;
1662         }
1663
1664 #ifdef HAVE_JANSSON
1665         if (c->opt_json) {
1666                 TALLOC_FREE(state.headline);
1667
1668                 _message_json = json_new_object();
1669                 if (json_is_invalid(&_message_json)) {
1670                         goto out;
1671                 }
1672
1673                 ret = json_add_string(&_message_json,
1674                                       "type",
1675                                       "FORCE_UNREGISTER");
1676                 if (ret != 0) {
1677                         goto out;
1678                 }
1679
1680                 message_json = &_message_json;
1681         }
1682 #endif /* HAVE_JANSSON */
1683
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;
1688         }
1689
1690         ret = 0;
1691 out:
1692 #ifdef HAVE_JANSSON
1693         if (!json_is_invalid(&_message_json)) {
1694                 json_free(&_message_json);
1695         }
1696 #endif /* HAVE_JANSSON */
1697         TALLOC_FREE(frame);
1698         return ret;
1699 }
1700
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;
1708 };
1709
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)
1717 {
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;
1723
1724         if (!json_is_object(jsmsg)) {
1725                 DBG_ERR("'message[%zu]' needs to be an object\n", mi);
1726                 return NT_STATUS_INVALID_PARAMETER;
1727         }
1728
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;
1733         }
1734         if (!json_is_integer(jsctype)) {
1735                 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1736                 return NT_STATUS_INVALID_PARAMETER;
1737         }
1738         ctype = json_integer_value(jsctype);
1739
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;
1744         }
1745         if (!json_is_string(jscname)) {
1746                 DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
1747                 return NT_STATUS_INVALID_PARAMETER;
1748         }
1749         cname = json_string_value(jscname);
1750
1751         rc->type = ctype;
1752         rc->name = talloc_strdup(mem_ctx, cname);
1753         if (rc->name == NULL) {
1754                 return NT_STATUS_NO_MEMORY;
1755         }
1756
1757         return NT_STATUS_OK;
1758 }
1759
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)
1766 {
1767         struct witness_IPaddrInfoList *ipl =
1768                 &message->client_move;
1769         size_t ai, num_addrs = 0;
1770         struct witness_IPaddrInfo *addrs = NULL;
1771
1772         if (!json_is_array(jsmsg)) {
1773                 DBG_ERR("'messages[%zu]' needs to be an array\n", mi);
1774                 return NT_STATUS_INVALID_PARAMETER;
1775         }
1776
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;
1782         }
1783
1784         addrs = talloc_zero_array(mem_ctx,
1785                                   struct witness_IPaddrInfo,
1786                                   num_addrs);
1787         if (addrs == NULL) {
1788                 return NT_STATUS_NO_MEMORY;
1789         }
1790
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;
1801
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;
1806                 }
1807
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;
1813                 }
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;
1819                 }
1820                 flags = json_integer_value(jsflags);
1821
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;
1829                         }
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;
1836                         }
1837                 } else {
1838                         ipv4 = "0.0.0.0";
1839                 }
1840
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;
1849                         }
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;
1856                         }
1857                 } else {
1858                         ipv6 = "::";
1859                 }
1860
1861                 info->flags = flags;
1862                 info->ipv4 = talloc_strdup(addrs, ipv4);
1863                 if (info->ipv4 == NULL) {
1864                         return NT_STATUS_NO_MEMORY;
1865                 }
1866                 info->ipv6 = talloc_strdup(addrs, ipv6);
1867                 if (info->ipv6 == NULL) {
1868                         return NT_STATUS_NO_MEMORY;
1869                 }
1870         }
1871
1872         ipl->num = num_addrs;
1873         ipl->addr = addrs;
1874
1875         return NT_STATUS_OK;
1876 }
1877 #endif /* HAVE_JANSSON */
1878
1879 static NTSTATUS net_witness_force_response_parse(struct net_witness_force_response_state *state)
1880 {
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;
1892
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;
1897
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;
1904                 }
1905                 state->json_root = (struct json_object) {
1906                         .root = jsroot,
1907                         .valid = true,
1908                 };
1909         }
1910
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;
1915
1916         if (jsroot == NULL) {
1917                 return NT_STATUS_OK;
1918         }
1919
1920         jsresult = json_object_get(jsroot, "result");
1921         if (jsresult != NULL) {
1922                 int val_type = json_typeof(jsresult);
1923
1924                 switch (val_type) {
1925                 case JSON_INTEGER: {
1926                         json_int_t val = json_integer_value(jsresult);
1927
1928                         if (val > UINT32_MAX) {
1929                                 DBG_ERR("Invalid 'result' value: %d\n",
1930                                         (int) val);
1931                                 return NT_STATUS_INVALID_PARAMETER;
1932                         }
1933                         if (val < 0) {
1934                                 DBG_ERR("invalid 'result' value: %d\n",
1935                                         (int) val);
1936                                 return NT_STATUS_INVALID_PARAMETER;
1937                         }
1938
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;
1944                 }
1945         }
1946
1947         jsresponse = json_object_get(jsroot, "response");
1948         if (jsresponse == NULL) {
1949                 return NT_STATUS_OK;
1950         }
1951
1952         if (!json_is_object(jsresponse)) {
1953                 DBG_ERR("Invalid json type 'response' needs object\n");
1954                 return NT_STATUS_INVALID_PARAMETER;
1955         }
1956
1957         response = talloc_zero(talloc_tos(), struct witness_notifyResponse);
1958         if (response == NULL) {
1959                 return NT_STATUS_NO_MEMORY;
1960         }
1961
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;
1966         }
1967         {
1968                 int val_type = json_typeof(jstype);
1969
1970                 switch (val_type) {
1971                 case JSON_INTEGER: {
1972                         json_int_t val = json_integer_value(jstype);
1973
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;
1978                         }
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;
1983                         }
1984
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;
1991                 }
1992         }
1993
1994         force->response = response;
1995
1996         jsmessages = json_object_get(jsresponse, "messages");
1997         if (jsmessages == NULL) {
1998                 return NT_STATUS_OK;
1999         }
2000
2001         if (!json_is_array(jsmessages)) {
2002                 DBG_ERR("'messages' in 'response' needs to be an array\n");
2003                 return NT_STATUS_INVALID_PARAMETER;
2004         }
2005
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;
2011         }
2012
2013         messages = talloc_zero_array(response,
2014                                      union witness_notifyResponse_message,
2015                                      num_messages);
2016         if (messages == NULL) {
2017                 return NT_STATUS_NO_MEMORY;
2018         }
2019
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;
2024
2025                 switch (response->type) {
2026                 case WITNESS_NOTIFY_RESOURCE_CHANGE:
2027                         status = net_witness_force_response_parse_rc(state,
2028                                                                      jsmsg,
2029                                                                      messages,
2030                                                                      mi,
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;
2038                         }
2039
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,
2047                                                                       mi,
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;
2055                         }
2056
2057                         break;
2058                 }
2059         }
2060
2061         response->num = num_messages;
2062         response->messages = messages;
2063
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 */
2069 }
2070
2071 static bool net_witness_force_response_prepare_fn(void *private_data)
2072 {
2073         struct net_witness_force_response_state *state =
2074                 (struct net_witness_force_response_state *)private_data;
2075
2076         if (state->headline != NULL) {
2077                 d_printf("%s\n", state->headline);
2078                 TALLOC_FREE(state->headline);
2079         }
2080
2081         return true;
2082 }
2083
2084 static bool net_witness_force_response_match_fn(void *private_data,
2085                         const struct rpcd_witness_registration *rg)
2086 {
2087         return true;
2088 }
2089
2090 static NTSTATUS net_witness_force_response_process_fn(void *private_data,
2091                         const struct rpcd_witness_registration *rg)
2092 {
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,
2100         };
2101         DATA_BLOB blob = { .length = 0, };
2102         enum ndr_err_code ndr_err;
2103         NTSTATUS status;
2104
2105         SMB_ASSERT(update.type != 0);
2106
2107         if (DEBUGLVL(DBGLVL_DEBUG)) {
2108                 NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
2109         }
2110
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;
2117         }
2118
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;
2126         }
2127
2128         return NT_STATUS_OK;
2129 }
2130
2131 static void net_witness_force_response_usage(void)
2132 {
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                                         "}]"
2181                                 "]"
2182                         "}}'",
2183                         "'{ \"result\": 0, \"response\": { \"type\": 3, "
2184                                 "\"messages\": ["
2185                                         "[{ "
2186                                                 "\"flags\": 9, "
2187                                                 "\"ipv4\": \"10.0.10.1\" "
2188                                         "}]"
2189                                 "]"
2190                         "}}'",
2191                         "'{ \"result\": 0, \"response\": { \"type\": 4, "
2192                                 "\"messages\": ["
2193                                         "[{ "
2194                                                 "\"flags\": 9, "
2195                                                 "\"ipv4\": \"10.0.10.1\" "
2196                                         "}]"
2197                                 "]"
2198                         "}}'");
2199 }
2200
2201 static int net_witness_force_response(struct net_context *c, int argc, const char **argv)
2202 {
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,
2214         };
2215         NTSTATUS status;
2216         int ret = -1;
2217         bool ok;
2218
2219         if (c->display_usage) {
2220                 net_witness_force_response_usage();
2221                 goto out;
2222         }
2223
2224         if (argc != 0) {
2225                 net_witness_force_response_usage();
2226                 goto out;
2227         }
2228
2229         if (!lp_clustering()) {
2230                 d_printf("ERROR: Only supported with clustering=yes!\n\n");
2231                 goto out;
2232         }
2233
2234         ok = net_witness_verify_update_options(c);
2235         if (!ok) {
2236                 goto out;
2237         }
2238
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;
2244         }
2245
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 : "");
2251
2252         if (state.headline == NULL) {
2253                 goto out;
2254         }
2255
2256 #ifdef HAVE_JANSSON
2257         if (c->opt_json) {
2258                 TALLOC_FREE(state.headline);
2259
2260                 _message_json = json_new_object();
2261                 if (json_is_invalid(&_message_json)) {
2262                         goto out;
2263                 }
2264
2265                 ret = json_add_string(&_message_json,
2266                                       "type",
2267                                       "FORCE_RESPONSE");
2268                 if (ret != 0) {
2269                         goto out;
2270                 }
2271
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;
2278                         }
2279                         state.json_root = json_empty_object;
2280                 }
2281                 message_json = &_message_json;
2282         }
2283 #endif /* HAVE_JANSSON */
2284
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;
2289         }
2290
2291         ret = 0;
2292 out:
2293 #ifdef HAVE_JANSSON
2294         if (!json_is_invalid(&_message_json)) {
2295                 json_free(&_message_json);
2296         }
2297         if (!json_is_invalid(&state.json_root)) {
2298                 json_free(&state.json_root);
2299         }
2300 #endif /* HAVE_JANSSON */
2301         TALLOC_FREE(frame);
2302         return ret;
2303 }
2304
2305 int net_witness(struct net_context *c, int argc, const char **argv)
2306 {
2307         struct functable func[] = {
2308                 {
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"),
2317                 },
2318                 {
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"),
2327                 },
2328                 {
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"),
2337                 },
2338                 {
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"),
2346                 },
2347                 {
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)"),
2356                 },
2357                 {NULL, NULL, 0, NULL, NULL}
2358         };
2359
2360         return net_run_function(c, argc, argv, "net witness", func);
2361 }