From a72d079d372a7e90f4822d6544b18023fa6a39fc Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Tue, 27 Feb 2018 17:51:48 +0100 Subject: Refactor XDR code, add support for fixed-length opaque data. --- xdr.c | 222 +++++++++++++++++++++++++++--------------------------------------- 1 file changed, 90 insertions(+), 132 deletions(-) (limited to 'xdr.c') diff --git a/xdr.c b/xdr.c index e7c81b2..9a958ac 100644 --- a/xdr.c +++ b/xdr.c @@ -2,8 +2,9 @@ * xdr.c * ----- * Serialization/deserialization routines, using XDR (RFC 4506) encoding. + * These functions are not part of the public libhal API. * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2016-2018, NORDUnet A/S All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -38,7 +39,7 @@ #include /* memcpy, memset */ #include "hal.h" -#include "hal_internal.h" +#include "hal_internal.h" /* htonl/ntohl */ #include "xdr_internal.h" /* encode/decode_int. This covers int, unsigned int, enum, and bool types, @@ -61,7 +62,8 @@ hal_error_t hal_xdr_encode_int(uint8_t ** const outbuf, const uint8_t * const li return HAL_OK; } -hal_error_t hal_xdr_decode_int(const uint8_t ** const inbuf, const uint8_t * const limit, uint32_t *value) +/* decode an integer value without advancing the input pointer */ +hal_error_t hal_xdr_decode_int_peek(const uint8_t ** const inbuf, const uint8_t * const limit, uint32_t *value) { /* arg checks */ if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL) @@ -72,44 +74,38 @@ hal_error_t hal_xdr_decode_int(const uint8_t ** const inbuf, const uint8_t * con return HAL_ERROR_XDR_BUFFER_OVERFLOW; *value = ntohl(**(uint32_t **)inbuf); - *inbuf += sizeof(*value); return HAL_OK; } -/* Undo the last decode_int - roll back the input pointer. - */ -hal_error_t hal_xdr_undecode_int(const uint8_t ** const inbuf) +/* decode an integer value the regular way */ +hal_error_t hal_xdr_decode_int(const uint8_t ** const inbuf, const uint8_t * const limit, uint32_t *value) { - if (inbuf == NULL || *inbuf == NULL) - return HAL_ERROR_BAD_ARGUMENTS; + hal_error_t err; - *inbuf -= sizeof(uint32_t); - return HAL_OK; + if ((err = hal_xdr_decode_int_peek(inbuf, limit, value)) == HAL_OK) + *inbuf += sizeof(*value); + + return err; } -/* encode/decode_buffer. This covers variable-length string and opaque types. - * The data is preceded by a 4-byte length word (encoded as above), and padded - * to a multiple of 4 bytes as necessary. +/* encode/decode_fixed_opaque. This covers fixed-length string and opaque types. + * The data is padded to a multiple of 4 bytes as necessary. */ -hal_error_t hal_xdr_encode_buffer(uint8_t **outbuf, const uint8_t * const limit, const uint8_t *value, const uint32_t len) +hal_error_t hal_xdr_encode_fixed_opaque(uint8_t ** const outbuf, const uint8_t * const limit, const uint8_t * const value, const size_t len) { - hal_error_t ret; + if (len == 0) + return HAL_OK; /* arg checks */ - if (outbuf == NULL || *outbuf == NULL || limit == NULL || - (value == NULL && len != 0)) + if (outbuf == NULL || *outbuf == NULL || limit == NULL || value == NULL) return HAL_ERROR_BAD_ARGUMENTS; /* buffer overflow check */ - if (limit - *outbuf < (ptrdiff_t)(((len + 3) & ~3) + sizeof(len))) + if (limit - *outbuf < (ptrdiff_t)((len + 3) & ~3)) return HAL_ERROR_XDR_BUFFER_OVERFLOW; - /* encode length */ - if ((ret = hal_xdr_encode_int(outbuf, limit, len)) != HAL_OK) - return ret; - - /* write the string or opaque data */ + /* write the data */ memcpy(*outbuf, value, len); *outbuf += len; @@ -123,139 +119,101 @@ hal_error_t hal_xdr_encode_buffer(uint8_t **outbuf, const uint8_t * const limit, return HAL_OK; } -/* This version returns a pointer to the data in the input buffer. - * It is used in the rpc server. - */ -hal_error_t hal_xdr_decode_buffer_in_place(const uint8_t **inbuf, const uint8_t * const limit, const uint8_t ** const value, uint32_t * const len) +hal_error_t hal_xdr_decode_fixed_opaque_ptr(const uint8_t ** const inbuf, const uint8_t * const limit, const uint8_t ** const value, const size_t len) { - hal_error_t ret; - uint32_t xdr_len; - const uint8_t *orig_inbuf = *inbuf; - /* arg checks */ - if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL || len == NULL) + if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL) return HAL_ERROR_BAD_ARGUMENTS; - /* decode the length */ - if ((ret = hal_xdr_decode_int(inbuf, limit, &xdr_len)) != HAL_OK) - return ret; - - /* input and output buffer overflow checks vs decoded length */ - - /* decoded length is past the end of the input buffer; - * we're probably out of sync, but nothing we can do now - */ - if (limit - *inbuf < (ptrdiff_t)xdr_len) { - /* undo read of length */ - *inbuf = orig_inbuf; + /* buffer overflow check */ + if (limit - *inbuf < (ptrdiff_t)len) return HAL_ERROR_XDR_BUFFER_OVERFLOW; - } - /* return a pointer to the string or opaque data */ + /* return a pointer to the data */ *value = *inbuf; - *len = xdr_len; /* update the buffer pointer, skipping any padding bytes */ - *inbuf += (xdr_len + 3) & ~3; + *inbuf += ((len + 3) & ~3); return HAL_OK; } -/* This version copies the data to the user-supplied buffer. - * It is used in the rpc client. - */ -hal_error_t hal_xdr_decode_buffer(const uint8_t **inbuf, const uint8_t * const limit, uint8_t * const value, uint32_t * const len) +hal_error_t hal_xdr_decode_fixed_opaque(const uint8_t ** const inbuf, const uint8_t * const limit, uint8_t * const value, const size_t len) { - if (inbuf == NULL || value == NULL || len == NULL) - return HAL_ERROR_BAD_ARGUMENTS; + const uint8_t *p; + hal_error_t err; - hal_error_t ret; - const uint8_t *vptr; - const uint8_t *orig_inbuf = *inbuf; - uint32_t xdr_len; + /* get and advance the input data pointer */ + if ((err = hal_xdr_decode_fixed_opaque_ptr(inbuf, limit, &p, len)) == HAL_OK) + /* read the data */ + memcpy(value, p, len); + + return err; +} - if ((ret = hal_xdr_decode_buffer_in_place(inbuf, limit, &vptr, &xdr_len)) != HAL_OK) - return ret; +/* encode/decode_variable_opaque. This covers variable-length string and opaque types. + * The data is preceded by a 4-byte length word (encoded as above), and padded + * to a multiple of 4 bytes as necessary. + */ - if (*len < xdr_len) { - /* user buffer is too small, undo read of length */ - *inbuf = orig_inbuf; - ret = HAL_ERROR_XDR_BUFFER_OVERFLOW; - } - else { - memcpy(value, vptr, xdr_len); - } +hal_error_t hal_xdr_encode_variable_opaque(uint8_t ** const outbuf, const uint8_t * const limit, const uint8_t * const value, const size_t len) +{ + hal_error_t err; - *len = xdr_len; + /* encode length */ + if ((err = hal_xdr_encode_int(outbuf, limit, (uint32_t)len)) == HAL_OK) { + /* encode data */ + if ((err = hal_xdr_encode_fixed_opaque(outbuf, limit, value, len)) != HAL_OK) + /* undo write of length */ + *outbuf -= 4; + } - return ret; + return err; } -/* ---------------------------------------------------------------- */ - -#ifdef TEST -static void hexdump(uint8_t *buf, uint32_t len) +/* This version returns a pointer to the data in the input buffer. + * It is used in the rpc server. + */ +hal_error_t hal_xdr_decode_variable_opaque_ptr(const uint8_t ** const inbuf, const uint8_t * const limit, const uint8_t ** const value, size_t * const len) { - for (uint32_t i = 0; i < len; ++i) - printf("%02x%c", buf[i], ((i & 0x07) == 0x07) ? '\n' : ' '); - if ((len & 0x07) != 0) - printf("\n"); + hal_error_t err; + uint32_t xdr_len; + + /* arg checks */ + if (len == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + /* read length */ + if ((err = hal_xdr_decode_int(inbuf, limit, &xdr_len)) == HAL_OK) { + /* get the data pointer */ + if ((err = hal_xdr_decode_fixed_opaque_ptr(inbuf, limit, value, xdr_len)) == HAL_OK) + *len = xdr_len; + else + /* undo read of length */ + *inbuf -= 4; + } + + return err; } -int main(int argc, char *argv[]) +/* This version copies the data to the user-supplied buffer. + * It is used in the rpc client. + */ +hal_error_t hal_xdr_decode_variable_opaque(const uint8_t ** const inbuf, const uint8_t * const limit, uint8_t * const value, size_t * const len) { - uint32_t i; - uint8_t buf[64] = {0}; - uint8_t *bufptr = buf, *readptr; - uint8_t *limit = buf + sizeof(buf); - hal_error_t ret; - uint8_t alphabet[] = "abcdefghijklmnopqrstuvwxyz"; - uint8_t readbuf[64] = {0}; - - printf("hal_xdr_encode_int: work to failure\n"); - for (i = 1; i < 100; ++i) { - if ((ret = hal_xdr_encode_int(&bufptr, limit, i)) != HAL_OK) { - printf("%d: %s\n", i, hal_error_string(ret)); - break; - } - } - hexdump(buf, ((uint8_t *)bufptr - buf)); - - printf("\nhal_xdr_decode_int:\n"); - readptr = buf; - while (readptr < bufptr) { - if ((ret = hal_xdr_decode_int(&readptr, limit, &i)) != HAL_OK) { - printf("%s\n", hal_error_string(ret)); - break; - } - printf("%u ", i); - } - printf("\n"); - - printf("\nhal_xdr_encode_buffer: work to failure\n"); - memset(buf, 0, sizeof(buf)); - bufptr = buf; - for (i = 1; i < 10; ++i) { - if ((ret = hal_xdr_encode_buffer(&bufptr, limit, alphabet, i)) != HAL_OK) { - printf("%d: %s\n", i, hal_error_string(ret)); - break; - } - } - hexdump(buf, ((uint8_t *)bufptr - buf)); - - printf("\nhal_xdr_decode_buffer:\n"); - readptr = buf; - i = sizeof(readbuf); - while (readptr < bufptr) { - if ((ret = hal_xdr_decode_buffer(&readptr, limit, readbuf, &i)) != HAL_OK) { - printf("%s\n", hal_error_string(ret)); - break; - } - printf("%u: ", i); for (int j = 0; j < i; ++j) putchar(readbuf[j]); putchar('\n'); - i = sizeof(readbuf); - memset(readbuf, 0, sizeof(readbuf)); + hal_error_t err; + size_t xdr_len; + const uint8_t *p; + + /* read data pointer and length */ + if ((err = hal_xdr_decode_variable_opaque_ptr(inbuf, limit, &p, &xdr_len)) == HAL_OK) { + /* user buffer overflow check */ + if (*len < xdr_len) + return HAL_ERROR_XDR_BUFFER_OVERFLOW; + /* read the data */ + memcpy(value, p, xdr_len); + *len = xdr_len; } - return 0; + return err; } -#endif -- cgit v1.2.3