aboutsummaryrefslogtreecommitdiff
path: root/xdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdr.c')
-rw-r--r--xdr.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/xdr.c b/xdr.c
new file mode 100644
index 0000000..42f0793
--- /dev/null
+++ b/xdr.c
@@ -0,0 +1,267 @@
+/*
+ * xdr.c
+ * -----
+ * Serialization/deserialization routines, using XDR (RFC 4506) encoding.
+ *
+ * Copyright (c) 2016, 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
+ * 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 the NORDUnet 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* memcpy, memset */
+
+#ifndef STM32F4XX
+#include <arpa/inet.h> /* htonl/ntohl */
+#else
+/* htonl is not available in arm-none-eabi headers or libc */
+#ifdef __ARMEL__ /* little endian */
+static inline uint32_t htonl(uint32_t w)
+{
+ return
+ ((w & 0x000000ff) << 24) +
+ ((w & 0x0000ff00) << 8) +
+ ((w & 0x00ff0000) >> 8) +
+ ((w & 0xff000000) >> 24);
+}
+#else
+#define htonl(x) (x)
+#endif
+#define ntohl htonl
+#endif
+
+#include "hal.h"
+#include "xdr_internal.h"
+
+/* encode/decode_int. This covers int, unsigned int, enum, and bool types,
+ * which are all encoded as 32-bit big-endian fields. Signed integers are
+ * defined to use two's complement, but that's universal these days, yes?
+ */
+
+hal_error_t hal_xdr_encode_int(uint8_t ** const outbuf, const uint8_t * const limit, const uint32_t value)
+{
+ /* arg checks */
+ if (outbuf == NULL || *outbuf == NULL || limit == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ /* buffer overflow check */
+ if (limit - *outbuf < sizeof(value))
+ return HAL_ERROR_IO_BAD_COUNT;
+
+ **(uint32_t **)outbuf = htonl(value);
+ *outbuf += sizeof(value);
+ return HAL_OK;
+}
+
+hal_error_t hal_xdr_decode_int(uint8_t **inbuf, const uint8_t * const limit, uint32_t *value)
+{
+ /* arg checks */
+ if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ /* buffer overflow check */
+ if (limit - *inbuf < sizeof(*value))
+ return HAL_ERROR_IO_BAD_COUNT;
+
+ *value = ntohl(**(uint32_t **)inbuf);
+ *inbuf += sizeof(*value);
+ return HAL_OK;
+}
+
+/* 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.
+ */
+
+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 ret;
+
+ /* arg checks */
+ if (outbuf == NULL || *outbuf == NULL || limit == NULL ||
+ (value == NULL && len != 0))
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ /* buffer overflow check */
+ if ((limit - *outbuf) < (((len + 3) & ~3) + sizeof(len)))
+ return HAL_ERROR_IO_BAD_COUNT;
+
+ /* encode length */
+ if ((ret = hal_xdr_encode_int(outbuf, limit, len)) != HAL_OK)
+ return ret;
+
+ /* write the string or opaque data */
+ memcpy(*outbuf, value, len);
+ *outbuf += len;
+
+ /* pad if necessary */
+ if (len & 3) {
+ size_t n = 4 - (len & 3);
+ memset(*outbuf, 0, n);
+ *outbuf += n;
+ }
+
+ 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(uint8_t **inbuf, const uint8_t * const limit, uint8_t ** const value, uint32_t * const len)
+{
+ hal_error_t ret;
+ uint32_t xdr_len;
+ uint8_t *orig_inbuf = *inbuf;
+
+ /* arg checks */
+ if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL || len == 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 < xdr_len) {
+ /* undo read of length */
+ *inbuf = orig_inbuf;
+ return HAL_ERROR_IO_BAD_COUNT;
+ }
+
+ /* return a pointer to the string or opaque data */
+ *value = *inbuf;
+ *len = xdr_len;
+
+ /* update the buffer pointer, skipping any padding bytes */
+ *inbuf += (xdr_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(uint8_t **inbuf, const uint8_t * const limit, uint8_t * const value, uint32_t * const len)
+{
+ hal_error_t ret;
+ uint8_t *vptr;
+ uint8_t *orig_inbuf = *inbuf;
+ uint32_t xdr_len;
+
+ if ((ret = hal_xdr_decode_buffer_in_place(inbuf, limit, &vptr, &xdr_len)) == HAL_OK) {
+ if (*len < xdr_len) {
+ /* user buffer is too small, update *len */
+ *len = xdr_len;
+ /* undo read of length */
+ *inbuf = orig_inbuf;
+ return HAL_ERROR_IO_BAD_COUNT;
+ }
+
+ memcpy(value, vptr, *len);
+ }
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+
+#ifdef TEST
+void hexdump(uint8_t *buf, uint32_t len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ uint8_t c = buf[i];
+ printf("%02x ", c);
+ if ((i & 0x07) == 0x07)
+ printf("\n");
+ }
+ if ((len & 0x07) != 0)
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ 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));
+ }
+
+ return 0;
+}
+#endif