diff options
Diffstat (limited to 'src/supplemental/websocket')
| -rw-r--r-- | src/supplemental/websocket/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/supplemental/websocket/base64.c | 145 | ||||
| -rw-r--r-- | src/supplemental/websocket/base64.h | 42 | ||||
| -rw-r--r-- | src/supplemental/websocket/base64_test.c | 108 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.c | 3 |
5 files changed, 298 insertions, 3 deletions
diff --git a/src/supplemental/websocket/CMakeLists.txt b/src/supplemental/websocket/CMakeLists.txt index 5d2f4b0c..37273c27 100644 --- a/src/supplemental/websocket/CMakeLists.txt +++ b/src/supplemental/websocket/CMakeLists.txt @@ -9,8 +9,9 @@ # if (NNG_SUPP_WEBSOCKET) - nng_sources(sha1.c sha1.h websocket.c websocket.h) + nng_sources(base64.c base64.h sha1.c sha1.h websocket.c websocket.h) nng_test(sha1_test) + nng_test(base64_test) else () nng_sources(stub.c) endif () diff --git a/src/supplemental/websocket/base64.c b/src/supplemental/websocket/base64.c new file mode 100644 index 00000000..3c19ad8a --- /dev/null +++ b/src/supplemental/websocket/base64.c @@ -0,0 +1,145 @@ +// +// Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#include "base64.h" + +#include <ctype.h> + +size_t +nni_base64_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) +{ + unsigned ii; + unsigned rem; + uint32_t v; + uint8_t ch; + size_t io; + + // Unrolled lookup of ASCII code points. + // 0xFF represents a non-base64 valid character. + const uint8_t decode[256] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, + 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, + 0x3D, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + for (io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) { + if (isspace(in[ii])) { + continue; + } + + if (in[ii] == '=') { + break; + } + + ch = decode[(int) (in[ii])]; + + // Discard invalid characters as per RFC 2045. + if (ch == 0xFF) { + break; + } + + v = (v << 6u) | ch; + rem += 6; + + if (rem >= 8) { + rem -= 8; + if (io >= out_len) + return (-1); + out[io++] = (v >> rem) & 255u; + } + } + if (rem >= 8) { + rem -= 8; + if (io >= out_len) + return (-1); + out[io++] = (v >> rem) & 255u; + } + return (io); +} + +size_t +nni_base64_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len) +{ + unsigned ii; + unsigned rem; + uint32_t v; + size_t io; + + const uint8_t encode[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + for (io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) { + uint8_t ch = in[ii]; + v = (v << 8u) | ch; + rem += 8; + while (rem >= 6) { + rem -= 6; + if (io >= out_len) + return (-1); + out[io++] = encode[(v >> rem) & 63u]; + } + } + + if (rem) { + v <<= (6 - rem); + if (io >= out_len) + return (-1); + out[io++] = encode[v & 63u]; + } + + // Pad to a multiple of 3. + while (io & 3u) { + if (io >= out_len) + return (-1); + out[io++] = '='; + } + + if (io >= out_len) + return (-1); + + out[io] = '\0'; + + return (io); +} diff --git a/src/supplemental/websocket/base64.h b/src/supplemental/websocket/base64.h new file mode 100644 index 00000000..97ca8968 --- /dev/null +++ b/src/supplemental/websocket/base64.h @@ -0,0 +1,42 @@ +// +// Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef NNI_BASE64_INCLUDED +#define NNI_BASE64_INCLUDED + +#include <stddef.h> +#include <stdint.h> + +// Based on base64.c (Public Domain) by Jon Mayo. +// Base64 is defined in RFC 2045, section 6.8. + +// This function encodes an arbitrary byte array into base64 +// null-terminated string. It returns the number of characters +// emitted. +size_t nni_base64_encode(const uint8_t *, size_t, char *, size_t); + +// This function decodes a base64 string into supplied buffer. +// It returns the number of bytes emitted. +size_t nni_base64_decode(const char *, size_t, uint8_t *, size_t); + +#endif diff --git a/src/supplemental/websocket/base64_test.c b/src/supplemental/websocket/base64_test.c new file mode 100644 index 00000000..ae8df8e6 --- /dev/null +++ b/src/supplemental/websocket/base64_test.c @@ -0,0 +1,108 @@ +// +// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include <string.h> + +#include <nng/nng.h> + +#include "base64.h" + +#include <acutest.h> + +typedef struct { + char *decoded; + char *encoded; +} test_case; + +static test_case cases[] = { + { "", "" }, + { "f", "Zg==" }, + { "fo", "Zm8=" }, + { "foo", "Zm9v" }, + { "foob", "Zm9vYg==" }, + { "fooba", "Zm9vYmE=" }, + { "foobar", "Zm9vYmFy" }, + { NULL, NULL }, +}; + +void +test_encode(void) +{ + int i; + void *dec; + + for (i = 0; (dec = cases[i].decoded) != NULL; i++) { + char buf[1024]; + char name[8]; + int rv; + + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + rv = (int) nni_base64_encode(dec, strlen(dec), buf, 1024); + TEST_CHECK(rv >= 0); + TEST_CHECK(rv == (int) strlen(cases[i].encoded)); + buf[rv] = 0; + TEST_CHECK(strcmp(buf, cases[i].encoded) == 0); + } +} + +void +test_decode(void) +{ + int i; + void *enc; + + for (i = 0; (enc = cases[i].encoded) != NULL; i++) { + char buf[1024]; + char name[8]; + size_t sz; + + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + + sz = nni_base64_decode(enc, strlen(enc), (void *) buf, 1024); + TEST_CHECK(sz != (size_t) -1); + TEST_CHECK(sz == strlen(cases[i].decoded)); + buf[sz] = 0; + TEST_CHECK(strcmp(buf, cases[i].decoded) == 0); + } +} + +void +test_overflow(void) +{ + char tmp[1024]; + for (int i = 1; cases[i].decoded != NULL; i++) { + void *enc = cases[i].encoded; + void *dec = cases[i].decoded; + void *buf = tmp; + + char name[8]; + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + + TEST_CHECK(nni_base64_encode(dec, strlen(dec), buf, + strlen(enc) - 1) == (size_t) -1); + TEST_CHECK(nni_base64_encode(dec, strlen(dec), buf, 0) == + (size_t) -1); + + TEST_CHECK(nni_base64_decode(enc, strlen(enc), buf, + strlen(dec) - 1) == (size_t) -1); + TEST_CHECK(nni_base64_encode(enc, strlen(enc), buf, 0) == + (size_t) -1); + } +} + +TEST_LIST = { + { "encode", test_encode }, + { "decode", test_decode }, + { "overflow", test_overflow }, + { NULL, NULL }, +}; diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 9cb1a677..84d3fd72 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -16,8 +16,7 @@ #include "core/nng_impl.h" #include "supplemental/http/http_api.h" -#include "supplemental/base64/base64.h" - +#include "base64.h" #include "sha1.h" #include "websocket.h" |
