/* * 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 #include #include /* ptrdiff_t */ #include /* memcpy, memset */ #include "hal.h" #include "hal_internal.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 < (ptrdiff_t)sizeof(value)) return HAL_ERROR_XDR_BUFFER_OVERFLOW; **(uint32_t **)outbuf = htonl(value); *outbuf += sizeof(value); return HAL_OK; } hal_error_t hal_xdr_decode_int(const uint8_t ** const 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 < (ptrdiff_t)sizeof(*value)) 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) { if (inbuf == NULL || *inbuf == NULL) return HAL_ERROR_BAD_ARGUMENTS; *inbuf -= sizeof(uint32_t); 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 < (ptrdiff_t)(((len + 3) & ~3) + sizeof(len))) 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 */ 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(const uint8_t **inbuf, const uint8_t * const limit, const uint8_t ** const value, uint32_t * const 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) 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; return HAL_ERROR_XDR_BUFFER_OVERFLOW; } /* 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(const uint8_t **inbuf, const uint8_t * const limit, uint8_t * const value, uint32_t * const len) { if (inbuf == NULL || value == NULL || len == NULL) return HAL_ERROR_BAD_ARGUMENTS; hal_error_t ret; const uint8_t *vptr; const uint8_t *orig_inbuf = *inbuf; uint32_t xdr_len; if ((ret = hal_xdr_decode_buffer_in_place(inbuf, limit, &vptr, &xdr_len)) != HAL_OK) return ret; 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); } *len = xdr_len; return ret; } /* ---------------------------------------------------------------- */ #ifdef TEST static void hexdump(uint8_t *buf, uint32_t len) { for (uint32_t i = 0; i < len; ++i) printf("%02x%c", buf[i], ((i & 0x07) == 0x07) ? '\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