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 } 437