xref: /titanic_50/usr/src/cmd/fs.d/smbclnt/test/srvenum.c (revision 4eab410fb63816fe2c0ad0fd18b4c948613f6616)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * Test program for the smbfs named pipe API.
29  */
30 
31 #include <sys/types.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <libintl.h>
39 
40 #include <netsmb/smbfs_api.h>
41 
42 /*
43  * This is a quick hack for testing client-side named pipes.
44  * Its purpose is to test the ability to connect to a server,
45  * open a pipe, send and receive data.  The "hack" aspect is
46  * the use of hand-crafted RPC messages, which allows testing
47  * of the named pipe API separately from the RPC libraries.
48  *
49  * I captured the two small name pipe messages sent when
50  * requesting a share list via RPC over /pipe/srvsvc and
51  * dropped them into the arrays below (bind and enum).
52  * This program sends the two messages (with adjustments)
53  * and just dumps whatever comes back over the pipe.
54  * Use wireshark if you want to see decoded messages.
55  */
56 
57 extern char *optarg;
58 extern int optind, opterr, optopt;
59 
60 /* This is a DCE/RPC bind call for "srvsvc". */
61 static const uchar_t
62 srvsvc_bind[] = {
63 	0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
64 	0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
65 	0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
66 	0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
67 	0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01,
68 	0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88,
69 	0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
70 	0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
71 	0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 };
72 
73 /* This is a srvsvc "enum servers" call, in two parts */
74 static const uchar_t
75 srvsvc_enum1[] = {
76 	0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
77 #define	ENUM_RPCLEN_OFF	8
78 	/* V - RPC frag length */
79 	0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 	/* ... and the operation number is: VVVV */
81 	0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00,
82 #define	ENUM_SLEN1_OFF	28
83 #define	ENUM_SLEN2_OFF	36
84 	/* server name, length 14 vv ... */
85 	0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
86 	0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00 };
87 	/* UNC server here, i.e.: "\\192.168.1.6" */
88 
89 static const uchar_t
90 srvsvc_enum2[] = {
91 	0x01, 0x00, 0x00, 0x00,
92 	0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
93 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
95 
96 static uchar_t sendbuf[1024];
97 static uchar_t recvbuf[4096];
98 static char *server;
99 
100 static int pipetest(struct smb_ctx *);
101 
102 static void
103 srvenum_usage(void)
104 {
105 	printf("usage: srvenum [-d domain][-u user][-p passwd] server\n");
106 	exit(1);
107 }
108 
109 int
110 main(int argc, char *argv[])
111 {
112 	int c, error;
113 	struct smb_ctx *ctx = NULL;
114 	char *dom = NULL;
115 	char *usr = NULL;
116 	char *pw = NULL;
117 
118 	while ((c = getopt(argc, argv, "vd:u:p:")) != -1) {
119 		switch (c) {
120 		case 'v':
121 			smb_verbose = 1;
122 			break;
123 
124 		case 'd':
125 			dom = optarg;
126 			break;
127 		case 'u':
128 			usr = optarg;
129 			break;
130 		case 'p':
131 			pw = optarg;
132 			break;
133 		case '?':
134 			srvenum_usage();
135 			break;
136 		}
137 	}
138 	if (optind >= argc)
139 		srvenum_usage();
140 	server = argv[optind];
141 
142 	if (pw != NULL && (dom == NULL || usr == NULL)) {
143 		fprintf(stderr, "%s: -p arg requires -d dom -u usr\n",
144 		    argv[0]);
145 		srvenum_usage();
146 	}
147 
148 	/*
149 	 * This section is intended to demonstrate how an
150 	 * RPC client library might use this interface.
151 	 */
152 	error = smb_ctx_alloc(&ctx);
153 	if (error) {
154 		fprintf(stderr, "%s: smb_ctx_alloc failed\n", argv[0]);
155 		goto out;
156 	}
157 
158 	/*
159 	 * Set server, share, domain, user
160 	 * (in the ctx handle).
161 	 */
162 	smb_ctx_setfullserver(ctx, server);
163 	smb_ctx_setshare(ctx, "IPC$", USE_IPC);
164 	if (dom)
165 		smb_ctx_setdomain(ctx, dom, B_TRUE);
166 	if (usr)
167 		smb_ctx_setuser(ctx, usr, B_TRUE);
168 	if (pw)
169 		smb_ctx_setpassword(ctx, pw, NULL);
170 
171 
172 	/*
173 	 * If this code were in smbutil or mount_smbfs, it would
174 	 * get system and $HOME/.nsmbrc settings here, like this:
175 	 */
176 #if 0
177 	error = smb_ctx_readrc(ctx);
178 	if (error) {
179 		fprintf(stderr, "%s: smb_ctx_readrc failed\n", argv[0]);
180 		goto out;
181 	}
182 #endif
183 
184 	/*
185 	 * Resolve the server address,
186 	 * setup derived defaults.
187 	 */
188 	error = smb_ctx_resolve(ctx);
189 	if (error) {
190 		fprintf(stderr, "%s: smb_ctx_resolve failed\n", argv[0]);
191 		goto out;
192 	}
193 
194 	/*
195 	 * Get the session and tree.
196 	 */
197 	error = smb_ctx_get_ssn(ctx);
198 	if (error) {
199 		fprintf(stderr, "//%s: login failed, error %d\n",
200 		    server, error);
201 		goto out;
202 	}
203 	error = smb_ctx_get_tree(ctx);
204 	if (error) {
205 		fprintf(stderr, "//%s/%s: tree connect failed, %d\n",
206 		    server, "IPC$", error);
207 		goto out;
208 	}
209 
210 	/*
211 	 * Do some named pipe I/O.
212 	 */
213 	error = pipetest(ctx);
214 	if (error) {
215 		fprintf(stderr, "pipetest, %d\n", error);
216 		goto out;
217 	}
218 
219 out:
220 	smb_ctx_free(ctx);
221 
222 	return ((error) ? 1 : 0);
223 }
224 
225 static void
226 hexdump(const uchar_t *buf, int len) {
227 	int idx;
228 	char ascii[24];
229 	char *pa = ascii;
230 
231 	memset(ascii, '\0', sizeof (ascii));
232 
233 	idx = 0;
234 	while (len--) {
235 		if ((idx & 15) == 0) {
236 			printf("[%04X] ", idx);
237 			pa = ascii;
238 		}
239 		if (*buf > ' ' && *buf <= '~')
240 			*pa++ = *buf;
241 		else
242 			*pa++ = '.';
243 		printf("%02x ", *buf++);
244 
245 		idx++;
246 		if ((idx & 7) == 0) {
247 			*pa++ = ' ';
248 			putchar(' ');
249 		}
250 		if ((idx & 15) == 0) {
251 			*pa = '\0';
252 			printf("%s\n", ascii);
253 		}
254 	}
255 
256 	if ((idx & 15) != 0) {
257 		*pa = '\0';
258 		/* column align the last ascii row */
259 		do {
260 			printf("   ");
261 			idx++;
262 			if ((idx & 7) == 0)
263 				putchar(' ');
264 		} while ((idx & 15) != 0);
265 		printf("%s\n", ascii);
266 	}
267 }
268 
269 /*
270  * Put a unicode UNC server name, including the null.
271  * Quick-n-dirty, just for this test...
272  */
273 static int
274 put_uncserver(const char *s, uchar_t *buf)
275 {
276 	uchar_t *p = buf;
277 	char c;
278 
279 	*p++ = '\\'; *p++ = '\0';
280 	*p++ = '\\'; *p++ = '\0';
281 
282 	do {
283 		c = *s++;
284 		if (c == '/')
285 			c = '\\';
286 		*p++ = c;
287 		*p++ = '\0';
288 
289 	} while (c != 0);
290 
291 	return (p - buf);
292 }
293 
294 /*
295  * Send the bind and read the ack.
296  * This tests smb_fh_xactnp.
297  */
298 static int
299 do_bind(int fid)
300 {
301 	int err, len, more;
302 
303 	more = 0;
304 	len = sizeof (recvbuf);
305 	err = smb_fh_xactnp(fid,
306 	    sizeof (srvsvc_bind), (char *)srvsvc_bind,
307 	    &len, (char *)recvbuf, &more);
308 	if (err) {
309 		printf("xact bind, err=%d\n", err);
310 		return (err);
311 	}
312 	if (smb_verbose) {
313 		printf("bind ack, len=%d\n", len);
314 		hexdump(recvbuf, len);
315 	}
316 	if (more > 0) {
317 		if (more > sizeof (recvbuf)) {
318 			printf("bogus more=%d\n", more);
319 			more = sizeof (recvbuf);
320 		}
321 		len = smb_fh_read(fid, 0,
322 		    more, (char *)recvbuf);
323 		if (len == -1) {
324 			err = EIO;
325 			printf("read enum resp, err=%d\n", err);
326 			return (err);
327 		}
328 		if (smb_verbose) {
329 			printf("bind ack (more), len=%d\n", len);
330 			hexdump(recvbuf, len);
331 		}
332 	}
333 
334 	return (0);
335 }
336 
337 static int
338 do_enum(int fid)
339 {
340 	int err, len, rlen, wlen;
341 	uchar_t *p;
342 
343 	/*
344 	 * Build the enum request - three parts.
345 	 * See above: srvsvc_enum1, srvsvc_enum2
346 	 *
347 	 * First part: RPC header, etc.
348 	 */
349 	p = sendbuf;
350 	len = sizeof (srvsvc_enum1); /* 40 */
351 	memcpy(p, srvsvc_enum1, len);
352 	p += len;
353 
354 	/* Second part: UNC server name */
355 	len = put_uncserver(server, p);
356 	p += len;
357 	sendbuf[ENUM_SLEN1_OFF] = len / 2;
358 	sendbuf[ENUM_SLEN2_OFF] = len / 2;
359 
360 	/* Third part: level, etc. (align4) */
361 	for (len = (p - sendbuf) & 3; len; len--)
362 		*p++ = '\0';
363 	len = sizeof (srvsvc_enum2); /* 28 */
364 	memcpy(p, srvsvc_enum2, len);
365 	p += len;
366 
367 	/*
368 	 * Compute total length, and fixup RPC header.
369 	 */
370 	len = p - sendbuf;
371 	sendbuf[ENUM_RPCLEN_OFF] = len;
372 
373 	/*
374 	 * Send the enum request, read the response.
375 	 * This tests smb_fh_write, smb_fh_read.
376 	 */
377 	wlen = smb_fh_write(fid, 0, len, (char *)sendbuf);
378 	if (wlen == -1) {
379 		err = errno;
380 		printf("write enum req, err=%d\n", err);
381 		return (err);
382 	}
383 	if (wlen != len) {
384 		printf("write enum req, short write %d\n", wlen);
385 		return (EIO);
386 	}
387 
388 	rlen = smb_fh_read(fid, 0,
389 	    sizeof (recvbuf), (char *)recvbuf);
390 	if (rlen == -1) {
391 		err = errno;
392 		printf("read enum resp, err=%d\n", err);
393 		return (err);
394 	}
395 
396 	/* Just dump the response data. */
397 	printf("enum recv, len=%d\n", rlen);
398 	hexdump(recvbuf, rlen);
399 
400 	return (0);
401 }
402 
403 static int
404 pipetest(struct smb_ctx *ctx)
405 {
406 	static char path[] = "/srvsvc";
407 	static uchar_t key[16];
408 	int err, fd;
409 
410 	printf("open pipe: %s\n", path);
411 	fd = smb_fh_open(ctx, path, O_RDWR);
412 	if (fd < 0) {
413 		perror(path);
414 		return (errno);
415 	}
416 
417 	/* Test this too. */
418 	err = smb_fh_getssnkey(fd, key, sizeof (key));
419 	if (err) {
420 		printf("getssnkey: %d\n", err);
421 		goto out;
422 	}
423 
424 	err = do_bind(fd);
425 	if (err) {
426 		printf("do_bind: %d\n", err);
427 		goto out;
428 	}
429 	err = do_enum(fd);
430 	if (err)
431 		printf("do_enum: %d\n", err);
432 
433 out:
434 	smb_fh_close(fd);
435 	return (0);
436 }
437