/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* or http://www.opensolaris.org/os/licensing.
* 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 (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <rad/adr_typeset.h>
#include "radproto_util.h"
#include "radproto_adr.h"
#include "protocol.h"
#define zalloc(x) calloc(1, x)
/*
* XDR-like routines for reading and writing ADR data and types.
* There are two major differences between these and "normal" XDR
* routines:
* a) Some take more than two arguments. This is because the
* routines aren't dealing with fixed data structures and
* require additional inputs to perform their processing.
* This is a contributing factor to...
* b) They aren't symmetric. i.e. there are separate routines
* for reading (or sizing) and writing. The only substantial
* effect of merging reading and writing into a single function
* would be to create functions which are large and difficult
* to maintain.
*/
/*
* Marshal type definitions.
*/
static bool_t
typeset_emit_type(XDR *xdrs, adr_typeset_t *typeset, adr_type_t *type)
{
assert(xdrs->x_op != XDR_DECODE);
switch (type->t_type) {
case DT_ARRAY:
if (!xdr_int(xdrs, (int *)&type->t_type) ||
!xdr_typeref_t(xdrs, typeset, type->t_aux.t_array))
return (FALSE);
break;
case DT_STRUCT: {
if (!xdr_int(xdrs, (int *)&type->t_type) ||
!xdr_string(xdrs, (char **)&type->adr_t_name, -1) ||
!xdr_u_int(xdrs, &type->t_size))
return (FALSE);
for (int i = 0; i < type->t_size; i++) {
adr_structfield_t *field = &type->t_aux.t_fields[i];
if (!xdr_string(xdrs, (char **)&field->sf_name, -1) ||
!xdr_bool(xdrs, (int *)&field->sf_nullable) ||
!xdr_typeref_t(xdrs, typeset, field->sf_type))
return (FALSE);
}
break;
}
case DT_UNION: {
adr_unionarm_t *dfltarm = &type->t_aux.t_union.arms[0];
bool_t dflt = dfltarm->ua_type != NULL;
if (!xdr_int(xdrs, (int *)&type->t_type) ||
!xdr_string(xdrs, (char **)&type->adr_t_name, -1) ||
!xdr_typeref_t(xdrs, typeset, type->t_aux.t_union.type) ||
!xdr_bool(xdrs, &dflt) ||
(dflt &&
(!xdr_bool(xdrs, (int *)&dfltarm->ua_nullable) ||
!xdr_typeref_t(xdrs, typeset, dfltarm->ua_type))) ||
!xdr_u_int(xdrs, &type->t_size))
return (FALSE);
for (int i = 0; i < type->t_size; i++) {
adr_unionarm_t *ua = &type->t_aux.t_union.arms[i + 1];
if (!xdr_data_t(xdrs, ua->ua_value) ||
!xdr_bool(xdrs, (int *)&ua->ua_nullable) ||
!xdr_typeref_t(xdrs, typeset, ua->ua_type))
return (FALSE);
}
break;
}
case DT_ENUM: {
const char *fbname = type->t_aux.t_enum[0].ev_name;
bool_t fallback = fbname != NULL;
if (!xdr_int(xdrs, (int *)&type->t_type) ||
!xdr_string(xdrs, (char **)&type->adr_t_name, -1) ||
!xdr_bool(xdrs, &fallback) ||
(fallback && !xdr_string(xdrs, (char **)&fbname, -1)) ||
!xdr_u_int(xdrs, &type->t_size))
return (FALSE);
for (int i = 1; i < type->t_size + 1; i++) {
adr_enumval_t *ev = &type->t_aux.t_enum[i];
if (!xdr_string(xdrs, (char **)&ev->ev_name, -1) ||
!xdr_int(xdrs, &ev->ev_value))
return (FALSE);
}
break;
}
}
return (TRUE);
}
static bool_t
typeset_read_type(XDR *xdrs, adr_typeset_t *typeset)
{
assert(xdrs->x_op == XDR_DECODE);
int ttype;
if (!xdr_int(xdrs, &ttype))
return (FALSE);
if (!ADR_DT_DERIVED(ttype))
return (FALSE);
adr_type_t *result = zalloc(sizeof (adr_type_t));
if (result == NULL)
return (FALSE);
result->t_type = ttype;
result->adr_t_name = NULL;
result->t_size = 0;
result->t_dynamic = B_TRUE;
switch (ttype) {
case DT_ARRAY: {
adr_type_t *atype;
if (!xdr_r_typeref_t(xdrs, typeset, &atype))
goto error;
result->t_aux.t_array = atype;
break;
}
case DT_STRUCT: {
char *name = NULL;
if (!xdr_string(xdrs, &name, -1))
goto error;
result->adr_t_name = name;
if (!xdr_u_int(xdrs, &result->t_size) ||
(result->t_aux.t_fields = zalloc(result->t_size *
sizeof (adr_structfield_t))) == NULL)
goto error;
for (int i = 0; i < result->t_size; i++) {
adr_structfield_t *f = &result->t_aux.t_fields[i];
bool_t fopt;
if (!xdr_string(xdrs, (char **)&f->sf_name, -1) ||
!xdr_bool(xdrs, &fopt) ||
!xdr_r_typeref_t(xdrs, typeset, &f->sf_type))
goto error;
f->sf_nullable = fopt;
}
break;
}
case DT_UNION: {
char *name = NULL;
bool_t dflt, dfltopt;
adr_type_t *dflttype = NULL, *utype;
if (!xdr_string(xdrs, &name, -1) ||
!xdr_r_typeref_t(xdrs, typeset, &utype))
goto error;
result->adr_t_name = name;
result->t_aux.t_union.type = utype;
if (!xdr_bool(xdrs, &dflt) ||
(dflt && (
!xdr_bool(xdrs, &dfltopt) ||
!xdr_r_typeref_t(xdrs, typeset, &dflttype))))
goto error;
if (!xdr_u_int(xdrs, &result->t_size) ||
(result->t_aux.t_union.arms =
zalloc((result->t_size + 1) * sizeof (adr_unionarm_t))) ==
NULL)
goto error;
if (dflt) {
result->t_aux.t_union.arms[0].ua_nullable = dfltopt;
result->t_aux.t_union.arms[0].ua_type = dflttype;
}
for (int i = 0; i < result->t_size; i++) {
adr_unionarm_t *ua = &result->t_aux.t_union.arms[i + 1];
if (!xdr_r_data_t(xdrs, &ua->ua_value, utype, NULL) ||
!xdr_bool(xdrs, (int *)&ua->ua_nullable) ||
!xdr_r_typeref_t(xdrs, typeset, &ua->ua_type))
return (FALSE);
}
break;
}
case DT_ENUM: {
char *name = NULL, *fbname = NULL;
bool_t fallback;
if (!xdr_string(xdrs, &name, -1))
goto error;
result->adr_t_name = name;
if (!xdr_bool(xdrs, &fallback) ||
(fallback && !xdr_string(xdrs, &fbname, -1)))
goto error;
if (!xdr_u_int(xdrs, &result->t_size) ||
(result->t_aux.t_enum = zalloc((result->t_size + 1) *
sizeof (adr_enumval_t))) == NULL) {
free(fbname);
goto error;
}
result->t_aux.t_enum[0].ev_name = fbname;
for (int i = 1; i < result->t_size + 1; i++) {
adr_enumval_t *e = &result->t_aux.t_enum[i];
int eval;
if (!xdr_string(xdrs, (char **)&e->ev_name, -1) ||
!xdr_int(xdrs, &eval))
goto error;
e->ev_value = eval;
e->ev_data = adr_data_new_enum_index(result, 0);
if (e->ev_data == NULL)
goto error;
}
break;
}
}
(void) adr_typeset_insert(typeset, result);
return (TRUE);
error:
adr_type_free(result);
return (FALSE);
}
bool_t
xdr_typeref_t(XDR *xdrs, adr_typeset_t *typeset, adr_type_t *type)
{
if (!xdr_int(xdrs, (int *)&type->t_type))
return (FALSE);
if (!ADR_DT_DERIVED(type->t_type))
return (TRUE);
int tnum = adr_typeset_find(typeset, type);
assert(tnum >= 0);
if (!xdr_int(xdrs, &tnum))
return (FALSE);
return (TRUE);
}
bool_t
xdr_r_typeref_t(XDR *xdrs, adr_typeset_t *typeset, adr_type_t **typep)
{
int ttype, tnum;
if (!xdr_int(xdrs, &ttype) || !ADR_DT_VALID(ttype))
return (FALSE);
if (!ADR_DT_DERIVED(ttype)) {
*typep = adr_basetype(ttype);
return (TRUE);
}
adr_type_t *type;
if (!xdr_int(xdrs, &tnum) ||
(type = adr_typeset_id(typeset, tnum)) == NULL)
return (FALSE);
*typep = type;
return (TRUE);
}
bool_t
xdr_typeset_t(XDR *xdrs, adr_typeset_t *typeset)
{
if (!xdr_int(xdrs, &typeset->TS_USED))
return (FALSE);
for (int i = 0; i < typeset->TS_USED; i++) {
adr_type_t *type = typeset->TS_TYPES[i].TR_TYPE;
if (!typeset_emit_type(xdrs, typeset, type))
return (FALSE);
}
return (TRUE);
}
bool_t
xdr_r_typeset_t(XDR *xdrs, adr_typeset_t **typesetp)
{
adr_typeset_t *result;
int ntypes;
if (!xdr_int(xdrs, &ntypes))
return (FALSE);
if ((result = adr_typeset_create(ntypes)) == NULL)
return (FALSE);
for (int i = 0; i < ntypes; i++)
if (!typeset_read_type(xdrs, result)) {
adr_typeset_free(result);
return (FALSE);
}
*typesetp = result;
return (TRUE);
}
/*
* Normally the absence of a type is represented by void. Attribute/
* method error payload types require additional information: it is
* desirable to distinguish between an operation that doesn't return
* error payload data, and an operation that doesn't return errors.
* We effectively treat an error payload type as "optional data".
*
* Note that we don't use this when serializing standard error types
* at protocol negotiation time.
*/
bool_t
xdr_errortype_t(XDR *xdrs, adr_typeset_t *typeset, adr_type_t *type)
{
assert(xdrs->x_op != XDR_DECODE);
bool_t b = type != NULL;
if (!xdr_bool(xdrs, &b))
return (FALSE);
if (b)
return (xdr_typeref_t(xdrs, typeset, type));
return (TRUE);
}
bool_t
xdr_r_errortype_t(XDR *xdrs, adr_typeset_t *typeset, adr_type_t **type)
{
assert(xdrs->x_op == XDR_DECODE);
bool_t b;
if (!xdr_bool(xdrs, &b))
return (FALSE);
if (b)
return (xdr_r_typeref_t(xdrs, typeset, type));
*type = NULL;
return (TRUE);
}
bool_t
xdr_stability_t(XDR *xdrs, adr_stability_t *stability)
{
if (!xdr_enum(xdrs, (int *)stability))
return (FALSE);
return (TRUE);
}
bool_t
xdr_version_t(XDR *xdrs, adr_version_t *version)
{
if (!xdr_stability_t(xdrs, &version->av_stability) ||
!xdr_int(xdrs, &version->av_major) ||
!xdr_int(xdrs, &version->av_minor))
return (FALSE);
return (TRUE);
}
/*
* Marshal/unmarshall an interface/version tuple.
*
* nparents indicates how many parent Interfaces should be processed when
* decoding. It isn't used when encoding.
*/
bool_t
xdr_iface_t(XDR *xdrs, adr_object_t **objp, int nparents)
{
adr_object_t *obj = *objp;
int i;
if (xdrs->x_op == XDR_DECODE) {
if (obj == NULL) {
obj = zalloc(sizeof (adr_object_t));
if ((*objp = obj) == NULL)
return (FALSE);
}
assert(obj->ao_parents == NULL);
if (nparents > 0) {
obj->ao_parents =
zalloc(nparents * sizeof (adr_object_t *));
if (obj->ao_parents == NULL)
return (FALSE);
obj->ao_nparents = nparents;
}
}
/* Depth first; primary interface should be last listed */
for (i = 0; i < obj->ao_nparents; i++)
if (!xdr_iface_t(xdrs, &obj->ao_parents[i], 0))
return (FALSE);
if (!xdr_string(xdrs, (char **)&obj->ao_ifname, -1))
return (FALSE);
for (i = 0; i < NSTABILITY; i++)
if (!xdr_version_t(xdrs, &obj->ao_versions[i]))
return (FALSE);
return (TRUE);
}
static int
count_parents(adr_object_t *obj)
{
int n = 0;
for (int i = 0; i < obj->ao_nparents; i++)
n += count_parents(obj->ao_parents[i]);
return (n + 1);
}
/*
* Marshal an interface definition.
*/
bool_t
xdr_object_t(XDR *xdrs, adr_typeset_t *typeset, adr_object_t *obj)
{
int i, j;
assert(xdrs->x_op != XDR_DECODE);
/*
* Compatible interfaces and their versions
*/
int n = count_parents(obj);
if (!xdr_string(xdrs, (char **)&obj->ao_aname, -1) ||
!xdr_int(xdrs, &n) ||
!xdr_iface_t(xdrs, &obj, 0))
return (FALSE);
/*
* Type definitions
*/
if (!xdr_typeset_t(xdrs, typeset))
return (FALSE);
/*
* Attributes
*/
if (!xdr_int(xdrs, &obj->ao_nattributes))
return (FALSE);
for (i = 0; i < obj->ao_nattributes; i++) {
adr_attribute_t *a = obj->ao_attributes[i];
bool_t br = a->aa_readable ? TRUE : FALSE;
bool_t bw = a->aa_writeable ? TRUE : FALSE;
if (!xdr_string(xdrs, (char **)&a->aa_name, -1) ||
!xdr_stability_t(xdrs, &a->aa_stability) ||
!xdr_bool(xdrs, &br) ||
!xdr_bool(xdrs, &bw) ||
!xdr_bool(xdrs, (int *)&a->aa_nullable) ||
!xdr_typeref_t(xdrs, typeset, a->aa_type) ||
!xdr_errortype_t(xdrs, typeset, a->aa_read_error) ||
!xdr_errortype_t(xdrs, typeset, a->aa_write_error))
return (FALSE);
}
/*
* Methods
*/
if (!xdr_int(xdrs, &obj->ao_nmethods))
return (FALSE);
for (i = 0; i < obj->ao_nmethods; i++) {
adr_method_t *m = obj->ao_methods[i];
if (!xdr_string(xdrs, (char **)&m->am_name, -1) ||
!xdr_stability_t(xdrs, &m->am_stability) ||
!xdr_bool(xdrs, (int *)&m->am_nullable) ||
!xdr_typeref_t(xdrs, typeset, m->am_result) ||
!xdr_errortype_t(xdrs, typeset, m->am_error) ||
!xdr_int(xdrs, &m->am_nargs))
return (FALSE);
for (j = 0; j < m->am_nargs; j++) {
adr_parameter_t *p = &m->am_args[j];
if (!xdr_string(xdrs, (char **)&p->ap_name, -1) ||
!xdr_bool(xdrs, (int *)&p->ap_nullable) ||
!xdr_typeref_t(xdrs, typeset, p->ap_type))
return (FALSE);
}
}
/*
* Events
*/
if (!xdr_int(xdrs, &obj->ao_nevents))
return (FALSE);
for (i = 0; i < obj->ao_nevents; i++) {
adr_event_t *e = &obj->ao_events[i];
if (!xdr_string(xdrs, (char **)&e->ae_name, -1) ||
!xdr_stability_t(xdrs, &e->ae_stability) ||
!xdr_typeref_t(xdrs, typeset, e->ae_type))
return (FALSE);
}
return (TRUE);
}
/*
* Unmarshal an interface definition.
*/
bool_t
xdr_r_object_t(XDR *xdrs, adr_object_t **obj)
{
assert(xdrs->x_op == XDR_DECODE);
adr_typeset_t *typeset = NULL;
adr_object_t *result = zalloc(sizeof (adr_object_t));
if (result == NULL)
return (FALSE);
/*
* Versions
*/
int n;
if (!xdr_string(xdrs, (char **)&result->ao_aname, -1) ||
!xdr_int(xdrs, &n) ||
!xdr_iface_t(xdrs, &result, n))
goto error;
if (!xdr_r_typeset_t(xdrs, &typeset))
goto error;
/*
* Attributes
*/
if (!xdr_int(xdrs, &n))
goto error;
result->ao_attributes = zalloc(n * sizeof (adr_attribute_t *));
if (result->ao_attributes == NULL)
goto error;
result->ao_nattributes = n;
for (int i = 0; i < result->ao_nattributes; i++) {
adr_attribute_t *attr = zalloc(sizeof (adr_attribute_t));
if (attr == NULL)
goto error;
result->ao_attributes[i] = attr;
attr->aa_name = NULL;
if (!xdr_string(xdrs, (char **)&attr->aa_name, -1) ||
!xdr_stability_t(xdrs, &attr->aa_stability) ||
!xdr_bool(xdrs, (bool_t *)&attr->aa_readable) ||
!xdr_bool(xdrs, (bool_t *)&attr->aa_writeable) ||
!xdr_bool(xdrs, (bool_t *)&attr->aa_nullable) ||
!xdr_r_typeref_t(xdrs, typeset, &attr->aa_type) ||
!xdr_r_errortype_t(xdrs, typeset, &attr->aa_read_error) ||
!xdr_r_errortype_t(xdrs, typeset, &attr->aa_write_error))
goto error;
}
/*
* Methods
*/
if (!xdr_int(xdrs, &n))
goto error;
result->ao_methods = zalloc(n * sizeof (adr_method_t *));
if (result->ao_methods == NULL)
goto error;
result->ao_nmethods = n;
for (int i = 0; i < result->ao_nmethods; i++) {
adr_method_t *method = zalloc(sizeof (adr_method_t));
if (method == NULL)
goto error;
result->ao_methods[i] = method;
method->am_name = NULL;
if (!xdr_string(xdrs, (char **)&method->am_name, -1) ||
!xdr_stability_t(xdrs, &method->am_stability) ||
!xdr_bool(xdrs, (bool_t *)&method->am_nullable) ||
!xdr_r_typeref_t(xdrs, typeset, &method->am_result) ||
!xdr_r_errortype_t(xdrs, typeset, &method->am_error) ||
!xdr_int(xdrs, &n))
goto error;
method->am_args = zalloc(n * sizeof (adr_parameter_t));
if (method->am_args == NULL)
goto error;
method->am_nargs = n;
for (int j = 0; j < method->am_nargs; j++) {
adr_parameter_t *p = &method->am_args[j];
p->ap_name = NULL;
if (!xdr_string(xdrs, (char **)&p->ap_name, -1) ||
!xdr_bool(xdrs, (bool_t *)&p->ap_nullable) ||
!xdr_r_typeref_t(xdrs, typeset, &p->ap_type))
goto error;
}
}
/*
* Events
*/
if (!xdr_int(xdrs, &n))
goto error;
result->ao_events = zalloc(result->ao_nevents * sizeof (adr_event_t));
if (result->ao_events == NULL)
goto error;
result->ao_nevents = n;
for (int i = 0; i < result->ao_nevents; i++) {
adr_event_t *e = &result->ao_events[i];
e->ae_name = NULL;
if (!xdr_string(xdrs, (char **)&e->ae_name, -1) ||
!xdr_stability_t(xdrs, &e->ae_stability) ||
!xdr_r_typeref_t(xdrs, typeset, &e->ae_type))
goto error;
}
*obj = result;
adr_typeset_free(typeset);
return (TRUE);
error:
adr_typeset_free(typeset);
adr_free(result);
return (FALSE);
}
/*
* Marshal (typed) data.
*/
bool_t
xdr_data_t(XDR *xdrs, adr_data_t *data)
{
int i;
assert(xdrs->x_op != XDR_DECODE);
switch (adr_data_basetype(data)) {
case DT_BOOLEAN:
if (!xdr_bool(xdrs, (int *)&data->d_data.boolean))
return (FALSE);
break;
case DT_INT:
if (!xdr_int(xdrs, &data->d_data.integer))
return (FALSE);
break;
case DT_LONG:
if (!xdr_hyper(xdrs, &data->d_data.longint))
return (FALSE);
break;
case DT_UINT:
if (!xdr_u_int(xdrs, &data->d_data.uinteger))
return (FALSE);
break;
case DT_ULONG:
if (!xdr_u_hyper(xdrs, &data->d_data.ulongint))
return (FALSE);
break;
case DT_FLOAT:
if (!xdr_float(xdrs, &data->d_data.afloat))
return (FALSE);
break;
case DT_DOUBLE:
if (!xdr_double(xdrs, &data->d_data.adouble))
return (FALSE);
break;
case DT_TIME: {
wiretime tv = {
data->d_data.time.seconds,
data->d_data.time.nanos
};
if (!xdr_wiretime(xdrs, &tv))
return (FALSE);
break;
}
case DT_STRING:
if (!xdr_string(xdrs, (char **)&data->d_data.string, -1))
return (FALSE);
break;
case DT_SECRET:
if (!xdr_string(xdrs, (char **)&data->d_data.string, -1))
return (FALSE);
break;
case DT_NAME: {
char *tmp = adr_name_tostr(data->d_data.name);
if (tmp == NULL)
return (FALSE);
if (!xdr_string(xdrs, &tmp, -1)) {
free(tmp);
return (FALSE);
}
free(tmp);
break;
}
case DT_ARRAY:
if (!xdr_u_int(xdrs, &data->d_rsize))
return (FALSE);
for (i = 0; i < data->d_rsize; i++) {
assert(adr_array_get(data, i) != NULL);
if (!xdr_data_t(xdrs, adr_array_get(data, i)))
return (FALSE);
}
break;
case DT_STRUCT:
for (i = 0; i < data->d_size; i++) {
adr_structfield_t *sf =
&data->d_type->t_aux.t_fields[i];
if (sf->sf_nullable) {
if (!xdr_optdata_t(xdrs, data->d_data.array[i]))
return (FALSE);
} else {
if (!xdr_data_t(xdrs, data->d_data.array[i]))
return (FALSE);
}
}
break;
case DT_ENUM: {
/* Communicated as an index */
if (!xdr_int(xdrs, &data->d_data.integer))
return (FALSE);
break;
}
case DT_OPAQUE:
if (!xdr_bytes(xdrs, (char **)&data->d_data.opaque,
(uint_t *)&data->d_size, -1))
return (FALSE);
break;
case DT_UNION: {
adr_unionarm_t *ua = &data->d_type->t_aux.t_union.arms[
data->d_rsize];
/* Write arm index, discriminant value */
if (!xdr_u_int(xdrs, &data->d_rsize) ||
(data->d_rsize == 0 &&
!xdr_data_t(xdrs, data->d_data.aunion.value)))
return (FALSE);
/* Write data, if any */
if (ua->ua_type->t_type != DT_VOID && (ua->ua_nullable ?
!xdr_optdata_t(xdrs, data->d_data.aunion.data) :
!xdr_data_t(xdrs, data->d_data.aunion.data)))
return (FALSE);
break;
}
}
return (TRUE);
}
/*
* Unmarshal data of the specified type
*/
bool_t
xdr_r_data_t(XDR *xdrs, adr_data_t **data, adr_type_t *type, char *buf)
{
assert(xdrs->x_op == XDR_DECODE);
switch (type->t_type) {
case DT_BOOLEAN: {
bool_t b;
if (!xdr_bool(xdrs, &b) ||
(*data = adr_data_new_boolean(b)) == NULL)
return (FALSE);
break;
}
case DT_INT: {
int i;
if (!xdr_int(xdrs, &i) ||
(*data = adr_data_new_integer(i)) == NULL)
return (FALSE);
break;
}
case DT_LONG: {
long long l;
if (!xdr_hyper(xdrs, &l) ||
(*data = adr_data_new_long(l)) == NULL)
return (FALSE);
break;
}
case DT_UINT: {
unsigned int ui;
if (!xdr_u_int(xdrs, &ui) ||
(*data = adr_data_new_uinteger(ui)) == NULL)
return (FALSE);
break;
}
case DT_ULONG: {
unsigned long long ul;
if (!xdr_u_hyper(xdrs, &ul) ||
(*data = adr_data_new_ulong(ul)) == NULL)
return (FALSE);
break;
}
case DT_FLOAT: {
float f;
if (!xdr_float(xdrs, &f) ||
(*data = adr_data_new_float(f)) == NULL)
return (FALSE);
break;
}
case DT_DOUBLE: {
double d;
if (!xdr_double(xdrs, &d) ||
(*data = adr_data_new_double(d)) == NULL)
return (FALSE);
break;
}
case DT_TIME: {
struct wiretime tv;
if (!xdr_wiretime(xdrs, &tv) ||
tv.seconds < 0 || tv.nanos < 0 || tv.nanos >= NANOSEC ||
(*data = adr_data_new_time(tv.seconds, tv.nanos)) == NULL)
return (FALSE);
break;
}
case DT_NAME: {
char *c = NULL;
if (!xdr_string(xdrs, &c, -1))
return (FALSE);
if ((*data = adr_data_new_name(adr_name_fromstr(c))) == NULL) {
free(c);
return (FALSE);
}
free(c);
break;
}
case DT_STRING: {
char *c = NULL;
if (!xdr_string(xdrs, &c, -1) ||
(*data = adr_data_new_string(c, LT_FREE)) == NULL)
return (FALSE);
break;
}
case DT_SECRET: {
char *c = NULL;
if (!xdr_string(xdrs, &c, -1) ||
(*data = adr_data_new_secret(c)) == NULL)
return (FALSE);
break;
}
case DT_ARRAY: {
int i, count;
xdr_bytesrec bytes;
if (!xdr_int(xdrs, &count))
return (FALSE);
/* Check size; only works for xdrmem */
if (!xdr_control(xdrs, XDR_GET_BYTES_AVAIL, &bytes) ||
count > bytes.xc_num_avail / 4) {
if (buf != NULL)
(void) sprintf(buf, "bogus array size: %d",
count);
return (FALSE);
}
if ((*data = adr_data_new_array(type, count)) == NULL)
return (FALSE);
for (i = 0; i < count; i++)
if (!xdr_r_data_t(xdrs, &(*data)->d_data.array[i],
type->t_aux.t_array, buf))
break;
(*data)->d_rsize = i;
if (i < count) {
adr_data_free(*data);
*data = NULL;
return (FALSE);
}
break;
}
case DT_STRUCT: {
int i;
extern void adr_struct_set_internal(adr_data_t *,
adr_structfield_t *, adr_data_t *);
if ((*data = adr_data_new_struct(type)) == NULL)
return (FALSE);
for (i = 0; i < type->t_size; i++) {
adr_structfield_t *sf = &type->t_aux.t_fields[i];
adr_data_t *d;
if (sf->sf_nullable) {
if (!xdr_r_optdata_t(xdrs, &d, sf->sf_type))
break;
} else {
if (!xdr_r_data_t(xdrs, &d, sf->sf_type, buf))
break;
}
adr_struct_set_internal(*data, sf, d);
}
if (i < type->t_size) {
adr_data_free(*data);
*data = NULL;
return (FALSE);
}
break;
}
case DT_OPAQUE: {
char *c = NULL;
xdr_bytesrec bytes;
uint_t len;
if (!xdr_u_int(xdrs, &len))
return (FALSE);
/* Check size; only works for xdrmem */
if (!xdr_control(xdrs, XDR_GET_BYTES_AVAIL, &bytes) ||
len > bytes.xc_num_avail) {
if (buf != NULL)
(void) sprintf(buf, "bogus opaque size: %d",
len);
return (FALSE);
}
if ((c = malloc(len)) == NULL)
return (FALSE);
if (!xdr_opaque(xdrs, c, len)) {
free(c);
return (FALSE);
}
if ((*data = adr_data_new_opaque(c, len, LT_FREE)) == NULL)
return (FALSE);
break;
}
case DT_ENUM: {
int index;
if (!xdr_int(xdrs, &index) || index > type->t_size || index < 0)
return (FALSE);
if (type->t_aux.t_enum[index].ev_name == NULL) {
assert(index == 0);
return (FALSE);
}
*data = adr_data_ref(type->t_aux.t_enum[index].ev_data);
break;
}
case DT_UNION: {
adr_data_t *arm, *value = NULL;
unsigned int index;
/* Read arm index */
if (!xdr_u_int(xdrs, &index) || index > type->t_size)
return (FALSE);
adr_unionarm_t *ua = &type->t_aux.t_union.arms[index];
/* Find/read discriminant value */
if (index > 0) {
arm = adr_data_ref(ua->ua_value);
} else {
if (!xdr_r_data_t(xdrs, &arm, type->t_aux.t_union.type,
buf))
return (FALSE);
/* Ensure that discriminant maps to default/none */
if (ua != adr_union_arm_type(type, arm)) {
adr_data_free(arm);
return (FALSE);
}
}
if (ua->ua_type == NULL)
return (FALSE);
/* Read data, if any */
if (ua->ua_type->t_type != DT_VOID && (ua->ua_nullable ?
!xdr_r_optdata_t(xdrs, &value, ua->ua_type) :
!xdr_r_data_t(xdrs, &value, ua->ua_type, buf))) {
adr_data_free(arm);
return (FALSE);
}
if ((*data =
adr_data_new_union_indexed(type, index, arm, value))
== NULL)
return (FALSE);
break;
}
default:
if (buf != NULL)
(void) sprintf(buf,
"Invalid deserialized data type (%d)",
type->t_type);
return (FALSE);
}
return (TRUE);
}
/*
* Marshal a piece of optional data.
*/
bool_t
xdr_optdata_t(XDR *xdrs, adr_data_t *data)
{
assert(xdrs->x_op != XDR_DECODE);
bool_t b = data != NULL;
if (!xdr_bool(xdrs, &b))
return (FALSE);
if (b)
return (xdr_data_t(xdrs, data));
return (TRUE);
}
/*
* Unmarshal a piece of typed optional data.
*/
bool_t
xdr_r_optdata_t(XDR *xdrs, adr_data_t **data, adr_type_t *type)
{
assert(xdrs->x_op == XDR_DECODE);
bool_t b;
if (!xdr_bool(xdrs, &b))
return (FALSE);
if (b)
return (xdr_r_data_t(xdrs, data, type, NULL));
else
*data = NULL;
return (TRUE);
}
bool_t
xdr_unembed_optdata(adr_data_t **data, adr_type_t *t, uint_t len, char *val)
{
XDR mxdr;
mxdr.x_ops = NULL;
xdrmem_create(&mxdr, val, len, XDR_DECODE);
if (mxdr.x_ops == NULL)
return (FALSE);
if (!xdr_r_optdata_t(&mxdr, data, t)) {
xdr_destroy(&mxdr);
return (FALSE);
}
xdr_destroy(&mxdr);
return (TRUE);
}