diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2018-02-07 01:05:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-07 01:05:04 +0100 |
commit | b0a73077cb8395ad39124dd08490195adbfb90c9 (patch) | |
tree | 974b5c9302fd98f4a4a69dec9e57ad08297cfd12 /thirdparty/lws/misc | |
parent | 3cb1b2227ff211cd06e4929b2b1e8d775a0937fd (diff) | |
parent | f3abd4a0652152956d1ceaed491694aaacd9ccf3 (diff) |
Merge pull request #14888 from Faless/websocket
Websocket module
Diffstat (limited to 'thirdparty/lws/misc')
-rw-r--r-- | thirdparty/lws/misc/base64-decode.c | 206 | ||||
-rw-r--r-- | thirdparty/lws/misc/getifaddrs.c | 270 | ||||
-rw-r--r-- | thirdparty/lws/misc/getifaddrs.h | 80 | ||||
-rw-r--r-- | thirdparty/lws/misc/lejp.c | 709 | ||||
-rw-r--r-- | thirdparty/lws/misc/lejp.h | 232 | ||||
-rw-r--r-- | thirdparty/lws/misc/sha-1.c | 300 |
6 files changed, 1797 insertions, 0 deletions
diff --git a/thirdparty/lws/misc/base64-decode.c b/thirdparty/lws/misc/base64-decode.c new file mode 100644 index 0000000000..c8f11d21b8 --- /dev/null +++ b/thirdparty/lws/misc/base64-decode.c @@ -0,0 +1,206 @@ +/* + * This code originally came from here + * + * http://base64.sourceforge.net/b64.c + * + * with the following license: + * + * LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. + * + * 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. + * + * VERSION HISTORY: + * Bob Trower 08/04/01 -- Create Version 0.00.00B + * + * I cleaned it up quite a bit to match the (linux kernel) style of the rest + * of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws + * since he explicitly allows sublicensing, but I give the URL above so you can + * get the original with Bob's super-liberal terms directly if you prefer. + */ + +#include <stdio.h> +#include <string.h> +#include "private-libwebsockets.h" + +static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW" + "$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +LWS_VISIBLE int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size) +{ + unsigned char triple[3]; + int i; + int len; + int line = 0; + int done = 0; + + while (in_len) { + len = 0; + for (i = 0; i < 3; i++) { + if (in_len) { + triple[i] = *in++; + len++; + in_len--; + } else + triple[i] = 0; + } + + if (done + 4 >= out_size) + return -1; + + *out++ = encode[triple[0] >> 2]; + *out++ = encode[((triple[0] & 0x03) << 4) | + ((triple[1] & 0xf0) >> 4)]; + *out++ = (len > 1 ? encode[((triple[1] & 0x0f) << 2) | + ((triple[2] & 0xc0) >> 6)] : '='); + *out++ = (len > 2 ? encode[triple[2] & 0x3f] : '='); + + done += 4; + line += 4; + } + + if (done + 1 >= out_size) + return -1; + + *out++ = '\0'; + + return done; +} + +/* + * returns length of decoded string in out, or -1 if out was too small + * according to out_size + */ + +LWS_VISIBLE int +lws_b64_decode_string(const char *in, char *out, int out_size) +{ + int len, i, c = 0, done = 0; + unsigned char v, quad[4]; + + while (*in) { + + len = 0; + for (i = 0; i < 4 && *in; i++) { + + v = 0; + c = 0; + while (*in && !v) { + c = v = *in++; + v = (v < 43 || v > 122) ? 0 : decode[v - 43]; + if (v) + v = (v == '$') ? 0 : v - 61; + } + if (c) { + len++; + if (v) + quad[i] = v - 1; + } else + quad[i] = 0; + } + + if (out_size < (done + len - 1)) + /* out buffer is too small */ + return -1; + + /* + * "The '==' sequence indicates that the last group contained + * only one byte, and '=' indicates that it contained two + * bytes." (wikipedia) + */ + + if (!*in && c == '=') + len--; + + if (len >= 2) + *out++ = quad[0] << 2 | quad[1] >> 4; + if (len >= 3) + *out++ = quad[1] << 4 | quad[2] >> 2; + if (len >= 4) + *out++ = ((quad[2] << 6) & 0xc0) | quad[3]; + + done += len - 1; + } + + if (done + 1 >= out_size) + return -1; + + *out = '\0'; + + return done; +} + +#if 0 +int +lws_b64_selftest(void) +{ + char buf[64]; + unsigned int n, r = 0; + unsigned int test; + /* examples from https://en.wikipedia.org/wiki/Base64 */ + static const char * const plaintext[] = { + "any carnal pleasure.", + "any carnal pleasure", + "any carnal pleasur", + "any carnal pleasu", + "any carnal pleas", + "Admin:kloikloi" + }; + static const char * const coded[] = { + "YW55IGNhcm5hbCBwbGVhc3VyZS4=", + "YW55IGNhcm5hbCBwbGVhc3VyZQ==", + "YW55IGNhcm5hbCBwbGVhc3Vy", + "YW55IGNhcm5hbCBwbGVhc3U=", + "YW55IGNhcm5hbCBwbGVhcw==", + "QWRtaW46a2xvaWtsb2k=" + }; + + for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) { + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_encode_string(plaintext[test], + strlen(plaintext[test]), buf, sizeof buf); + if (n != strlen(coded[test]) || strcmp(buf, coded[test])) { + lwsl_err("Failed lws_b64 encode selftest " + "%d result '%s' %d\n", test, buf, n); + r = -1; + } + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_decode_string(coded[test], buf, sizeof buf); + if (n != strlen(plaintext[test]) || + strcmp(buf, plaintext[test])) { + lwsl_err("Failed lws_b64 decode selftest " + "%d result '%s' / '%s', %d / %d\n", + test, buf, plaintext[test], n, strlen(plaintext[test])); + r = -1; + } + } + + lwsl_notice("Base 64 selftests passed\n"); + + return r; +} +#endif diff --git a/thirdparty/lws/misc/getifaddrs.c b/thirdparty/lws/misc/getifaddrs.c new file mode 100644 index 0000000000..4f42ab4595 --- /dev/null +++ b/thirdparty/lws/misc/getifaddrs.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2000 - 2001 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * originally downloaded from + * + * http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef LWS_HAVE_NETINET_IN6_VAR_H +#include <netinet/in6_var.h> +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#include "getifaddrs.h" + +static int +getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags, + size_t ifreq_sz) +{ + int ret; + int fd; + size_t buf_size; + char *buf; + struct ifconf ifconf; + char *p; + size_t sz; + struct sockaddr sa_zero; + struct ifreq *ifr; + struct ifaddrs *start, **end = &start; + + buf = NULL; + + memset(&sa_zero, 0, sizeof(sa_zero)); + fd = socket(af, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + buf_size = 8192; + for (;;) { + buf = lws_zalloc(buf_size, "getifaddrs2"); + if (buf == NULL) { + ret = ENOMEM; + goto error_out; + } + ifconf.ifc_len = buf_size; + ifconf.ifc_buf = buf; + + /* + * Solaris returns EINVAL when the buffer is too small. + */ + if (ioctl(fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { + ret = errno; + goto error_out; + } + /* + * Can the difference between a full and a overfull buf + * be determined? + */ + + if (ifconf.ifc_len < (int)buf_size) + break; + lws_free(buf); + buf_size *= 2; + } + + for (p = ifconf.ifc_buf; p < ifconf.ifc_buf + ifconf.ifc_len; p += sz) { + struct ifreq ifreq; + struct sockaddr *sa; + size_t salen; + + ifr = (struct ifreq *)p; + sa = &ifr->ifr_addr; + + sz = ifreq_sz; + salen = sizeof(struct sockaddr); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + salen = sa->sa_len; + sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); +#endif +#ifdef SA_LEN + salen = SA_LEN(sa); + sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); +#endif + memset(&ifreq, 0, sizeof(ifreq)); + memcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + + if (ioctl(fd, siocgifflags, &ifreq) < 0) { + ret = errno; + goto error_out; + } + + *end = lws_malloc(sizeof(**end), "getifaddrs"); + + (*end)->ifa_next = NULL; + (*end)->ifa_name = strdup(ifr->ifr_name); + (*end)->ifa_flags = ifreq.ifr_flags; + (*end)->ifa_addr = lws_malloc(salen, "getifaddrs"); + memcpy((*end)->ifa_addr, sa, salen); + (*end)->ifa_netmask = NULL; + +#if 0 + /* fix these when we actually need them */ + if (ifreq.ifr_flags & IFF_BROADCAST) { + (*end)->ifa_broadaddr = + lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs"); + memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, + sizeof(ifr->ifr_broadaddr)); + } else if (ifreq.ifr_flags & IFF_POINTOPOINT) { + (*end)->ifa_dstaddr = + lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs"); + memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, + sizeof(ifr->ifr_dstaddr)); + } else + (*end)->ifa_dstaddr = NULL; +#else + (*end)->ifa_dstaddr = NULL; +#endif + (*end)->ifa_data = NULL; + + end = &(*end)->ifa_next; + + } + *ifap = start; + close(fd); + lws_free(buf); + return 0; + +error_out: + close(fd); + lws_free(buf); + errno = ret; + + return -1; +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + int ret = -1; + errno = ENXIO; +#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, + sizeof(struct in6_ifreq)); +#endif +#if defined(LWS_HAVE_IPV6) && defined(SIOCGIFCONF) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif +#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif + return ret; +} + +void +freeifaddrs(struct ifaddrs *ifp) +{ + struct ifaddrs *p, *q; + + for (p = ifp; p; ) { + lws_free(p->ifa_name); + lws_free(p->ifa_addr); + lws_free(p->ifa_dstaddr); + lws_free(p->ifa_netmask); + lws_free(p->ifa_data); + q = p; + p = p->ifa_next; + lws_free(q); + } +} + +#ifdef TEST + +void +print_addr(const char *s, struct sockaddr *sa) +{ + int i; + printf(" %s=%d/", s, sa->sa_family); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + for (i = 0; + i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#else + for (i = 0; i < sizeof(sa->sa_data); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#endif + printf("\n"); +} + +void +print_ifaddrs(struct ifaddrs *x) +{ + struct ifaddrs *p; + + for (p = x; p; p = p->ifa_next) { + printf("%s\n", p->ifa_name); + printf(" flags=%x\n", p->ifa_flags); + if (p->ifa_addr) + print_addr("addr", p->ifa_addr); + if (p->ifa_dstaddr) + print_addr("dstaddr", p->ifa_dstaddr); + if (p->ifa_netmask) + print_addr("netmask", p->ifa_netmask); + printf(" %p\n", p->ifa_data); + } +} + +int +main() +{ + struct ifaddrs *a = NULL, *b; + getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); + print_ifaddrs(a); + printf("---\n"); + getifaddrs(&b); + print_ifaddrs(b); + return 0; +} +#endif diff --git a/thirdparty/lws/misc/getifaddrs.h b/thirdparty/lws/misc/getifaddrs.h new file mode 100644 index 0000000000..d26670c082 --- /dev/null +++ b/thirdparty/lws/misc/getifaddrs.h @@ -0,0 +1,80 @@ +#ifndef LWS_HAVE_GETIFADDRS +#define LWS_HAVE_GETIFADDRS 0 +#endif + +#if LWS_HAVE_GETIFADDRS +#include <sys/types.h> +#include <ifaddrs.h> +#else +#ifdef __cplusplus +extern "C" { +#endif +/* + * Copyright (c) 2000 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + */ + +/* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */ + +#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 +#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 + +/* + * the interface is defined in terms of the fields below, and this is + * sometimes #define'd, so there seems to be no simple way of solving + * this and this seemed the best. */ + +#undef ifa_dstaddr + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr +#endif + +int getifaddrs(struct ifaddrs **); + +void freeifaddrs(struct ifaddrs *); + +#endif /* __ifaddrs_h__ */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/lws/misc/lejp.c new file mode 100644 index 0000000000..5407c90f97 --- /dev/null +++ b/thirdparty/lws/misc/lejp.c @@ -0,0 +1,709 @@ +/* + * Lightweight Embedded JSON Parser + * + * Copyright (C) 2013 Andy Green <andy@warmcat.com> + * This code is licensed under LGPL 2.1 + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + +#include <string.h> +#include "lejp.h" + +#include <stdio.h> + +/** + * lejp_construct - prepare a struct lejp_ctx for use + * + * \param ctx: pointer to your struct lejp_ctx + * \param callback: your user callback which will received parsed tokens + * \param user: optional user data pointer untouched by lejp + * \param paths: your array of name elements you are interested in + * \param count_paths: ARRAY_SIZE() of @paths + * + * Prepares your context struct for use with lejp + */ + +void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user, + const char * const *paths, unsigned char count_paths) +{ + ctx->st[0].s = 0; + ctx->st[0].p = 0; + ctx->st[0].i = 0; + ctx->st[0].b = 0; + ctx->sp = 0; + ctx->ipos = 0; + ctx->ppos = 0; + ctx->path_match = 0; + ctx->path[0] = '\0'; + ctx->callback = callback; + ctx->user = user; + ctx->paths = paths; + ctx->count_paths = count_paths; + ctx->line = 1; + ctx->callback(ctx, LEJPCB_CONSTRUCTED); +} + +/** + * lejp_destruct - retire a previously constructed struct lejp_ctx + * + * \param ctx: pointer to your struct lejp_ctx + * + * lejp does not perform any allocations, but since your user code might, this + * provides a one-time LEJPCB_DESTRUCTED callback at destruction time where + * you can clean up in your callback. + */ + +void +lejp_destruct(struct lejp_ctx *ctx) +{ + /* no allocations... just let callback know what it happening */ + ctx->callback(ctx, LEJPCB_DESTRUCTED); +} + +/** + * lejp_change_callback - switch to a different callback from now on + * + * \param ctx: pointer to your struct lejp_ctx + * \param callback: your user callback which will received parsed tokens + * + * This tells the old callback it was destroyed, in case you want to take any + * action because that callback "lost focus", then changes to the new + * callback and tells it first that it was constructed, and then started. + * + * Changing callback is a cheap and powerful trick to split out handlers + * according to information earlier in the parse. For example you may have + * a JSON pair "schema" whose value defines what can be expected for the rest + * of the JSON. Rather than having one huge callback for all cases, you can + * have an initial one looking for "schema" which then calls + * lejp_change_callback() to a handler specific for the schema. + * + * Notice that afterwards, you need to construct the context again anyway to + * parse another JSON object, and the callback is reset then to the main, + * schema-interpreting one. The construction action is very lightweight. + */ + +void +lejp_change_callback(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason)) +{ + ctx->callback(ctx, LEJPCB_DESTRUCTED); + ctx->callback = callback; + ctx->callback(ctx, LEJPCB_CONSTRUCTED); + ctx->callback(ctx, LEJPCB_START); +} + +static void +lejp_check_path_match(struct lejp_ctx *ctx) +{ + const char *p, *q; + int n; + + /* we only need to check if a match is not active */ + for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) { + ctx->wildcount = 0; + p = ctx->path; + q = ctx->paths[n]; + while (*p && *q) { + if (*q != '*') { + if (*p != *q) + break; + p++; + q++; + continue; + } + ctx->wild[ctx->wildcount++] = p - ctx->path; + q++; + /* + * if * has something after it, match to . + * if ends with *, eat everything. + * This implies match sequences must be ordered like + * x.*.* + * x.* + * if both options are possible + */ + while (*p && (*p != '.' || !*q)) + p++; + } + if (*p || *q) + continue; + + ctx->path_match = n + 1; + ctx->path_match_len = ctx->ppos; + return; + } + + if (!ctx->path_match) + ctx->wildcount = 0; +} + +int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len) +{ + int n; + + if (wildcard >= ctx->wildcount || !len) + return 0; + + n = ctx->wild[wildcard]; + + while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.')) + *dest++ = ctx->path[n++]; + + *dest = '\0'; + n++; + + return n - ctx->wild[wildcard]; +} + +/** + * lejp_parse - interpret some more incoming data incrementally + * + * \param ctx: previously constructed parsing context + * \param json: char buffer with the new data to interpret + * \param len: amount of data in the buffer + * + * Because lejp is a stream parser, it incrementally parses as new data + * becomes available, maintaining all state in the context struct. So an + * incomplete JSON is a normal situation, getting you a LEJP_CONTINUE + * return, signalling there's no error but to call again with more data when + * it comes to complete the parsing. Successful parsing completes with a + * 0 or positive integer indicating how much of the last input buffer was + * unused. + */ + +int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) +{ + unsigned char c, n, s, ret = LEJP_REJECT_UNKNOWN; + static const char esc_char[] = "\"\\/bfnrt"; + static const char esc_tran[] = "\"\\/\b\f\n\r\t"; + static const char tokens[] = "rue alse ull "; + + if (!ctx->sp && !ctx->ppos) + ctx->callback(ctx, LEJPCB_START); + + while (len--) { + c = *json++; + + s = ctx->st[ctx->sp].s; + + /* skip whitespace unless we should care */ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') { + if (c == '\n') { + ctx->line++; + ctx->st[ctx->sp].s &= ~LEJP_FLAG_WS_COMMENTLINE; + } + if (!(s & LEJP_FLAG_WS_KEEP)) { + if (c == '#') + ctx->st[ctx->sp].s |= + LEJP_FLAG_WS_COMMENTLINE; + continue; + } + } + + if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE) + continue; + + switch (s) { + case LEJP_IDLE: + if (c != '{') { + ret = LEJP_REJECT_IDLE_NO_BRACE; + goto reject; + } + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_MEMBERS; + break; + case LEJP_MEMBERS: + if (c == '}') { + ctx->st[ctx->sp].s = LEJP_IDLE; + ret = LEJP_REJECT_MEMBERS_NO_CLOSE; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_M_P; + goto redo_character; + case LEJP_M_P: + if (c != '\"') { + ret = LEJP_REJECT_MP_NO_OPEN_QUOTE; + goto reject; + } + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_DELIM; + c = LEJP_MP_STRING; + goto add_stack_level; + + case LEJP_MP_STRING: + if (c == '\"') { + if (!ctx->sp) { + ret = LEJP_REJECT_MP_STRING_UNDERRUN; + goto reject; + } + if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { + ctx->buf[ctx->npos] = '\0'; + if (ctx->callback(ctx, + LEJPCB_VAL_STR_END) < 0) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } + /* pop */ + ctx->sp--; + break; + } + if (c == '\\') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC; + break; + } + if (c < ' ') {/* "control characters" not allowed */ + ret = LEJP_REJECT_MP_ILLEGAL_CTRL; + goto reject; + } + goto emit_string_char; + + case LEJP_MP_STRING_ESC: + if (c == 'u') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1; + ctx->uni = 0; + break; + } + for (n = 0; n < sizeof(esc_char); n++) { + if (c != esc_char[n]) + continue; + /* found it */ + c = esc_tran[n]; + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + } + ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC; + /* illegal escape char */ + goto reject; + + case LEJP_MP_STRING_ESC_U1: + case LEJP_MP_STRING_ESC_U2: + case LEJP_MP_STRING_ESC_U3: + case LEJP_MP_STRING_ESC_U4: + ctx->uni <<= 4; + if (c >= '0' && c <= '9') + ctx->uni |= c - '0'; + else + if (c >= 'a' && c <= 'f') + ctx->uni = c - 'a' + 10; + else + if (c >= 'A' && c <= 'F') + ctx->uni = c - 'A' + 10; + else { + ret = LEJP_REJECT_ILLEGAL_HEX; + goto reject; + } + ctx->st[ctx->sp].s++; + switch (s) { + case LEJP_MP_STRING_ESC_U2: + if (ctx->uni < 0x08) + break; + /* + * 0x08-0xff (0x0800 - 0xffff) + * emit 3-byte UTF-8 + */ + c = 0xe0 | ((ctx->uni >> 4) & 0xf); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U3: + if (ctx->uni >= 0x080) { + /* + * 0x080 - 0xfff (0x0800 - 0xffff) + * middle 3-byte seq + * send ....XXXXXX.. + */ + c = 0x80 | ((ctx->uni >> 2) & 0x3f); + goto emit_string_char; + } + if (ctx->uni < 0x008) + break; + /* + * 0x008 - 0x7f (0x0080 - 0x07ff) + * start 2-byte seq + */ + c = 0xc0 | (ctx->uni >> 2); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U4: + if (ctx->uni >= 0x0080) + /* end of 2 or 3-byte seq */ + c = 0x80 | (ctx->uni & 0x3f); + else + /* literal */ + c = (unsigned char)ctx->uni; + + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + default: + break; + } + break; + + case LEJP_MP_DELIM: + if (c != ':') { + ret = LEJP_REJECT_MP_DELIM_MISSING_COLON; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + ctx->path[ctx->ppos] = '\0'; + + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + + case LEJP_MP_VALUE: + if (c >= '0' && c <= '9') { + ctx->npos = 0; + ctx->dcount = 0; + ctx->f = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + goto redo_character; + } + switch (c) { + case'\"': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MP_STRING; + ctx->npos = 0; + ctx->buf[0] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + goto add_stack_level; + + case '{': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MEMBERS; + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->path_match = 0; + goto add_stack_level; + + case '[': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; + c = LEJP_MP_VALUE; + ctx->path[ctx->ppos++] = '['; + ctx->path[ctx->ppos++] = ']'; + ctx->path[ctx->ppos] = '\0'; + if (ctx->callback(ctx, LEJPCB_ARRAY_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->i[ctx->ipos++] = 0; + if (ctx->ipos > ARRAY_SIZE(ctx->i)) { + ret = LEJP_REJECT_MP_DELIM_ISTACK; + goto reject; + } + goto add_stack_level; + + case 't': /* true */ + ctx->uni = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'f': + ctx->uni = 4; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'n': + ctx->uni = 4 + 5; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + default: + ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START; + goto reject; + } + break; + + case LEJP_MP_VALUE_NUM_INT: + if (!ctx->npos && c == '-') { + ctx->f |= LEJP_SEEN_MINUS; + goto append_npos; + } + + if (ctx->dcount < 10 && c >= '0' && c <= '9') { + if (ctx->f & LEJP_SEEN_POINT) + ctx->f |= LEJP_SEEN_POST_POINT; + ctx->dcount++; + goto append_npos; + } + if (c == '.') { + if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_POINT; + goto append_npos; + } + /* + * before exponent, if we had . we must have had at + * least one more digit + */ + if ((ctx->f & + (LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) == + LEJP_SEEN_POINT) { + ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC; + goto reject; + } + if (c == 'e' || c == 'E') { + if (ctx->f & LEJP_SEEN_EXP) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_EXP; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP; + goto append_npos; + } + /* if none of the above, did we even have a number? */ + if (!ctx->dcount) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + + ctx->buf[ctx->npos] = '\0'; + if (ctx->f & LEJP_SEEN_POINT) { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } else { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } + + /* then this is the post-number character, loop */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + goto redo_character; + + case LEJP_MP_VALUE_NUM_EXP: + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + if (c >= '0' && c <= '9') + goto redo_character; + if (c == '+' || c == '-') + goto append_npos; + ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP; + goto reject; + + case LEJP_MP_VALUE_TOK: /* true, false, null */ + if (c != tokens[ctx->uni]) { + ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN; + goto reject; + } + ctx->uni++; + if (tokens[ctx->uni] != ' ') + break; + switch (ctx->uni) { + case 3: + ctx->buf[0] = '1'; + ctx->buf[1] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + case 8: + ctx->buf[0] = '0'; + ctx->buf[1] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + case 12: + ctx->buf[0] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_NULL)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + } + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + break; + + case LEJP_MP_COMMA_OR_END: + ctx->path[ctx->ppos] = '\0'; + if (c == ',') { + /* increment this stack level's index */ + ctx->st[ctx->sp].s = LEJP_M_P; + if (!ctx->sp) { + ctx->ppos = 0; + /* + * since we came back to root level, + * no path can still match + */ + ctx->path_match = 0; + break; + } + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + + if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END) + break; + /* top level is definitely an array... */ + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + break; + } + if (c == ']') { + if (!ctx->sp) { + ret = LEJP_REJECT_MP_C_OR_E_UNDERF; + goto reject; + } + /* pop */ + ctx->sp--; + if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { + ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; + goto reject; + } + /* drop the path [n] bit */ + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + + /* do LEJP_MP_ARRAY_END processing */ + goto redo_character; + } + if (c == '}') { + if (ctx->sp == 0) { + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->callback(ctx, LEJPCB_COMPLETE); + /* done, return unused amount */ + return len; + } + /* pop */ + ctx->sp--; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + } + + ret = LEJP_REJECT_MP_C_OR_E_NEITHER; + goto reject; + + case LEJP_MP_ARRAY_END: + ctx->path[ctx->ppos] = '\0'; + if (c == ',') { + /* increment this stack level's index */ + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + if (ctx->sp) + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; + break; + } + if (c != ']') { + ret = LEJP_REJECT_MP_ARRAY_END_MISSING; + goto reject; + } + + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + ctx->callback(ctx, LEJPCB_ARRAY_END); + break; + } + + continue; + +emit_string_char: + if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { + /* assemble the string value into chunks */ + ctx->buf[ctx->npos++] = c; + if (ctx->npos == sizeof(ctx->buf) - 1) { + if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->npos = 0; + } + continue; + } + /* name part of name:value pair */ + ctx->path[ctx->ppos++] = c; + continue; + +add_stack_level: + /* push on to the object stack */ + if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && + ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) + ctx->path[ctx->ppos++] = '.'; + + ctx->st[ctx->sp].p = ctx->ppos; + ctx->st[ctx->sp].i = ctx->ipos; + if (++ctx->sp == ARRAY_SIZE(ctx->st)) { + ret = LEJP_REJECT_STACK_OVERFLOW; + goto reject; + } + ctx->path[ctx->ppos] = '\0'; + ctx->st[ctx->sp].s = c; + ctx->st[ctx->sp].b = 0; + continue; + +append_npos: + if (ctx->npos >= sizeof(ctx->buf)) { + ret = LEJP_REJECT_NUM_TOO_LONG; + goto reject; + } + ctx->buf[ctx->npos++] = c; + continue; + +redo_character: + json--; + len++; + } + + return LEJP_CONTINUE; + +reject: + ctx->callback(ctx, LEJPCB_FAILED); + return ret; +} diff --git a/thirdparty/lws/misc/lejp.h b/thirdparty/lws/misc/lejp.h new file mode 100644 index 0000000000..0b37bb3e42 --- /dev/null +++ b/thirdparty/lws/misc/lejp.h @@ -0,0 +1,232 @@ +#include "libwebsockets.h" +struct lejp_ctx; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) +#endif +#define LEJP_FLAG_WS_KEEP 64 +#define LEJP_FLAG_WS_COMMENTLINE 32 + +enum lejp_states { + LEJP_IDLE = 0, + LEJP_MEMBERS = 1, + LEJP_M_P = 2, + LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3, + LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4, + LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5, + LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6, + LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7, + LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8, + LEJP_MP_DELIM = 9, + LEJP_MP_VALUE = 10, + LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11, + LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12, + LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13, + LEJP_MP_COMMA_OR_END = 14, + LEJP_MP_ARRAY_END = 15, +}; + +enum lejp_reasons { + LEJP_CONTINUE = -1, + LEJP_REJECT_IDLE_NO_BRACE = -2, + LEJP_REJECT_MEMBERS_NO_CLOSE = -3, + LEJP_REJECT_MP_NO_OPEN_QUOTE = -4, + LEJP_REJECT_MP_STRING_UNDERRUN = -5, + LEJP_REJECT_MP_ILLEGAL_CTRL = -6, + LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7, + LEJP_REJECT_ILLEGAL_HEX = -8, + LEJP_REJECT_MP_DELIM_MISSING_COLON = -9, + LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10, + LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11, + LEJP_REJECT_MP_VAL_NUM_FORMAT = -12, + LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13, + LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14, + LEJP_REJECT_MP_C_OR_E_UNDERF = -15, + LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16, + LEJP_REJECT_MP_ARRAY_END_MISSING = -17, + LEJP_REJECT_STACK_OVERFLOW = -18, + LEJP_REJECT_MP_DELIM_ISTACK = -19, + LEJP_REJECT_NUM_TOO_LONG = -20, + LEJP_REJECT_MP_C_OR_E_NEITHER = -21, + LEJP_REJECT_UNKNOWN = -22, + LEJP_REJECT_CALLBACK = -23 +}; + +#define LEJP_FLAG_CB_IS_VALUE 64 + +enum lejp_callbacks { + LEJPCB_CONSTRUCTED = 0, + LEJPCB_DESTRUCTED = 1, + + LEJPCB_START = 2, + LEJPCB_COMPLETE = 3, + LEJPCB_FAILED = 4, + + LEJPCB_PAIR_NAME = 5, + + LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6, + LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7, + LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8, + LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9, + LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10, + LEJPCB_VAL_STR_START = 11, /* notice handle separately */ + LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12, + LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13, + + LEJPCB_ARRAY_START = 14, + LEJPCB_ARRAY_END = 15, + + LEJPCB_OBJECT_START = 16, + LEJPCB_OBJECT_END = 17 +}; + +/** + * _lejp_callback() - User parser actions + * \param ctx: LEJP context + * \param reason: Callback reason + * + * Your user callback is associated with the context at construction time, + * and receives calls as the parsing progresses. + * + * All of the callbacks may be ignored and just return 0. + * + * The reasons it might get called, found in @reason, are: + * + * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to + * perform one-time allocation for the life of the context. + * + * LEJPCB_DESTRUCTED: The context is being destructed... if you made any + * allocations at construction-time, you can free them now + * + * LEJPCB_START: Parsing is beginning at the first byte of input + * + * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or + * positive return code from lejp_parse indicating the + * amount of unused bytes left in the input buffer + * + * LEJPCB_FAILED: Parsing failed. You'll get a negative error code + * returned from lejp_parse + * + * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed, + * this callback occurs. You can find the new name at + * the end of ctx->path[] + * + * LEJPCB_VAL_TRUE: The "true" value appeared + * + * LEJPCB_VAL_FALSE: The "false" value appeared + * + * LEJPCB_VAL_NULL: The "null" value appeared + * + * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf + * + * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf + * + * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet + * + * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in + * ctx->buf, which is as much as we can buffer, so we are + * spilling it. If all your strings are less than + * LEJP_STRING_CHUNK - 1 bytes, you will never see this + * callback. + * + * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the + * string is in ctx->buf. + * + * LEJPCB_ARRAY_START: An array started + * + * LEJPCB_ARRAY_END: An array ended + * + * LEJPCB_OBJECT_START: An object started + * + * LEJPCB_OBJECT_END: An object ended + */ +LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason); + +typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); + +#ifndef LEJP_MAX_DEPTH +#define LEJP_MAX_DEPTH 12 +#endif +#ifndef LEJP_MAX_INDEX_DEPTH +#define LEJP_MAX_INDEX_DEPTH 5 +#endif +#ifndef LEJP_MAX_PATH +#define LEJP_MAX_PATH 128 +#endif +#ifndef LEJP_STRING_CHUNK +/* must be >= 30 to assemble floats */ +#define LEJP_STRING_CHUNK 255 +#endif + +enum num_flags { + LEJP_SEEN_MINUS = (1 << 0), + LEJP_SEEN_POINT = (1 << 1), + LEJP_SEEN_POST_POINT = (1 << 2), + LEJP_SEEN_EXP = (1 << 3) +}; + +struct _lejp_stack { + char s; /* lejp_state stack*/ + char p; /* path length */ + char i; /* index array length */ + char b; /* user bitfield */ +}; + +struct lejp_ctx { + + /* sorted by type for most compact alignment + * + * pointers + */ + + signed char (*callback)(struct lejp_ctx *ctx, char reason); + void *user; + const char * const *paths; + + /* arrays */ + + struct _lejp_stack st[LEJP_MAX_DEPTH]; + unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */ + unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ + char path[LEJP_MAX_PATH]; + char buf[LEJP_STRING_CHUNK]; + + /* int */ + + unsigned int line; + + /* short */ + + unsigned short uni; + + /* char */ + + unsigned char npos; + unsigned char dcount; + unsigned char f; + unsigned char sp; /* stack head */ + unsigned char ipos; /* index stack depth */ + unsigned char ppos; + unsigned char count_paths; + unsigned char path_match; + unsigned char path_match_len; + unsigned char wildcount; +}; + +LWS_VISIBLE LWS_EXTERN void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), + void *user, const char * const *paths, unsigned char paths_count); + +LWS_VISIBLE LWS_EXTERN void +lejp_destruct(struct lejp_ctx *ctx); + +LWS_VISIBLE LWS_EXTERN int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len); + +LWS_VISIBLE LWS_EXTERN void +lejp_change_callback(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason)); + +LWS_VISIBLE LWS_EXTERN int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len); diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/lws/misc/sha-1.c new file mode 100644 index 0000000000..9353fbefe4 --- /dev/null +++ b/thirdparty/lws/misc/sha-1.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. + */ +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * based on: http://csrc.nist.gov/fips/fip180-1.txt + * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org> + */ + +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +struct sha1_ctxt { + union { + unsigned char b8[20]; + unsigned int b32[5]; + } h; + union { + unsigned char b8[8]; + u_int64_t b64[1]; + } c; + union { + unsigned char b8[64]; + unsigned int b32[16]; + } m; + unsigned char count; +}; + +/* sanity check */ +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) +# define unsupported 1 +#elif BYTE_ORDER != BIG_ENDIAN +# if BYTE_ORDER != LITTLE_ENDIAN +# define unsupported 1 +# endif +#endif + +#ifndef unsupported + +/* constant table */ +static const unsigned int _K[] = + { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; +#define K(t) _K[(t) / 20] + +#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F1(b, c, d) (((b) ^ (c)) ^ (d)) +#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) +#define F3(b, c, d) (((b) ^ (c)) ^ (d)) + +#define S(n, x) (((x) << (n)) | ((x) >> (32 - n))) + +#define H(n) (ctxt->h.b32[(n)]) +#define COUNT (ctxt->count) +#define BCOUNT (ctxt->c.b64[0] / 8) +#define W(n) (ctxt->m.b32[(n)]) + +#define PUTBYTE(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + ctxt->c.b64[0] += 8; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + +#define PUTPAD(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + + +static void +sha1_step(struct sha1_ctxt *ctxt) +{ + unsigned int a, b, c, d, e, tmp; + size_t t, s; + +#if BYTE_ORDER == LITTLE_ENDIAN + struct sha1_ctxt tctxt; + + memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64); + ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2]; + ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0]; + ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6]; + ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4]; + ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10]; + ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8]; + ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14]; + ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12]; + ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18]; + ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16]; + ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22]; + ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20]; + ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26]; + ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24]; + ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30]; + ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28]; + ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34]; + ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32]; + ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38]; + ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36]; + ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42]; + ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40]; + ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46]; + ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44]; + ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50]; + ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48]; + ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54]; + ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52]; + ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58]; + ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56]; + ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62]; + ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60]; +#endif + + a = H(0); b = H(1); c = H(2); d = H(3); e = H(4); + + for (t = 0; t < 20; t++) { + s = t & 0x0f; + if (t >= 16) + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + + tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 20; t < 40; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 40; t < 60; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 60; t < 80; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + + H(0) = H(0) + a; + H(1) = H(1) + b; + H(2) = H(2) + c; + H(3) = H(3) + d; + H(4) = H(4) + e; + + bzero(&ctxt->m.b8[0], 64); +} + +/*------------------------------------------------------------*/ + +static void +_sha1_init(struct sha1_ctxt *ctxt) +{ + bzero(ctxt, sizeof(struct sha1_ctxt)); + H(0) = 0x67452301; + H(1) = 0xefcdab89; + H(2) = 0x98badcfe; + H(3) = 0x10325476; + H(4) = 0xc3d2e1f0; +} + +void +sha1_pad(struct sha1_ctxt *ctxt) +{ + size_t padlen; /*pad length in bytes*/ + size_t padstart; + + PUTPAD(0x80); + + padstart = COUNT % 64; + padlen = 64 - padstart; + if (padlen < 8) { + bzero(&ctxt->m.b8[padstart], padlen); + COUNT += (unsigned char)padlen; + COUNT %= 64; + sha1_step(ctxt); + padstart = COUNT % 64; /* should be 0 */ + padlen = 64 - padstart; /* should be 64 */ + } + bzero(&ctxt->m.b8[padstart], padlen - 8); + COUNT += ((unsigned char)padlen - 8); + COUNT %= 64; +#if BYTE_ORDER == BIG_ENDIAN + PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]); + PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]); + PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]); + PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]); +#else + PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]); + PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]); + PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]); + PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]); +#endif +} + +void +sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len) +{ + size_t gaplen; + size_t gapstart; + size_t off; + size_t copysiz; + + off = 0; + + while (off < len) { + gapstart = COUNT % 64; + gaplen = 64 - gapstart; + + copysiz = (gaplen < len - off) ? gaplen : len - off; + memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz); + COUNT += (unsigned char)copysiz; + COUNT %= 64; + ctxt->c.b64[0] += copysiz * 8; + if (COUNT % 64 == 0) + sha1_step(ctxt); + off += copysiz; + } +} + +void +sha1_result(struct sha1_ctxt *ctxt, void *digest0) +{ + unsigned char *digest; + + digest = (unsigned char *)digest0; + sha1_pad(ctxt); +#if BYTE_ORDER == BIG_ENDIAN + memcpy(digest, &ctxt->h.b8[0], 20); +#else + digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; + digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; + digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; + digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; + digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; + digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; + digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; + digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; + digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; + digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; +#endif +} + +/* + * This should look and work like the libcrypto implementation + */ + +LWS_VISIBLE unsigned char * +lws_SHA1(const unsigned char *d, size_t n, unsigned char *md) +{ + struct sha1_ctxt ctx; + + _sha1_init(&ctx); + sha1_loop(&ctx, d, n); + sha1_result(&ctx, (void *)md); + + return md; +} + +#endif /*unsupported*/ |