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
srvinfo_usage(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
main(int argc,char * argv[])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
hexdump(const uchar_t * buf,int len)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
put_uncserver(const char * s,uchar_t * buf)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
getint(const uchar_t * p)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
do_bind(int fid)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
do_info(int fid)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
pipetest(struct smb_ctx * ctx)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