Skip to content

Commit

Permalink
Add value_data_cast and use it to perform casts for hex xlat
Browse files Browse the repository at this point in the history
Eventual plan is to use it everywhere we need to perform casts
  • Loading branch information
arr2036 committed Oct 30, 2014
1 parent 4c3c072 commit 9adc030
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 7 deletions.
5 changes: 5 additions & 0 deletions src/include/libradius.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *out,
PW_TYPE *type, DICT_ATTR const *enumv,
char const *value, ssize_t inlen);

ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
PW_TYPE dst_type, DICT_ATTR const *dst_enumv,
PW_TYPE src_type, DICT_ATTR const *src_enumv,
value_data_t const *src, size_t src_len);

/*
* Error functions.
*/
Expand Down
223 changes: 223 additions & 0 deletions src/lib/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -916,3 +916,226 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *out,
return ret;
}

/** Performs byte order reversal for types that need it
*
*/
static void value_data_hton(value_data_t *dst, PW_TYPE type, void const *src, size_t src_len)
{
/* 8 byte integers */
switch (type) {
case PW_TYPE_INTEGER64:
dst->integer64 = htonll(*(uint64_t *)src);
break;

/* 4 byte integers */
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
case PW_TYPE_SIGNED:
dst->integer = htonl(*(uint32_t *)src);
break;

/* 2 byte integers */
case PW_TYPE_SHORT:
dst->ushort = htons(*(uint16_t *)src);
break;

case PW_TYPE_OCTETS:
case PW_TYPE_STRING:
fr_assert(0);

default:
memcpy(dst, src, src_len);
}
}

/** Convert one type of value_data_t to another
*
* @note This should be the canonical function used to convert between data types.
*
* @param ctx to allocate buffers in (usually the same as dst)
* @param dst Where to write result of casting.
* @param dst_type to cast to.
* @param dst_enumv Enumerated values used to converts strings to integers.
* @param src_type to cast from.
* @param src_enumv Enumerated values used to convert integers to strings.
* @param src Input data.
* @param src_len Input data len.
* @return the length of data in the dst.
*/
ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
PW_TYPE dst_type, DICT_ATTR const *dst_enumv,
PW_TYPE src_type, DICT_ATTR const *src_enumv,
value_data_t const *src, size_t src_len)
{
if (!fr_assert(dst_type != src_type)) return -1;

/*
* Converts the src data to octets with no processing.
*/
if (dst_type == PW_TYPE_OCTETS) {
if (src_type == PW_TYPE_STRING) {
dst->octets = talloc_memdup(ctx, src->strvalue, src_len);
} else {
value_data_hton(dst, src_type, src, src_len);
dst->octets = talloc_memdup(ctx, dst, src_len);
}
talloc_set_type(dst->octets, uint8_t);
return talloc_array_length(dst->strvalue);
}

/*
* Serialise a value_data_t
*/
if (dst_type == PW_TYPE_STRING) {
dst->strvalue = vp_data_aprints_value(ctx, src_type, src_enumv, src, src_len, '\0');
return talloc_array_length(dst->strvalue) - 1;
}

/*
* Deserialise a value_data_t
*/
if (src_type == PW_TYPE_STRING) {
return value_data_from_str(ctx, dst, &dst_type, dst_enumv, src->strvalue, src_len);
}

if ((src_type == PW_TYPE_IFID) &&
(dst_type == PW_TYPE_INTEGER64)) {
memcpy(&dst->integer64, &src->ifid, sizeof(src->ifid));
dst->integer64 = htonll(dst->integer64);
fixed_length:
return dict_attr_sizes[dst_type][0];
}

if ((src_type == PW_TYPE_INTEGER64) &&
(dst_type == PW_TYPE_ETHERNET)) {
uint8_t array[8];
uint64_t i;

i = htonll(src->integer64);
memcpy(array, &i, 8);

/*
* For OUIs in the DB.
*/
if ((array[0] != 0) || (array[1] != 0)) return -1;

memcpy(&dst->ether, &array[2], 6);
goto fixed_length;
}

/*
* For integers, we allow the casting of a SMALL type to
* a larger type, but not vice-versa.
*/
if (dst_type == PW_TYPE_INTEGER64) {
switch (src_type) {
case PW_TYPE_BYTE:
dst->integer64 = src->byte;
break;

case PW_TYPE_SHORT:
dst->integer64 = src->ushort;
break;

case PW_TYPE_INTEGER:
dst->integer64 = src->integer;
break;

case PW_TYPE_OCTETS:
goto do_octets;

default:
invalid_cast:
fr_strerror_printf("Invalid cast from %s to %s",
fr_int2str(dict_attr_types, src_type, "<INVALID>"),
fr_int2str(dict_attr_types, dst_type, "<INVALID>"));
return -1;

}
goto fixed_length;
}

/*
* We can cast LONG integers to SHORTER ones, so long
* as the long one is on the LHS.
*/
if (dst_type == PW_TYPE_INTEGER) {
switch (src_type) {
case PW_TYPE_BYTE:
dst->integer = src->byte;
break;

case PW_TYPE_SHORT:
dst->integer = src->ushort;
break;

case PW_TYPE_OCTETS:
goto do_octets;

default:
goto invalid_cast;
}
goto fixed_length;
}

if (dst_type == PW_TYPE_SHORT) {
switch (src_type) {
case PW_TYPE_BYTE:
dst->ushort = src->byte;
break;

case PW_TYPE_OCTETS:
goto do_octets;

default:
goto invalid_cast;
}
goto fixed_length;
}

/*
* The attribute we've found has to have a size which is
* compatible with the type of the destination cast.
*/
if ((src_len < dict_attr_sizes[dst_type][0]) ||
(src_len > dict_attr_sizes[dst_type][1])) {
char const *src_type_name;

src_type_name = fr_int2str(dict_attr_types, src_type, "<INVALID>");
fr_strerror_printf("Invalid cast from %s to %s. Length should be between %zu and %zu but is %zu",
src_type_name,
fr_int2str(dict_attr_types, dst_type, "<INVALID>"),
dict_attr_sizes[dst_type][0], dict_attr_sizes[dst_type][1],
src_len);
return -1;
}

if (src_type == PW_TYPE_OCTETS) {
do_octets:
value_data_hton(dst, dst_type, src->octets, src_len);
return src_len;
}

/*
* Convert host order to network byte order.
*/
if ((dst_type == PW_TYPE_IPV4_ADDR) &&
((src_type == PW_TYPE_INTEGER) ||
(src_type == PW_TYPE_DATE) ||
(src_type == PW_TYPE_SIGNED))) {
dst->ipaddr.s_addr = htonl(src->integer);

} else if ((src_type == PW_TYPE_IPV4_ADDR) &&
((dst_type == PW_TYPE_INTEGER) ||
(dst_type == PW_TYPE_DATE) ||
(dst_type == PW_TYPE_SIGNED))) {
dst->integer = htonl(src->ipaddr.s_addr);

} else { /* they're of the same byte order */
memcpy(&dst, &src, src_len);
}

return src_len;
}


35 changes: 28 additions & 7 deletions src/main/xlat.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,31 +217,52 @@ static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
uint8_t const *p;
ssize_t ret;
size_t len;
value_data_t dst;
uint8_t const *buff = NULL;

while (isspace((int) *fmt)) fmt++;

if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
error:
*out = '\0';
return -1;
}

ret = rad_vp2data(&p, vp);
if (ret < 0) {
return ret;
/*
* The easy case.
*/
if (vp->da->type == PW_TYPE_OCTETS) {
p = vp->vp_octets;
len = vp->length;
/*
* Cast the value_data_t of the VP to an octets string and
* print that as hex.
*/
} else {
ret = value_data_cast(request, &dst, PW_TYPE_OCTETS, NULL, vp->da->type,
NULL, &vp->data, vp->length);
if (ret < 0) {
REDEBUG("%s", fr_strerror());
goto error;
}
len = (size_t) ret;
p = buff = dst.octets;
}
len = (size_t) ret;

rad_assert(p);

/*
* Don't truncate the data.
*/
if ((ret < 0 ) || (outlen < (len * 2))) {
*out = 0;
return 0;
if (outlen < (len * 2)) {
rad_const_free(buff);
goto error;
}

for (i = 0; i < len; i++) {
snprintf(out + 2*i, 3, "%02x", p[i]);
}
rad_const_free(buff);

return len * 2;
}
Expand Down

0 comments on commit 9adc030

Please sign in to comment.