xref: /titanic_44/usr/src/cmd/fs.d/smbclnt/test/srvinfo.c (revision 5fd03bc0f2e00e7ba02316c2e08f45d52aab15db)
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 server info via RPC over /pipe/srvsvc and
51  * dropped them into the arrays below (bind and info).
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 "get server info" call, in two parts */
74 static const uchar_t
75 srvsvc_info[] = {
76 	0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
77 #define	INFO_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, 0x15, 0x00,
82 #define	INFO_SLEN1_OFF	28
83 #define	INFO_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 uchar_t sendbuf[1024];
90 static uchar_t recvbuf[1024];
91 static char *server;
92 
93 static int pipetest(struct smb_ctx *);
94 
95 static void
96 srvinfo_usage(void)
97 {
98 	printf("usage: srvinfo [-d domain][-u user][-p passwd] server\n");
99 	exit(1);
100 }
101 
102 int
103 main(int argc, char *argv[])
104 {
105 	int c, error;
106 	struct smb_ctx *ctx = NULL;
107 	char *dom = NULL;
108 	char *usr = NULL;
109 	char *pw = NULL;
110 
111 	while ((c = getopt(argc, argv, "vd:u:p:")) != -1) {
112 		switch (c) {
113 		case 'v':
114 			smb_verbose = 1;
115 			break;
116 
117 		case 'd':
118 			dom = optarg;
119 			break;
120 		case 'u':
121 			usr = optarg;
122 			break;
123 		case 'p':
124 			pw = optarg;
125 			break;
126 		case '?':
127 			srvinfo_usage();
128 			break;
129 		}
130 	}
131 	if (optind >= argc)
132 		srvinfo_usage();
133 	server = argv[optind];
134 
135 	if (pw != NULL && (dom == NULL || usr == NULL)) {
136 		fprintf(stderr, "%s: -p arg requires -d dom -u usr\n",
137 		    argv[0]);
138 		srvinfo_usage();
139 	}
140 
141 	/*
142 	 * This section is intended to demonstrate how an
143 	 * RPC client library might use this interface.
144 	 */
145 	error = smb_ctx_alloc(&ctx);
146 	if (error) {
147 		fprintf(stderr, "%s: smb_ctx_alloc failed\n", argv[0]);
148 		goto out;
149 	}
150 
151 	/*
152 	 * Set server, share, domain, user
153 	 * (in the ctx handle).
154 	 */
155 	smb_ctx_setfullserver(ctx, server);
156 	smb_ctx_setshare(ctx, "IPC$", USE_IPC);
157 	if (dom)
158 		smb_ctx_setdomain(ctx, dom, B_TRUE);
159 	if (usr)
160 		smb_ctx_setuser(ctx, usr, B_TRUE);
161 	if (pw)
162 		smb_ctx_setpassword(ctx, pw, NULL);
163 
164 
165 	/*
166 	 * If this code were in smbutil or mount_smbfs, it would
167 	 * get system and $HOME/.nsmbrc settings here, like this:
168 	 */
169 #if 0
170 	error = smb_ctx_readrc(ctx);
171 	if (error) {
172 		fprintf(stderr, "%s: smb_ctx_readrc failed\n", argv[0]);
173 		goto out;
174 	}
175 #endif
176 
177 	/*
178 	 * Resolve the server address,
179 	 * setup derived defaults.
180 	 */
181 	error = smb_ctx_resolve(ctx);
182 	if (error) {
183 		fprintf(stderr, "%s: smb_ctx_resolve failed\n", argv[0]);
184 		goto out;
185 	}
186 
187 	/*
188 	 * Get the session and tree.
189 	 */
190 	error = smb_ctx_get_ssn(ctx);
191 	if (error) {
192 		fprintf(stderr, "//%s: login failed, error %d\n",
193 		    server, error);
194 		goto out;
195 	}
196 	error = smb_ctx_get_tree(ctx);
197 	if (error) {
198 		fprintf(stderr, "//%s/%s: tree connect failed, %d\n",
199 		    server, "IPC$", error);
200 		goto out;
201 	}
202 
203 	/*
204 	 * Do some named pipe I/O.
205 	 */
206 	error = pipetest(ctx);
207 	if (error) {
208 		fprintf(stderr, "pipetest, %d\n", error);
209 		goto out;
210 	}
211 
212 out:
213 	smb_ctx_free(ctx);
214 
215 	return ((error) ? 1 : 0);
216 }
217 
218 static void
219 hexdump(const uchar_t *buf, int len) {
220 	int ofs = 0;
221 
222 	while (len--) {
223 		if (ofs % 16 == 0)
224 			printf("\n%02X: ", ofs);
225 		printf("%02x ", *buf++);
226 		ofs++;
227 	}
228 	printf("\n");
229 }
230 
231 /*
232  * Put a unicode UNC server name, including the null.
233  * Quick-n-dirty, just for this test...
234  */
235 static int
236 put_uncserver(const char *s, uchar_t *buf)
237 {
238 	uchar_t *p = buf;
239 	char c;
240 
241 	*p++ = '\\'; *p++ = '\0';
242 	*p++ = '\\'; *p++ = '\0';
243 
244 	do {
245 		c = *s++;
246 		if (c == '/')
247 			c = '\\';
248 		*p++ = c;
249 		*p++ = '\0';
250 
251 	} while (c != 0);
252 
253 	return (p - buf);
254 }
255 
256 /* Get a little-endian int.  Just for testing. */
257 static int
258 getint(const uchar_t *p)
259 {
260 	return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
261 }
262 
263 /*
264  * Send the bind and read the ack.
265  * This tests smb_fh_xactnp.
266  */
267 static int
268 do_bind(int fid)
269 {
270 	int err, len, more;
271 
272 	more = 0;
273 	len = sizeof (recvbuf);
274 	err = smb_fh_xactnp(fid,
275 	    sizeof (srvsvc_bind), (char *)srvsvc_bind,
276 	    &len, (char *)recvbuf, &more);
277 	if (err) {
278 		printf("xact bind, err=%d\n", err);
279 		return (err);
280 	}
281 	if (smb_verbose) {
282 		printf("bind ack, len=%d\n", len);
283 		hexdump(recvbuf, len);
284 	}
285 	if (more > 0) {
286 		if (more > sizeof (recvbuf)) {
287 			printf("bogus more=%d\n", more);
288 			more = sizeof (recvbuf);
289 		}
290 		len = smb_fh_read(fid, 0,
291 		    more, (char *)recvbuf);
292 		if (len == -1) {
293 			err = EIO;
294 			printf("read info resp, err=%d\n", err);
295 			return (err);
296 		}
297 		if (smb_verbose) {
298 			printf("bind ack (more), len=%d\n", len);
299 			hexdump(recvbuf, len);
300 		}
301 	}
302 
303 	return (0);
304 }
305 
306 static int
307 do_info(int fid)
308 {
309 	int err, len, rlen, wlen, x;
310 	uchar_t *p;
311 
312 	/*
313 	 * Build the info request - two parts.
314 	 * See above: srvsvc_info
315 	 *
316 	 * First part: RPC header, etc.
317 	 */
318 	p = sendbuf;
319 	len = sizeof (srvsvc_info); /* 40 */
320 	memcpy(p, srvsvc_info, len);
321 	p += len;
322 
323 	/* Second part: UNC server name */
324 	len = put_uncserver(server, p);
325 	p += len;
326 	sendbuf[INFO_SLEN1_OFF] = len / 2;
327 	sendbuf[INFO_SLEN2_OFF] = len / 2;
328 
329 	/* Third part: level, etc. (align4) */
330 	for (len = (p - sendbuf) & 3; len; len--)
331 		*p++ = '\0';
332 	*p++ = 101;	/* the "level" */
333 	*p++ = 0; *p++ = 0; *p++ = 0;
334 
335 	/*
336 	 * Compute total length, and fixup RPC header.
337 	 */
338 	len = p - sendbuf;
339 	sendbuf[INFO_RPCLEN_OFF] = len;
340 
341 	/*
342 	 * Send the info request, read the response.
343 	 * This tests smb_fh_write, smb_fh_read.
344 	 */
345 	wlen = smb_fh_write(fid, 0, len, (char *)sendbuf);
346 	if (wlen == -1) {
347 		err = errno;
348 		printf("write info req, err=%d\n", err);
349 		return (err);
350 	}
351 	if (wlen != len) {
352 		printf("write info req, short write %d\n", wlen);
353 		return (EIO);
354 	}
355 
356 	rlen = smb_fh_read(fid, 0,
357 	    sizeof (recvbuf), (char *)recvbuf);
358 	if (rlen == -1) {
359 		err = errno;
360 		printf("read info resp, err=%d\n", err);
361 		return (err);
362 	}
363 
364 	if (smb_verbose) {
365 		printf("info recv, len=%d\n", rlen);
366 		hexdump(recvbuf, rlen);
367 	}
368 
369 	x = getint(recvbuf + 4);
370 	if (x != 0x10) {
371 		printf("Data representation 0x%x not supported\n", x);
372 		return (ENOTSUP);
373 	}
374 	printf("Platform Id: %d\n", getint(recvbuf + 0x20));
375 	printf("Version Major: %d\n", getint(recvbuf + 0x28));
376 	printf("Version Minor: %d\n", getint(recvbuf + 0x2c));
377 	printf("Srv type flags: 0x%x\n", getint(recvbuf + 0x30));
378 
379 	return (0);
380 }
381 
382 static int
383 pipetest(struct smb_ctx *ctx)
384 {
385 	static char path[] = "/srvsvc";
386 	static uchar_t key[16];
387 	int err, fd;
388 
389 	printf("open pipe: %s\n", path);
390 	fd = smb_fh_open(ctx, path, O_RDWR);
391 	if (fd < 0) {
392 		perror(path);
393 		return (errno);
394 	}
395 
396 	/* Test this too. */
397 	err = smb_fh_getssnkey(fd, key, sizeof (key));
398 	if (err) {
399 		printf("getssnkey: %d\n", err);
400 		goto out;
401 	}
402 
403 	err = do_bind(fd);
404 	if (err) {
405 		printf("do_bind: %d\n", err);
406 		goto out;
407 	}
408 	err = do_info(fd);
409 	if (err)
410 		printf("do_info: %d\n", err);
411 
412 out:
413 	smb_fh_close(fd);
414 	return (err);
415 }
416