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 }