rpcclient: Fix ncacn_ip_tcp:<ip-address>
[samba.git] / source3 / rpcclient / cmd_witness.c
1 /*
2    Unix SMB/CIFS implementation.
3    RPC pipe client
4
5    Copyright (C) Gregor Beck 2013-2014
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "rpcclient.h"
23 #include "librpc/gen_ndr/ndr_witness_c.h"
24 #include <popt.h>
25
26 /*
27  * We have to use the same connection for each subcommand
28  * for the context handles to be meaningful.
29  */
30 static void use_only_one_rpc_pipe_hack(struct rpc_pipe_client *cli);
31
32 static WERROR cmd_witness_GetInterfaceList(struct rpc_pipe_client *cli,
33                                            TALLOC_CTX *mem_ctx, int argc,
34                                            const char **argv)
35 {
36         NTSTATUS status;
37         WERROR result;
38         TALLOC_CTX *frame = talloc_stackframe();
39         struct witness_interfaceList *interface_list = NULL;
40         uint32_t num_interfaces, n;
41         struct witness_interfaceInfo *interfaces;
42
43         use_only_one_rpc_pipe_hack(cli);
44
45         status = dcerpc_witness_GetInterfaceList(cli->binding_handle, frame,
46                                                  &interface_list, &result);
47         if (!NT_STATUS_IS_OK(status)) {
48                 DEBUG(0, ("dcerpc_witness_GetInterfaceList failed, status: %s\n", nt_errstr(status)));
49                 result = ntstatus_to_werror(status);
50                 goto done;
51         }
52         if (!W_ERROR_IS_OK(result)) {
53                 DEBUG(0, ("dcerpc_witness_GetInterfaceList failed, error: %s\n", win_errstr(result)));
54                 goto done;
55         }
56
57         SMB_ASSERT(interface_list);
58         interfaces = interface_list->interfaces;
59         num_interfaces = interface_list->num_interfaces;
60
61         for (n=0; n < num_interfaces; n++) {
62                 char wif = (interfaces[n].flags & WITNESS_INFO_WITNESS_IF) ? '*' : ' ';
63                 char state = 'X';
64
65                 if (interfaces[n].state == WITNESS_STATE_AVAILABLE) {
66                         state = '+';
67                 } else if (interfaces[n].state == WITNESS_STATE_UNAVAILABLE) {
68                         state = '-';
69                 } else if (interfaces[n].state == WITNESS_STATE_UNKNOWN) {
70                         state = '?';
71                 }
72
73                 d_printf("%c%c %s", wif, state, interfaces[n].group_name);
74
75                 if (interfaces[n].flags & WITNESS_INFO_IPv4_VALID) {
76                         d_printf(" %s", interfaces[n].ipv4);
77                 }
78
79                 if (interfaces[n].flags & WITNESS_INFO_IPv6_VALID) {
80                         d_printf(" %s", interfaces[n].ipv6);
81                 }
82
83                 switch (interfaces[n].version) {
84                 case WITNESS_V1:
85                         d_printf(" V1");
86                         break;
87                 case WITNESS_V2:
88                         d_printf(" V2");
89                         break;
90                 default:
91                         d_printf(" Unsupported Version (0x%08x)", interfaces[n].version);
92                 }
93
94                 d_printf("\n");
95         }
96
97 done:
98         talloc_free(frame);
99         return result;
100 }
101
102 static WERROR cmd_witness_Register(struct rpc_pipe_client *cli,
103                                    TALLOC_CTX *mem_ctx, int argc,
104                                    const char **argv)
105 {
106         static char hostname[MAXHOSTNAMELEN] = {'\0'};
107         NTSTATUS status;
108         WERROR result = WERR_OK;
109         TALLOC_CTX *frame = talloc_stackframe();
110         struct policy_handle hnd;
111         const char *net_name = NULL;
112         const char *ip_addr = NULL;
113         const char *client_name = hostname;
114         long version = WITNESS_V1;
115         int c;
116         poptContext optCon;
117         struct poptOption optionsTable[] = {
118                 {
119                         .longName   = "version",
120                         .shortName  = 'v',
121                         .argInfo    = POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT,
122                         .arg        = &version,
123                         .val        = WITNESS_V2,
124                         .descrip    = "witness version",
125                         .argDescrip = "version"
126                 },
127                 {
128                         .longName   = "V1",
129                         .shortName  = '1',
130                         .argInfo    = POPT_ARG_LONG|POPT_ARG_VAL,
131                         .arg        = &version,
132                         .val        = WITNESS_V1,
133                         .descrip    = "witness version 1",
134                         .argDescrip = NULL
135                 },
136                 {
137                         .longName   = "V2",
138                         .shortName  = '2',
139                         .argInfo    = POPT_ARG_LONG|POPT_ARG_VAL,
140                         .arg        = &version,
141                         .val        = WITNESS_V2,
142                         .descrip    = "witness version 2",
143                         .argDescrip = NULL
144                 },
145                 {
146                         .longName   = "net",
147                         .shortName  = 'n',
148                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
149                         .arg        = &net_name,
150                         .val        = 0,
151                         .descrip    = "net name",
152                         .argDescrip = NULL
153                 },
154                 {
155                         .longName   = "ip",
156                         .shortName  =  'i',
157                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
158                         .arg        = &ip_addr,
159                         .val        = 0,
160                         .descrip    = "ip address",
161                         .argDescrip = NULL
162                 },
163                 {
164                         .longName   = "client",
165                         .shortName  = 'c',
166                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT|POPT_ARGFLAG_OPTIONAL,
167                         .arg        = &client_name,
168                         .val        = 0,
169                         .descrip    = "client name",
170                         .argDescrip = NULL
171                 },
172                 POPT_TABLEEND
173         };
174
175         use_only_one_rpc_pipe_hack(cli);
176
177         if (hostname[0] == '\0') {
178                 gethostname (hostname, sizeof(hostname));
179         }
180
181         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
182
183         while ((c = poptGetNextOpt(optCon)) >= 0) { }
184
185         if (c < -1) {
186              /* an error occurred during option processing */
187                 d_fprintf(stderr, "%s: %s\n",
188                           poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
189                           poptStrerror(c));
190              goto done;
191         }
192
193         if (argc < 2 || poptPeekArg(optCon) != NULL) {
194                 poptPrintHelp(optCon, stderr, 0);
195                 goto done;
196         }
197
198         status = dcerpc_witness_Register(cli->binding_handle, frame,
199                                          &hnd,
200                                          version,
201                                          net_name, ip_addr, client_name,
202                                          &result);
203         if (!NT_STATUS_IS_OK(status)) {
204                 DEBUG(0, ("dcerpc_witness_Register failed, status: %s\n", nt_errstr(status)));
205                 result = ntstatus_to_werror(status);
206                 goto done;
207         }
208         if (!W_ERROR_IS_OK(result)) {
209                 DEBUG(0, ("dcerpc_witness_Register failed, error: %s\n", win_errstr(result)));
210                 goto done;
211         }
212
213         d_printf("%x:%s\n", hnd.handle_type, GUID_string(frame, &hnd.uuid));
214
215 done:
216         poptFreeContext(optCon);
217         talloc_free(frame);
218         return result;
219 }
220
221 static WERROR cmd_witness_RegisterEx(struct rpc_pipe_client *cli,
222                                      TALLOC_CTX *mem_ctx, int argc,
223                                      const char **argv)
224 {
225         static char hostname[MAXHOSTNAMELEN] = {'\0'};
226         NTSTATUS status;
227         WERROR result = WERR_OK;
228         TALLOC_CTX *frame = talloc_stackframe();
229         struct policy_handle hnd;
230         const char *net_name = NULL;
231         const char *ip_addr = NULL;
232         const char *share_name = NULL;
233         const char *client_name = hostname;
234         long version = WITNESS_V2;
235         long flags = 0;
236         long timeout = 0;
237         int c;
238         poptContext optCon;
239         struct poptOption optionsTable[] = {
240                 {
241                         .longName   = "version",
242                         .shortName  = 'v',
243                         .argInfo    = POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT,
244                         .arg        = &version,
245                         .val        = WITNESS_V2,
246                         .descrip    = "witness version",
247                         .argDescrip = "version"
248                 },
249                 {
250                         .longName   = "V1",
251                         .shortName  = '1',
252                         .argInfo    = POPT_ARG_LONG|POPT_ARG_VAL,
253                         .arg        = &version,
254                         .val        = WITNESS_V1,
255                         .descrip    = "witness version 1",
256                 },
257                 {
258                         .longName   = "V2",
259                         .shortName  = '2',
260                         .argInfo    = POPT_ARG_LONG|POPT_ARG_VAL,
261                         .arg        = &version,
262                         .val        = WITNESS_V2,
263                         .descrip    = "witness version 2",
264                 },
265                 {
266                         .longName   = "net",
267                         .shortName  = 'n',
268                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
269                         .arg        = &net_name,
270                         .val        = 0,
271                         .descrip    = "net name",
272                 },
273                 {
274                         .longName   = "ip",
275                         .shortName  = 'i',
276                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
277                         .arg        = &ip_addr,
278                         .val        = 0,
279                         .descrip    = "ip address",
280                 },
281                 {
282                         .longName   = "share",
283                         .shortName  = 's',
284                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
285                         .arg        = &share_name,
286                         .val        = 0,
287                         .descrip    = "share name",
288                 },
289                 {
290                         .longName   = "client",
291                         .shortName  = 'c',
292                         .argInfo    = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT|POPT_ARGFLAG_OPTIONAL,
293                         .arg        = &client_name,
294                         .val        = 0,
295                         .descrip    = "client name",
296                 },
297                 {
298                         .longName   = "flags",
299                         .shortName  = 'f',
300                         .argInfo    = POPT_ARG_LONG|POPT_ARGFLAG_OR|POPT_ARGFLAG_SHOW_DEFAULT,
301                         .arg        = &flags,
302                         .val        = 0,
303                         .descrip    = "flags",
304                 },
305                 {
306                         .longName   = "timeout",
307                         .shortName  = 't',
308                         .argInfo    = POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT,
309                         .arg        = &timeout,
310                         .val        = 0,
311                         .descrip    = "timeout",
312                 },
313                 POPT_TABLEEND
314         };
315
316         use_only_one_rpc_pipe_hack(cli);
317
318         if (hostname[0] == '\0') {
319                 gethostname (hostname, sizeof(hostname));
320         }
321
322         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
323
324         while ((c = poptGetNextOpt(optCon)) >= 0) { }
325
326         if (c < -1) {
327              /* an error occurred during option processing */
328                 d_fprintf(stderr, "%s: %s\n",
329                           poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
330                           poptStrerror(c));
331              goto done;
332         }
333
334         if (argc < 2 || poptPeekArg(optCon) != NULL) {
335                 poptPrintHelp(optCon, stderr, 0);
336                 goto done;
337         }
338
339         status = dcerpc_witness_RegisterEx(cli->binding_handle, frame,
340                                            &hnd,
341                                            version,
342                                            net_name, share_name, ip_addr, client_name,
343                                            flags, timeout,
344                                            &result);
345         if (!NT_STATUS_IS_OK(status)) {
346                 DEBUG(0, ("dcerpc_witness_RegisterEx failed, status: %s\n", nt_errstr(status)));
347                 result = ntstatus_to_werror(status);
348                 goto done;
349         }
350         if (!W_ERROR_IS_OK(result)) {
351                 DEBUG(0, ("dcerpc_witness_RegisterEx failed, error: %s\n", win_errstr(result)));
352                 goto done;
353         }
354
355         d_printf("%x:%s\n", hnd.handle_type, GUID_string(frame, &hnd.uuid));
356
357 done:
358         poptFreeContext(optCon);
359         talloc_free(frame);
360         return result;
361 }
362
363 static bool
364 read_context_handle(const char *str, struct policy_handle *hnd)
365 {
366         NTSTATUS status;
367         long type;
368         char *pos;
369         struct GUID guid;
370
371         type = strtol(str, &pos, 16);
372         if (*pos != ':') {
373                 DEBUG(0, ("read_context_handle: failed to parse type\n"));
374                 return false;
375         }
376         status = GUID_from_string(pos+1, &guid);
377         if (!NT_STATUS_IS_OK(status)) {
378                 DEBUG(0, ("read_context_handle: failed to parse guid %s\n", nt_errstr(status)));
379                 return false;
380         }
381
382         hnd->handle_type = type;
383         hnd->uuid = guid;
384         return true;
385 }
386
387 static WERROR cmd_witness_UnRegister(struct rpc_pipe_client *cli,
388                                      TALLOC_CTX *mem_ctx, int argc,
389                                      const char **argv)
390 {
391         NTSTATUS status;
392         WERROR result = WERR_OK;
393         TALLOC_CTX *frame = talloc_stackframe();
394         struct policy_handle hnd;
395
396         use_only_one_rpc_pipe_hack(cli);
397
398         if (argc != 2) {
399                 d_printf("%s <context_handle>\n", argv[0]);
400                 goto done;
401         }
402
403         if (!read_context_handle(argv[1], &hnd)) {
404                 result = WERR_INVALID_PARAMETER;
405                 goto done;
406         }
407
408         status = dcerpc_witness_UnRegister(cli->binding_handle, frame,
409                                            hnd, &result);
410         if (!NT_STATUS_IS_OK(status)) {
411                 DEBUG(0, ("dcerpc_witness_UnRegister failed, status: %s\n", nt_errstr(status)));
412                 result = ntstatus_to_werror(status);
413                 goto done;
414         }
415         if (!W_ERROR_IS_OK(result)) {
416                 DEBUG(0, ("dcerpc_witness_UnRegister failed, error: %s\n", win_errstr(result)));
417                 goto done;
418         }
419
420 done:
421         talloc_free(frame);
422         return result;
423 }
424
425 static void print_notify_response_resource_change(struct witness_ResourceChange *r)
426 {
427         const char *type_str;
428
429         if (r->type == WITNESS_RESOURCE_STATE_UNKNOWN) {
430                 type_str = "Unknown";
431         } else if (r->type == WITNESS_RESOURCE_STATE_AVAILABLE) {
432                 type_str = "Available\n";
433         } else if (r->type == WITNESS_RESOURCE_STATE_UNAVAILABLE) {
434                 type_str = "Unavailable";
435         } else {
436                 type_str = talloc_asprintf(r, "Invalid (%u)", r->type);
437         }
438         d_printf("%s -> %s\n", r->name, type_str);
439 }
440
441 static void print_notify_response_ip_addr_info_list(struct witness_IPaddrInfoList *r)
442 {
443         int i;
444
445         for (i=0; i < r->num; i++) {
446                 uint32_t flags = r->addr[i].flags;
447                 const char *str4 = r->addr[i].ipv4;
448                 const char *str6 = r->addr[i].ipv6;
449
450                 d_printf("Flags 0x%08x", flags);
451                 if (flags & WITNESS_IPADDR_V4) {
452                         d_printf(" %s", str4);
453                 }
454                 if (flags & WITNESS_IPADDR_V6) {
455                         d_printf(" %s", str6);
456                 }
457                 if (flags & WITNESS_IPADDR_ONLINE) {
458                         d_printf(" Online");
459                 }
460                 if (flags & WITNESS_IPADDR_ONLINE) {
461                         d_printf(" Offline");
462                 }
463                 d_printf("\n");
464         }
465 }
466
467 static void print_notify_response(union witness_notifyResponse_message *r,
468                                   uint32_t type)
469 {
470         switch (type) {
471         case WITNESS_NOTIFY_RESOURCE_CHANGE:
472                 print_notify_response_resource_change(&r->resource_change);
473                 break;
474         case WITNESS_NOTIFY_CLIENT_MOVE:
475         case WITNESS_NOTIFY_SHARE_MOVE:
476         case WITNESS_NOTIFY_IP_CHANGE:
477                 print_notify_response_ip_addr_info_list(&r->client_move);
478                 break;
479         default:
480                 break;
481         }
482 }
483
484 static WERROR cmd_witness_AsyncNotify(struct rpc_pipe_client *cli,
485                                       TALLOC_CTX *mem_ctx, int argc,
486                                       const char **argv)
487 {
488         NTSTATUS status;
489         WERROR result = WERR_OK;
490         TALLOC_CTX *frame = talloc_stackframe();
491         struct policy_handle hnd;
492         struct witness_notifyResponse *response = NULL;
493         uint32_t timeout;
494         int i;
495
496         use_only_one_rpc_pipe_hack(cli);
497
498         if (argc != 2) {
499                 d_printf("%s <context_handle>\n", argv[0]);
500                 goto done;
501         }
502
503         if (!read_context_handle(argv[1], &hnd)) {
504                 result = WERR_INVALID_PARAMETER;
505                 goto done;
506         }
507
508         timeout = dcerpc_binding_handle_set_timeout(cli->binding_handle, UINT32_MAX);
509         status = dcerpc_witness_AsyncNotify(cli->binding_handle, frame, hnd,
510                                             &response, &result);
511         dcerpc_binding_handle_set_timeout(cli->binding_handle, timeout);
512         if (!NT_STATUS_IS_OK(status)) {
513                 DEBUG(0, ("dcerpc_witness_AsyncNotify failed, status: %s\n", nt_errstr(status)));
514                 result = ntstatus_to_werror(status);
515                 goto done;
516         }
517         if (!W_ERROR_IS_OK(result)) {
518                 DEBUG(0, ("dcerpc_witness_AsyncNotify failed, error: %s\n", win_errstr(result)));
519                 goto done;
520         }
521
522         if (response == NULL) {
523                 d_printf("Got an empty response\n");
524                 goto done;
525         }
526
527         switch(response->type) {
528         case WITNESS_NOTIFY_RESOURCE_CHANGE:
529                 d_printf("Resource change");
530                 break;
531         case WITNESS_NOTIFY_CLIENT_MOVE:
532                 d_printf("Client move");
533                 break;
534         case WITNESS_NOTIFY_SHARE_MOVE:
535                 d_printf("Share move");
536                 break;
537         case WITNESS_NOTIFY_IP_CHANGE:
538                 d_printf("IP change");
539                 break;
540         default:
541                 d_printf("Unknown (0x%x)", (int)response->type);
542         }
543         d_printf(" with %d messages\n", response->num);
544
545         for (i=0; i < response->num; i++) {
546                 print_notify_response(&response->messages[i], response->type);
547         }
548 done:
549         talloc_free(frame);
550         return result;
551 }
552
553 struct cmd_set witness_commands[] = {
554         {
555                 .name = "WITNESS",
556         },
557         {
558                 .name               = "GetInterfaceList",
559                 .returntype         = RPC_RTYPE_WERROR,
560                 .ntfn               = NULL,
561                 .wfn                = &cmd_witness_GetInterfaceList,
562                 .table              = &ndr_table_witness,
563                 .rpc_pipe           = NULL,
564                 .description        = "List the interfaces to which witness client connections can be made",
565                 .usage              = "",
566         },
567         {
568                 .name               = "Register",
569                 .returntype         = RPC_RTYPE_WERROR,
570                 .ntfn               = NULL,
571                 .wfn                = &cmd_witness_Register,
572                 .table              = &ndr_table_witness,
573                 .rpc_pipe           = NULL,
574                 .description        = "Register for resource state change notifications of a NetName and IPAddress",
575                 .usage              = "",
576         },
577         {
578                 .name               = "UnRegister",
579                 .returntype         = RPC_RTYPE_WERROR,
580                 .ntfn               = NULL,
581                 .wfn                = &cmd_witness_UnRegister,
582                 .table              = &ndr_table_witness,
583                 .rpc_pipe           = NULL,
584                 .description        = "Unregister for notifications from the server</para></listitem></varlistentry>",
585                 .usage              = "",
586         },
587         {
588                 .name               = "AsyncNotify",
589                 .returntype         = RPC_RTYPE_WERROR,
590                 .ntfn               = NULL,
591                 .wfn                = &cmd_witness_AsyncNotify,
592                 .table              = &ndr_table_witness,
593                 .rpc_pipe           = NULL,
594                 .description        = "Request notification of registered resource changes from the server",
595                 .usage              = "",
596         },
597         {
598                 .name               = "RegisterEx",
599                 .returntype         = RPC_RTYPE_WERROR,
600                 .ntfn               = NULL,
601                 .wfn                = &cmd_witness_RegisterEx,
602                 .table              = &ndr_table_witness,
603                 .rpc_pipe           = NULL,
604                 .description        = "Register for resource state change notifications of a NetName, ShareName and multiple IPAddresses",
605                 .usage              = "",
606         },
607         {
608                 .name = NULL,
609         }
610 };
611
612 /*
613  * We have to use the same connection for each subcommand
614  * for the context handles to be meaningful.
615  */
616 static void use_only_one_rpc_pipe_hack(struct rpc_pipe_client *cli)
617 {
618         struct cmd_set *ptr;
619
620         for (ptr = &witness_commands[0]; ptr->name; ptr++) {
621                 ptr->rpc_pipe = cli;
622         }
623 }