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 }