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