1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
14 */
15
16
17#include <stdio.h>
18#include <libintl.h>
19#include <stdlib.h>
20#include <strings.h>
21#include <err.h>
22#include <ads/dsgetdc.h>
23#include <smb/nterror.h>
24#include <uuid/uuid.h>
25
26
27static void dclist_usage(void);
28static int cmd_dclist(char *);
29static void dcname_usage(void);
30static int cmd_dcname(char *);
31static void dsgetdc_usage(void);
32static int cmd_dsgetdc(char *);
33static void dsgetdcname_usage(void);
34static int cmd_dsgetdcname(char *);
35static void kick_usage(void);
36static int cmd_kick(char *);
37static void help(void);
38
39typedef int cmd_fn_t (char *);
40typedef void cmd_usage_t (void);
41
42
43static struct commands {
44	const char	*name;	/* name of subcommand */
45	cmd_fn_t	*fn;	/* pointer to subcommand handler function */
46	cmd_usage_t	*usage;	/* pointer to subcommand help function */
47	int		optreq; /* does this have a required optval */
48} commands[] = {
49	{"dclist", cmd_dclist, dclist_usage, 0},
50	{"dcname", cmd_dcname, dcname_usage, 0},
51	{"dsgetdc", cmd_dsgetdc, dsgetdc_usage, 0},
52	{"dsgetdcname", cmd_dsgetdcname, dsgetdcname_usage, 0},
53	{"kick", cmd_kick, kick_usage, 0},
54	{NULL, NULL, NULL, 0}
55};
56
57
58/*
59 * lookupcmd
60 */
61static struct commands *
62lookupcmd(const char *name)
63{
64	struct commands *cmd;
65
66	for (cmd = commands; cmd->name; cmd++) {
67		if (strcasecmp(cmd->name, name) == 0)
68			return (cmd);
69	}
70	return (NULL);
71}
72
73/*
74 * dclist
75 */
76static void
77dclist_usage(void)
78{
79	(void) printf(gettext("usage: nltest dclist... \n"));
80	exit(1);
81}
82
83/* ARGSUSED */
84static int
85cmd_dclist(char *optval)
86{
87	(void) printf("cmd_dclist() \n");
88	return (0);
89}
90
91/*
92 * dcname
93 */
94static void
95dcname_usage(void)
96{
97	(void) printf(gettext("usage: nltest dcname... \n"));
98	exit(1);
99}
100
101/* ARGSUSED */
102static int
103cmd_dcname(char *optval)
104{
105	(void) printf("cmd_dcname() \n");
106	return (0);
107}
108
109/*
110 * dsgetdc
111 */
112static void
113dsgetdc_usage(void)
114{
115	(void) printf(gettext("usage: nltest dsgetdc... \n"));
116	exit(1);
117}
118
119/* ARGSUSED */
120static int
121cmd_dsgetdc(char *optval)
122{
123	(void) printf("cmd_dsgetdc() \n");
124	return (0);
125}
126
127/*
128 * dsgetdcname
129 */
130static void
131dsgetdcname_usage(void)
132{
133	(void) printf(gettext("usage: nltest dsgetdcname domainname \n"));
134	exit(1);
135}
136
137static int
138cmd_dsgetdcname(char *domname)
139{
140	char uuid_buf[UUID_PRINTABLE_STRING_LENGTH];
141	int err = 0;
142	char *atype;
143	DOMAIN_CONTROLLER_INFO *dcinfo;
144
145	if (domname != NULL)
146		(void) printf("  Domain name supplied:  %s \n", domname);
147
148	err = DsGetDcName(NULL, domname, NULL, NULL, 0, &dcinfo);
149
150	switch (err) {
151	case 0:
152		break;
153	case ERROR_NO_SUCH_DOMAIN:
154		(void) printf("Domain controller not found.\n");
155		(void) printf("See: /var/run/idmap/discovery.log\n");
156		exit(1);
157	default:
158		(void) printf("Unexpected error %d\n", err);
159		exit(1);
160	}
161
162	switch (dcinfo->DomainControllerAddressType) {
163	case DS_INET_ADDRESS:
164		atype = "inet";
165		break;
166	case DS_NETBIOS_ADDRESS:
167		atype = "netbios";
168		break;
169	default:
170		atype = "?";
171		break;
172	}
173
174	uuid_unparse(dcinfo->DomainGuid, uuid_buf);
175
176	(void) printf("Data Returned from DsGetDcName() call: \n");
177	(void) printf("  DC Name:  %s \n", dcinfo->DomainControllerName);
178	(void) printf("  DC Addr:  %s \n", dcinfo->DomainControllerAddress);
179	(void) printf("  DC Addr Type:  %s \n", atype);
180	(void) printf("  Domain Name:  %s \n", dcinfo->DomainName);
181	(void) printf("  Domain GUID:  %s \n", uuid_buf);
182	(void) printf("  DNS Forest Name:  %s \n", dcinfo->DnsForestName);
183	(void) printf("  Flags:  0x%x \n", dcinfo->Flags);
184	(void) printf("  DC Site Name:  %s \n", dcinfo->DcSiteName);
185	(void) printf("  Client Site Name:  %s \n", dcinfo->ClientSiteName);
186
187	return (0);
188}
189
190/*
191 * kick
192 */
193static void
194kick_usage(void)
195{
196	(void) printf(gettext("usage: nltest /KICK \n"));
197	exit(1);
198}
199
200
201static int
202cmd_kick(char *domname)
203{
204	int flags = 0;
205	int result;
206
207	result = _DsForceRediscovery(domname, flags);
208
209	return (result);
210}
211
212/*
213 * help functions
214 */
215
216static void
217help(void) {
218	(void) printf("\n");
219	/*
220	 * TODO: We may want to revise this help text.  It's basically
221	 * a copy-paste from:
222	 *   http://technet.microsoft.com/en-us/library/cc731935.aspx
223	 */
224	(void) printf(gettext("usage: %s /subcommand\n"),
225	    (char *)getexecname());
226	(void) printf(gettext("where subcommands are:\n"
227#if 0	/* not yet */
228		" dclist        Lists all domain controllers in the domain.\n"
229		" dcname        Lists the PDC or PDC emulator.\n"
230		" dsgetdc       Queries DNS server for list of DCs and"
231			" their IP addresses and contacts each DC to check"
232			" for connectivity.\n"
233#endif
234		" dsgetdcname   returns the name of a domain controller in a"
235			" specified domain\n"
236		" help          display help on specified subcommand\n"
237		" kick          trigger domain controller re-discovery\n"
238		"\n"));
239	exit(1);
240}
241
242int
243main(int argc, char *argv[])
244{
245	struct commands *cmd;
246	int err = 0;
247	char *option_cmd = NULL;
248	char *arg;
249	char *p;
250	char *optname;
251	char *optval = NULL;
252	int i;
253	int optind = 1;
254
255	/*
256	 * Parse options.
257	 */
258	while (optind < argc) {
259		arg = argv[optind];
260		optname = NULL;
261		optval = NULL;
262
263		/* Is this an option? */
264		if (arg[0] == '/') {
265			optname = arg + 1;
266			optind++;
267
268			/*
269			 * May have  /optname:value
270			 */
271			if ((p = strchr(optname, ':')) != NULL) {
272				*p++ = '\0';
273				optval = p;
274			}
275		} else if (arg[0] == '-' && arg[1] == '-') {
276			optname = arg + 2;
277			optind++;
278
279			/*
280			 * May have  --optname=value
281			 */
282			if ((p = strchr(optname, '=')) != NULL) {
283				*p++ = '\0';
284				optval = p;
285			}
286		} else {
287			/* Not an option.  Stop parsing. */
288			break;
289		}
290
291		/*
292		 * Handle each optname (and maybe its optval)
293		 * Might put this logic in a table of options.
294		 * (including a flag for "optval required",
295		 * so that check could be factored out)
296		 */
297		for (cmd = commands; cmd->name; cmd++) {
298			if (!strcasecmp(optname, cmd->name)) {
299				/* cmd->name  requires an optval */
300				if (optval == NULL && optind < argc)
301					optval = argv[optind++];
302
303				if (optval == NULL && cmd->optreq > 0) {
304					(void) fprintf(stderr,
305					    "%s: option %s requires a value\n",
306					    argv[0], optname);
307					return (1);
308				}
309				option_cmd = optname;
310			}
311		}
312	}
313
314	/*
315	 * Handle remaining non-option arguments
316	 */
317	for (i = optind; i < argc; i++) {
318		(void) printf("arg: %s\n", argv[i]);
319	}
320
321	if (option_cmd == NULL)
322		help();
323
324	cmd = lookupcmd(option_cmd);
325	if (cmd == NULL)
326		err = 1;
327	else
328		err = cmd->fn(optval);
329
330	return (err);
331}
332