/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley
* 4.3 BSD under license from the Regents of the University of
* California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* layer above connection oriented transport layer (e.g. tcp) (for rpc's use).
*
*
* There is a record marking layer between the xdr stream
* and the (tcp) cv transport level. A record is composed on one or more
* record fragments. A record fragment is a thirty-two bit header followed
* by n bytes of data, where n is contained in the header. The header
* is represented as a htonl(ulong_t). The order bit encodes
* whether or not the fragment is the last fragment of the record
* (1 => fragment is last, 0 => more fragments to follow.
* The other 31 bits encode the byte length of the fragment.
*/
#include "mt.h"
#include "rpc_mt.h"
#include <stdio.h>
#include <syslog.h>
#include <memory.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
/*
* A record is composed of one or more record fragments.
* A record fragment is a four-byte header followed by zero to
* 2**32-1 bytes. The header is treated as a long unsigned and is
* are a byte count of the fragment. The highest order bit is a boolean:
* 1 => this fragment is the last fragment of the record,
* 0 => this fragment is followed by more fragment(s).
*
* meet the needs of xdr and rpc based on tcp.
*/
/*
* Minimum fragment size is size of rpc callmsg over TCP:
* xid direction vers prog vers proc
* cred flavor, cred length, cred
* verf flavor, verf length, verf
* (with no cred or verf allocated)
*/
typedef struct rec_strm {
/*
* out-going bits
*/
int (*writeit)();
/*
* in-coming bits
*/
int (*readit)();
/*
* Is this the first time that the
* getbytes routine has been called ?
*/
/*
* Is this non-blocked?
*/
} RECSTREAM;
static struct xdr_ops *xdrrec_ops(void);
/*
* Create an xdr handle for xdrrec
* xdrrec_create fills in xdrs. Sendsize and recvsize are
* send and recv buffer sizes (0 => use default).
* vc_handle is an opaque handle that is passed as the first parameter to
* the procedures readit and writeit. Readit and writeit are read and
* write respectively. They are like the system calls expect that they
* take an opaque handle rather than an fd.
*/
void
{
/*
* XXX: Should still rework xdrrec_create to return a handle,
* and in any malloc-failure case return NULL.
*/
return;
}
/*
* Adjust sizes and allocate buffers; malloc(3C)
* provides a buffer suitably aligned for any use, so
* there's no need for us to mess around with alignment.
*
* Since non-blocking connections may need to reallocate the input
* buffer, we use separate malloc()s for input and output.
*/
return;
}
return;
}
/*
* now the rest ...
*/
/* LINTED pointer cast */
rstrm->in_nonblock = 0;
rstrm->in_maxrecsz = 0;
rstrm->in_nextrecsz = 0;
}
/*
* Align input stream. If all applications behaved correctly, this
* defensive procedure will not be necessary, since received data will be
* aligned correctly.
*/
static void
{
}
/*
* The routines defined below are the xdr ops which will go into the
* xdr handle filled in by xdrrec_create.
*/
static bool_t
{
/* LINTED pointer cast */
/* LINTED pointer cast */
/* first try the inline, fast case */
/*
* Check if buflp is longword aligned. If not, align it.
*/
/* LINTED pointer cast */
}
} else {
return (FALSE);
}
return (TRUE);
}
static bool_t
{
/* LINTED pointer cast */
/* LINTED pointer cast */
/*
* this case should almost never happen so the code is
* inefficient
*/
return (FALSE);
/* LINTED pointer cast */
}
return (TRUE);
}
static bool_t
{
int32_t i;
if (!xdrrec_getint32(xdrs, &i))
return (FALSE);
*lp = (long)i;
return (TRUE);
}
static bool_t
{
int32_t i;
#if defined(_LP64)
return (FALSE);
#endif
return (xdrrec_putint32(xdrs, &i));
}
static bool_t /* must manage buffers, fragments, and records */
{
/* LINTED pointer cast */
int current;
while (len > 0) {
if (current == 0) {
return (FALSE);
if (!set_input_fragment(rstrm))
return (FALSE);
continue;
}
return (FALSE);
}
return (TRUE);
}
static bool_t
{
/* LINTED pointer cast */
int current;
while (len > 0) {
return (FALSE);
}
}
return (TRUE);
}
/*
* This is just like the ops vector x_getbytes(), except that
* instead of returning success or failure on getting a certain number
* of bytes, it behaves much more like the read() system call against a
* pipe -- it returns up to the number of bytes requested and a return of
* zero indicates end-of-record. A -1 means something very bad happened.
*/
uint_t /* must manage buffers, fragments, and records */
{
/* LINTED pointer cast */
len = l;
while (len > 0) {
if (current == 0) {
return (l - len);
if (!set_input_fragment(rstrm))
return ((uint_t)-1);
continue;
}
return ((uint_t)-1);
}
return (l - len);
}
static uint_t
{
/* LINTED pointer cast */
if (pos != -1)
case XDR_ENCODE:
break;
case XDR_DECODE:
break;
default:
break;
}
}
static bool_t
{
/* LINTED pointer cast */
if ((int)currpos != -1)
case XDR_ENCODE:
return (TRUE);
}
break;
case XDR_DECODE:
return (TRUE);
}
break;
}
return (FALSE);
}
static rpc_inline_t *
{
/* LINTED pointer cast */
case XDR_ENCODE:
/* LINTED pointer cast */
}
break;
case XDR_DECODE:
/*
* Check if rstrm->in_finger is longword aligned;
* if not, align it.
*/
(sizeof (int32_t) - 1))
/* LINTED pointer cast */
}
break;
}
return (buf);
}
static void
{
/* LINTED pointer cast */
}
/*
* Exported routines to manage xdr records
*/
/*
* Before reading (deserializing) from the stream, one should always call
* this procedure to guarantee proper record alignment.
*/
{
/* LINTED pointer cast */
if (rstrm->in_nonblock) {
/*
* Read and discard a record from the non-blocking
* buffer. Return succes only if a complete record can
* be retrieved without blocking, or if the buffer was
* empty and there was no data to fetch.
*/
(pstat == XPRT_MOREREQS &&
return (TRUE);
}
return (FALSE);
}
return (FALSE);
return (FALSE);
}
return (TRUE);
}
/*
* Look ahead fuction.
* Returns TRUE iff there is no more input in the buffer
* after consuming the rest of the current record.
*/
{
/* LINTED pointer cast */
if (rstrm->in_nonblock) {
/*
* If in_needpoll is true, the non-blocking XDR stream
* does not have a complete record.
*/
return (rstrm->in_needpoll);
}
return (TRUE);
return (TRUE);
}
return (TRUE);
return (FALSE);
}
/*
* The client must tell the package when an end-of-record has occurred.
* The second parameters tells whether the record should be flushed to the
* (output) tcp stream. (This let's the package support batched or
* pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
*/
{
/* LINTED pointer cast */
}
sizeof (uint32_t);
/* LINTED pointer cast */
return (TRUE);
}
/*
* Internal useful routines
*/
static bool_t
{
int written;
/*
* Handle the specific 'CANT_STORE' error. In this case, the
* fragment must be cleared.
*/
return (FALSE);
/* LINTED pointer cast */
return (TRUE);
}
/* knows nothing about records! Only about input buffers */
static bool_t
{
int len;
if (rstrm->in_nonblock) {
/* Should never get here in the non-blocking case */
return (FALSE);
}
if (do_align) {
} else {
where += i;
}
return (FALSE);
return (TRUE);
}
/* knows nothing about records! Only about input buffers */
static bool_t
{
int current;
if (rstrm->in_nonblock) {
/*
* Data should already be in the rstrm buffer, so we just
* need to copy it to 'addr'.
*/
return (FALSE);
return (TRUE);
}
while (len > 0) {
if (current == 0) {
return (FALSE);
continue;
}
}
return (TRUE);
}
/* next four bytes of the input stream are treated as a header */
static bool_t
{
if (rstrm->in_nonblock) {
/*
* In the non-blocking case, the fragment headers should
* already have been consumed, so we should never get
* here. Might as well return failure right away.
*/
return (FALSE);
}
return (FALSE);
return (TRUE);
}
/* consumes input bytes; knows nothing about records! */
static bool_t
{
int current;
while (cnt > 0) {
if (current == 0) {
return (FALSE);
continue;
}
}
return (TRUE);
}
static bool_t
{
if (newbuf == 0) {
} else {
/* Make pointers valid for the new buffer */
}
}
return (ret);
}
/*
* adjust sizes and allocate buffer quad byte aligned
*/
{
/* LINTED pointer cast */
if (tcp_maxrecsz == 0) {
/*
* If maxrecsz has not been set, use the default
* that was set from xdrrec_create() and
* fix_buf_size()
*/
return (TRUE);
}
return (TRUE);
/*
* For nonblocked connection, the entire record is read into the
* buffer before any xdr processing. This implies that the record
* size must allow for the maximum expected message size of the
* service. However, it's inconvenient to allocate very large
* buffers up front, so we limit ourselves to a reasonable
* default size here, and reallocate (up to the maximum record
* size allowed for the connection) as necessary.
*/
}
return (FALSE);
}
return (TRUE);
}
/*
* Retrieve input data from the non-blocking connection, increase
* the size of the read buffer if necessary, and check that the
* record size stays below the allowed maximum for the connection.
*/
{
/* LINTED pointer cast */
/*
* For connection oriented protocols, there's no guarantee that
* we will receive the data nicely chopped into records, no
* matter how it was sent. We use the in_nextrec pointer to
* indicate where in the buffer the next record starts. If
* in_nextrec != in_base, there's data in the buffer from
* previous reads, and if in_nextrecsz > 0, we need to copy
* the portion of the next record already read to the start of
* the input buffer
*/
if (rstrm->in_nextrecsz > 0) {
/* Starting on new record with data already in the buffer */
rstrm->in_nextrecsz = 0;
/* Starting on new record with empty buffer */
}
/* Do we need to retrieve data ? */
if (rstrm->in_needpoll) {
/*
* if len_requested is 0, this means that the input
* buffer is full and need to be increased.
* The minimum record size we will need is whatever's
* already in the buffer, plus what's yet to be
* consumed in the current fragment, plus space for at
* least one more fragment header, if this is not the
* last fragment. We use the RNDUP() macro to
* account for possible realignment of the next
* fragment header.
*/
if (len_requested == 0) {
/*
* no more bytes to be consumed and
* last fragment. We should never end up
* here. Might as well return failure
* right away.
*/
return (FALSE);
}
goto recsz_invalid;
else
goto needpoll;
}
return (FALSE);
}
}
/* Account for any left over data from previous processing */
/* Set a lower limit on the buffer space we'll need */
/*
* Consume bytes for this record until it's either complete,
* rejected, or we need to poll for more bytes.
*
* If fbtbc == 0, in_finger points to the start of the fragment
* header. Otherwise, it points to the start of the fragment data.
*/
while (len_received > 0) {
/* LINTED pointer cast */
len_recvd_thisfrag : sizeof (*header);
/*
* The minimum record size we will need is whatever's
* already in the buffer, plus the size of this
* fragment, plus (if this isn't the last fragment)
* space for at least one more fragment header. We
* use the RNDUP() macro to account for possible
* realignment of the next fragment header.
*/
minreqrecsize += minfraglen +
/*
* We only have a partial fragment header,
* but we can still put a lower limit on the
* final fragment size, and check against the
* maximum allowed.
*/
if (len_recvd_thisfrag > 0 &&
goto recsz_invalid;
}
/* Need more bytes to obtain fbtbc value */
goto needpoll;
}
/*
* We've got a complete fragment header, so
* 'minfraglen' is the actual fragment length, and
* 'minreqrecsize' the requested record size.
*/
/*
* Check that the sum of the total number of bytes read
* so far (for the record) and the size of the incoming
* fragment is less than the maximum allowed.
*
* If this is the last fragment, also check that the
* record (message) meets the minimum length
* requirement.
*
* If this isn't the last fragment, check for a zero
* fragment length. Accepting such fragments would
* leave us open to an attack where the sender keeps
* the connection open indefinitely, without any
* progress, by occasionally sending a zero length
* fragment.
*/
return (FALSE);
}
/*
* Make this fragment abut the previous one. If it's
* the first fragment, just advance in_finger past
* the header. This avoids buffer copying for the
* usual case where there's one fragment per record.
*/
} else {
}
/* Consume the fragment header */
if (len_received > sizeof (*header)) {
len_received -= sizeof (*header);
} else {
len_received = 0;
}
}
/*
* Consume whatever fragment bytes we have.
* If we've received all bytes for this fragment, advance
* in_finger to point to the start of the next fragment
* header. Otherwise, make fbtbc tell how much is left in
* in this fragment and advance finger to point to end of
* fragment data.
*/
} else {
len_received = 0;
}
/*
* If there's more data in the buffer, there are two
* possibilities:
*
* (1) This is the last fragment, so the extra data
* presumably belongs to the next record.
*
* (2) Not the last fragment, so we'll start over
* from the top of the loop.
*/
rstrm->in_nextrec);
len_received = 0;
}
}
/* Was this the last fragment, and have we read the entire record ? */
*pstat = XPRT_MOREREQS;
/*
* We've been using both in_finger and fbtbc for our own
* purposes. Now's the time to update them to be what
* xdrrec_inline() expects. Set in_finger to point to the
* start of data for this record, and fbtbc to the number
* of bytes in the record.
*/
if (rstrm->in_nextrecsz == 0)
return (TRUE);
}
/*
* Need more bytes, so we set the needpoll flag, and go back to
* the main RPC request loop. However, first we reallocate the
* input buffer, if necessary.
*/
return (FALSE);
}
}
*pstat = XPRT_MOREREQS;
return (FALSE);
}
int
{
/* LINTED pointer cast */
}
int
{
/* LINTED pointer cast */
/*
* Set rstrm->firsttime only if the input buffer is empty.
* Otherwise, the first read from the network could skip
* a poll.
*/
else
return (1);
}
int
{
/* LINTED pointer cast */
return (1);
}
static uint_t
{
if (s < 100)
s = 4000;
return (RNDUP(s));
}
static bool_t
{
/* LINTED pointer cast */
switch (request) {
case XDR_GET_BYTES_AVAIL:
/* Check if at end of fragment and not last fragment */
if (!set_input_fragment(rstrm)) {
return (FALSE);
};
return (TRUE);
default:
return (FALSE);
}
}
static struct xdr_ops *
xdrrec_ops(void)
{
/* VARIABLES PROTECTED BY ops_lock: ops */
(void) mutex_lock(&ops_lock);
#if defined(_LP64)
#endif
}
(void) mutex_unlock(&ops_lock);
return (&ops);
}