blob: 6734a7e49b8790415729d7b5287012594d611ea2 [file] [log] [blame]
/*
* Copyright (c) 2009, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - 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.
* - Neither the name of Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
*/
/*
* rpc_dplx_msg.c
*
* Based upon/copied from:
* rpc_callmsg.c
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*
*/
#include <config.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/types.h>
#include <reentrant.h>
#include <misc/portable.h>
#include <rpc/rpc.h>
#include <rpc/xdr_inline.h>
#include <rpc/auth_inline.h>
static const struct xdr_discrim reply_dscrm[3] = {
{(int)MSG_ACCEPTED, (xdrproc_t) xdr_naccepted_reply},
{(int)MSG_DENIED, (xdrproc_t) xdr_nrejected_reply},
{__dontcare__, NULL_xdrproc_t}
};
/* in glibc 2.14+ x86_64, memcpy no longer tries to handle overlapping areas,
* see Fedora Bug 691336 (NOTABUG); we dont permit overlapping segments,
* so memcpy may be a small win over memmove.
*/
/*
* encode a reply message, log error messages
*/
bool
xdr_reply_encode(XDR *xdrs, struct rpc_msg *dmsg)
{
struct opaque_auth *oa;
int32_t *buf;
switch (dmsg->rm_reply.rp_stat) {
case MSG_ACCEPTED:
{
struct accepted_reply *ar = (struct accepted_reply *)
&(dmsg->rm_reply.ru);
oa = &ar->ar_verf;
if (oa->oa_length > MAX_AUTH_BYTES) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_verf.oa_length (%u) > %u",
__func__, __LINE__,
oa->oa_length,
MAX_AUTH_BYTES);
return (false);
}
buf = XDR_INLINE(xdrs,
6 * BYTES_PER_XDR_UNIT +
RNDUP(oa->oa_length));
if (buf != NULL) {
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u ACCEPTED INLINE",
__func__, __LINE__);
IXDR_PUT_INT32(buf, dmsg->rm_xid);
IXDR_PUT_ENUM(buf, dmsg->rm_direction);
IXDR_PUT_ENUM(buf, dmsg->rm_reply.rp_stat);
IXDR_PUT_ENUM(buf, oa->oa_flavor);
IXDR_PUT_INT32(buf, oa->oa_length);
if (oa->oa_length) {
memcpy(buf, oa->oa_base, oa->oa_length);
buf += RNDUP(oa->oa_length) / sizeof(int32_t);
}
IXDR_PUT_ENUM(buf, ar->ar_stat);
switch (ar->ar_stat) {
case SUCCESS:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u SUCCESS",
__func__, __LINE__);
return ((*(ar->ar_results.proc))(xdrs,
ar->ar_results.where));
case PROG_MISMATCH:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u MISMATCH",
__func__, __LINE__);
buf = XDR_INLINE(xdrs,
2 * BYTES_PER_XDR_UNIT);
if (buf != NULL) {
IXDR_PUT_ENUM(buf, ar->ar_vers.low);
IXDR_PUT_ENUM(buf, ar->ar_vers.high);
} else if (!xdr_putuint32(xdrs,
&(ar->ar_vers.low))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_vers.low %u",
__func__, __LINE__,
ar->ar_vers.low);
return (false);
} else if (!xdr_putuint32(xdrs,
&(ar->ar_vers.high))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_vers.high %u",
__func__, __LINE__,
ar->ar_vers.high);
return (false);
}
/* fallthru */
case GARBAGE_ARGS:
case SYSTEM_ERR:
case PROC_UNAVAIL:
case PROG_UNAVAIL:
break;
};
return (true);
} else {
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u ACCEPTED non-INLINE",
__func__, __LINE__);
return (inline_xdr_union(xdrs,
(enum_t *) &(dmsg->rm_reply.rp_stat),
(caddr_t)(void *)&(dmsg->rm_reply.ru),
reply_dscrm, NULL_xdrproc_t));
}
/* never arrives here */
break;
}
case MSG_DENIED:
{
struct rejected_reply *rr = (struct rejected_reply *)
&(dmsg->rm_reply.ru);
switch (rr->rj_stat) {
case RPC_MISMATCH:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u DENIED MISMATCH",
__func__, __LINE__);
buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
if (buf != NULL) {
IXDR_PUT_ENUM(buf, rr->rj_stat);
IXDR_PUT_U_INT32(buf, rr->rj_vers.low);
IXDR_PUT_U_INT32(buf, rr->rj_vers.high);
} else if (!xdr_putenum(xdrs, rr->rj_stat)) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_stat %u",
__func__, __LINE__,
rr->rj_stat);
return (false);
} else if (!xdr_putuint32(xdrs,
&(rr->rj_vers.low))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_vers.low %u",
__func__, __LINE__,
rr->rj_vers.low);
return (false);
} else if (!xdr_putuint32(xdrs,
&(rr->rj_vers.high))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_vers.high %u",
__func__, __LINE__,
rr->rj_vers.high);
return (false);
}
return (true); /* bugfix */
case AUTH_ERROR:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u DENIED AUTH",
__func__, __LINE__);
buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT);
if (buf != NULL) {
IXDR_PUT_ENUM(buf, rr->rj_stat);
IXDR_PUT_ENUM(buf, rr->rj_why);
} else if (!xdr_putenum(xdrs, rr->rj_stat)) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_stat %u",
__func__, __LINE__,
rr->rj_stat);
return (false);
} else if (!xdr_putenum(xdrs, rr->rj_why)) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_why %u",
__func__, __LINE__,
rr->rj_why);
return (false);
}
return (true); /* bugfix */
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rr->rj_stat (%u)",
__func__, __LINE__,
rr->rj_stat);
break;
};
break;
}
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR dmsg->rm_reply.rp_stat (%u)",
__func__, __LINE__,
dmsg->rm_reply.rp_stat);
break;
};
return (false);
} /* XDR_ENCODE */
/*
* decode a reply message
*
* param[IN] buf 3 more inline
*/
bool
xdr_reply_decode(XDR *xdrs, struct rpc_msg *dmsg, int32_t *buf)
{
if (buf != NULL) {
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u INLINE",
__func__, __LINE__);
dmsg->rm_reply.rp_stat = IXDR_GET_ENUM(buf, enum_t);
} else {
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u non-INLINE",
__func__, __LINE__);
if (!xdr_getenum(xdrs, (enum_t *)&(dmsg->rm_reply.rp_stat))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rm_reply.rp_stat",
__func__, __LINE__);
return (false);
}
}
switch (dmsg->rm_reply.rp_stat) {
case MSG_ACCEPTED:
{
struct accepted_reply *ar = (struct accepted_reply *)
&(dmsg->rm_reply.ru);
if (!inline_auth_decode(xdrs, &ar->ar_verf, buf)) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR (return)",
__func__, __LINE__);
return (false);
}
if (!xdr_getenum(xdrs, (enum_t *)&(ar->ar_stat))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_stat",
__func__, __LINE__);
return (false);
}
switch (ar->ar_stat) {
case SUCCESS:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u SUCCESS",
__func__, __LINE__);
return ((*(ar->ar_results.proc))(xdrs,
&(ar->ar_results.where)));
case PROG_MISMATCH:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u MISMATCH",
__func__, __LINE__);
if (!xdr_getuint32(xdrs, &(ar->ar_vers.low))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_vers.low",
__func__, __LINE__);
return (false);
}
if (!xdr_getuint32(xdrs, &(ar->ar_vers.high))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR ar_vers.high",
__func__, __LINE__);
return (false);
}
case GARBAGE_ARGS:
case SYSTEM_ERR:
case PROC_UNAVAIL:
case PROG_UNAVAIL:
/* true */
break;
default:
break;
}; /* ar_stat */
return (true);
} /* MSG_ACCEPTED */
case MSG_DENIED:
{
/* XXX branch not verified */
struct rejected_reply *rr = (struct rejected_reply *)
&(dmsg->rm_reply.ru);
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u MSG_DENIED not verified",
__func__, __LINE__);
if (buf != NULL) {
rr->rj_stat = IXDR_GET_ENUM(buf, enum_t);
} else if (!xdr_getenum(xdrs, (enum_t *)&(rr->rj_stat))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_stat",
__func__, __LINE__);
return (false);
}
switch (rr->rj_stat) {
case RPC_MISMATCH:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u DENIED MISMATCH",
__func__, __LINE__);
if (buf != NULL) {
rr->rj_vers.low = IXDR_GET_U_INT32(buf);
} else if (!xdr_getuint32(xdrs, &(rr->rj_vers.low))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_vers.low",
__func__, __LINE__);
return (false);
}
if (!xdr_getuint32(xdrs, &(rr->rj_vers.high))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_vers.high",
__func__, __LINE__);
return (false);
}
break;
case AUTH_ERROR:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u DENIED AUTH",
__func__, __LINE__);
if (buf != NULL) {
rr->rj_why = IXDR_GET_ENUM(buf, enum_t);
} else if (!xdr_getenum(xdrs,
(enum_t *)&(rr->rj_why))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rj_why",
__func__, __LINE__);
return (false);
}
break;
};
return (true);
} /* MSG_DENIED */
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR dmsg->rm_reply.rp_stat %u",
__func__, __LINE__,
dmsg->rm_reply.rp_stat);
break;
}; /* rm_reply.rp_stat */
return (false);
}
/*
* decode a duplex message, log error messages
*/
bool
xdr_dplx_decode(XDR *xdrs, struct rpc_msg *dmsg)
{
int32_t *buf;
/*
* NOTE: 5 here, 3 more in each _decode
*/
buf = XDR_INLINE(xdrs, 5 * BYTES_PER_XDR_UNIT);
if (buf != NULL) {
dmsg->rm_xid = IXDR_GET_U_INT32(buf);
dmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
} else {
if (!xdr_getuint32(xdrs, &(dmsg->rm_xid))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rm_xid",
__func__, __LINE__);
return (false);
}
if (!xdr_getenum(xdrs, (enum_t *)&(dmsg->rm_direction))) {
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR rm_direction",
__func__, __LINE__);
return (false);
}
}
switch (dmsg->rm_direction) {
case CALL:
return (xdr_call_decode(xdrs, dmsg, buf));
case REPLY:
return (xdr_reply_decode(xdrs, dmsg, buf));
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR dmsg->rm_direction (%u)",
__func__, __LINE__,
dmsg->rm_direction);
break;
};
return (false);
}
/*
* XDR a duplex message
*/
bool
xdr_dplx_msg(XDR *xdrs, struct rpc_msg *dmsg)
{
assert(xdrs != NULL);
assert(dmsg != NULL);
switch (xdrs->x_op) {
case XDR_ENCODE:
switch (dmsg->rm_direction) {
case CALL:
return (xdr_call_encode(xdrs, dmsg));
case REPLY:
return (xdr_reply_encode(xdrs, dmsg));
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR dmsg->rm_direction (%u)",
__func__, __LINE__,
dmsg->rm_direction);
break;
};
break;
case XDR_DECODE:
return (xdr_dplx_decode(xdrs, dmsg));
case XDR_FREE:
__warnx(TIRPC_DEBUG_FLAG_RPC_MSG,
"%s:%u xdrs->x_op XDR_FREE",
__func__, __LINE__);
return (true);
default:
__warnx(TIRPC_DEBUG_FLAG_ERROR,
"%s:%u ERROR xdrs->x_op (%u)",
__func__, __LINE__,
xdrs->x_op);
break;
};
return (false);
} /* xdr_dplx_msg */