s4-dns: dlz_bind9: Fix ipv6 updates
[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(" Unsuported 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                 {"version", 'v', POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT, &version, WITNESS_V2, "witness version", "version"},
119                 {"V1", '1', POPT_ARG_LONG|POPT_ARG_VAL, &version, WITNESS_V1, "witness version 1", NULL},
120                 {"V2", '2', POPT_ARG_LONG|POPT_ARG_VAL, &version, WITNESS_V2, "witness version 2", NULL},
121                 {"net", 'n', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &net_name, 0, "net name", NULL},
122                 {"ip",  'i', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &ip_addr, 0, "ip address", NULL},
123                 {"client", 'c', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT|POPT_ARGFLAG_OPTIONAL, &client_name, 0, "client name", NULL},
124                 { NULL, 0, 0, NULL, 0 }
125         };
126
127         use_only_one_rpc_pipe_hack(cli);
128
129         if (hostname[0] == '\0') {
130                 gethostname (hostname, sizeof(hostname));
131         }
132
133         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
134
135         while ((c = poptGetNextOpt(optCon)) >= 0) { }
136
137         if (c < -1) {
138              /* an error occurred during option processing */
139                 d_fprintf(stderr, "%s: %s\n",
140                           poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
141                           poptStrerror(c));
142              goto done;
143         }
144
145         if (argc < 2 || poptPeekArg(optCon) != NULL) {
146                 poptPrintHelp(optCon, stderr, 0);
147                 goto done;
148         }
149
150         status = dcerpc_witness_Register(cli->binding_handle, frame,
151                                          &hnd,
152                                          version,
153                                          net_name, ip_addr, client_name,
154                                          &result);
155         if (!NT_STATUS_IS_OK(status)) {
156                 DEBUG(0, ("dcerpc_witness_Register failed, status: %s\n", nt_errstr(status)));
157                 result = ntstatus_to_werror(status);
158                 goto done;
159         }
160         if (!W_ERROR_IS_OK(result)) {
161                 DEBUG(0, ("dcerpc_witness_Register failed, error: %s\n", win_errstr(result)));
162                 goto done;
163         }
164
165         d_printf("%x:%s\n", hnd.handle_type, GUID_string(frame, &hnd.uuid));
166
167 done:
168         talloc_free(frame);
169         return result;
170 }
171
172 static WERROR cmd_witness_RegisterEx(struct rpc_pipe_client *cli,
173                                      TALLOC_CTX *mem_ctx, int argc,
174                                      const char **argv)
175 {
176         static char hostname[MAXHOSTNAMELEN] = {'\0'};
177         NTSTATUS status;
178         WERROR result = WERR_OK;
179         TALLOC_CTX *frame = talloc_stackframe();
180         struct policy_handle hnd;
181         const char *net_name = NULL;
182         const char *ip_addr = NULL;
183         const char *share_name = NULL;
184         const char *client_name = hostname;
185         long version = WITNESS_V2;
186         long flags = 0;
187         long timeout = 0;
188         int c;
189         poptContext optCon;
190         struct poptOption optionsTable[] = {
191                 {"version", 'v', POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT, &version, WITNESS_V2, "witness version", "version"},
192                 {"V1", '1', POPT_ARG_LONG|POPT_ARG_VAL, &version, WITNESS_V1, "witness version 1", NULL},
193                 {"V2", '2', POPT_ARG_LONG|POPT_ARG_VAL, &version, WITNESS_V2, "witness version 2", NULL},
194                 {"net", 'n', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &net_name, 0, "net name", NULL},
195                 {"ip",  'i', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &ip_addr, 0, "ip address", NULL},
196                 {"share", 's', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &share_name, 0, "share name", NULL},
197                 {"client", 'c', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT|POPT_ARGFLAG_OPTIONAL, &client_name, 0, "client name", NULL},
198                 {"flags", 'f', POPT_ARG_LONG|POPT_ARGFLAG_OR|POPT_ARGFLAG_SHOW_DEFAULT, &flags, 0, "flags", NULL},
199                 {"timeout", 't', POPT_ARG_LONG|POPT_ARGFLAG_SHOW_DEFAULT, &timeout, 0, "timeout", NULL},
200                 { NULL, 0, 0, NULL, 0 }
201         };
202
203         use_only_one_rpc_pipe_hack(cli);
204
205         if (hostname[0] == '\0') {
206                 gethostname (hostname, sizeof(hostname));
207         }
208
209         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
210
211         while ((c = poptGetNextOpt(optCon)) >= 0) { }
212
213         if (c < -1) {
214              /* an error occurred during option processing */
215                 d_fprintf(stderr, "%s: %s\n",
216                           poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
217                           poptStrerror(c));
218              goto done;
219         }
220
221         if (argc < 2 || poptPeekArg(optCon) != NULL) {
222                 poptPrintHelp(optCon, stderr, 0);
223                 goto done;
224         }
225
226         status = dcerpc_witness_RegisterEx(cli->binding_handle, frame,
227                                            &hnd,
228                                            version,
229                                            net_name, share_name, ip_addr, client_name,
230                                            flags, timeout,
231                                            &result);
232         if (!NT_STATUS_IS_OK(status)) {
233                 DEBUG(0, ("dcerpc_witness_RegisterEx failed, status: %s\n", nt_errstr(status)));
234                 result = ntstatus_to_werror(status);
235                 goto done;
236         }
237         if (!W_ERROR_IS_OK(result)) {
238                 DEBUG(0, ("dcerpc_witness_RegisterEx failed, error: %s\n", win_errstr(result)));
239                 goto done;
240         }
241
242         d_printf("%x:%s\n", hnd.handle_type, GUID_string(frame, &hnd.uuid));
243
244 done:
245         poptFreeContext(optCon);
246         talloc_free(frame);
247         return result;
248 }
249
250 static bool
251 read_context_handle(const char *str, struct policy_handle *hnd)
252 {
253         NTSTATUS status;
254         long type;
255         char *pos;
256         struct GUID guid;
257
258         type = strtol(str, &pos, 16);
259         if (*pos != ':') {
260                 DEBUG(0, ("read_context_handle: failed to parse type\n"));
261                 return false;
262         }
263         status = GUID_from_string(pos+1, &guid);
264         if (!NT_STATUS_IS_OK(status)) {
265                 DEBUG(0, ("read_context_handle: failed to parse guid %s\n", nt_errstr(status)));
266                 return false;
267         }
268
269         hnd->handle_type = type;
270         hnd->uuid = guid;
271         return true;
272 }
273
274 static WERROR cmd_witness_UnRegister(struct rpc_pipe_client *cli,
275                                      TALLOC_CTX *mem_ctx, int argc,
276                                      const char **argv)
277 {
278         NTSTATUS status;
279         WERROR result = WERR_OK;
280         TALLOC_CTX *frame = talloc_stackframe();
281         struct policy_handle hnd;
282
283         use_only_one_rpc_pipe_hack(cli);
284
285         if (argc != 2) {
286                 d_printf("%s <context_handle>\n", argv[0]);
287                 goto done;
288         }
289
290         if (!read_context_handle(argv[1], &hnd)) {
291                 result = WERR_INVALID_PARAM;
292                 goto done;
293         }
294
295         status = dcerpc_witness_UnRegister(cli->binding_handle, frame,
296                                            hnd, &result);
297         if (!NT_STATUS_IS_OK(status)) {
298                 DEBUG(0, ("dcerpc_witness_UnRegister failed, status: %s\n", nt_errstr(status)));
299                 result = ntstatus_to_werror(status);
300                 goto done;
301         }
302         if (!W_ERROR_IS_OK(result)) {
303                 DEBUG(0, ("dcerpc_witness_UnRegister failed, error: %s\n", win_errstr(result)));
304                 goto done;
305         }
306
307 done:
308         talloc_free(frame);
309         return result;
310 }
311
312 enum {
313         RESOURCE_STATE_UNKNOWN     = 0x00,
314         RESOURCE_STATE_AVAILABLE   = 0x01,
315         RESOURCE_STATE_UNAVAILABLE = 0xff
316 };
317
318 static bool AsyncNotify_Change(TALLOC_CTX *mem_ctx, const uint8_t **ptr)
319 {
320         const uint8_t *pos = *ptr;
321         uint32_t length = IVAL(pos,0);
322         uint32_t type   = IVAL(pos,4);
323         char *name = NULL;
324         const char *type_str;
325         bool ok;
326         ok = convert_string_talloc(mem_ctx, CH_UTF16LE, CH_UNIX, pos + 8,
327                                    length - 8, &name, NULL);
328         if (!ok) {
329                 return false;
330         }
331
332         if (type == RESOURCE_STATE_UNKNOWN) {
333                 type_str = "Unknown";
334         } else if(type == RESOURCE_STATE_AVAILABLE) {
335                 type_str = "Available\n";
336         } else if(type == RESOURCE_STATE_UNAVAILABLE) {
337                 type_str = "Unavailable";
338         } else {
339                 type_str = talloc_asprintf(name, "Invalid (%u)", type);
340         }
341         d_printf("%s -> %s\n", name, type_str);
342
343         TALLOC_FREE(name);
344         *ptr += length;
345         return true;
346 }
347
348 enum {
349         IPADDR_V4      = 0x01,
350         IPADDR_V6      = 0x02,
351         IPADDR_ONLINE  = 0x08,
352         IPADDR_OFFLINE = 0x10,
353 };
354
355 /* IPADDR_INFO_LIST */
356 static bool AsyncNotify_Move(TALLOC_CTX *mem_ctx, const uint8_t **ptr)
357 {
358         const uint8_t *pos = *ptr;
359         uint32_t length   = IVAL(pos,0);
360         /* uint32_t reserved = IVAL(pos,4); */
361         uint32_t num      = IVAL(pos,8);
362         uint32_t n;
363
364         pos += 12;
365
366         for (n=0; n<num; n++) {
367                 uint32_t flags = IVAL(pos,0);
368                 struct in_addr ipv4;
369                 struct sockaddr_storage sas4;
370                 char *str4, *str6;
371                 pos += 4;
372
373                 ipv4.s_addr = *((const in_addr_t*)pos);
374                 in_addr_to_sockaddr_storage(&sas4, ipv4);
375                 str4 = print_canonical_sockaddr(mem_ctx, &sas4);
376                 pos += 4;
377
378                 {
379 #ifdef HAVE_IPV6
380                         struct in6_addr ipv6;
381                         struct sockaddr_storage sas6;
382
383                         memcpy(&ipv6.s6_addr, pos, 16);
384                         in6_addr_to_sockaddr_storage(&sas6, ipv6);
385                         str6 = print_canonical_sockaddr(mem_ctx, &sas6);
386 #else
387                         DATA_BLOB ipv6 = data_blob(pos, 16);
388                         str6 = data_blob_hex_string_upper(mem_ctx, &ipv6);
389 #endif
390                 }
391                 pos += 16;
392
393                 d_printf("Flags 0x%08x", flags);
394                 if (flags & IPADDR_V4) {
395                         d_printf(" %s", str4);
396                 }
397                 if (flags & IPADDR_V6) {
398                         d_printf(" %s", str6);
399                 }
400                 if (flags & IPADDR_ONLINE) {
401                         d_printf(" Online");
402                 }
403                 if (flags & IPADDR_ONLINE) {
404                         d_printf(" Offline");
405                 }
406                 d_printf("\n");
407                 TALLOC_FREE(str4);
408                 TALLOC_FREE(str6);
409         }
410
411         if (pos - *ptr == length) {
412                 *ptr = pos;
413                 return true;
414         }
415         return false;
416 }
417
418 static WERROR cmd_witness_AsyncNotify(struct rpc_pipe_client *cli,
419                                       TALLOC_CTX *mem_ctx, int argc,
420                                       const char **argv)
421 {
422         NTSTATUS status;
423         WERROR result = WERR_OK;
424         TALLOC_CTX *frame = talloc_stackframe();
425         struct policy_handle hnd;
426         struct witness_notifyResponse *response = NULL;
427         uint32_t timeout;
428         bool (*read_response)(TALLOC_CTX*, const uint8_t**) = NULL;
429
430         use_only_one_rpc_pipe_hack(cli);
431
432         if (argc != 2) {
433                 d_printf("%s <context_handle>\n", argv[0]);
434                 goto done;
435         }
436
437         if (!read_context_handle(argv[1], &hnd)) {
438                 result = WERR_INVALID_PARAM;
439                 goto done;
440         }
441
442         timeout = dcerpc_binding_handle_set_timeout(cli->binding_handle, UINT32_MAX);
443         status = dcerpc_witness_AsyncNotify(cli->binding_handle, frame, hnd,
444                                             &response, &result);
445         dcerpc_binding_handle_set_timeout(cli->binding_handle, timeout);
446         if (!NT_STATUS_IS_OK(status)) {
447                 DEBUG(0, ("dcerpc_witness_AsyncNotify failed, status: %s\n", nt_errstr(status)));
448                 result = ntstatus_to_werror(status);
449                 goto done;
450         }
451         if (!W_ERROR_IS_OK(result)) {
452                 DEBUG(0, ("dcerpc_witness_AsyncNotify failed, error: %s\n", win_errstr(result)));
453                 goto done;
454         }
455
456         switch(response->message_type) {
457         case WITNESS_NOTIFY_RESOURCE_CHANGE:
458                 d_printf("Resource change");
459                 read_response = AsyncNotify_Change;
460                 break;
461         case WITNESS_NOTIFY_CLIENT_MOVE:
462                 d_printf("Client move");
463                 read_response = AsyncNotify_Move;
464                 break;
465         case WITNESS_NOTIFY_SHARE_MOVE:
466                 d_printf("Share move");
467                 read_response = AsyncNotify_Move;
468                 break;
469         case WITNESS_NOTIFY_IP_CHANGE:
470                 d_printf("IP change");
471                 read_response = AsyncNotify_Move;
472                 break;
473         default:
474                 d_printf("Unknown (0x%x)", (int)response->message_type);
475         }
476         d_printf(" with %d messages\n", response->num_messages);
477
478         if (read_response) {
479                 unsigned n;
480                 const uint8_t *pos = response->message_buffer;
481
482                 for (n=0; n<response->num_messages; n++) {
483                         read_response(frame, &pos);
484                 }
485         }
486
487 done:
488         talloc_free(frame);
489         return result;
490 }
491
492 struct cmd_set witness_commands[] = {
493         {"WITNESS"},
494         {"GetInterfaceList", RPC_RTYPE_WERROR, NULL, &cmd_witness_GetInterfaceList, &ndr_table_witness, NULL, "", ""},
495         {"Register", RPC_RTYPE_WERROR, NULL, &cmd_witness_Register, &ndr_table_witness, NULL, "", ""},
496         {"UnRegister", RPC_RTYPE_WERROR, NULL, &cmd_witness_UnRegister, &ndr_table_witness, NULL, "", ""},
497         {"AsyncNotify", RPC_RTYPE_WERROR, NULL, &cmd_witness_AsyncNotify, &ndr_table_witness, NULL, "", ""},
498         {"RegisterEx", RPC_RTYPE_WERROR, NULL, &cmd_witness_RegisterEx, &ndr_table_witness, NULL, "", ""},
499         {NULL}
500 };
501
502 /*
503  * We have to use the same connection for each subcommand
504  * for the context handles to be meaningful.
505  */
506 static void use_only_one_rpc_pipe_hack(struct rpc_pipe_client *cli)
507 {
508         struct cmd_set *ptr;
509
510         for (ptr = &witness_commands[0]; ptr->name; ptr++) {
511                 ptr->rpc_pipe = cli;
512         }
513 }