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