view usr/src/lib/libsmbfs/smb/rap.c @ 13394:96340372b72d

1120 Use real file descriptors for smbfs named pipes. Reviewed by: Eric Schrock <eric.schrock@delphix.com> Reviewed by: Albert Lee <trisk@nexenta.com> Approved by: Garrett D'Amore <garrett@nexenta.com>
author Gordon Ross <gwr@nexenta.com>
date Thu, 30 Jun 2011 17:58:05 -0400
parents ed3411181494
children
line wrap: on
line source

/*
 * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 * 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.
 */

#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>
#include "private.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)
{
	int len = 0;
	uint_t uv = (uint_t)value;
	uint32_t *lp;
	uint16_t *sp;
	char *p;

	switch (ptype) {
	case 'L':
	case 'W':
		/* LINTED */
		sp = (uint16_t *)rap->r_npbuf;
		*sp = htoles(uv);
		len = sizeof (*sp);
		break;
	case 'D':
		/* LINTED */
		lp = (uint32_t *)rap->r_npbuf;
		*lp = htolel(uv);
		len = sizeof (*lp);
		break;
	case 'b':
		p = rap->r_npbuf;
		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;

	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);
	if (rap->r_pbuf == NULL)
		return (ENOMEM);
	(void) smb_rap_rqparam(rap, 'W', 1, fn);
	(void) smb_rap_rqparam_z(rap, rap->r_sparam);
	(void) 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);
}

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;
	char *dp, *p = rap->r_nparam;
	char ptype;
	int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow;

	rdatacnt = rap->r_rcvbuflen;
	rparamcnt = rap->r_plen;
	error = smb_t2_request(ctx->ct_dev_fd,
	    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);
}