diff options
Diffstat (limited to 'usr/src/lib/libsmbfs/smb/rap.c')
-rw-r--r-- | usr/src/lib/libsmbfs/smb/rap.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/usr/src/lib/libsmbfs/smb/rap.c b/usr/src/lib/libsmbfs/smb/rap.c new file mode 100644 index 0000000000..00ccbd54a2 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/rap.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2000, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: rap.c,v 1.5 2004/12/13 00:25:23 lindak Exp $ + * + * This is very simple implementation of RAP protocol. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/isa_defs.h> + +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <stdlib.h> +#include <libintl.h> +#include <sysexits.h> + +#include <netsmb/mchain.h> +#include <netsmb/smb_lib.h> +#include <netsmb/smb_rap.h> + +static int +smb_rap_parserqparam(const char *s, char **next, int *rlen) +{ + char *np; + int len; + + switch (*s++) { + case 'L': + case 'T': + case 'W': + len = 2; + break; + case 'D': + case 'O': + len = 4; + break; + case 'b': + case 'F': + len = 1; + break; + case 'r': + case 's': + len = 0; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_parserpparam(const char *s, char **next, int *rlen) +{ + char *np; + int len = 0; + + switch (*s++) { + case 'e': + case 'h': + len = 2; + break; + case 'i': + len = 4; + break; + case 'g': + len = 1; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_parserpdata(const char *s, char **next, int *rlen) +{ + char *np; + int len; + + switch (*s++) { + case 'B': + len = 1; + break; + case 'W': + len = 2; + break; + case 'D': + case 'O': + case 'z': + len = 4; + break; + default: + return (EINVAL); + } + if (isdigit(*s)) { + len *= strtoul(s, &np, 10); + s = np; + } + *rlen = len; + *(const char **)next = s; + return (0); +} + +static int +smb_rap_rqparam_z(struct smb_rap *rap, const char *value) +{ + int len = strlen(value) + 1; + + bcopy(value, rap->r_npbuf, len); + rap->r_npbuf += len; + rap->r_plen += len; + return (0); +} + +/* + * Marshal RAP request parameters. + * Note: value is in host order. + */ +static int +smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, int value) +{ + char *p = rap->r_npbuf; + int len = 0; + uint_t uv = (uint_t)value; + + switch (ptype) { + case 'L': + case 'W': + /* LINTED */ + setwle(p, 0, uv); + len = 2; + break; + case 'D': + /* LINTED */ + setdle(p, 0, uv); + len = 4; + break; + case 'b': + memset(p, uv, plen); + len = plen; + default: + return (EINVAL); + } + rap->r_npbuf += len; + rap->r_plen += len; + return (0); +} + +int +smb_rap_create(int fn, const char *param, const char *data, + struct smb_rap **rapp) +{ + struct smb_rap *rap; + char *p; + int plen = 0, len = 0; + int i; + + rap = malloc(sizeof (*rap)); + if (rap == NULL) + return (ENOMEM); + bzero(rap, sizeof (*rap)); + p = rap->r_sparam = rap->r_nparam = strdup(param); + rap->r_sdata = rap->r_ndata = strdup(data); + + /* + * Calculate length of request parameter block + */ + len = 2 + strlen(param) + 1 + strlen(data) + 1; + while (*p) { + if (smb_rap_parserqparam(p, &p, &plen) != 0) + break; + len += plen; + } + rap->r_pbuf = rap->r_npbuf = malloc(len); + smb_rap_rqparam(rap, 'W', 1, fn); + smb_rap_rqparam_z(rap, rap->r_sparam); + smb_rap_rqparam_z(rap, rap->r_sdata); + *rapp = rap; + return (0); +} + +void +smb_rap_done(struct smb_rap *rap) +{ + if (rap->r_sparam) + free(rap->r_sparam); + if (rap->r_sdata) + free(rap->r_sdata); + if (rap->r_pbuf) + free(rap->r_pbuf); +#ifdef NOTYETDEFINED + if (rap->r_npbuf) + free(rap->r_npbuf); + if (rap->r_dbuf) + free(rap->r_dbuf); + if (rap->r_rcvbuf) + free(rap->r_rcvbuf); +#endif + free(rap); +} + +int +smb_rap_setNparam(struct smb_rap *rap, int value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + + error = smb_rap_parserqparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'L': + rap->r_rcvbuflen = value; + /* FALLTHROUGH */ + case 'W': + case 'D': + case 'b': + error = smb_rap_rqparam(rap, ptype, plen, value); + break; + default: + return (EINVAL); + } + rap->r_nparam = p; + return (0); +} + +int +smb_rap_setPparam(struct smb_rap *rap, void *value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + + error = smb_rap_parserqparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'r': + rap->r_rcvbuf = value; + break; + default: + return (EINVAL); + } + rap->r_nparam = p; + return (0); +} + +static int +smb_rap_getNparam(struct smb_rap *rap, long *value) +{ + char *p = rap->r_nparam; + char ptype = *p; + int error, plen; + uint16_t *te; + + error = smb_rap_parserpparam(p, &p, &plen); + if (error) + return (error); + switch (ptype) { + case 'h': + /* LINTED */ + te = (uint16_t *)rap->r_npbuf; + *value = letohs(*te); + break; + default: + return (EINVAL); + } + rap->r_npbuf += plen; + rap->r_nparam = p; + return (0); +} + +int +smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx) +{ + uint16_t *rp, conv, *tmp; + uint32_t *p32, ps1; + char *dp, *p = rap->r_nparam; + char ptype; + int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow, i; + + rdatacnt = rap->r_rcvbuflen; + rparamcnt = rap->r_plen; + error = smb_t2_request(ctx, 0, NULL, "\\PIPE\\LANMAN", + rap->r_plen, rap->r_pbuf, /* int tparamcnt,void *tparam */ + 0, NULL, /* int tdatacnt, void *tdata */ + &rparamcnt, rap->r_pbuf, /* rparamcnt, void *rparam */ + &rdatacnt, rap->r_rcvbuf, /* int *rdatacnt, void *rdata */ + &buffer_oflow); + if (error) + return (error); + + /* LINTED */ + rp = (uint16_t *)rap->r_pbuf; + + /* + * Note: First is a "LanMan API" error code. + * See: usr/src/uts/common/smbsrv/lmerr.h + */ + if (rparamcnt < 2) + return (EBADRPC); + rap->r_result = letohs(*rp); + rp++; rparamcnt -= 2; + + if (rap->r_result != 0) { + /* + * Could also return zero and let the caller + * come get r_result via smb_rap_error(), + * but in case they dont... + */ + return (rap->r_result | SMB_RAP_ERROR); + } + + if (rparamcnt < 2) + return (EBADRPC); + conv = letohs(*rp); + rp++; rparamcnt -= 2; + + rap->r_npbuf = (char *)rp; + rap->r_entries = entries = 0; + /* Save the returned data length */ + rap->r_rcvbuflen = rdatacnt; + done = 0; + + while (!done && *p) { + ptype = *p; + switch (ptype) { + case 'e': + if (rparamcnt < 2) + return (EBADRPC); + /* LINTED */ + tmp = (uint16_t *)rap->r_npbuf; + rap->r_entries = entries = letohs(*tmp); + rap->r_npbuf += 2; + rparamcnt -= 2; + p++; + break; + default: + done = 1; + } +#if 0 /* commented out in Darwin. Why? */ + error = smb_rap_parserpparam(p, &p, &plen); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "reply parameter mismatch %s"), 0, p); + return (EBADRPC); + } +#endif + } + rap->r_nparam = p; + /* + * In general, unpacking entries we may need to relocate + * entries for proper aligning. For now use them as is. + */ + dp = rap->r_rcvbuf; + while (entries--) { + p = rap->r_sdata; + while (*p) { + ptype = *p; + error = smb_rap_parserpdata(p, &p, &dlen); + if (error) { + smb_error(dgettext(TEXT_DOMAIN, + "reply data mismatch %s"), 0, p); + return (EBADRPC); + } + if (rdatacnt < dlen) + return (EBADRPC); + switch (ptype) { + case 'z': + /* LINTED */ + p32 = (uint32_t *)dp; + *p32 = (letohl(*p32) & 0xffff) - conv; + break; + } + dp += dlen; + rdatacnt -= dlen; + } + } + return (error); +} + +int +smb_rap_error(struct smb_rap *rap, int error) +{ + if (error) + return (error); + if (rap->r_result == 0) + return (0); + return (rap->r_result | SMB_RAP_ERROR); +} + +/* todo: move this function to libnetapi */ +int +smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer, + int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail) +{ + struct smb_rap *rap; + long lval = -1; + int error; + char *pass; + int i; + + error = smb_rap_create(0, "WrLeh", "B13BWz", &rap); + if (error) + return (error); + smb_rap_setNparam(rap, sLevel); /* W - sLevel */ + smb_rap_setPparam(rap, pbBuffer); /* r - pbBuffer */ + smb_rap_setNparam(rap, *cbBuffer); /* L - cbBuffer */ + error = smb_rap_request(rap, ctx); + if (error == 0) { + *pcEntriesRead = rap->r_entries; + error = smb_rap_getNparam(rap, &lval); + *pcTotalAvail = lval; + /* Copy the data length into the IN/OUT variable. */ + *cbBuffer = rap->r_rcvbuflen; + } + error = smb_rap_error(rap, error); + smb_rap_done(rap); + return (error); +} |