diff --git a/feeds/wlan-ap/libwebsocket/Makefile b/feeds/wlan-ap/libwebsocket/Makefile new file mode 100644 index 00000000..1f7abd0e --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/Makefile @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: BSD-3-Clause + +include $(TOPDIR)/rules.mk + +PKG_NAME:=libwebsocket +PKG_RELEASE:=1.0.0 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/libwebsocket + SECTION:=base + CATEGORY:=Base system + TITLE:=Websocket client library + DEPENDS:=+libopenssl +endef + +define Package/libwebsocket/description + websocket library. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + [ ! -d ./src/ ] || $(CP) ./src/* $(PKG_BUILD_DIR) +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/include/websocket + $(CP) $(PKG_BUILD_DIR)/include/websocket/* $(1)/usr/include/websocket/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/libwebsocket.so $(1)/usr/lib/ +endef + +define Package/libwebsocket/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DATA) $(PKG_BUILD_DIR)/libwebsocket.so $(1)/usr/lib/ +endef +$(eval $(call BuildPackage,libwebsocket)) diff --git a/feeds/wlan-ap/libwebsocket/src/Makefile b/feeds/wlan-ap/libwebsocket/src/Makefile new file mode 100644 index 00000000..a8226dd0 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/Makefile @@ -0,0 +1,22 @@ +#/* SPDX-License-Identifier: BSD-3-Clause */ +CC=gcc +WFLAGS=-Wall +CFLAGS=-O2 +INCLUDES=-Iinclude +LIBS= -lssl +LIBNAME=libwebsocket.so + +CFLAGS += -O -Wall -Werror -Wshadow -fPIC + +all: $(LIBNAME) + +%.o: %.c + $(CC) $(WFLAGS) -c -o $@ $(INCLUDES) $(CFLAGS) $< + +OBJS=alloc.o base64-decode.o client.o client-handshake.o client-parser.o context.o getifaddrs.o handshake.o header.o libwebsockets.o lws-plat-unix.o output.o parsers.o pollfd.o server.o server-handshake.o service.o sha-1.o ssl.o ssl-client.o ssl-server.o + +$(LIBNAME): $(OBJS) + $(CC) $(CFLAGS) -Wl,-Bsymbolic-functions -shared -o $@ $^ $(LIBS) + +clean: + $(RM) -f libwebsocket.so $(OBJS) diff --git a/feeds/wlan-ap/libwebsocket/src/alloc.c b/feeds/wlan-ap/libwebsocket/src/alloc.c new file mode 100644 index 00000000..dd099d2d --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/alloc.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#include "websocket/private-libwebsockets.h" + +static void *_realloc(void *ptr, size_t size) +{ + if (size) + return (void *)realloc(ptr, size); + else if (ptr) + free(ptr); + return NULL; +} + +void *(*_lws_realloc)(void *ptr, size_t size) = _realloc; + +void *lws_realloc(void *ptr, size_t size) +{ + return _lws_realloc(ptr, size); +} + +void *lws_zalloc(size_t size) +{ + void *ptr = _lws_realloc(NULL, size); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +void lws_set_allocator(void *(*cb)(void *ptr, size_t size)) +{ + _lws_realloc = cb; +} diff --git a/feeds/wlan-ap/libwebsocket/src/base64-decode.c b/feeds/wlan-ap/libwebsocket/src/base64-decode.c new file mode 100644 index 00000000..b12434f5 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/base64-decode.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include "websocket/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/feeds/wlan-ap/libwebsocket/src/client-handshake.c b/feeds/wlan-ap/libwebsocket/src/client-handshake.c new file mode 100644 index 00000000..15b453e3 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/client-handshake.c @@ -0,0 +1,752 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#include "websocket/private-libwebsockets.h" + +struct lws * +lws_client_connect_2(struct lws *wsi) +{ +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 server_addr6; + struct addrinfo hints, *result; +#endif + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct sockaddr_in server_addr4; + struct lws_pollfd pfd; + struct sockaddr *v; + const char *cce = ""; + int n, plen = 0; + const char *ads; + + lwsl_client("%s\n", __func__); + + if (!wsi->u.hdr.ah) { + cce = "ah was NULL at cc2"; + lwsl_err("%s\n", cce); + goto oom4; + } + + /* proxy? */ + + if (wsi->vhost->http_proxy_port) { + plen = sprintf((char *)pt->serv_buf, + "CONNECT %s:%u HTTP/1.0\x0d\x0a" + "User-agent: libwebsockets\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), + wsi->u.hdr.c_port); + + if (wsi->vhost->proxy_basic_auth_token[0]) + plen += sprintf((char *)pt->serv_buf + plen, + "Proxy-authorization: basic %s\x0d\x0a", + wsi->vhost->proxy_basic_auth_token); + + plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); + ads = wsi->vhost->http_proxy_address; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + memset(&server_addr6, 0, sizeof(struct sockaddr_in6)); + server_addr6.sin6_port = htons(wsi->vhost->http_proxy_port); + } else +#endif + server_addr4.sin_port = htons(wsi->vhost->http_proxy_port); + + } else { + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + memset(&server_addr6, 0, sizeof(struct sockaddr_in6)); + server_addr6.sin6_port = htons(wsi->u.hdr.c_port); + } else +#endif + server_addr4.sin_port = htons(wsi->u.hdr.c_port); + } + + /* + * prepare the actual connection (to the proxy, if any) + */ + lwsl_client("%s: address %s\n", __func__, ads); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + memset(&hints, 0, sizeof(struct addrinfo)); +#if !defined(__ANDROID__) + hints.ai_family = AF_INET6; + hints.ai_flags = AI_V4MAPPED; +#endif + n = getaddrinfo(ads, NULL, &hints, &result); + if (n) { +#ifdef _WIN32 + lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n)); +#else + lwsl_err("getaddrinfo: %s\n", gai_strerror(n)); +#endif + cce = "getaddrinfo (ipv6) failed"; + goto oom4; + } + + server_addr6.sin6_family = AF_INET6; + switch (result->ai_family) { +#if defined(__ANDROID__) + case AF_INET: + /* map IPv4 to IPv6 */ + bzero((char *)&server_addr6.sin6_addr, + sizeof(struct in6_addr)); + server_addr6.sin6_addr.s6_addr[10] = 0xff; + server_addr6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&server_addr6.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + break; +#endif + case AF_INET6: + memcpy(&server_addr6.sin6_addr, + &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + default: + lwsl_err("Unknown address family\n"); + freeaddrinfo(result); + cce = "unknown address family"; + goto oom4; + } + + freeaddrinfo(result); + } else +#endif + { + struct addrinfo ai, *res, *result; + void *p = NULL; + + memset (&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_CANONNAME; + + if (getaddrinfo(ads, NULL, &ai, &result)) { + lwsl_err("getaddrinfo failed\n"); + cce = "getaddrinfo (ipv4) failed"; + goto oom4; + } + + res = result; + while (!p && res) { + switch (res->ai_family) { + case AF_INET: + p = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + } + + res = res->ai_next; + } + + if (!p) { + lwsl_err("Couldn't identify address\n"); + freeaddrinfo(result); + goto oom4; + } + + server_addr4.sin_family = AF_INET; + server_addr4.sin_addr = *((struct in_addr *)p); + bzero(&server_addr4.sin_zero, 8); + freeaddrinfo(result); + } + + if (!lws_socket_is_valid(wsi->sock)) { + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) + wsi->sock = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + wsi->sock = socket(AF_INET, SOCK_STREAM, 0); + + if (!lws_socket_is_valid(wsi->sock)) { + lwsl_warn("Unable to open socket\n"); + goto oom4; + } + + if (lws_plat_set_socket_options(wsi->vhost, wsi->sock)) { + lwsl_err("Failed to set wsi socket options\n"); + compatible_close(wsi->sock); + cce = "set socket opts failed"; + goto oom4; + } + + wsi->mode = LWSCM_WSCL_WAITING_CONNECT; + + lws_libev_accept(wsi, wsi->sock); + lws_libuv_accept(wsi, wsi->sock); + if (insert_wsi_socket_into_fds(context, wsi)) { + compatible_close(wsi->sock); + cce = "insert wsi failed"; + goto oom4; + } + + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + /* + * past here, we can't simply free the structs as error + * handling as oom4 does. We have to run the whole close flow. + */ + + if (!wsi->protocol) + wsi->protocol = &wsi->vhost->protocols[0]; + + wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, + wsi->user_space, NULL, 0); + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + n = lws_socket_bind(wsi->vhost, wsi->sock, 0, wsi->vhost->iface); + if (n < 0) + goto failed; + } + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + v = (struct sockaddr *)&server_addr6; + n = sizeof(struct sockaddr_in6); + } else +#endif + { + v = (struct sockaddr *)&server_addr4; + n = sizeof(struct sockaddr); + } + + if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) { + if (LWS_ERRNO == LWS_EALREADY || + LWS_ERRNO == LWS_EINPROGRESS || + LWS_ERRNO == LWS_EWOULDBLOCK +#ifdef _WIN32 + || LWS_ERRNO == WSAEINVAL +#endif + ) { + lwsl_client("nonblocking connect retry (errno = %d)\n", + LWS_ERRNO); + + if (lws_plat_check_connection_error(wsi)) + goto failed; + + /* + * must do specifically a POLLOUT poll to hear + * about the connect completion + */ + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + goto failed; + + return wsi; + } + + if (LWS_ERRNO != LWS_EISCONN) { + lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO); + goto failed; + } + } + + lwsl_client("connected\n"); + + /* we are connected to server, or proxy */ + + if (wsi->vhost->http_proxy_port) { + + /* + * OK from now on we talk via the proxy, so connect to that + * + * (will overwrite existing pointer, + * leaving old string/frag there but unreferenced) + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->http_proxy_address)) + goto failed; + wsi->u.hdr.c_port = wsi->vhost->http_proxy_port; + + n = send(wsi->sock, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to proxy socket\n"); + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY; + + return wsi; + } + + /* + * provoke service to issue the handshake directly + * we need to do it this way because in the proxy case, this is the + * next state and executed only if and when we get a good proxy + * response inside the state machine... but notice in SSL case this + * may not have sent anything yet with 0 return, and won't until some + * many retries from main loop. To stop that becoming endless, + * cover with a timeout. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE; + pfd.fd = wsi->sock; + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLIN; + + n = lws_service_fd(context, &pfd); + if (n < 0) + goto failed; + if (n) /* returns 1 on failure after closing wsi */ + return NULL; + + return wsi; + +oom4: + /* we're closing, losing some rx is OK */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + //lwsl_err("%d\n", wsi->mode); + if (wsi->mode == LWSCM_HTTP_CLIENT) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; + } + /* take care that we might be inserted in fds already */ + if (wsi->position_in_fds_table != -1) + goto failed; + lws_header_table_detach(wsi, 0); + lws_free(wsi); + + return NULL; + +failed: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +/** + * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) + * this only works if still in HTTP, ie, not upgraded yet + * wsi: connection to reset + * address: network address of the new server + * port: port to connect to + * path: uri path to connect to on the new server + * host: host header to send to the new server + */ +LWS_VISIBLE struct lws * +lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const char *path, const char *host) +{ + if (wsi->u.hdr.redirects == 3) { + lwsl_err("%s: Too many redirects\n", __func__); + return NULL; + } + wsi->u.hdr.redirects++; + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = ssl; +#else + if (ssl) { + lwsl_err("%s: not configured for ssl\n", __func__); + return NULL; + } +#endif + + lwsl_notice("redirect ads='%s', port=%d, path='%s'\n", address, port, path); + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, path)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) + return NULL; + + compatible_close(wsi->sock); + remove_wsi_socket_from_fds(wsi); + wsi->sock = LWS_SOCK_INVALID; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->protocol = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->u.hdr.c_port = port; + + return lws_client_connect_2(wsi); +} + +#ifdef LWS_WITH_HTTP_PROXY +static hubbub_error +html_parser_cb(const hubbub_token *token, void *pw) +{ + struct lws_rewrite *r = (struct lws_rewrite *)pw; + char buf[1024], *start = buf + LWS_PRE, *p = start, + *end = &buf[sizeof(buf) - 1]; + size_t i; + + switch (token->type) { + case HUBBUB_TOKEN_DOCTYPE: + + p += lws_snprintf(p, end - p, "data.doctype.name.len, + token->data.doctype.name.ptr, + token->data.doctype.force_quirks ? + "(force-quirks) " : ""); + + if (token->data.doctype.public_missing) + printf("\tpublic: missing\n"); + else + p += lws_snprintf(p, end - p, "PUBLIC \"%.*s\"\n", + (int) token->data.doctype.public_id.len, + token->data.doctype.public_id.ptr); + + if (token->data.doctype.system_missing) + printf("\tsystem: missing\n"); + else + p += lws_snprintf(p, end - p, " \"%.*s\">\n", + (int) token->data.doctype.system_id.len, + token->data.doctype.system_id.ptr); + + break; + case HUBBUB_TOKEN_START_TAG: + p += lws_snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len, + token->data.tag.name.ptr); + +/* (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) || + !hstrcmp(&token->data.tag.attributes[i].name, "action", 6) || + !hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) { + const char *pp = (const char *)token->data.tag.attributes[i].value.ptr; + int plen = (int) token->data.tag.attributes[i].value.len; + + if (!hstrcmp(&token->data.tag.attributes[i].value, + r->from, r->from_len)) { + pp += r->from_len; + plen -= r->from_len; + } + p += lws_snprintf(p, end - p, " %.*s=\"%s/%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + r->to, plen, pp); + + } else + + p += lws_snprintf(p, end - p, " %.*s=\"%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">\n"); + break; + case HUBBUB_TOKEN_END_TAG: + p += lws_snprintf(p, end - p, "data.tag.name.len, + token->data.tag.name.ptr); +/* + (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + p += lws_snprintf(p, end - p, " %.*s='%.*s'\n", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">\n"); + break; + case HUBBUB_TOKEN_COMMENT: + p += lws_snprintf(p, end - p, "\n", + (int) token->data.comment.len, + token->data.comment.ptr); + break; + case HUBBUB_TOKEN_CHARACTER: + p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len, + token->data.character.ptr); + break; + case HUBBUB_TOKEN_EOF: + p += lws_snprintf(p, end - p, "\n"); + break; + } + + if (user_callback_handle_rxflow(r->wsi->protocol->callback, + r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + r->wsi->user_space, start, p - start)) + return -1; + + return HUBBUB_OK; +} +#endif + +LWS_VISIBLE struct lws * +lws_client_connect_via_info(struct lws_client_connect_info *i) +{ + struct lws *wsi; + int v = SPEC_LATEST_SUPPORTED; + + if (i->context->requested_kill) + return NULL; + + if (!i->context->protocol_init_done) + lws_protocol_init(i->context); + + wsi = lws_zalloc(sizeof(struct lws)); + if (wsi == NULL) + goto bail; + + wsi->context = i->context; + /* assert the mode and union status (hdr) clearly */ + lws_union_transition(wsi, LWSCM_HTTP_CLIENT); + wsi->sock = LWS_SOCK_INVALID; + + /* 1) fill up the wsi with stuff from the connect_info as far as it + * can go. It's because not only is our connection async, we might + * not even be able to get ahold of an ah at this point. + */ + + /* -1 means just use latest supported */ + if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) + v = i->ietf_version_or_minus_one; + + wsi->ietf_spec_revision = v; + wsi->user_space = NULL; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->protocol = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->position_in_fds_table = -1; + wsi->u.hdr.c_port = i->port; + wsi->vhost = i->vhost; + if (!wsi->vhost) + wsi->vhost = i->context->vhost_list; + + wsi->protocol = &wsi->vhost->protocols[0]; + if (wsi && !wsi->user_space && i->userdata) { + wsi->user_space_externally_allocated = 1; + wsi->user_space = i->userdata; + } else + /* if we stay in http, we can assign the user space now, + * otherwise do it after the protocol negotiated + */ + if (i->method) + if (lws_ensure_user_space(wsi)) + goto bail; + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = i->ssl_connection; +#else + if (i->ssl_connection) { + lwsl_err("libwebsockets not configured for ssl\n"); + goto bail; + } +#endif + + /* 2) stash the things from connect_info that we can't process without + * an ah. Because if no ah, we will go on the ah waiting list and + * process those things later (after the connect_info and maybe the + * things pointed to have gone out of scope. + */ + + wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash)); + if (!wsi->u.hdr.stash) { + lwsl_err("%s: OOM\n", __func__); + goto bail; + } + + wsi->u.hdr.stash->origin[0] = '\0'; + wsi->u.hdr.stash->protocol[0] = '\0'; + wsi->u.hdr.stash->method[0] = '\0'; + + strncpy(wsi->u.hdr.stash->address, i->address, + sizeof(wsi->u.hdr.stash->address) - 1); + strncpy(wsi->u.hdr.stash->path, i->path, + sizeof(wsi->u.hdr.stash->path) - 1); + strncpy(wsi->u.hdr.stash->host, i->host, + sizeof(wsi->u.hdr.stash->host) - 1); + if (i->origin) + strncpy(wsi->u.hdr.stash->origin, i->origin, + sizeof(wsi->u.hdr.stash->origin) - 1); + if (i->protocol) + strncpy(wsi->u.hdr.stash->protocol, i->protocol, + sizeof(wsi->u.hdr.stash->protocol) - 1); + if (i->method) + strncpy(wsi->u.hdr.stash->method, i->method, + sizeof(wsi->u.hdr.stash->method) - 1); + + wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; + wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; + wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; + wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; + wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; + wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; + + if (i->pwsi) + *i->pwsi = wsi; + + /* if we went on the waiting list, no probs just return the wsi + * when we get the ah, now or later, he will call + * lws_client_connect_via_info2() below. + */ + if (lws_header_table_attach(wsi, 0) < 0) { + /* + * if we failed here, the connection is already closed + * and freed. + */ + goto bail1; + } + + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } +#ifdef LWS_WITH_HTTP_PROXY + if (i->uri_replace_to) + wsi->rw = lws_rewrite_create(wsi, html_parser_cb, + i->uri_replace_from, + i->uri_replace_to); +#endif + + return wsi; + +bail: + lws_free(wsi); + +bail1: + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +} + +struct lws * +lws_client_connect_via_info2(struct lws *wsi) +{ + struct client_info_stash *stash = wsi->u.hdr.stash; + + if (!stash) + return wsi; + + /* + * we're not necessarily in a position to action these right away, + * stash them... we only need during connect phase so u.hdr is fine + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + stash->address)) + goto bail1; + + /* these only need u.hdr lifetime as well */ + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) + goto bail1; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) + goto bail1; + + if (stash->origin[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, + stash->origin)) + goto bail1; + /* + * this is a list of protocols we tell the server we're okay with + * stash it for later when we compare server response with it + */ + if (stash->protocol[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + stash->protocol)) + goto bail1; + if (stash->method[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, + stash->method)) + goto bail1; + + lws_free_set_NULL(wsi->u.hdr.stash); + + /* + * Check with each extension if it is able to route and proxy this + * connection for us. For example, an extension like x-google-mux + * can handle this and then we don't need an actual socket for this + * connection. + */ + + if (lws_ext_cb_all_exts(wsi->context, wsi, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, + (void *)stash->address, + wsi->u.hdr.c_port) > 0) { + lwsl_client("lws_client_connect: ext handling conn\n"); + + lws_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; + return wsi; + } + lwsl_client("lws_client_connect: direct conn\n"); + wsi->context->count_wsi_allocated++; + + return lws_client_connect_2(wsi); + +bail1: + lws_free_set_NULL(wsi->u.hdr.stash); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_client_connect_extended(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one, + void *userdata) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = userdata; + + return lws_client_connect_via_info(&i); +} + +LWS_VISIBLE struct lws * +lws_client_connect(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = NULL; + + return lws_client_connect_via_info(&i); +} + diff --git a/feeds/wlan-ap/libwebsocket/src/client-parser.c b/feeds/wlan-ap/libwebsocket/src/client-parser.c new file mode 100644 index 00000000..bc655d6f --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/client-parser.c @@ -0,0 +1,551 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +int lws_client_rx_sm(struct lws *wsi, unsigned char c) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; + int handled, n, m, rx_draining_ext = 0; + unsigned short close_code; + struct lws_tokens eff_buf; + unsigned char *pp; + + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__, c); + assert(!c); + eff_buf.token = NULL; + eff_buf.token_len = 0; + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + rx_draining_ext = 1; + + goto drain_extension; + } + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + /* control frames (PING) may interrupt checkable sequences */ + wsi->u.ws.defeat_check_utf8 = 0; + + switch (wsi->ietf_spec_revision) { + case 13: + wsi->u.ws.opcode = c & 0xf; + /* revisit if an extension wants them... */ + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.continuation_possible = 1; + wsi->u.ws.check_utf8 = lws_check_opt( + wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8); + wsi->u.ws.utf8 = 0; + break; + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.continuation_possible = 1; + break; + case LWSWSOPC_CONTINUATION: + if (!wsi->u.ws.continuation_possible) { + lwsl_info("disordered continuation\n"); + return -1; + } + break; + case LWSWSOPC_CLOSE: + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.utf8 = 0; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + default: + wsi->u.ws.defeat_check_utf8 = 1; + break; + } + wsi->u.ws.rsv = (c & 0x70); + /* revisit if an extension wants them... */ + if ( +#ifndef LWS_NO_EXTENSIONS + !wsi->count_act_ext && +#endif + wsi->u.ws.rsv) { + lwsl_info("illegal rsv bits set\n"); + return -1; + } + wsi->u.ws.final = !!((c >> 7) & 1); + lwsl_ext("%s: This RX frame Final %d\n", __func__, wsi->u.ws.final); + + if (wsi->u.ws.owed_a_fin && + (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME || + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) { + lwsl_info("hey you owed us a FIN\n"); + return -1; + } + if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) { + wsi->u.ws.continuation_possible = 0; + wsi->u.ws.owed_a_fin = 0; + } + + if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) { + lwsl_info("control message cannot be fragmented\n"); + return -1; + } + if (!wsi->u.ws.final) + wsi->u.ws.owed_a_fin = 1; + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == + LWSWSOPC_BINARY_FRAME; + break; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + default: + lwsl_err("unknown spec version %02d\n", + wsi->ietf_spec_revision); + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; + break; + case 127: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (c) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.ws.rx_packet_length = 0; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_7: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_6: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_5: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_4: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_3: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_2: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_1: + wsi->u.ws.rx_packet_length |= (size_t)c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_2: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_3: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_4: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce) + c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3]; + + wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) + break; + + /* spill because we filled our rx buffer */ +spill: + + handled = 0; + + /* + * is this frame a control packet we should take care of at this + * layer? If so service it and hide it from the user callback + */ + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_CLOSE: + pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE]; + if (lws_check_opt(wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8) && + wsi->u.ws.rx_ubuf_head > 2 && + lws_check_utf8(&wsi->u.ws.utf8, pp + 2, + wsi->u.ws.rx_ubuf_head - 2)) + goto utf8_fail; + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen server's close ack\n"); + return -1; + } + + lwsl_parser("client sees server close len = %d\n", + wsi->u.ws.rx_ubuf_head); + if (wsi->u.ws.rx_ubuf_head >= 2) { + close_code = (pp[0] << 8) | pp[1]; + if (close_code < 1000 || close_code == 1004 || + close_code == 1005 || close_code == 1006 || + close_code == 1012 || close_code == 1013 || + close_code == 1014 || close_code == 1015 || + (close_code >= 1016 && close_code < 3000) + ) { + pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff; + pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff; + } + } + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, pp, + wsi->u.ws.rx_ubuf_head)) + return -1; + /* + * parrot the close packet payload back + * we do not care about how it went, we are closing + * immediately afterwards + */ + lws_write(wsi, (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head, LWS_WRITE_CLOSE); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* close the connection */ + return -1; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + /* he set a close reason on this guy, ignore PING */ + if (wsi->u.ws.close_in_ping_buffer_len) + goto ping_drop; + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } + + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + handled = 1; + break; + + case LWSWSOPC_PONG: + lwsl_info("client receied pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("received expected PONG on wsi %p\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG; + break; + + case LWSWSOPC_CONTINUATION: + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + break; + + default: + + lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); + + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) { /* not handle or fail */ + + lwsl_ext("Unhandled ext opc 0x%x\n", wsi->u.ws.opcode); + wsi->u.ws.rx_ubuf_head = 0; + + return 0; + } + handled = 1; + break; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + if (handled) + goto already_done; + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + +drain_extension: + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + lwsl_ext("Ext RX returned %d\n", n); + if (n < 0) /* fail */ + return -1; + + lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len); + + if (rx_draining_ext && !eff_buf.token_len) { + lwsl_err(" --- ignoring zero drain result, ending drain\n"); + goto already_done; + } + + if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) { + if (lws_check_utf8(&wsi->u.ws.utf8, + (unsigned char *)eff_buf.token, + eff_buf.token_len)) + goto utf8_fail; + + /* we are ending partway through utf-8 character? */ + if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && wsi->u.ws.utf8 && !n) { + lwsl_info("FINAL utf8 error\n"); +utf8_fail: lwsl_info("utf8 error\n"); + return -1; + } + } + + if (eff_buf.token_len < 0 && + callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) + goto already_done; + + if (!eff_buf.token) + goto already_done; + + eff_buf.token[eff_buf.token_len] = '\0'; + + if (!wsi->protocol->callback) + goto already_done; + + if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) + lwsl_info("Client doing pong callback\n"); + + if (n && eff_buf.token_len) { + /* extension had more... main loop will come back + * we want callback to be done with this set, if so, + * because lws_is_final() hides it was final until the + * last chunk + */ + wsi->u.ws.rx_draining_ext = 1; + wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list; + pt->rx_draining_ext_list = wsi; + lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__); + } + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + m = wsi->protocol->callback(wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, eff_buf.token, eff_buf.token_len); + + /* if user code wants to close, let caller know */ + if (m) + return 1; + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + default: + lwsl_err("client rx illegal state\n"); + return 1; + } + + return 0; + +illegal_ctl_length: + lwsl_warn("Control frame asking for extended length is illegal\n"); + /* kill the connection */ + return -1; +} + + diff --git a/feeds/wlan-ap/libwebsocket/src/client.c b/feeds/wlan-ap/libwebsocket/src/client.c new file mode 100755 index 00000000..221e11ff --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/client.c @@ -0,0 +1,1073 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +int +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + switch (wsi->mode) { + case LWSCM_WSCL_WAITING_PROXY_REPLY: + case LWSCM_WSCL_ISSUE_HANDSHAKE: + case LWSCM_WSCL_WAITING_SERVER_REPLY: + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + case LWSCM_WS_CLIENT: + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) { + lwsl_debug("%s: caching %d\n", __func__, len); + lws_rxflow_cache(wsi, *buf, 0, len); + return 0; + } + if (wsi->u.ws.rx_draining_ext) { + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) + wsi->rxflow_pos++; + + if (lws_client_rx_sm(wsi, *(*buf)++)) { + lwsl_debug("client_rx_sm exited\n"); + return -1; + } + len--; + } + lwsl_debug("%s: finished with %d\n", __func__, len); + return 0; + default: + break; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) +{ + wsi->client_http_body_pending = !!something_left_to_send; +} + +int +lws_client_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + const char *cce = NULL; + unsigned char c; + char *sb = p; + int n, len; + + switch (wsi->mode) { + + case LWSCM_WSCL_WAITING_CONNECT: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + */ + + if (!lws_client_connect_2(wsi)) { + /* closed */ + lwsl_client("closed\n"); + return -1; + } + + /* either still pending connection, or changed mode */ + return 0; + + case LWSCM_WSCL_WAITING_PROXY_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("Proxy connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + n = recv(wsi->sock, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("Proxy read returned EAGAIN... retrying\n"); + return 0; + } + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR reading from proxy socket\n"); + return 0; + } + + pt->serv_buf[13] = '\0'; + if (strcmp(sb, "HTTP/1.0 200 ") && + strcmp(sb, "HTTP/1.1 200 ")) { + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + lwsl_err("ERROR proxy: %s\n", sb); + return 0; + } + + /* clear his proxy connection timeout */ + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + * + * take care of our lws_callback_on_writable + * happening at a time when there's no real connection yet + */ + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return -1; + +#ifdef LWS_OPENSSL_SUPPORT + /* we can retry this... just cook the SSL BIO the first time */ + + if (wsi->use_ssl && !wsi->ssl) { + if (lws_ssl_client_bio_create(wsi)) + return -1; + } + + if (wsi->use_ssl) { + n = lws_ssl_client_connect1(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect1 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; + + /* fallthru */ + + case LWSCM_WSCL_WAITING_SSL: + + if (wsi->use_ssl) { + n = lws_ssl_client_connect2(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect2 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; +#endif + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + context->timeout_secs); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE2: + p = lws_generate_client_handshake(wsi, p); + if (p == NULL) { + lwsl_err("Failed to generate handshake for client\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + /* send our request to the server */ + + lws_latency_pre(context, wsi); + + n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == p - sb); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + lwsl_debug("ERROR writing to client socket\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lws_callback_on_writable(wsi); + break; + } + + if (wsi->client_http_body_pending) { + wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } + + goto client_http_body_sent; + + case LWSCM_WSCL_ISSUE_HTTP_BODY: + if (wsi->client_http_body_pending) { + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } +client_http_body_sent: + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + context->timeout_secs); + break; + + case LWSCM_WSCL_WAITING_SERVER_REPLY: + + /* handle server hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_debug("Server connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + cce = "Peer hung up"; + goto bail3; + } + + if (!(pollfd->revents & LWS_POLLIN)) + break; + + /* interpret the server response */ + + /* + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + */ + + /* + * we have to take some care here to only take from the + * socket bytewise. The browser may (and has been seen to + * in the case that onopen() performs websocket traffic) + * coalesce both handshake response and websocket traffic + * in one packet, since at that point the connection is + * definitively ready from browser pov. + */ + len = 1; + while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && + len > 0) { + n = lws_ssl_capable_read(wsi, &c, 1); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == 1); + switch (n) { + case 0: + case LWS_SSL_CAPABLE_ERROR: + cce = "read failed"; + goto bail3; + case LWS_SSL_CAPABLE_MORE_SERVICE: + return 0; + } + + if (lws_parse(wsi, c)) { + lwsl_warn("problems parsing header\n"); + goto bail3; + } + } + + /* + * hs may also be coming in multiple packets, there is a 5-sec + * libwebsocket timeout still active here too, so if parsing did + * not complete just wait for next packet coming in this state + */ + + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + break; + + /* + * otherwise deal with the handshake. If there's any + * packet traffic already arrived we'll trigger poll() again + * right away and deal with it that way + */ + + return lws_client_interpret_server_handshake(wsi); + +bail3: + lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); + wsi->already_did_cce = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return -1; + + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); + break; + + case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: + lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); + break; + default: + break; + } + + return 0; +} + +/* + * In-place str to lower case + */ + +static void +strtolower(char *s) +{ + while (*s) { + *s = tolower((int)*s); + s++; + } +} + +int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi) +{ + lwsl_debug("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED; + wsi->u.http.content_length = 0; + wsi->hdr_parsing_completed = 0; + + /* He asked for it to stay alive indefinitely */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* + * As client, nothing new is going to come until we ask for it + * we can drop the ah, if any + */ + if (wsi->u.hdr.ah) { + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + lws_header_table_detach(wsi, 0); + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +} + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *wsi) +{ + if (!wsi->u.http.ah) + return 0; + + return wsi->u.http.ah->http_response; +} + +int +lws_client_interpret_server_handshake(struct lws *wsi) +{ + int n, len, okay = 0, port = 0, ssl = 0; + int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; + struct lws_context *context = wsi->context; + const char *pc, *prot, *ads = NULL, *path, *cce = NULL; + struct allocated_headers *ah = NULL; + char *p; +#ifndef LWS_NO_EXTENSIONS + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *sb = (char *)&pt->serv_buf[0]; + const struct lws_ext_options *opts; + const struct lws_extension *ext; + char ext_name[128]; + const char *c, *a; + char ignore; + int more = 1; + void *v; +#endif + + if (!wsi->do_ws) { + /* we are being an http client... + */ + ah = wsi->u.hdr.ah; + lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED); + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->u.http.ah = ah; + ah->http_response = 0; + } + + /* + * well, what the server sent looked reasonable for syntax. + * Now let's confirm it sent all the necessary headers + * + * http (non-ws) client will expect something like this + * + * HTTP/1.0.200 + * server:.libwebsockets + * content-type:.text/html + * content-length:.17703 + * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 + * + * + * + */ + + wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE; + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); + if (wsi->do_ws && !p) { + lwsl_info("no URI\n"); + cce = "HS: URI missing"; + goto bail3; + } + if (!p) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + } + if (!p) { + cce = "HS: URI missing"; + lwsl_info("no URI\n"); + goto bail3; + } + n = atoi(p); + if (ah) + ah->http_response = n; + + if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); + if (!p) { + cce = "HS: Redirect code but no Location"; + goto bail3; + } + + if (lws_parse_uri(p, &prot, &ads, &port, &path)) { + cce = "HS: URI did not parse"; + goto bail3; + } + + if (!strcmp(prot, "wss://") || !strcmp(prot, "https://")) + ssl = 1; + + if (lws_client_reset(wsi, ssl, ads, port, path, ads)) { + lwsl_err("Redirect failed\n"); + cce = "HS: Redirect failed"; + goto bail3; + } + return 0; + } + + if (!wsi->do_ws) { + if (n != 200 && n != 304) { + lwsl_notice("Connection failed with code %d\n", n); + cce = "HS: Server did not return 200 or 304"; + goto bail2; + } + +#ifdef LWS_WITH_HTTP_PROXY + wsi->perform_rewrite = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE), + "text/html", 9)) + wsi->perform_rewrite = 1; + } +#endif + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* he may choose to send us stuff in chunked transfer-coding */ + wsi->chunked = 0; + wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { + wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING), + "chunked"); + /* first thing is hex, after payload there is crlf */ + wsi->chunk_parser = ELCP_HEX; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + wsi->u.http.content_length = + atoi(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + lwsl_notice("%s: incoming content length %d\n", __func__, + wsi->u.http.content_length); + wsi->u.http.content_remain = wsi->u.http.content_length; + } else /* can't do 1.1 without a content length or chunked */ + if (!wsi->chunked) + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + + cce = "HS: disallowed by client filter"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* call him back to inform him he is up */ + if (wsi->protocol->callback(wsi, + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + cce = "HS: disallowed at ESTABLISHED"; + goto bail3; + } + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lwsl_notice("%s: client connection up\n", __func__); + + return 0; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { + lwsl_info("no ACCEPT\n"); + cce = "HS: ACCEPT missing"; + goto bail3; + } + + if (p && strncmp(p, "101", 3)) { + lwsl_warn( + "lws_client_handshake: got bad HTTP response '%s'\n", p); + cce = "HS: ws upgrade response not 101"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); + if (!p) { + lwsl_info("no UPGRADE\n"); + cce = "HS: UPGRADE missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "websocket")) { + lwsl_warn( + "lws_client_handshake: got bad Upgrade header '%s'\n", p); + cce = "HS: Upgrade to something other than websocket"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION); + if (!p) { + lwsl_info("no Connection hdr\n"); + cce = "HS: CONNECTION missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "upgrade")) { + lwsl_warn("lws_client_int_s_hs: bad header %s\n", p); + cce = "HS: UPGRADE malformed"; + goto bail3; + } + + pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + if (!pc) { + lwsl_parser("lws_client_int_s_hs: no protocol list\n"); + } else + lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc); + + /* + * confirm the protocol the server wants to talk was in the list + * of protocols we offered + */ + + len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + if (!len) { + lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n"); + /* + * no protocol name to work from, + * default to first protocol + */ + wsi->protocol = &wsi->vhost->protocols[0]; + goto check_extensions; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); + len = strlen(p); + + while (pc && *pc && !okay) { + if (!strncmp(pc, p, len) && + (pc[len] == ',' || pc[len] == '\0')) { + okay = 1; + continue; + } + while (*pc && *pc++ != ',') + ; + while (*pc && *pc == ' ') + pc++; + } + + if (!okay) { + lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p); + cce = "HS: PROTOCOL malformed"; + goto bail2; + } + + /* + * identify the selected protocol struct and set it + */ + n = 0; + wsi->protocol = NULL; + while (wsi->vhost->protocols[n].callback && !wsi->protocol) { + if (strcmp(p, wsi->vhost->protocols[n].name) == 0) { + wsi->protocol = &wsi->vhost->protocols[n]; + break; + } + n++; + } + + if (wsi->protocol == NULL) { + lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p); + cce = "HS: Cannot match protocol"; + goto bail2; + } + + + /* + * stitch protocol choice into the vh protocol linked list + * We always insert ourselves at the start of the list + * + * X <-> B + * X <-> pAn <-> pB + */ + //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n", + // __func__, + // wsi->vhost->same_vh_protocol_list[n], + // wsi->same_vh_protocol_prev); + wsi->same_vh_protocol_prev = /* guy who points to us */ + &wsi->vhost->same_vh_protocol_list[n]; + wsi->same_vh_protocol_next = /* old first guy is our next */ + wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; + +check_extensions: +#ifndef LWS_NO_EXTENSIONS + /* instantiate the accepted extensions */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { + lwsl_ext("no client extensions allowed by server\n"); + goto check_accept; + } + + /* + * break down the list of server accepted extensions + * and go through matching them or identifying bogons + */ + + if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) { + lwsl_warn("ext list from server failed to copy\n"); + cce = "HS: EXT: list too big"; + goto bail2; + } + + c = sb; + n = 0; + ignore = 0; + a = NULL; + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') { + ignore = 1; + if (!a) + a = c + 1; + } + if (ignore || *c == ' ') { + c++; + continue; + } + + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check we actually support it */ + + lwsl_notice("checking client ext %s\n", ext_name); + + n = 0; + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + n = 1; + lwsl_notice("instantiating client ext %s\n", ext_name); + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->count_act_ext] = ext; + + /* allow him to construct his ext instance */ + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_CLIENT_CONSTRUCT, + (void *)&wsi->act_ext_user[wsi->count_act_ext], + (void *)&opts, 0)) { + lwsl_notice(" ext %s failed construction\n", ext_name); + ext++; + continue; + } + + /* + * allow the user code to override ext defaults if it + * wants to + */ + ext_name[0] = '\0'; + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, + (char *)ext->name, ext_name, + sizeof(ext_name))) { + cce = "HS: EXT: failed setting defaults"; + goto bail2; + } + + if (ext_name[0] && + lws_ext_parse_options(ext, wsi, wsi->act_ext_user[ + wsi->count_act_ext], opts, ext_name, + strlen(ext_name))) { + lwsl_err("%s: unable to parse user defaults '%s'", + __func__, ext_name); + cce = "HS: EXT: failed parsing defaults"; + goto bail2; + } + + /* + * give the extension the server options + */ + if (a && lws_ext_parse_options(ext, wsi, + wsi->act_ext_user[wsi->count_act_ext], + opts, a, c - a)) { + lwsl_err("%s: unable to parse remote def '%s'", + __func__, a); + cce = "HS: EXT: failed parsing options"; + goto bail2; + } + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_OPTION_CONFIRM, + wsi->act_ext_user[wsi->count_act_ext], + NULL, 0)) { + lwsl_err("%s: ext %s rejects server options %s", + ext->name, a); + cce = "HS: EXT: Rejects server options"; + goto bail2; + } + + wsi->count_act_ext++; + + ext++; + } + + if (n == 0) { + lwsl_warn("Unknown ext '%s'!\n", ext_name); + cce = "HS: EXT: unknown ext"; + goto bail2; + } + + a = NULL; + n = 0; + } + +check_accept: +#endif + + /* + * Confirm his accept token is the one we precomputed + */ + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); + if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) { + lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, + wsi->u.hdr.ah->initial_handshake_hash_base64); + cce = "HS: Accept hash wrong"; + goto bail2; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected by filter cb"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lws_union_transition(wsi, LWSCM_WS_CLIENT); + wsi->state = LWSS_ESTABLISHED; + lws_restart_ws_ping_pong_timer(wsi); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, then + * use a big default for compatibility + */ + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + cce = "HS: OOM"; + goto bail2; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_info("Allocating client RX buffer %d\n", n); + + if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, + sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + cce = "HS: SO_SNDBUF failed"; + goto bail3; + } + + lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); + + /* call him back to inform him he is up */ + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected at CLIENT_ESTABLISHED"; + goto bail3; + } +#ifndef LWS_NO_EXTENSIONS + /* + * inform all extensions, not just active ones since they + * already know + */ + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + v = NULL; + for (n = 0; n < wsi->count_act_ext; n++) + if (wsi->active_extensions[n] == ext) + v = wsi->act_ext_user[n]; + + ext->callback(context, ext, wsi, + LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0); + ext++; + } +#endif + + return 0; + +bail3: + close_reason = LWS_CLOSE_STATUS_NOSTATUS; + +bail2: + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, + (unsigned int)strlen(cce)); + wsi->already_did_cce = 1; + + lwsl_info("closing connection due to bail2 connection error\n"); + + /* closing will free up his parsing allocations */ + lws_close_free_wsi(wsi, close_reason); + + return 1; +} + + +char * +lws_generate_client_handshake(struct lws *wsi, char *pkt) +{ + char buf[128], hash[20], key_b64[40], *p = pkt; + struct lws_context *context = wsi->context; + const char *meth; + int n; +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *ext; + int ext_count = 0; +#endif + + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (!meth) { + meth = "GET"; + wsi->do_ws = 1; + } else + wsi->do_ws = 0; + + if (wsi->do_ws) { + /* + * create the random key + */ + n = lws_get_random(context, hash, 16); + if (n != 16) { + lwsl_err("Unable to read from random dev %s\n", + SYSTEM_RANDOM_FILEPATH); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return NULL; + } + + lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); + } + + /* + * 04 example client handshake + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + */ + + p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth, + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); + + p += sprintf(p, "Pragma: no-cache\x0d\x0a" + "Cache-Control: no-cache\x0d\x0a"); + + p += sprintf(p, "Host: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); + + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) + p += sprintf(p, "Origin: http://%s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); + + if (wsi->do_ws) { + p += sprintf(p, "Upgrade: websocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Key: "); + strcpy(p, key_b64); + p += strlen(key_b64); + p += sprintf(p, "\x0d\x0a"); + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) + p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); + + /* tell the server what extensions we could support */ + +#ifndef LWS_NO_EXTENSIONS + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + n = lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, + (char *)ext->name, 0); + if (n) { /* an extension vetos us */ + lwsl_ext("ext %s vetoed\n", (char *)ext->name); + ext++; + continue; + } + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, + wsi->user_space, (char *)ext->name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *p++ = ','; + else + p += sprintf(p, "Sec-WebSocket-Extensions: "); + p += sprintf(p, "%s", ext->client_offer); + ext_count++; + + ext++; + } + if (ext_count) + p += sprintf(p, "\x0d\x0a"); +#endif + + if (wsi->ietf_spec_revision) + p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", + wsi->ietf_spec_revision); + + /* prepare the expected server accept response */ + + key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ + n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); + + lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); + + lws_b64_encode_string(hash, 20, + wsi->u.hdr.ah->initial_handshake_hash_base64, + sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); + } + + /* give userland a chance to append, eg, cookies */ + + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12); + + p += sprintf(p, "\x0d\x0a"); + + return p; +} + diff --git a/feeds/wlan-ap/libwebsocket/src/context.c b/feeds/wlan-ap/libwebsocket/src/context.c new file mode 100644 index 00000000..ec95eadc --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/context.c @@ -0,0 +1,928 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#ifndef LWS_BUILD_HASH +#define LWS_BUILD_HASH "unknown-build-hash" +#endif + +static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; + +/** + * lws_get_library_version: get version and git hash library built from + * + * returns a const char * to a string like "1.1 178d78c" + * representing the library version followed by the git head hash it + * was built from + */ +LWS_VISIBLE const char * +lws_get_library_version(void) +{ + return library_version; +} + +static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" +}; + +LWS_VISIBLE void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot, + int size) +{ + int n = 0; + + /* allocate the vh priv array only on demand */ + if (!vhost->protocol_vh_privs) { + vhost->protocol_vh_privs = (void **)lws_zalloc( + vhost->count_protocols * sizeof(void *)); + if (!vhost->protocol_vh_privs) + return NULL; + } + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) + return NULL; + } + + vhost->protocol_vh_privs[n] = lws_zalloc(size); + return vhost->protocol_vh_privs[n]; +} + +LWS_VISIBLE void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot) +{ + int n = 0; + + if (!vhost->protocol_vh_privs) + return NULL; + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) { + lwsl_err("%s: unknown protocol %p\n", __func__, prot); + return NULL; + } + } + + return vhost->protocol_vh_privs[n]; +} + +static const struct lws_protocol_vhost_options * +lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) +{ + const struct lws_protocol_vhost_options *pvo = vh->pvo; + + while (pvo) { + // lwsl_notice("%s: '%s' '%s'\n", __func__, pvo->name, name); + if (!strcmp(pvo->name, name)) + return pvo; + pvo = pvo->next; + } + + return NULL; +} + +int +lws_protocol_init(struct lws_context *context) +{ + struct lws_vhost *vh = context->vhost_list; + const struct lws_protocol_vhost_options *pvo, *pvo1; + struct lws wsi; + int n; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + lwsl_notice("%s\n", __func__); + + while (vh) { + wsi.vhost = vh; + + /* initialize supported protocols on this vhost */ + + for (n = 0; n < vh->count_protocols; n++) { + wsi.protocol = &vh->protocols[n]; + + pvo = lws_vhost_protocol_options(vh, + vh->protocols[n].name); + if (pvo) { + /* + * linked list of options specific to + * vh + protocol + */ + pvo1 = pvo; + pvo = pvo1->options; + + while (pvo) { + lwsl_notice(" vh %s prot %s opt %s\n", + vh->name, + vh->protocols[n].name, + pvo->name); + + if (!strcmp(pvo->name, "default")) { + lwsl_notice("Setting default " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->default_protocol_index = n; + } + pvo = pvo->next; + } + + pvo = pvo1->options; + } + + /* + * inform all the protocols that they are doing their one-time + * initialization if they want to. + * + * NOTE the wsi is all zeros except for the context, vh and + * protocol ptrs so lws_get_context(wsi) etc can work + */ + if (vh->protocols[n].callback(&wsi, + LWS_CALLBACK_PROTOCOL_INIT, NULL, + (void *)pvo, 0)) + return 1; + } + + vh = vh->vhost_next; + } + + context->protocol_init_done = 1; + lws_finalize_startup(context); + + return 0; +} + +LWS_VISIBLE int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ +#ifdef LWS_WITH_CGI + struct lws_cgi_args *args; + char buf[128]; + int n; +#endif + + switch (reason) { + case LWS_CALLBACK_HTTP: +#ifndef LWS_NO_SERVER + if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) + return -1; + + if (lws_http_transaction_completed(wsi)) +#endif + return -1; + break; + + case LWS_CALLBACK_HTTP_WRITEABLE: +#ifdef LWS_WITH_CGI + if (wsi->reason_bf & 1) { + if (lws_cgi_write_split_stdout_headers(wsi) < 0) + return -1; + + wsi->reason_bf &= ~1; + break; + } +#endif + + break; + +#ifdef LWS_WITH_CGI + /* CGI IO events (POLLIN/OUT) appear here, our default policy is: + * + * - POST data goes on subprocess stdin + * - subprocess stdout goes on http via writeable callback + * - subprocess stderr goes to the logs + */ + case LWS_CALLBACK_CGI: + args = (struct lws_cgi_args *)in; + switch (args->ch) { /* which of stdin/out/err ? */ + case LWS_STDIN: + /* TBD stdin rx flow control */ + break; + case LWS_STDOUT: + wsi->reason_bf |= 1; + /* when writing to MASTER would not block */ + lws_callback_on_writable(wsi); + break; + case LWS_STDERR: + n = read(lws_get_socket_fd(args->stdwsi[LWS_STDERR]), + buf, sizeof(buf) - 2); + if (n > 0) { + if (buf[n - 1] != '\n') + buf[n++] = '\n'; + buf[n] = '\0'; + lwsl_notice("CGI-stderr: %s\n", buf); + } + break; + } + break; + + case LWS_CALLBACK_CGI_TERMINATED: + return -1; + + case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ + args = (struct lws_cgi_args *)in; + args->data[args->len] = '\0'; + n = write(lws_get_socket_fd(args->stdwsi[LWS_STDIN]), + args->data, args->len); + if (n < args->len) + lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + return n; +#endif + default: + break; + } + + return 0; +} + +/* list of supported protocols and callbacks */ + +static const struct lws_protocols protocols_dummy[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + lws_callback_http_dummy, /* callback */ + 0, /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + /* + * the other protocols are provided by lws plugins + */ + { NULL, NULL, 0, 0 } /* terminator */ +}; + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_vhost *vh = lws_zalloc(sizeof(*vh)), + **vh1 = &context->vhost_list; + const struct lws_http_mount *mounts; + const struct lws_protocol_vhost_options *pvo; +#ifdef LWS_WITH_PLUGINS + struct lws_plugin *plugin = context->plugin_list; + struct lws_protocols *lwsp; + int m, f = !info->pvo; +#endif +#ifdef LWS_HAVE_GETENV + char *p; +#endif + int n; + + if (!vh) + return NULL; + + if (!info->protocols) + info->protocols = &protocols_dummy[0]; + + vh->context = context; + if (!info->vhost_name) + vh->name = "default"; + else + vh->name = info->vhost_name; + + vh->iface = info->iface; + for (vh->count_protocols = 0; + info->protocols[vh->count_protocols].callback; + vh->count_protocols++) + ; + + vh->options = info->options; + vh->pvo = info->pvo; + vh->headers = info->headers; + if (info->keepalive_timeout) + vh->keepalive_timeout = info->keepalive_timeout; + else + vh->keepalive_timeout = 5; + +#ifdef LWS_WITH_PLUGINS + if (plugin) { + /* + * give the vhost a unified list of protocols including the + * ones that came from plugins + */ + lwsp = lws_zalloc(sizeof(struct lws_protocols) * + (vh->count_protocols + + context->plugin_protocol_count + 1)); + if (!lwsp) + return NULL; + + m = vh->count_protocols; + memcpy(lwsp, info->protocols, + sizeof(struct lws_protocols) * m); + + /* for compatibility, all protocols enabled on vhost if only + * the default vhost exists. Otherwise only vhosts who ask + * for a protocol get it enabled. + */ + + if (info->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + f = 0; + + while (plugin) { + for (n = 0; n < plugin->caps.count_protocols; n++) { + /* + * for compatibility's sake, no pvo implies + * allow all protocols + */ + if (f || lws_vhost_protocol_options(vh, + plugin->caps.protocols[n].name)) { + memcpy(&lwsp[m], + &plugin->caps.protocols[n], + sizeof(struct lws_protocols)); + m++; + vh->count_protocols++; + } + } + plugin = plugin->list; + } + vh->protocols = lwsp; + } else +#endif + vh->protocols = info->protocols; + + vh->same_vh_protocol_list = (struct lws **) + lws_zalloc(sizeof(struct lws *) * vh->count_protocols); + + vh->mount_list = info->mounts; + +#ifdef LWS_USE_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(context)) { + lwsl_notice("Creating Vhost '%s' path \"%s\", %d protocols\n", + vh->name, info->iface, vh->count_protocols); + } else +#endif + lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n", + vh->name, info->port, vh->count_protocols, LWS_IPV6_ENABLED(vh) ? "on" : "off"); + + mounts = info->mounts; + while (mounts) { + lwsl_notice(" mounting %s%s to %s\n", + mount_protocols[mounts->origin_protocol], + mounts->origin, mounts->mountpoint); + + /* convert interpreter protocol names to pointers */ + pvo = mounts->interpret; + while (pvo) { + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(pvo->value, vh->protocols[n].name)) { + ((struct lws_protocol_vhost_options *)pvo)->value = + (const char *)(long)n; + break; + } + if (n == vh->count_protocols) + lwsl_err("ignoring unknown interpret protocol %s\n", pvo->value); + pvo = pvo->next; + } + + mounts = mounts->mount_next; + } + +#ifndef LWS_NO_EXTENSIONS +#ifdef LWS_WITH_PLUGINS + if (context->plugin_extension_count) { + + m = 0; + while (info->extensions && info->extensions[m].callback) + m++; + + /* + * give the vhost a unified list of extensions including the + * ones that came from plugins + */ + vh->extensions = lws_zalloc(sizeof(struct lws_extension) * + (m + + context->plugin_extension_count + 1)); + if (!vh->extensions) + return NULL; + + memcpy((struct lws_extension *)vh->extensions, info->extensions, + sizeof(struct lws_extension) * m); + plugin = context->plugin_list; + while (plugin) { + memcpy((struct lws_extension *)&vh->extensions[m], + plugin->caps.extensions, + sizeof(struct lws_extension) * + plugin->caps.count_extensions); + m += plugin->caps.count_extensions; + plugin = plugin->list; + } + } else +#endif + vh->extensions = info->extensions; +#endif + + vh->listen_port = info->port; +#if !defined(LWS_WITH_ESP8266) + vh->http_proxy_port = 0; + vh->http_proxy_address[0] = '\0'; + + /* either use proxy from info, or try get it from env var */ + + if (info->http_proxy_address) { + /* override for backwards compatibility */ + if (info->http_proxy_port) + vh->http_proxy_port = info->http_proxy_port; + lws_set_proxy(vh, info->http_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("http_proxy"); + if (p) + lws_set_proxy(vh, p); +#endif + } +#endif + vh->ka_time = info->ka_time; + vh->ka_interval = info->ka_interval; + vh->ka_probes = info->ka_probes; + + if (vh->options & LWS_SERVER_OPTION_STS) + lwsl_notice(" STS enabled\n"); + +#ifdef LWS_WITH_ACCESS_LOG + if (info->log_filepath) { + vh->log_fd = open(info->log_filepath, O_CREAT | O_APPEND | O_RDWR, 0600); + if (vh->log_fd == (int)LWS_INVALID_FILE) { + lwsl_err("unable to open log filepath %s\n", + info->log_filepath); + goto bail; + } +#ifndef WIN32 + if (context->uid != -1) + if (chown(info->log_filepath, context->uid, + context->gid) == -1) + lwsl_err("unable to chown log file %s\n", + info->log_filepath); +#endif + } else + vh->log_fd = (int)LWS_INVALID_FILE; +#endif + + if (lws_context_init_server_ssl(info, vh)) + goto bail; + + if (lws_context_init_client_ssl(info, vh)) + goto bail; + + if (lws_context_init_server(info, vh)) + goto bail; + + while (1) { + if (!(*vh1)) { + *vh1 = vh; + break; + } + vh1 = &(*vh1)->vhost_next; + }; + + return vh; + +bail: + lws_free(vh); + + return NULL; +} + +LWS_VISIBLE int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context_creation_info i; + + memcpy(&i, info, sizeof(i)); + i.port = CONTEXT_PORT_NO_LISTEN; + + return lws_context_init_client_ssl(&i, vhost); +} + + +LWS_VISIBLE struct lws_context * +lws_create_context(struct lws_context_creation_info *info) +{ + struct lws_context *context = NULL; +#ifndef LWS_NO_DAEMONIZE + int pid_daemon = get_daemonize_pid(); +#endif + int n, m; +#if defined(__ANDROID__) + struct rlimit rt; +#endif + + lwsl_notice("Initial logging level %d\n", log_level); + lwsl_notice("Libwebsockets version: %s\n", library_version); +#if LWS_POSIX +#ifdef LWS_USE_IPV6 + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_notice("IPV6 compiled in and enabled\n"); + else + lwsl_notice("IPV6 compiled in but disabled\n"); +#else + lwsl_notice("IPV6 not compiled in\n"); +#endif + lws_feature_status_libev(info); + lws_feature_status_libuv(info); +#endif + lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN); + lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS); + lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP); + lwsl_info(" SPEC_LATEST_SUPPORTED : %u\n", SPEC_LATEST_SUPPORTED); + lwsl_info(" sizeof (*info) : %u\n", sizeof(*info)); +#if LWS_POSIX + lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); +#endif + if (lws_plat_context_early_init()) + return NULL; + + context = lws_zalloc(sizeof(struct lws_context)); + if (!context) { + lwsl_err("No memory for websocket context\n"); + return NULL; + } + if (info->pt_serv_buf_size) + context->pt_serv_buf_size = info->pt_serv_buf_size; + else + context->pt_serv_buf_size = (4096*5); + + context->reject_service_keywords = info->reject_service_keywords; + + context->time_up = time(NULL); +#ifndef LWS_NO_DAEMONIZE + if (pid_daemon) { + context->started_with_parent = pid_daemon; + lwsl_notice(" Started with daemon pid %d\n", pid_daemon); + } +#endif +#if defined(__ANDROID__) + n = getrlimit ( RLIMIT_NOFILE,&rt); + if (-1 == n) { + lwsl_err("Get RLIMIT_NOFILE failed!\n"); + return NULL; + } + context->max_fds = rt.rlim_cur; +#else + context->max_fds = getdtablesize(); +#endif + + if (info->count_threads) + context->count_threads = info->count_threads; + else + context->count_threads = 1; + + if (context->count_threads > LWS_MAX_SMP) + context->count_threads = LWS_MAX_SMP; + + context->token_limits = info->token_limits; + + context->options = info->options; + + if (info->timeout_secs) + context->timeout_secs = info->timeout_secs; + else + context->timeout_secs = AWAITING_TIMEOUT; + + context->ws_ping_pong_interval = info->ws_ping_pong_interval; + + lwsl_info(" default timeout (secs): %u\n", context->timeout_secs); + + if (info->max_http_header_data) + context->max_http_header_data = info->max_http_header_data; + else + if (info->max_http_header_data2) + context->max_http_header_data = + info->max_http_header_data2; + else + context->max_http_header_data = LWS_DEF_HEADER_LEN; + if (info->max_http_header_pool) + context->max_http_header_pool = info->max_http_header_pool; + else + context->max_http_header_pool = LWS_DEF_HEADER_POOL; + + /* + * Allocate the per-thread storage for scratchpad buffers, + * and header data pool + */ + for (n = 0; n < context->count_threads; n++) { + context->pt[n].serv_buf = lws_zalloc(context->pt_serv_buf_size); + if (!context->pt[n].serv_buf) { + lwsl_err("OOM\n"); + return NULL; + } + +#ifdef LWS_USE_LIBUV + context->pt[n].context = context; +#endif + context->pt[n].tid = n; + context->pt[n].http_header_data = lws_malloc(context->max_http_header_data * + context->max_http_header_pool); + if (!context->pt[n].http_header_data) + goto bail; + + context->pt[n].ah_pool = lws_zalloc(sizeof(struct allocated_headers) * + context->max_http_header_pool); + for (m = 0; m < context->max_http_header_pool; m++) + context->pt[n].ah_pool[m].data = + (char *)context->pt[n].http_header_data + + (m * context->max_http_header_data); + if (!context->pt[n].ah_pool) + goto bail; + + lws_pt_mutex_init(&context->pt[n]); + } + + if (info->fd_limit_per_thread) + context->fd_limit_per_thread = info->fd_limit_per_thread; + else + context->fd_limit_per_thread = context->max_fds / + context->count_threads; + + lwsl_notice(" Threads: %d each %d fds\n", context->count_threads, + context->fd_limit_per_thread); + struct lws wsi; + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + if (!info->ka_interval && info->ka_time > 0) { + lwsl_err("info->ka_interval can't be 0 if ka_time used\n"); + return NULL; + } + +#ifdef LWS_USE_LIBEV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; +#endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; +#endif + + lwsl_info(" mem: context: %5u bytes (%d ctx + (%d thr x %d))\n", + sizeof(struct lws_context) + + (context->count_threads * context->pt_serv_buf_size), + sizeof(struct lws_context), + context->count_threads, + context->pt_serv_buf_size); + + lwsl_info(" mem: http hdr rsvd: %5u bytes (%u thr x (%u + %u) x %u))\n", + (context->max_http_header_data + + sizeof(struct allocated_headers)) * + context->max_http_header_pool * context->count_threads, + context->count_threads, + context->max_http_header_data, + sizeof(struct allocated_headers), + context->max_http_header_pool); + n = sizeof(struct lws_pollfd) * context->count_threads * + context->fd_limit_per_thread; + context->pt[0].fds = lws_zalloc(n); + if (context->pt[0].fds == NULL) { + lwsl_err("OOM allocating %d fds\n", context->max_fds); + goto bail; + } + lwsl_info(" mem: pollfd map: %5u\n", n); + + if (info->server_string) { + context->server_string = info->server_string; + context->server_string_len = (short) + strlen(context->server_string); + } else { + context->server_string = "libwebsockets"; + context->server_string_len = 13; + } + +#if LWS_MAX_SMP > 1 + /* each thread serves his own chunk of fds */ + for (n = 1; n < (int)info->count_threads; n++) + context->pt[n].fds = context->pt[n - 1].fds + + context->fd_limit_per_thread; +#endif + + if (lws_plat_init(context, info)) + goto bail; + + lws_context_init_ssl_library(info); + + context->user_space = info->user; + + /* + * if he's not saying he'll make his own vhosts later then act + * compatibly and make a default vhost using the data in the info + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + if (!lws_create_vhost(context, info)) { + lwsl_err("Failed to create default vhost\n"); + return NULL; + } + + lws_context_init_extensions(info, context); + + lwsl_notice(" mem: per-conn: %5u bytes + protocol rx buf\n", + sizeof(struct lws)); + + strcpy(context->canonical_hostname, "unknown"); + lws_server_get_canonical_hostname(context, info); + + context->uid = info->uid; + context->gid = info->gid; + + /* + * drop any root privs for this process + * to listen on port < 1023 we would have needed root, but now we are + * listening, we don't want the power for anything else + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(info); + + /* + * give all extensions a chance to create any per-context + * allocations they need + */ + if (info->port != CONTEXT_PORT_NO_LISTEN) { + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + } else + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + + return context; + +bail: + lws_context_destroy(context); + return NULL; +} + +LWS_VISIBLE void +lws_context_destroy(struct lws_context *context) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context_per_thread *pt; + struct lws_vhost *vh = NULL, *vh1; + struct lws wsi; + int n, m; + + lwsl_notice("%s\n", __func__); + + if (!context) + return; + + m = context->count_threads; + context->being_destroyed = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + +#ifdef LWS_LATENCY + if (context->worst_latency_info[0]) + lwsl_notice("Worst latency: %s\n", context->worst_latency_info); +#endif + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi1 = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi1) + continue; + + lws_close_free_wsi(wsi1, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + lws_pt_mutex_destroy(pt); + } + /* + * give all extensions a chance to clean up any per-context + * allocations they might have made + */ + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0); + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0); + + /* + * inform all the protocols that they are done and will have no more + * callbacks. + * + * We can't free things until after the event loop shuts down. + */ + if (context->protocol_init_done) + vh = context->vhost_list; + while (vh) { + wsi.vhost = vh; + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + wsi.protocol = protocol; + protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + n++; + } + } + + vh = vh->vhost_next; + } + + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + + lws_libev_destroyloop(context, n); + lws_libuv_destroyloop(context, n); + + lws_free_set_NULL(context->pt[n].serv_buf); + if (pt->ah_pool) + lws_free(pt->ah_pool); + if (pt->http_header_data) + lws_free(pt->http_header_data); + } + lws_plat_context_early_destroy(context); + lws_ssl_context_destroy(context); + + if (context->pt[0].fds) + lws_free_set_NULL(context->pt[0].fds); + + /* free all the vhost allocations */ + + vh = context->vhost_list; + while (vh) { + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + if (vh->protocol_vh_privs && + vh->protocol_vh_privs[n]) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + } + protocol++; + n++; + } + } + if (vh->protocol_vh_privs) + lws_free(vh->protocol_vh_privs); + lws_ssl_SSL_CTX_destroy(vh); + lws_free(vh->same_vh_protocol_list); +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_free((void *)vh->protocols); +#ifndef LWS_NO_EXTENSIONS + if (context->plugin_extension_count) + lws_free((void *)vh->extensions); +#endif +#endif +#ifdef LWS_WITH_ACCESS_LOG + if (vh->log_fd != (int)LWS_INVALID_FILE) + close(vh->log_fd); +#endif + + vh1 = vh->vhost_next; + lws_free(vh); + vh = vh1; + } + + lws_plat_context_late_destroy(context); + + lws_free(context); +} diff --git a/feeds/wlan-ap/libwebsocket/src/getifaddrs.c b/feeds/wlan-ap/libwebsocket/src/getifaddrs.c new file mode 100644 index 00000000..ed603f45 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/getifaddrs.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#if !LWS_HAVE_GETIFADDRS + +#include +#include +#include +#include +#include +#include +#include +#include +#include "websocket/private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_SOCKIO_H +#include +#endif + +#ifdef LWS_HAVE_NETINET_IN6_VAR_H +#include +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#include "websocket/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); + 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)); + + (*end)->ifa_next = NULL; + (*end)->ifa_name = strdup(ifr->ifr_name); + (*end)->ifa_flags = ifreq.ifr_flags; + (*end)->ifa_addr = lws_malloc(salen); + 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)); + 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)); + 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 - ((long)sa->sa_data - (long)&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 +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/handshake.c b/feeds/wlan-ap/libwebsocket/src/handshake.c new file mode 100644 index 00000000..1d188f9e --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/handshake.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +/* + * -04 of the protocol (actually the 80th version) has a radically different + * handshake. The 04 spec gives the following idea + * + * The handshake from the client looks as follows: + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + * + * The handshake from the server looks as follows: + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + */ + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * We have to take care about parsing because the headers may be split + * into multiple fragments. They may contain unknown headers with arbitrary + * argument lengths. So, we parse using a single-character at a time state + * machine that is completely independent of packet size. + * + * Returns <0 for error or length of chars consumed from buf (up to len) + */ + +LWS_VISIBLE int +lws_read(struct lws *wsi, unsigned char *buf, size_t len) +{ + unsigned char *last_char, *oldbuf = buf; + int body_chunk_len; + size_t n; + + lwsl_debug("%s: incoming len %d state %d\n", __func__, (int)len, wsi->state); + + switch (wsi->state) { +#ifdef LWS_USE_HTTP2 + case LWSS_HTTP2_AWAIT_CLIENT_PREFACE: + case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS: + case LWSS_HTTP2_ESTABLISHED: + n = 0; + while (n < len) { + /* + * we were accepting input but now we stopped doing so + */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) { + lws_rxflow_cache(wsi, buf, n, len); + + return 1; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) + wsi->rxflow_pos++; + if (lws_http2_parser(wsi, buf[n++])) { + lwsl_debug("%s: http2_parser bailed\n", __func__); + goto bail; + } + } + break; +#endif + + case LWSS_CLIENT_HTTP_ESTABLISHED: + break; + + case LWSS_HTTP: + wsi->hdr_parsing_completed = 0; + /* fallthru */ + case LWSS_HTTP_ISSUING_FILE: + wsi->state = LWSS_HTTP_HEADERS; + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + /* fallthru */ + case LWSS_HTTP_HEADERS: + if (!wsi->u.hdr.ah) { + lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__); + assert(0); + } + lwsl_parser("issuing %d bytes to parser\n", (int)len); + + if (lws_handshake_client(wsi, &buf, len)) + goto bail; + + last_char = buf; + if (lws_handshake_server(wsi, &buf, len)) + /* Handshake indicates this session is done. */ + goto bail; + + /* + * It's possible that we've exhausted our data already, or + * rx flow control has stopped us dealing with this early, + * but lws_handshake_server doesn't update len for us. + * Figure out how much was read, so that we can proceed + * appropriately: + */ + len -= (buf - last_char); + lwsl_debug("%s: thinks we have used %d\n", __func__, len); + + if (!wsi->hdr_parsing_completed) + /* More header content on the way */ + goto read_ok; + + switch (wsi->state) { + case LWSS_HTTP: + case LWSS_HTTP_HEADERS: + goto read_ok; + case LWSS_HTTP_ISSUING_FILE: + goto read_ok; + case LWSS_HTTP_BODY: + wsi->u.http.content_remain = + wsi->u.http.content_length; + if (wsi->u.http.content_remain) + goto http_postbody; + + /* there is no POST content */ + goto postbody_completion; + default: + break; + } + break; + + case LWSS_HTTP_BODY: +http_postbody: + while (len && wsi->u.http.content_remain) { + /* Copy as much as possible, up to the limit of: + * what we have in the read buffer (len) + * remaining portion of the POST body (content_remain) + */ + body_chunk_len = min(wsi->u.http.content_remain,len); + wsi->u.http.content_remain -= body_chunk_len; + len -= body_chunk_len; +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + struct lws_cgi_args args; + + args.ch = LWS_STDIN; + args.stdwsi = &wsi->cgi->stdwsi[0]; + args.data = buf; + args.len = body_chunk_len; + + /* returns how much used */ + n = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_CGI_STDIN_DATA, + wsi->user_space, + (void *)&args, 0); + if ((int)n < 0) + goto bail; + } else { +#endif + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY, wsi->user_space, + buf, body_chunk_len); + if (n) + goto bail; + n = body_chunk_len; +#ifdef LWS_WITH_CGI + } +#endif + buf += n; + + if (wsi->u.http.content_remain) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); + break; + } + /* he sent all the content in time */ +postbody_completion: +#ifdef LWS_WITH_CGI + /* if we're running a cgi, we can't let him off the hook just because he sent his POST data */ + if (wsi->cgi) + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, wsi->context->timeout_secs); + else +#endif + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); +#ifdef LWS_WITH_CGI + if (!wsi->cgi) +#endif + { + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY_COMPLETION, + wsi->user_space, NULL, 0); + if (n) + goto bail; + } + + break; + } + break; + + case LWSS_ESTABLISHED: + case LWSS_AWAITING_CLOSE_ACK: + case LWSS_SHUTDOWN: + if (lws_handshake_client(wsi, &buf, len)) + goto bail; + switch (wsi->mode) { + case LWSCM_WS_SERVING: + + if (lws_interpret_incoming_packet(wsi, &buf, len) < 0) { + lwsl_info("interpret_incoming_packet has bailed\n"); + goto bail; + } + break; + } + break; + default: + lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state); + break; + } + +read_ok: + /* Nothing more to do for now */ + lwsl_info("%s: read_ok, used %d\n", __func__, buf - oldbuf); + + return buf - oldbuf; + +bail: + //lwsl_notice("closing connection at lws_read bail:\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return -1; +} diff --git a/feeds/wlan-ap/libwebsocket/src/header.c b/feeds/wlan-ap/libwebsocket/src/header.c new file mode 100644 index 00000000..4820b786 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/header.c @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#include "websocket/lextable-strings.h" + + +const unsigned char *lws_token_to_string(enum lws_token_indexes token) +{ + if ((unsigned int)token >= ARRAY_SIZE(set)) + return NULL; + + return (unsigned char *)set[token]; +} + +int +lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end) +{ +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_by_name(wsi, name, + value, length, p, end); +#else + (void)wsi; +#endif + if (name) { + while (*p < end && *name) + *((*p)++) = *name++; + if (*p == end) + return 1; + *((*p)++) = ' '; + } + if (*p + length + 3 >= end) + return 1; + + memcpy(*p, value, length); + *p += length; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + + return 0; +} + +int lws_finalize_http_header(struct lws *wsi, unsigned char **p, + unsigned char *end) +{ +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return 0; +#else + (void)wsi; +#endif + if ((long)(end - *p) < 3) + return 1; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + + return 0; +} + +int +lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end) +{ + const unsigned char *name; +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_by_token(wsi, token, value, length, p, end); +#endif + name = lws_token_to_string(token); + if (!name) + return 1; + return lws_add_http_header_by_name(wsi, name, value, length, p, end); +} + +int lws_add_http_header_content_length(struct lws *wsi, + unsigned long content_length, + unsigned char **p, unsigned char *end) +{ + char b[24]; + int n; + + n = sprintf(b, "%lu", content_length); + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)b, n, p, end)) + return 1; + wsi->u.http.content_length = content_length; + wsi->u.http.content_remain = content_length; + + return 0; +} + +STORE_IN_ROM static const char * const err400[] = { + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Auth Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request URI too Long", + "Unsupported Media Type", + "Requested Range Not Satisfiable", + "Expectation Failed" +}; + +STORE_IN_ROM static const char * const err500[] = { + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported" +}; + +int +lws_add_http_header_status(struct lws *wsi, unsigned int code, + unsigned char **p, unsigned char *end) +{ + const struct lws_protocol_vhost_options *headers; + unsigned char code_and_desc[60]; + const char *description = "", *p1; + int n; + STORE_IN_ROM static const char * const hver[] = { + "HTTP/1.0", "HTTP/1.1", "HTTP/2" + }; + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.response = code; +#endif + +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_status(wsi, code, p, end); +#endif + if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + ARRAY_SIZE(err500))) + description = err500[code - 500]; + + if (code == 200) + description = "OK"; + + if (code == 304) + description = "Not Modified"; + else + if (code >= 300 && code < 400) + description = "Redirect"; + + if (wsi->u.http.request_version < ARRAY_SIZE(hver)) + p1 = hver[wsi->u.http.request_version]; + else + p1 = hver[0]; + + n = sprintf((char *)code_and_desc, "%s %u %s", + p1, code, description); + + if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, + n, p, end)) + return 1; + + headers = wsi->vhost->headers; + while (headers) { + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)headers->name, + (unsigned char *)headers->value, + strlen(headers->value), p, end)) + return 1; + + headers = headers->next; + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, + (unsigned char *) + wsi->context->server_string, + wsi->context->server_string_len, + p, end)) + return 1; + + if (wsi->vhost->options & LWS_SERVER_OPTION_STS) + if (lws_add_http_header_by_name(wsi, (unsigned char *) + "Strict-Transport-Security:", + (unsigned char *)"max-age=15768000 ; " + "includeSubDomains", 36, p, end)) + return 1; + + return 0; +} + +LWS_VISIBLE int +lws_return_http_status(struct lws *wsi, unsigned int code, + const char *html_body) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + unsigned char *p = pt->serv_buf + LWS_PRE; + unsigned char *start = p, *body = p + 512; + unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; + int n, m, len; + char slen[20]; + + if (!html_body) + html_body = ""; + + len = sprintf((char *)body, "

%u

%s", + code, html_body); + + if (lws_add_http_header_status(wsi, code, &p, end)) + return 1; + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"text/html", 9, + &p, end)) + return 1; + n = sprintf(slen, "%d", len); + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)slen, n, + &p, end)) + return 1; + + if (lws_finalize_http_header(wsi, &p, end)) + return 1; + + m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + if (m != (int)(p - start)) + return 1; + + m = lws_write(wsi, body, len, LWS_WRITE_HTTP); + + return m != n; +} + +LWS_VISIBLE int +lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, + unsigned char **p, unsigned char *end) +{ + unsigned char *start = *p; + int n; + + if (lws_add_http_header_status(wsi, code, p, end)) + return -1; + + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_LOCATION, + loc, len, p, end)) + return -1; + /* + * if we're going with http/1.1 and keepalive, + * we have to give fake content metadata so the + * client knows we completed the transaction and + * it can do the redirect... + */ + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"text/html", 9, + p, end)) + return -1; + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)"0", 1, p, end)) + return -1; + + if (lws_finalize_http_header(wsi, p, end)) + return -1; + + n = lws_write(wsi, start, *p - start, + LWS_WRITE_HTTP_HEADERS); + + return n; +} diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/getifaddrs.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/getifaddrs.h new file mode 100644 index 00000000..4337c1aa --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/getifaddrs.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef LWS_HAVE_GETIFADDRS +#define LWS_HAVE_GETIFADDRS 0 +#endif + +#if LWS_HAVE_GETIFADDRS +#include +#include +#else +#ifdef __cplusplus +extern "C" { +#endif + +#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/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable-strings.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable-strings.h new file mode 100644 index 00000000..a0d713ca --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable-strings.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* set of parsable strings -- ALL LOWER CASE */ + +#if !defined(STORE_IN_ROM) +#define STORE_IN_ROM +#endif + +STORE_IN_ROM static const char * const set[] = { + "get ", + "post ", + "options ", + "host:", + "connection:", + "upgrade:", + "origin:", + "sec-websocket-draft:", + "\x0d\x0a", + + "sec-websocket-extensions:", + "sec-websocket-key1:", + "sec-websocket-key2:", + "sec-websocket-protocol:", + + "sec-websocket-accept:", + "sec-websocket-nonce:", + "http/1.1 ", + "http2-settings:", + + "accept:", + "access-control-request-headers:", + "if-modified-since:", + "if-none-match:", + "accept-encoding:", + "accept-language:", + "pragma:", + "cache-control:", + "authorization:", + "cookie:", + "content-length:", + "content-type:", + "date:", + "range:", + "referer:", + "sec-websocket-key:", + "sec-websocket-version:", + "sec-websocket-origin:", + + ":authority", + ":method", + ":path", + ":scheme", + ":status", + + "accept-charset:", + "accept-ranges:", + "access-control-allow-origin:", + "age:", + "allow:", + "content-disposition:", + "content-encoding:", + "content-language:", + "content-location:", + "content-range:", + "etag:", + "expect:", + "expires:", + "from:", + "if-match:", + "if-range:", + "if-unmodified-since:", + "last-modified:", + "link:", + "location:", + "max-forwards:", + "proxy-authenticate:", + "proxy-authorization:", + "refresh:", + "retry-after:", + "server:", + "set-cookie:", + "strict-transport-security:", + "transfer-encoding:", + "user-agent:", + "vary:", + "via:", + "www-authenticate:", + + "patch", + "put", + "delete", + + "uri-args", /* fake header used for uri-only storage */ + + "proxy ", + "x-real-ip:", + "http/1.0 ", + + "", /* not matchable */ + +}; diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable.h new file mode 100644 index 00000000..5cdfbbf5 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/lextable.h @@ -0,0 +1,780 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, + 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, + 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */, + 0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */, + 0x63 /* 'c' */, 0x66, 0x00 /* (to 0x0072 state 23) */, + 0x75 /* 'u' */, 0x81, 0x00 /* (to 0x0090 state 34) */, + 0x73 /* 's' */, 0x97, 0x00 /* (to 0x00A9 state 48) */, + 0x0D /* '.' */, 0xD0, 0x00 /* (to 0x00E5 state 68) */, + 0x61 /* 'a' */, 0x28, 0x01 /* (to 0x0140 state 129) */, + 0x69 /* 'i' */, 0x67, 0x01 /* (to 0x0182 state 163) */, + 0x64 /* 'd' */, 0x10, 0x02 /* (to 0x022E state 265) */, + 0x72 /* 'r' */, 0x19, 0x02 /* (to 0x023A state 270) */, + 0x3A /* ':' */, 0x4A, 0x02 /* (to 0x026E state 299) */, + 0x65 /* 'e' */, 0xD6, 0x02 /* (to 0x02FD state 409) */, + 0x66 /* 'f' */, 0xF2, 0x02 /* (to 0x031C state 425) */, + 0x6C /* 'l' */, 0x14, 0x03 /* (to 0x0341 state 458) */, + 0x6D /* 'm' */, 0x37, 0x03 /* (to 0x0367 state 484) */, + 0x74 /* 't' */, 0xA6, 0x03 /* (to 0x03D9 state 578) */, + 0x76 /* 'v' */, 0xC1, 0x03 /* (to 0x03F7 state 606) */, + 0x77 /* 'w' */, 0xCE, 0x03 /* (to 0x0407 state 614) */, + 0x78 /* 'x' */, 0xF5, 0x03 /* (to 0x0431 state 650) */, + 0x08, /* fail */ +/* pos 0040: 1 */ 0xE5 /* 'e' -> */, +/* pos 0041: 2 */ 0xF4 /* 't' -> */, +/* pos 0042: 3 */ 0xA0 /* ' ' -> */, +/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, + 0x72 /* 'r' */, 0x8C, 0x01 /* (to 0x01D4 state 211) */, + 0x61 /* 'a' */, 0xCE, 0x03 /* (to 0x0419 state 631) */, + 0x75 /* 'u' */, 0xD0, 0x03 /* (to 0x041E state 635) */, + 0x08, /* fail */ +/* pos 0052: 6 */ 0xF3 /* 's' -> */, +/* pos 0053: 7 */ 0xF4 /* 't' -> */, +/* pos 0054: 8 */ 0xA0 /* ' ' -> */, +/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */, + 0x72 /* 'r' */, 0x48, 0x00 /* (to 0x00A2 state 42) */, + 0x08, /* fail */ +/* pos 005e: 11 */ 0xF4 /* 't' -> */, +/* pos 005f: 12 */ 0xE9 /* 'i' -> */, +/* pos 0060: 13 */ 0xEF /* 'o' -> */, +/* pos 0061: 14 */ 0xEE /* 'n' -> */, +/* pos 0062: 15 */ 0xF3 /* 's' -> */, +/* pos 0063: 16 */ 0xA0 /* ' ' -> */, +/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 0066: 18 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 19) */, + 0x74 /* 't' */, 0xB6, 0x00 /* (to 0x011F state 110) */, + 0x08, /* fail */ +/* pos 006d: 19 */ 0xF3 /* 's' -> */, +/* pos 006e: 20 */ 0xF4 /* 't' -> */, +/* pos 006f: 21 */ 0xBA /* ':' -> */, +/* pos 0070: 22 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0072: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0079 state 24) */, + 0x61 /* 'a' */, 0x6C, 0x01 /* (to 0x01E1 state 217) */, + 0x08, /* fail */ +/* pos 0079: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0080 state 25) */, + 0x6F /* 'o' */, 0x81, 0x01 /* (to 0x01FD state 243) */, + 0x08, /* fail */ +/* pos 0080: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0087 state 26) */, + 0x74 /* 't' */, 0x80, 0x01 /* (to 0x0203 state 248) */, + 0x08, /* fail */ +/* pos 0087: 26 */ 0xE5 /* 'e' -> */, +/* pos 0088: 27 */ 0xE3 /* 'c' -> */, +/* pos 0089: 28 */ 0xF4 /* 't' -> */, +/* pos 008a: 29 */ 0xE9 /* 'i' -> */, +/* pos 008b: 30 */ 0xEF /* 'o' -> */, +/* pos 008c: 31 */ 0xEE /* 'n' -> */, +/* pos 008d: 32 */ 0xBA /* ':' -> */, +/* pos 008e: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 0090: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x009A state 35) */, + 0x73 /* 's' */, 0x59, 0x03 /* (to 0x03EC state 596) */, + 0x72 /* 'r' */, 0x91, 0x03 /* (to 0x0427 state 642) */, + 0x08, /* fail */ +/* pos 009a: 35 */ 0xE7 /* 'g' -> */, +/* pos 009b: 36 */ 0xF2 /* 'r' -> */, +/* pos 009c: 37 */ 0xE1 /* 'a' -> */, +/* pos 009d: 38 */ 0xE4 /* 'd' -> */, +/* pos 009e: 39 */ 0xE5 /* 'e' -> */, +/* pos 009f: 40 */ 0xBA /* ':' -> */, +/* pos 00a0: 41 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 00a2: 42 */ 0xE9 /* 'i' -> */, +/* pos 00a3: 43 */ 0xE7 /* 'g' -> */, +/* pos 00a4: 44 */ 0xE9 /* 'i' -> */, +/* pos 00a5: 45 */ 0xEE /* 'n' -> */, +/* pos 00a6: 46 */ 0xBA /* ':' -> */, +/* pos 00a7: 47 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 00a9: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B0 state 49) */, + 0x74 /* 't' */, 0x13, 0x03 /* (to 0x03BF state 553) */, + 0x08, /* fail */ +/* pos 00b0: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00BA state 50) */, + 0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03AF state 539) */, + 0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03B5 state 544) */, + 0x08, /* fail */ +/* pos 00ba: 50 */ 0xAD /* '-' -> */, +/* pos 00bb: 51 */ 0xF7 /* 'w' -> */, +/* pos 00bc: 52 */ 0xE5 /* 'e' -> */, +/* pos 00bd: 53 */ 0xE2 /* 'b' -> */, +/* pos 00be: 54 */ 0xF3 /* 's' -> */, +/* pos 00bf: 55 */ 0xEF /* 'o' -> */, +/* pos 00c0: 56 */ 0xE3 /* 'c' -> */, +/* pos 00c1: 57 */ 0xEB /* 'k' -> */, +/* pos 00c2: 58 */ 0xE5 /* 'e' -> */, +/* pos 00c3: 59 */ 0xF4 /* 't' -> */, +/* pos 00c4: 60 */ 0xAD /* '-' -> */, +/* pos 00c5: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00DE state 62) */, + 0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00E8 state 70) */, + 0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00F4 state 81) */, + 0x70 /* 'p' */, 0x38, 0x00 /* (to 0x0106 state 88) */, + 0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0110 state 97) */, + 0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0118 state 104) */, + 0x76 /* 'v' */, 0x86, 0x01 /* (to 0x025D state 284) */, + 0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x0266 state 292) */, + 0x08, /* fail */ +/* pos 00de: 62 */ 0xF2 /* 'r' -> */, +/* pos 00df: 63 */ 0xE1 /* 'a' -> */, +/* pos 00e0: 64 */ 0xE6 /* 'f' -> */, +/* pos 00e1: 65 */ 0xF4 /* 't' -> */, +/* pos 00e2: 66 */ 0xBA /* ':' -> */, +/* pos 00e3: 67 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 00e5: 68 */ 0x8A /* '.' -> */, +/* pos 00e6: 69 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00e8: 70 */ 0xF8 /* 'x' -> */, +/* pos 00e9: 71 */ 0xF4 /* 't' -> */, +/* pos 00ea: 72 */ 0xE5 /* 'e' -> */, +/* pos 00eb: 73 */ 0xEE /* 'n' -> */, +/* pos 00ec: 74 */ 0xF3 /* 's' -> */, +/* pos 00ed: 75 */ 0xE9 /* 'i' -> */, +/* pos 00ee: 76 */ 0xEF /* 'o' -> */, +/* pos 00ef: 77 */ 0xEE /* 'n' -> */, +/* pos 00f0: 78 */ 0xF3 /* 's' -> */, +/* pos 00f1: 79 */ 0xBA /* ':' -> */, +/* pos 00f2: 80 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00f4: 81 */ 0xE5 /* 'e' -> */, +/* pos 00f5: 82 */ 0xF9 /* 'y' -> */, +/* pos 00f6: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0100 state 84) */, + 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0103 state 86) */, + 0x3A /* ':' */, 0x5F, 0x01 /* (to 0x025B state 283) */, + 0x08, /* fail */ +/* pos 0100: 84 */ 0xBA /* ':' -> */, +/* pos 0101: 85 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 0103: 86 */ 0xBA /* ':' -> */, +/* pos 0104: 87 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 0106: 88 */ 0xF2 /* 'r' -> */, +/* pos 0107: 89 */ 0xEF /* 'o' -> */, +/* pos 0108: 90 */ 0xF4 /* 't' -> */, +/* pos 0109: 91 */ 0xEF /* 'o' -> */, +/* pos 010a: 92 */ 0xE3 /* 'c' -> */, +/* pos 010b: 93 */ 0xEF /* 'o' -> */, +/* pos 010c: 94 */ 0xEC /* 'l' -> */, +/* pos 010d: 95 */ 0xBA /* ':' -> */, +/* pos 010e: 96 */ 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 0110: 97 */ 0xE3 /* 'c' -> */, +/* pos 0111: 98 */ 0xE3 /* 'c' -> */, +/* pos 0112: 99 */ 0xE5 /* 'e' -> */, +/* pos 0113: 100 */ 0xF0 /* 'p' -> */, +/* pos 0114: 101 */ 0xF4 /* 't' -> */, +/* pos 0115: 102 */ 0xBA /* ':' -> */, +/* pos 0116: 103 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 0118: 104 */ 0xEF /* 'o' -> */, +/* pos 0119: 105 */ 0xEE /* 'n' -> */, +/* pos 011a: 106 */ 0xE3 /* 'c' -> */, +/* pos 011b: 107 */ 0xE5 /* 'e' -> */, +/* pos 011c: 108 */ 0xBA /* ':' -> */, +/* pos 011d: 109 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 011f: 110 */ 0xF4 /* 't' -> */, +/* pos 0120: 111 */ 0xF0 /* 'p' -> */, +/* pos 0121: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0128 state 113) */, + 0x32 /* '2' */, 0x10, 0x00 /* (to 0x0134 state 118) */, + 0x08, /* fail */ +/* pos 0128: 113 */ 0xB1 /* '1' -> */, +/* pos 0129: 114 */ 0xAE /* '.' -> */, +/* pos 012a: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x0131 state 116) */, + 0x30 /* '0' */, 0x0F, 0x03 /* (to 0x043C state 660) */, + 0x08, /* fail */ +/* pos 0131: 116 */ 0xA0 /* ' ' -> */, +/* pos 0132: 117 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 0134: 118 */ 0xAD /* '-' -> */, +/* pos 0135: 119 */ 0xF3 /* 's' -> */, +/* pos 0136: 120 */ 0xE5 /* 'e' -> */, +/* pos 0137: 121 */ 0xF4 /* 't' -> */, +/* pos 0138: 122 */ 0xF4 /* 't' -> */, +/* pos 0139: 123 */ 0xE9 /* 'i' -> */, +/* pos 013a: 124 */ 0xEE /* 'n' -> */, +/* pos 013b: 125 */ 0xE7 /* 'g' -> */, +/* pos 013c: 126 */ 0xF3 /* 's' -> */, +/* pos 013d: 127 */ 0xBA /* ':' -> */, +/* pos 013e: 128 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 0140: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x014D state 130) */, + 0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01EF state 230) */, + 0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02C3 state 358) */, + 0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02C7 state 361) */, + 0x08, /* fail */ +/* pos 014d: 130 */ 0xE3 /* 'c' -> */, +/* pos 014e: 131 */ 0xE5 /* 'e' -> */, +/* pos 014f: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x0156 state 133) */, + 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0160 state 136) */, + 0x08, /* fail */ +/* pos 0156: 133 */ 0xF4 /* 't' -> */, +/* pos 0157: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x015E state 135) */, + 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01B3 state 192) */, + 0x08, /* fail */ +/* pos 015e: 135 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 0160: 136 */ 0xF3 /* 's' -> */, +/* pos 0161: 137 */ 0xAD /* '-' -> */, +/* pos 0162: 138 */ 0xE3 /* 'c' -> */, +/* pos 0163: 139 */ 0xEF /* 'o' -> */, +/* pos 0164: 140 */ 0xEE /* 'n' -> */, +/* pos 0165: 141 */ 0xF4 /* 't' -> */, +/* pos 0166: 142 */ 0xF2 /* 'r' -> */, +/* pos 0167: 143 */ 0xEF /* 'o' -> */, +/* pos 0168: 144 */ 0xEC /* 'l' -> */, +/* pos 0169: 145 */ 0xAD /* '-' -> */, +/* pos 016a: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0171 state 147) */, + 0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02B5 state 345) */, + 0x08, /* fail */ +/* pos 0171: 147 */ 0xE5 /* 'e' -> */, +/* pos 0172: 148 */ 0xF1 /* 'q' -> */, +/* pos 0173: 149 */ 0xF5 /* 'u' -> */, +/* pos 0174: 150 */ 0xE5 /* 'e' -> */, +/* pos 0175: 151 */ 0xF3 /* 's' -> */, +/* pos 0176: 152 */ 0xF4 /* 't' -> */, +/* pos 0177: 153 */ 0xAD /* '-' -> */, +/* pos 0178: 154 */ 0xE8 /* 'h' -> */, +/* pos 0179: 155 */ 0xE5 /* 'e' -> */, +/* pos 017a: 156 */ 0xE1 /* 'a' -> */, +/* pos 017b: 157 */ 0xE4 /* 'd' -> */, +/* pos 017c: 158 */ 0xE5 /* 'e' -> */, +/* pos 017d: 159 */ 0xF2 /* 'r' -> */, +/* pos 017e: 160 */ 0xF3 /* 's' -> */, +/* pos 017f: 161 */ 0xBA /* ':' -> */, +/* pos 0180: 162 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 0182: 163 */ 0xE6 /* 'f' -> */, +/* pos 0183: 164 */ 0xAD /* '-' -> */, +/* pos 0184: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x0191 state 166) */, + 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01A7 state 181) */, + 0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0328 state 435) */, + 0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x032F state 441) */, + 0x08, /* fail */ +/* pos 0191: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0198 state 167) */, + 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x0322 state 430) */, + 0x08, /* fail */ +/* pos 0198: 167 */ 0xE4 /* 'd' -> */, +/* pos 0199: 168 */ 0xE9 /* 'i' -> */, +/* pos 019a: 169 */ 0xE6 /* 'f' -> */, +/* pos 019b: 170 */ 0xE9 /* 'i' -> */, +/* pos 019c: 171 */ 0xE5 /* 'e' -> */, +/* pos 019d: 172 */ 0xE4 /* 'd' -> */, +/* pos 019e: 173 */ 0xAD /* '-' -> */, +/* pos 019f: 174 */ 0xF3 /* 's' -> */, +/* pos 01a0: 175 */ 0xE9 /* 'i' -> */, +/* pos 01a1: 176 */ 0xEE /* 'n' -> */, +/* pos 01a2: 177 */ 0xE3 /* 'c' -> */, +/* pos 01a3: 178 */ 0xE5 /* 'e' -> */, +/* pos 01a4: 179 */ 0xBA /* ':' -> */, +/* pos 01a5: 180 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 01a7: 181 */ 0xEF /* 'o' -> */, +/* pos 01a8: 182 */ 0xEE /* 'n' -> */, +/* pos 01a9: 183 */ 0xE5 /* 'e' -> */, +/* pos 01aa: 184 */ 0xAD /* '-' -> */, +/* pos 01ab: 185 */ 0xED /* 'm' -> */, +/* pos 01ac: 186 */ 0xE1 /* 'a' -> */, +/* pos 01ad: 187 */ 0xF4 /* 't' -> */, +/* pos 01ae: 188 */ 0xE3 /* 'c' -> */, +/* pos 01af: 189 */ 0xE8 /* 'h' -> */, +/* pos 01b0: 190 */ 0xBA /* ':' -> */, +/* pos 01b1: 191 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 01b3: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C0 state 193) */, + 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01CA state 202) */, + 0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02A4 state 330) */, + 0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02AD state 338) */, + 0x08, /* fail */ +/* pos 01c0: 193 */ 0xEE /* 'n' -> */, +/* pos 01c1: 194 */ 0xE3 /* 'c' -> */, +/* pos 01c2: 195 */ 0xEF /* 'o' -> */, +/* pos 01c3: 196 */ 0xE4 /* 'd' -> */, +/* pos 01c4: 197 */ 0xE9 /* 'i' -> */, +/* pos 01c5: 198 */ 0xEE /* 'n' -> */, +/* pos 01c6: 199 */ 0xE7 /* 'g' -> */, +/* pos 01c7: 200 */ 0xBA /* ':' -> */, +/* pos 01c8: 201 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 01ca: 202 */ 0xE1 /* 'a' -> */, +/* pos 01cb: 203 */ 0xEE /* 'n' -> */, +/* pos 01cc: 204 */ 0xE7 /* 'g' -> */, +/* pos 01cd: 205 */ 0xF5 /* 'u' -> */, +/* pos 01ce: 206 */ 0xE1 /* 'a' -> */, +/* pos 01cf: 207 */ 0xE7 /* 'g' -> */, +/* pos 01d0: 208 */ 0xE5 /* 'e' -> */, +/* pos 01d1: 209 */ 0xBA /* ':' -> */, +/* pos 01d2: 210 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 01d4: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01DB state 212) */, + 0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x0375 state 497) */, + 0x08, /* fail */ +/* pos 01db: 212 */ 0xE7 /* 'g' -> */, +/* pos 01dc: 213 */ 0xED /* 'm' -> */, +/* pos 01dd: 214 */ 0xE1 /* 'a' -> */, +/* pos 01de: 215 */ 0xBA /* ':' -> */, +/* pos 01df: 216 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 01e1: 217 */ 0xE3 /* 'c' -> */, +/* pos 01e2: 218 */ 0xE8 /* 'h' -> */, +/* pos 01e3: 219 */ 0xE5 /* 'e' -> */, +/* pos 01e4: 220 */ 0xAD /* '-' -> */, +/* pos 01e5: 221 */ 0xE3 /* 'c' -> */, +/* pos 01e6: 222 */ 0xEF /* 'o' -> */, +/* pos 01e7: 223 */ 0xEE /* 'n' -> */, +/* pos 01e8: 224 */ 0xF4 /* 't' -> */, +/* pos 01e9: 225 */ 0xF2 /* 'r' -> */, +/* pos 01ea: 226 */ 0xEF /* 'o' -> */, +/* pos 01eb: 227 */ 0xEC /* 'l' -> */, +/* pos 01ec: 228 */ 0xBA /* ':' -> */, +/* pos 01ed: 229 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 01ef: 230 */ 0xF4 /* 't' -> */, +/* pos 01f0: 231 */ 0xE8 /* 'h' -> */, +/* pos 01f1: 232 */ 0xEF /* 'o' -> */, +/* pos 01f2: 233 */ 0xF2 /* 'r' -> */, +/* pos 01f3: 234 */ 0xE9 /* 'i' -> */, +/* pos 01f4: 235 */ 0xFA /* 'z' -> */, +/* pos 01f5: 236 */ 0xE1 /* 'a' -> */, +/* pos 01f6: 237 */ 0xF4 /* 't' -> */, +/* pos 01f7: 238 */ 0xE9 /* 'i' -> */, +/* pos 01f8: 239 */ 0xEF /* 'o' -> */, +/* pos 01f9: 240 */ 0xEE /* 'n' -> */, +/* pos 01fa: 241 */ 0xBA /* ':' -> */, +/* pos 01fb: 242 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 01fd: 243 */ 0xEB /* 'k' -> */, +/* pos 01fe: 244 */ 0xE9 /* 'i' -> */, +/* pos 01ff: 245 */ 0xE5 /* 'e' -> */, +/* pos 0200: 246 */ 0xBA /* ':' -> */, +/* pos 0201: 247 */ 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 0203: 248 */ 0xE5 /* 'e' -> */, +/* pos 0204: 249 */ 0xEE /* 'n' -> */, +/* pos 0205: 250 */ 0xF4 /* 't' -> */, +/* pos 0206: 251 */ 0xAD /* '-' -> */, +/* pos 0207: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0217 state 253) */, + 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0228 state 260) */, + 0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02CD state 366) */, + 0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02DA state 378) */, + 0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02F6 state 403) */, + 0x08, /* fail */ +/* pos 0217: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0221 state 254) */, + 0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02E4 state 387) */, + 0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02ED state 395) */, + 0x08, /* fail */ +/* pos 0221: 254 */ 0xEE /* 'n' -> */, +/* pos 0222: 255 */ 0xE7 /* 'g' -> */, +/* pos 0223: 256 */ 0xF4 /* 't' -> */, +/* pos 0224: 257 */ 0xE8 /* 'h' -> */, +/* pos 0225: 258 */ 0xBA /* ':' -> */, +/* pos 0226: 259 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 0228: 260 */ 0xF9 /* 'y' -> */, +/* pos 0229: 261 */ 0xF0 /* 'p' -> */, +/* pos 022a: 262 */ 0xE5 /* 'e' -> */, +/* pos 022b: 263 */ 0xBA /* ':' -> */, +/* pos 022c: 264 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 022e: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0235 state 266) */, + 0x65 /* 'e' */, 0xF0, 0x01 /* (to 0x0421 state 637) */, + 0x08, /* fail */ +/* pos 0235: 266 */ 0xF4 /* 't' -> */, +/* pos 0236: 267 */ 0xE5 /* 'e' -> */, +/* pos 0237: 268 */ 0xBA /* ':' -> */, +/* pos 0238: 269 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 023a: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0241 state 271) */, + 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0247 state 276) */, + 0x08, /* fail */ +/* pos 0241: 271 */ 0xEE /* 'n' -> */, +/* pos 0242: 272 */ 0xE7 /* 'g' -> */, +/* pos 0243: 273 */ 0xE5 /* 'e' -> */, +/* pos 0244: 274 */ 0xBA /* ':' -> */, +/* pos 0245: 275 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 0247: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x024E state 277) */, + 0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03A4 state 529) */, + 0x08, /* fail */ +/* pos 024e: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0255 state 278) */, + 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x039E state 524) */, + 0x08, /* fail */ +/* pos 0255: 278 */ 0xF2 /* 'r' -> */, +/* pos 0256: 279 */ 0xE5 /* 'e' -> */, +/* pos 0257: 280 */ 0xF2 /* 'r' -> */, +/* pos 0258: 281 */ 0xBA /* ':' -> */, +/* pos 0259: 282 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 025b: 283 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 025d: 284 */ 0xE5 /* 'e' -> */, +/* pos 025e: 285 */ 0xF2 /* 'r' -> */, +/* pos 025f: 286 */ 0xF3 /* 's' -> */, +/* pos 0260: 287 */ 0xE9 /* 'i' -> */, +/* pos 0261: 288 */ 0xEF /* 'o' -> */, +/* pos 0262: 289 */ 0xEE /* 'n' -> */, +/* pos 0263: 290 */ 0xBA /* ':' -> */, +/* pos 0264: 291 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* pos 0266: 292 */ 0xF2 /* 'r' -> */, +/* pos 0267: 293 */ 0xE9 /* 'i' -> */, +/* pos 0268: 294 */ 0xE7 /* 'g' -> */, +/* pos 0269: 295 */ 0xE9 /* 'i' -> */, +/* pos 026a: 296 */ 0xEE /* 'n' -> */, +/* pos 026b: 297 */ 0xBA /* ':' -> */, +/* pos 026c: 298 */ 0x00, 0x22 /* - terminal marker 34 - */, +/* pos 026e: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x027B state 300) */, + 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x0285 state 309) */, + 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x028C state 315) */, + 0x73 /* 's' */, 0x1A, 0x00 /* (to 0x0291 state 319) */, + 0x08, /* fail */ +/* pos 027b: 300 */ 0xF5 /* 'u' -> */, +/* pos 027c: 301 */ 0xF4 /* 't' -> */, +/* pos 027d: 302 */ 0xE8 /* 'h' -> */, +/* pos 027e: 303 */ 0xEF /* 'o' -> */, +/* pos 027f: 304 */ 0xF2 /* 'r' -> */, +/* pos 0280: 305 */ 0xE9 /* 'i' -> */, +/* pos 0281: 306 */ 0xF4 /* 't' -> */, +/* pos 0282: 307 */ 0xF9 /* 'y' -> */, +/* pos 0283: 308 */ 0x00, 0x23 /* - terminal marker 35 - */, +/* pos 0285: 309 */ 0xE5 /* 'e' -> */, +/* pos 0286: 310 */ 0xF4 /* 't' -> */, +/* pos 0287: 311 */ 0xE8 /* 'h' -> */, +/* pos 0288: 312 */ 0xEF /* 'o' -> */, +/* pos 0289: 313 */ 0xE4 /* 'd' -> */, +/* pos 028a: 314 */ 0x00, 0x24 /* - terminal marker 36 - */, +/* pos 028c: 315 */ 0xE1 /* 'a' -> */, +/* pos 028d: 316 */ 0xF4 /* 't' -> */, +/* pos 028e: 317 */ 0xE8 /* 'h' -> */, +/* pos 028f: 318 */ 0x00, 0x25 /* - terminal marker 37 - */, +/* pos 0291: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0298 state 320) */, + 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x029E state 325) */, + 0x08, /* fail */ +/* pos 0298: 320 */ 0xE8 /* 'h' -> */, +/* pos 0299: 321 */ 0xE5 /* 'e' -> */, +/* pos 029a: 322 */ 0xED /* 'm' -> */, +/* pos 029b: 323 */ 0xE5 /* 'e' -> */, +/* pos 029c: 324 */ 0x00, 0x26 /* - terminal marker 38 - */, +/* pos 029e: 325 */ 0xE1 /* 'a' -> */, +/* pos 029f: 326 */ 0xF4 /* 't' -> */, +/* pos 02a0: 327 */ 0xF5 /* 'u' -> */, +/* pos 02a1: 328 */ 0xF3 /* 's' -> */, +/* pos 02a2: 329 */ 0x00, 0x27 /* - terminal marker 39 - */, +/* pos 02a4: 330 */ 0xE8 /* 'h' -> */, +/* pos 02a5: 331 */ 0xE1 /* 'a' -> */, +/* pos 02a6: 332 */ 0xF2 /* 'r' -> */, +/* pos 02a7: 333 */ 0xF3 /* 's' -> */, +/* pos 02a8: 334 */ 0xE5 /* 'e' -> */, +/* pos 02a9: 335 */ 0xF4 /* 't' -> */, +/* pos 02aa: 336 */ 0xBA /* ':' -> */, +/* pos 02ab: 337 */ 0x00, 0x28 /* - terminal marker 40 - */, +/* pos 02ad: 338 */ 0xE1 /* 'a' -> */, +/* pos 02ae: 339 */ 0xEE /* 'n' -> */, +/* pos 02af: 340 */ 0xE7 /* 'g' -> */, +/* pos 02b0: 341 */ 0xE5 /* 'e' -> */, +/* pos 02b1: 342 */ 0xF3 /* 's' -> */, +/* pos 02b2: 343 */ 0xBA /* ':' -> */, +/* pos 02b3: 344 */ 0x00, 0x29 /* - terminal marker 41 - */, +/* pos 02b5: 345 */ 0xEC /* 'l' -> */, +/* pos 02b6: 346 */ 0xEC /* 'l' -> */, +/* pos 02b7: 347 */ 0xEF /* 'o' -> */, +/* pos 02b8: 348 */ 0xF7 /* 'w' -> */, +/* pos 02b9: 349 */ 0xAD /* '-' -> */, +/* pos 02ba: 350 */ 0xEF /* 'o' -> */, +/* pos 02bb: 351 */ 0xF2 /* 'r' -> */, +/* pos 02bc: 352 */ 0xE9 /* 'i' -> */, +/* pos 02bd: 353 */ 0xE7 /* 'g' -> */, +/* pos 02be: 354 */ 0xE9 /* 'i' -> */, +/* pos 02bf: 355 */ 0xEE /* 'n' -> */, +/* pos 02c0: 356 */ 0xBA /* ':' -> */, +/* pos 02c1: 357 */ 0x00, 0x2A /* - terminal marker 42 - */, +/* pos 02c3: 358 */ 0xE5 /* 'e' -> */, +/* pos 02c4: 359 */ 0xBA /* ':' -> */, +/* pos 02c5: 360 */ 0x00, 0x2B /* - terminal marker 43 - */, +/* pos 02c7: 361 */ 0xEC /* 'l' -> */, +/* pos 02c8: 362 */ 0xEF /* 'o' -> */, +/* pos 02c9: 363 */ 0xF7 /* 'w' -> */, +/* pos 02ca: 364 */ 0xBA /* ':' -> */, +/* pos 02cb: 365 */ 0x00, 0x2C /* - terminal marker 44 - */, +/* pos 02cd: 366 */ 0xE9 /* 'i' -> */, +/* pos 02ce: 367 */ 0xF3 /* 's' -> */, +/* pos 02cf: 368 */ 0xF0 /* 'p' -> */, +/* pos 02d0: 369 */ 0xEF /* 'o' -> */, +/* pos 02d1: 370 */ 0xF3 /* 's' -> */, +/* pos 02d2: 371 */ 0xE9 /* 'i' -> */, +/* pos 02d3: 372 */ 0xF4 /* 't' -> */, +/* pos 02d4: 373 */ 0xE9 /* 'i' -> */, +/* pos 02d5: 374 */ 0xEF /* 'o' -> */, +/* pos 02d6: 375 */ 0xEE /* 'n' -> */, +/* pos 02d7: 376 */ 0xBA /* ':' -> */, +/* pos 02d8: 377 */ 0x00, 0x2D /* - terminal marker 45 - */, +/* pos 02da: 378 */ 0xEE /* 'n' -> */, +/* pos 02db: 379 */ 0xE3 /* 'c' -> */, +/* pos 02dc: 380 */ 0xEF /* 'o' -> */, +/* pos 02dd: 381 */ 0xE4 /* 'd' -> */, +/* pos 02de: 382 */ 0xE9 /* 'i' -> */, +/* pos 02df: 383 */ 0xEE /* 'n' -> */, +/* pos 02e0: 384 */ 0xE7 /* 'g' -> */, +/* pos 02e1: 385 */ 0xBA /* ':' -> */, +/* pos 02e2: 386 */ 0x00, 0x2E /* - terminal marker 46 - */, +/* pos 02e4: 387 */ 0xEE /* 'n' -> */, +/* pos 02e5: 388 */ 0xE7 /* 'g' -> */, +/* pos 02e6: 389 */ 0xF5 /* 'u' -> */, +/* pos 02e7: 390 */ 0xE1 /* 'a' -> */, +/* pos 02e8: 391 */ 0xE7 /* 'g' -> */, +/* pos 02e9: 392 */ 0xE5 /* 'e' -> */, +/* pos 02ea: 393 */ 0xBA /* ':' -> */, +/* pos 02eb: 394 */ 0x00, 0x2F /* - terminal marker 47 - */, +/* pos 02ed: 395 */ 0xE3 /* 'c' -> */, +/* pos 02ee: 396 */ 0xE1 /* 'a' -> */, +/* pos 02ef: 397 */ 0xF4 /* 't' -> */, +/* pos 02f0: 398 */ 0xE9 /* 'i' -> */, +/* pos 02f1: 399 */ 0xEF /* 'o' -> */, +/* pos 02f2: 400 */ 0xEE /* 'n' -> */, +/* pos 02f3: 401 */ 0xBA /* ':' -> */, +/* pos 02f4: 402 */ 0x00, 0x30 /* - terminal marker 48 - */, +/* pos 02f6: 403 */ 0xE1 /* 'a' -> */, +/* pos 02f7: 404 */ 0xEE /* 'n' -> */, +/* pos 02f8: 405 */ 0xE7 /* 'g' -> */, +/* pos 02f9: 406 */ 0xE5 /* 'e' -> */, +/* pos 02fa: 407 */ 0xBA /* ':' -> */, +/* pos 02fb: 408 */ 0x00, 0x31 /* - terminal marker 49 - */, +/* pos 02fd: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x0304 state 410) */, + 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0309 state 414) */, + 0x08, /* fail */ +/* pos 0304: 410 */ 0xE1 /* 'a' -> */, +/* pos 0305: 411 */ 0xE7 /* 'g' -> */, +/* pos 0306: 412 */ 0xBA /* ':' -> */, +/* pos 0307: 413 */ 0x00, 0x32 /* - terminal marker 50 - */, +/* pos 0309: 414 */ 0xF0 /* 'p' -> */, +/* pos 030a: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0311 state 416) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0316 state 420) */, + 0x08, /* fail */ +/* pos 0311: 416 */ 0xE3 /* 'c' -> */, +/* pos 0312: 417 */ 0xF4 /* 't' -> */, +/* pos 0313: 418 */ 0xBA /* ':' -> */, +/* pos 0314: 419 */ 0x00, 0x33 /* - terminal marker 51 - */, +/* pos 0316: 420 */ 0xF2 /* 'r' -> */, +/* pos 0317: 421 */ 0xE5 /* 'e' -> */, +/* pos 0318: 422 */ 0xF3 /* 's' -> */, +/* pos 0319: 423 */ 0xBA /* ':' -> */, +/* pos 031a: 424 */ 0x00, 0x34 /* - terminal marker 52 - */, +/* pos 031c: 425 */ 0xF2 /* 'r' -> */, +/* pos 031d: 426 */ 0xEF /* 'o' -> */, +/* pos 031e: 427 */ 0xED /* 'm' -> */, +/* pos 031f: 428 */ 0xBA /* ':' -> */, +/* pos 0320: 429 */ 0x00, 0x35 /* - terminal marker 53 - */, +/* pos 0322: 430 */ 0xF4 /* 't' -> */, +/* pos 0323: 431 */ 0xE3 /* 'c' -> */, +/* pos 0324: 432 */ 0xE8 /* 'h' -> */, +/* pos 0325: 433 */ 0xBA /* ':' -> */, +/* pos 0326: 434 */ 0x00, 0x36 /* - terminal marker 54 - */, +/* pos 0328: 435 */ 0xE1 /* 'a' -> */, +/* pos 0329: 436 */ 0xEE /* 'n' -> */, +/* pos 032a: 437 */ 0xE7 /* 'g' -> */, +/* pos 032b: 438 */ 0xE5 /* 'e' -> */, +/* pos 032c: 439 */ 0xBA /* ':' -> */, +/* pos 032d: 440 */ 0x00, 0x37 /* - terminal marker 55 - */, +/* pos 032f: 441 */ 0xEE /* 'n' -> */, +/* pos 0330: 442 */ 0xED /* 'm' -> */, +/* pos 0331: 443 */ 0xEF /* 'o' -> */, +/* pos 0332: 444 */ 0xE4 /* 'd' -> */, +/* pos 0333: 445 */ 0xE9 /* 'i' -> */, +/* pos 0334: 446 */ 0xE6 /* 'f' -> */, +/* pos 0335: 447 */ 0xE9 /* 'i' -> */, +/* pos 0336: 448 */ 0xE5 /* 'e' -> */, +/* pos 0337: 449 */ 0xE4 /* 'd' -> */, +/* pos 0338: 450 */ 0xAD /* '-' -> */, +/* pos 0339: 451 */ 0xF3 /* 's' -> */, +/* pos 033a: 452 */ 0xE9 /* 'i' -> */, +/* pos 033b: 453 */ 0xEE /* 'n' -> */, +/* pos 033c: 454 */ 0xE3 /* 'c' -> */, +/* pos 033d: 455 */ 0xE5 /* 'e' -> */, +/* pos 033e: 456 */ 0xBA /* ':' -> */, +/* pos 033f: 457 */ 0x00, 0x38 /* - terminal marker 56 - */, +/* pos 0341: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x034B state 459) */, + 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0359 state 472) */, + 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x035E state 476) */, + 0x08, /* fail */ +/* pos 034b: 459 */ 0xF3 /* 's' -> */, +/* pos 034c: 460 */ 0xF4 /* 't' -> */, +/* pos 034d: 461 */ 0xAD /* '-' -> */, +/* pos 034e: 462 */ 0xED /* 'm' -> */, +/* pos 034f: 463 */ 0xEF /* 'o' -> */, +/* pos 0350: 464 */ 0xE4 /* 'd' -> */, +/* pos 0351: 465 */ 0xE9 /* 'i' -> */, +/* pos 0352: 466 */ 0xE6 /* 'f' -> */, +/* pos 0353: 467 */ 0xE9 /* 'i' -> */, +/* pos 0354: 468 */ 0xE5 /* 'e' -> */, +/* pos 0355: 469 */ 0xE4 /* 'd' -> */, +/* pos 0356: 470 */ 0xBA /* ':' -> */, +/* pos 0357: 471 */ 0x00, 0x39 /* - terminal marker 57 - */, +/* pos 0359: 472 */ 0xEE /* 'n' -> */, +/* pos 035a: 473 */ 0xEB /* 'k' -> */, +/* pos 035b: 474 */ 0xBA /* ':' -> */, +/* pos 035c: 475 */ 0x00, 0x3A /* - terminal marker 58 - */, +/* pos 035e: 476 */ 0xE3 /* 'c' -> */, +/* pos 035f: 477 */ 0xE1 /* 'a' -> */, +/* pos 0360: 478 */ 0xF4 /* 't' -> */, +/* pos 0361: 479 */ 0xE9 /* 'i' -> */, +/* pos 0362: 480 */ 0xEF /* 'o' -> */, +/* pos 0363: 481 */ 0xEE /* 'n' -> */, +/* pos 0364: 482 */ 0xBA /* ':' -> */, +/* pos 0365: 483 */ 0x00, 0x3B /* - terminal marker 59 - */, +/* pos 0367: 484 */ 0xE1 /* 'a' -> */, +/* pos 0368: 485 */ 0xF8 /* 'x' -> */, +/* pos 0369: 486 */ 0xAD /* '-' -> */, +/* pos 036a: 487 */ 0xE6 /* 'f' -> */, +/* pos 036b: 488 */ 0xEF /* 'o' -> */, +/* pos 036c: 489 */ 0xF2 /* 'r' -> */, +/* pos 036d: 490 */ 0xF7 /* 'w' -> */, +/* pos 036e: 491 */ 0xE1 /* 'a' -> */, +/* pos 036f: 492 */ 0xF2 /* 'r' -> */, +/* pos 0370: 493 */ 0xE4 /* 'd' -> */, +/* pos 0371: 494 */ 0xF3 /* 's' -> */, +/* pos 0372: 495 */ 0xBA /* ':' -> */, +/* pos 0373: 496 */ 0x00, 0x3C /* - terminal marker 60 - */, +/* pos 0375: 497 */ 0xF8 /* 'x' -> */, +/* pos 0376: 498 */ 0xF9 /* 'y' -> */, +/* pos 0377: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x037E state 500) */, + 0x20 /* ' ' */, 0xB5, 0x00 /* (to 0x042F state 649) */, + 0x08, /* fail */ +/* pos 037e: 500 */ 0xE1 /* 'a' -> */, +/* pos 037f: 501 */ 0xF5 /* 'u' -> */, +/* pos 0380: 502 */ 0xF4 /* 't' -> */, +/* pos 0381: 503 */ 0xE8 /* 'h' -> */, +/* pos 0382: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0389 state 505) */, + 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x0393 state 514) */, + 0x08, /* fail */ +/* pos 0389: 505 */ 0xEE /* 'n' -> */, +/* pos 038a: 506 */ 0xF4 /* 't' -> */, +/* pos 038b: 507 */ 0xE9 /* 'i' -> */, +/* pos 038c: 508 */ 0xE3 /* 'c' -> */, +/* pos 038d: 509 */ 0xE1 /* 'a' -> */, +/* pos 038e: 510 */ 0xF4 /* 't' -> */, +/* pos 038f: 511 */ 0xE5 /* 'e' -> */, +/* pos 0390: 512 */ 0xBA /* ':' -> */, +/* pos 0391: 513 */ 0x00, 0x3D /* - terminal marker 61 - */, +/* pos 0393: 514 */ 0xF2 /* 'r' -> */, +/* pos 0394: 515 */ 0xE9 /* 'i' -> */, +/* pos 0395: 516 */ 0xFA /* 'z' -> */, +/* pos 0396: 517 */ 0xE1 /* 'a' -> */, +/* pos 0397: 518 */ 0xF4 /* 't' -> */, +/* pos 0398: 519 */ 0xE9 /* 'i' -> */, +/* pos 0399: 520 */ 0xEF /* 'o' -> */, +/* pos 039a: 521 */ 0xEE /* 'n' -> */, +/* pos 039b: 522 */ 0xBA /* ':' -> */, +/* pos 039c: 523 */ 0x00, 0x3E /* - terminal marker 62 - */, +/* pos 039e: 524 */ 0xE5 /* 'e' -> */, +/* pos 039f: 525 */ 0xF3 /* 's' -> */, +/* pos 03a0: 526 */ 0xE8 /* 'h' -> */, +/* pos 03a1: 527 */ 0xBA /* ':' -> */, +/* pos 03a2: 528 */ 0x00, 0x3F /* - terminal marker 63 - */, +/* pos 03a4: 529 */ 0xF2 /* 'r' -> */, +/* pos 03a5: 530 */ 0xF9 /* 'y' -> */, +/* pos 03a6: 531 */ 0xAD /* '-' -> */, +/* pos 03a7: 532 */ 0xE1 /* 'a' -> */, +/* pos 03a8: 533 */ 0xE6 /* 'f' -> */, +/* pos 03a9: 534 */ 0xF4 /* 't' -> */, +/* pos 03aa: 535 */ 0xE5 /* 'e' -> */, +/* pos 03ab: 536 */ 0xF2 /* 'r' -> */, +/* pos 03ac: 537 */ 0xBA /* ':' -> */, +/* pos 03ad: 538 */ 0x00, 0x40 /* - terminal marker 64 - */, +/* pos 03af: 539 */ 0xF6 /* 'v' -> */, +/* pos 03b0: 540 */ 0xE5 /* 'e' -> */, +/* pos 03b1: 541 */ 0xF2 /* 'r' -> */, +/* pos 03b2: 542 */ 0xBA /* ':' -> */, +/* pos 03b3: 543 */ 0x00, 0x41 /* - terminal marker 65 - */, +/* pos 03b5: 544 */ 0xAD /* '-' -> */, +/* pos 03b6: 545 */ 0xE3 /* 'c' -> */, +/* pos 03b7: 546 */ 0xEF /* 'o' -> */, +/* pos 03b8: 547 */ 0xEF /* 'o' -> */, +/* pos 03b9: 548 */ 0xEB /* 'k' -> */, +/* pos 03ba: 549 */ 0xE9 /* 'i' -> */, +/* pos 03bb: 550 */ 0xE5 /* 'e' -> */, +/* pos 03bc: 551 */ 0xBA /* ':' -> */, +/* pos 03bd: 552 */ 0x00, 0x42 /* - terminal marker 66 - */, +/* pos 03bf: 553 */ 0xF2 /* 'r' -> */, +/* pos 03c0: 554 */ 0xE9 /* 'i' -> */, +/* pos 03c1: 555 */ 0xE3 /* 'c' -> */, +/* pos 03c2: 556 */ 0xF4 /* 't' -> */, +/* pos 03c3: 557 */ 0xAD /* '-' -> */, +/* pos 03c4: 558 */ 0xF4 /* 't' -> */, +/* pos 03c5: 559 */ 0xF2 /* 'r' -> */, +/* pos 03c6: 560 */ 0xE1 /* 'a' -> */, +/* pos 03c7: 561 */ 0xEE /* 'n' -> */, +/* pos 03c8: 562 */ 0xF3 /* 's' -> */, +/* pos 03c9: 563 */ 0xF0 /* 'p' -> */, +/* pos 03ca: 564 */ 0xEF /* 'o' -> */, +/* pos 03cb: 565 */ 0xF2 /* 'r' -> */, +/* pos 03cc: 566 */ 0xF4 /* 't' -> */, +/* pos 03cd: 567 */ 0xAD /* '-' -> */, +/* pos 03ce: 568 */ 0xF3 /* 's' -> */, +/* pos 03cf: 569 */ 0xE5 /* 'e' -> */, +/* pos 03d0: 570 */ 0xE3 /* 'c' -> */, +/* pos 03d1: 571 */ 0xF5 /* 'u' -> */, +/* pos 03d2: 572 */ 0xF2 /* 'r' -> */, +/* pos 03d3: 573 */ 0xE9 /* 'i' -> */, +/* pos 03d4: 574 */ 0xF4 /* 't' -> */, +/* pos 03d5: 575 */ 0xF9 /* 'y' -> */, +/* pos 03d6: 576 */ 0xBA /* ':' -> */, +/* pos 03d7: 577 */ 0x00, 0x43 /* - terminal marker 67 - */, +/* pos 03d9: 578 */ 0xF2 /* 'r' -> */, +/* pos 03da: 579 */ 0xE1 /* 'a' -> */, +/* pos 03db: 580 */ 0xEE /* 'n' -> */, +/* pos 03dc: 581 */ 0xF3 /* 's' -> */, +/* pos 03dd: 582 */ 0xE6 /* 'f' -> */, +/* pos 03de: 583 */ 0xE5 /* 'e' -> */, +/* pos 03df: 584 */ 0xF2 /* 'r' -> */, +/* pos 03e0: 585 */ 0xAD /* '-' -> */, +/* pos 03e1: 586 */ 0xE5 /* 'e' -> */, +/* pos 03e2: 587 */ 0xEE /* 'n' -> */, +/* pos 03e3: 588 */ 0xE3 /* 'c' -> */, +/* pos 03e4: 589 */ 0xEF /* 'o' -> */, +/* pos 03e5: 590 */ 0xE4 /* 'd' -> */, +/* pos 03e6: 591 */ 0xE9 /* 'i' -> */, +/* pos 03e7: 592 */ 0xEE /* 'n' -> */, +/* pos 03e8: 593 */ 0xE7 /* 'g' -> */, +/* pos 03e9: 594 */ 0xBA /* ':' -> */, +/* pos 03ea: 595 */ 0x00, 0x44 /* - terminal marker 68 - */, +/* pos 03ec: 596 */ 0xE5 /* 'e' -> */, +/* pos 03ed: 597 */ 0xF2 /* 'r' -> */, +/* pos 03ee: 598 */ 0xAD /* '-' -> */, +/* pos 03ef: 599 */ 0xE1 /* 'a' -> */, +/* pos 03f0: 600 */ 0xE7 /* 'g' -> */, +/* pos 03f1: 601 */ 0xE5 /* 'e' -> */, +/* pos 03f2: 602 */ 0xEE /* 'n' -> */, +/* pos 03f3: 603 */ 0xF4 /* 't' -> */, +/* pos 03f4: 604 */ 0xBA /* ':' -> */, +/* pos 03f5: 605 */ 0x00, 0x45 /* - terminal marker 69 - */, +/* pos 03f7: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x03FE state 607) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0403 state 611) */, + 0x08, /* fail */ +/* pos 03fe: 607 */ 0xF2 /* 'r' -> */, +/* pos 03ff: 608 */ 0xF9 /* 'y' -> */, +/* pos 0400: 609 */ 0xBA /* ':' -> */, +/* pos 0401: 610 */ 0x00, 0x46 /* - terminal marker 70 - */, +/* pos 0403: 611 */ 0xE1 /* 'a' -> */, +/* pos 0404: 612 */ 0xBA /* ':' -> */, +/* pos 0405: 613 */ 0x00, 0x47 /* - terminal marker 71 - */, +/* pos 0407: 614 */ 0xF7 /* 'w' -> */, +/* pos 0408: 615 */ 0xF7 /* 'w' -> */, +/* pos 0409: 616 */ 0xAD /* '-' -> */, +/* pos 040a: 617 */ 0xE1 /* 'a' -> */, +/* pos 040b: 618 */ 0xF5 /* 'u' -> */, +/* pos 040c: 619 */ 0xF4 /* 't' -> */, +/* pos 040d: 620 */ 0xE8 /* 'h' -> */, +/* pos 040e: 621 */ 0xE5 /* 'e' -> */, +/* pos 040f: 622 */ 0xEE /* 'n' -> */, +/* pos 0410: 623 */ 0xF4 /* 't' -> */, +/* pos 0411: 624 */ 0xE9 /* 'i' -> */, +/* pos 0412: 625 */ 0xE3 /* 'c' -> */, +/* pos 0413: 626 */ 0xE1 /* 'a' -> */, +/* pos 0414: 627 */ 0xF4 /* 't' -> */, +/* pos 0415: 628 */ 0xE5 /* 'e' -> */, +/* pos 0416: 629 */ 0xBA /* ':' -> */, +/* pos 0417: 630 */ 0x00, 0x48 /* - terminal marker 72 - */, +/* pos 0419: 631 */ 0xF4 /* 't' -> */, +/* pos 041a: 632 */ 0xE3 /* 'c' -> */, +/* pos 041b: 633 */ 0xE8 /* 'h' -> */, +/* pos 041c: 634 */ 0x00, 0x49 /* - terminal marker 73 - */, +/* pos 041e: 635 */ 0xF4 /* 't' -> */, +/* pos 041f: 636 */ 0x00, 0x4A /* - terminal marker 74 - */, +/* pos 0421: 637 */ 0xEC /* 'l' -> */, +/* pos 0422: 638 */ 0xE5 /* 'e' -> */, +/* pos 0423: 639 */ 0xF4 /* 't' -> */, +/* pos 0424: 640 */ 0xE5 /* 'e' -> */, +/* pos 0425: 641 */ 0x00, 0x4B /* - terminal marker 75 - */, +/* pos 0427: 642 */ 0xE9 /* 'i' -> */, +/* pos 0428: 643 */ 0xAD /* '-' -> */, +/* pos 0429: 644 */ 0xE1 /* 'a' -> */, +/* pos 042a: 645 */ 0xF2 /* 'r' -> */, +/* pos 042b: 646 */ 0xE7 /* 'g' -> */, +/* pos 042c: 647 */ 0xF3 /* 's' -> */, +/* pos 042d: 648 */ 0x00, 0x4C /* - terminal marker 76 - */, +/* pos 042f: 649 */ 0x00, 0x4D /* - terminal marker 77 - */, +/* pos 0431: 650 */ 0xAD /* '-' -> */, +/* pos 0432: 651 */ 0xF2 /* 'r' -> */, +/* pos 0433: 652 */ 0xE5 /* 'e' -> */, +/* pos 0434: 653 */ 0xE1 /* 'a' -> */, +/* pos 0435: 654 */ 0xEC /* 'l' -> */, +/* pos 0436: 655 */ 0xAD /* '-' -> */, +/* pos 0437: 656 */ 0xE9 /* 'i' -> */, +/* pos 0438: 657 */ 0xF0 /* 'p' -> */, +/* pos 0439: 658 */ 0xBA /* ':' -> */, +/* pos 043a: 659 */ 0x00, 0x4E /* - terminal marker 78 - */, +/* pos 043c: 660 */ 0xA0 /* ' ' -> */, +/* pos 043d: 661 */ 0x00, 0x4F /* - terminal marker 79 - */, +/* total size 1087 bytes */ diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf.h new file mode 100755 index 00000000..ff0b77ad --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf.h @@ -0,0 +1,470 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _LIBBRIDGE_H +#define _LIBBRIDGE_H + +#include +#include +#include +//#include + +/* defined in net/if.h but that conflicts with linux/if.h... */ +extern unsigned int if_nametoindex (const char *__ifname); +extern char *if_indextoname (unsigned int __ifindex, char *__ifname); + + +#define APC_MAX_NEIGHBORS 100 + +struct bridge_id +{ + unsigned char prio[2]; + unsigned char addr[6]; +}; + +struct fdb_entry +{ + unsigned int mac_addr[6]; + unsigned int port_no; + unsigned char is_local; + struct timeval ageing_timer_value; +}; + +struct port_info +{ + unsigned port_no; + struct bridge_id designated_root; + struct bridge_id designated_bridge; + unsigned int port_id; + unsigned int designated_port; + unsigned int priority; + unsigned char top_change_ack; + unsigned char config_pending; + unsigned char state; + unsigned path_cost; + unsigned designated_cost; + struct timeval message_age_timer_value; + struct timeval forward_delay_timer_value; + struct timeval hold_timer_value; +}; + +struct sip_call_start +{ + unsigned long long SessionId; + unsigned char CltMac[6]; + int WifiIf; /* To get WiFi session ID */ + char Url[50]; + char Codecs[15][30]; +} __attribute__((packed)); + +struct sip_call_end +{ + unsigned long long SessionId; + unsigned char CltMac[6]; + int WifiIf; /* To get WiFi session ID */ + unsigned int CltMos; + unsigned int Reason; + unsigned char Codecs[4]; + unsigned int Latency; + unsigned int Jitter; + unsigned int PktLostPerc; + unsigned int PktLostCons; + unsigned int VideoCodec; + unsigned int TotalPktSent; + unsigned int TotalPktLost; + unsigned int RtpSeqFirst; + unsigned int RtpSeqLast; + unsigned int SipReportIdx; +} __attribute__((packed)); + +struct sip_call_report +{ + unsigned int Latency; + unsigned int Jitter; + unsigned int PacketLoss; + unsigned int Mos; +} __attribute__((packed)); + +struct streaming_video_start +{ + unsigned long long SessionId; + unsigned char CltMac[6]; + int WifiIf; /* To get WiFi session ID */ + unsigned int ServerIp; + unsigned int Type; + char ServerDnsName[100]; +} __attribute__((packed)); + +struct streaming_video_stop +{ + unsigned long long SessionId; + unsigned char CltMac[6]; + int WifiIf; /* To get WiFi session ID */ + unsigned long long TotalBytes; + unsigned int Duration; + unsigned int Type; + unsigned int ServerIp; +} __attribute__((packed)); + +struct http_user_agent +{ + unsigned char macAddress[6]; + char userAgent[200]; +} __attribute__((packed)); + +struct add_tun_parms +{ + unsigned int MyIp; + unsigned int PeerIp; + unsigned int Primary; + unsigned char MyMac[6]; +} __attribute__((packed)); + +struct del_tun_parms +{ + unsigned int PeerIp; + unsigned char PeerMac[6]; +} __attribute__((packed)); + +struct wc_log_line +{ + unsigned int Num; + unsigned int Level; + unsigned int TimeStamp; + unsigned char Message[200]; +} __attribute__((packed)); + +struct fr_record +{ + unsigned int Round; + unsigned int Timestamp; + char Message[504]; +} __attribute__((packed)); + +struct wc_tunnel_stats +{ + unsigned int Ip; + unsigned int ConfTime; + unsigned int UpTime; + unsigned int Active; + unsigned int PingsSent; + unsigned int PingsReceived; +} __attribute__((packed)); + +struct radius_report_stats +{ + unsigned int Entry; + unsigned int ServerIp; + unsigned int NoAnswers; + /* All times are in milliseconds */ + unsigned int TimeMin; + unsigned int TimeMax; + unsigned int TimeAve; +} __attribute__((packed)); + +struct local_mac_set +{ + unsigned int Status; + unsigned int Vlan; + unsigned char Mac[6]; +} __attribute__((packed)); + +struct wc_capt_buf +{ + unsigned long long TimeStamp; + unsigned long long tsInUs; + unsigned long long SessionId; + unsigned char Type; + unsigned char From; + unsigned int Len; + unsigned int Channel; + unsigned int Direction; + unsigned int Count; + int Rssi; + unsigned int DataRate; + int wifiIf; // for dhcp + char staMac[6]; // for dhcp + unsigned char Buff[500]; +} __attribute__((packed)); + +struct wc_arp_entry +{ + unsigned char mac[6]; + unsigned int ip; + unsigned long long TimeStamp; + unsigned int action; + unsigned int vlan; +} __attribute__((packed)); + +#define NUM_OF_NEIGH_IN_REQUEST 160 + +struct wc_ngbr_capt_buf +{ + unsigned char Type; + unsigned long long TimeStamp; + unsigned int Channel; + int Rssi; + unsigned char Bssid[6]; + unsigned char SrcMac[6]; +} __attribute__((packed)); + +struct wc_perf_stats +{ + unsigned int MemFree; + unsigned int EthPackets; + unsigned int WiFiPackets; + unsigned int CpuBusy[2]; + unsigned int AppRestarted[4]; + unsigned int MgmtBytesTx[2]; + unsigned int MgmtBytesRx[2]; + int EthLinkState; + int EthSpeed; + int EthDuplex; +} __attribute__((packed)); + +struct clt_hash_info +{ + unsigned int VbrInd; + unsigned int Num; + void * EntryPtr; + unsigned short Vlan; + unsigned char Mac[6]; + unsigned int ApIp; + unsigned int Age; + + unsigned int CpTimeout; + unsigned int CpBpsUp; + unsigned int CpBpsDown; + unsigned int CpWhitelist; + + unsigned int SipPort; + unsigned int SipState; + unsigned int RtpPortsFrom; + unsigned int RtpPortsTo; + unsigned int RtpIpFrom; + unsigned int RtpIpTo; + + unsigned int FpAge; + unsigned int NbrAge; + unsigned int ArpAge; + unsigned int ArpIp; + + unsigned int UaFlag; + unsigned int UaTimeOut; +} __attribute__((packed)); + +struct mac_hash_info +{ + unsigned int VbrInd; + unsigned int Num; + void * EntryPtr; + unsigned short Vlan; + unsigned short Age; + unsigned char Mac[6]; + unsigned char IfName[20]; + unsigned int CreditsFrom; + unsigned int CreditsTo; + unsigned int DroppedFrom; + unsigned int DroppedTo; + unsigned char Flags; +} __attribute__((packed)); + +struct wc_iac_register +{ + unsigned int MessCode; + unsigned int UdpPort; + unsigned int Status; /* 1 - register; 0 - deregister */ +} __attribute__((packed)); + +struct wc_iac_send +{ + unsigned int DestIp; + unsigned int MessCode; + unsigned int MessLen; + unsigned char Body[15000]; +} __attribute__((packed)); + +struct wc_apc_neigh +{ + unsigned int Ip; + unsigned char BasicMac[6]; +} __attribute__((packed)); + +#define APC_UNKNOWN 0 +#define I_AM_APC 1 +#define I_AM_BAPC 2 + +struct wc_apc_spec +{ + unsigned int IsApc; + unsigned int Changed; + unsigned int FloatIp; + struct wc_apc_neigh Neighbors[APC_MAX_NEIGHBORS]; +} __attribute__((packed)); + +struct wc_mac_ident +{ + unsigned char Mac[6]; + unsigned char Ident[20]; + unsigned long long SessionId; + unsigned int IdentLen; +} __attribute__((packed)); + +struct wc_subnet_filter +{ + char IfName[20]; + unsigned int SubnetBase[5]; + unsigned int SubnetMask[5]; +} __attribute__((packed)); + +struct wc_dhcp_release +{ + unsigned int Vlan; + unsigned int DestIp; + unsigned int SrcIp; + unsigned char DestMac[6]; + unsigned char SrcMac[6]; +} __attribute__((packed)); + +struct wc_arp_sweep +{ + unsigned int Vlan; + unsigned int Subnet; //subnet = IP OR netMask e.g. 172.16.10.0 + unsigned int SrcIp; //Src IP + unsigned char DestMac[6]; + unsigned char SrcMac[6]; + unsigned int SubnetSize; //num of IPs in subnet e.g. 255 +} __attribute__((packed)); + +struct wc_cp_whitelist +{ + int Type; + char Hostname[256]; +} __attribute__((packed)); + +struct cp_auth_failed +{ + unsigned char Mac[10][6]; + unsigned int Count[10]; +} __attribute__((packed)); + +struct cp_auth_attempt +{ + unsigned char Mac[6]; + unsigned char Uname[52]; + unsigned int Status; +} __attribute__((packed)); + +struct band_limit +{ + char IfName[20]; + unsigned int ToLimit; /* Downstream */ + unsigned int FromLimit; /* Upstream */ + unsigned int Type; +} __attribute__((packed)); + +struct bss_upstream_stats +{ + char IfName[20]; + unsigned int PktsForwToLocal; + unsigned int PktsForwToLocalOver; + unsigned int BytesForwToLocal; + unsigned int BytesForwToLocalOver; + unsigned int PktsForwToIntern; + unsigned int PktsForwToInternOver; + unsigned int BytesForwToIntern; + unsigned int BytesForwToInternOver; +} __attribute__((packed)); + +struct bonjour_forw_rule +{ + char RuleName[20]; + unsigned int VLAN[5]; + unsigned char Services[5][100]; +} __attribute__((packed)); + +struct vlan_ssid +{ + char IfName[20]; + unsigned int VLAN; +} __attribute__((packed)); + +struct bj_profile_set +{ + char IfName[20]; + char ProfileName[20]; +} __attribute__((packed)); + +struct rogue_ap_macs +{ + unsigned int MacHigh; + unsigned int MacLow; + unsigned int MaskBits; +} __attribute__((packed)); + +struct raw_pkt_throttle +{ + unsigned int Type; + unsigned int Interval; + unsigned int Limit; +} __attribute__((packed)); + +struct forw_mode_set +{ + char IfName[20]; + int ForwMode; +} __attribute__((packed)); + +struct wc_ethports_stats +{ + int port0LinkState; + int port1LinkState; +} __attribute__((packed)); + +struct ua_timeout_upd +{ + unsigned char cltMac[6]; + unsigned int uaTimeout; +} __attribute__((packed)); + +struct delClient +{ + unsigned char cltMac[6]; +} __attribute__((packed)); + +struct stream_srvr +{ + unsigned int IpAddr; + unsigned int Type; + unsigned char DnsName[100]; +} __attribute__((packed)); + +extern int br_init(void); +extern int br_refresh(void); +extern void br_shutdown(void); + +//extern int br_foreach_bridge(int (*iterator)(const char *brname, void *), void *arg); +extern int br_foreach_port(const char *brname, + int (*iterator)(const char *brname, const char *port, + void *arg ), + void *arg); +extern const char *br_get_state_name(int state); + +//extern int br_get_bridge_info(const char *br, struct bridge_info *info); +extern int br_get_port_info(const char *brname, const char *port, + struct port_info *info); +extern int wc_add_vbr( const char * name ); +extern int wc_del_vbr( const char * name ); +extern int wc_add_interface( const char * vbr, const char * dev ); +extern int wc_del_interface( const char * vbr, const char * dev ); +//extern int br_set_bridge_forward_delay(const char *br, struct timeval *tv); +//extern int br_set_bridge_hello_time(const char *br, struct timeval *tv); +//extern int br_set_bridge_max_age(const char *br, struct timeval *tv); +//extern int br_set_ageing_time(const char *br, struct timeval *tv); +//extern int br_set_stp_state(const char *br, int stp_state); +//extern int br_set_bridge_priority(const char *br, int bridge_priority); +//extern int br_set_port_priority(const char *br, const char *p, int port_priority); +int wc_set_socket( void ); + +//extern int br_set_path_cost(const char *br, const char *p, int path_cost); +//extern int br_read_fdb(const char *br, struct fdb_entry *fdbs, unsigned long skip, int num); +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_debug.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_debug.h new file mode 100755 index 00000000..cd37e3e3 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_debug.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#define WRITE_TO_FR 0x40000000 + +#define DEBUG_LEVEL_1 0x00000001 +#define DEBUG_LEVEL_2 0x00000002 +#define DEBUG_LEVEL_3 0x00000004 +#define DEBUG_LEVEL_4 0x00000008 +#define APP_LOG_CAMI 0x00000010 +#define APP_LOG_REDIR 0x00000020 +#define APP_LOG_DMAN 0x00000040 +#define APP_LOG_SYSLOG 0x00000080 +#define APP_LOG_WCFCTL 0x00000100 +#define APP_LOG_CORE 0x00000200 +#define APP_LOG_WEBSOCK 0x00000400 +#define APP_LOG_DHCP_DISC 0x00000800 +#define APP_LOG_STATUS_AGENT_DISC 0x00001000 +#define APP_LOG_APC 0x00002000 +#define APP_LOG_RADIUS_PROXY 0x00004000 +#define APP_LOG_NANNY 0x00008000 +#define APP_LOG_CP_UAP 0x00010000 +#define APP_LOG_RADIUS_PROBE 0x00020000 +#define APP_LOG_RTLS 0x00040000 +#define APP_LOG_HOSTAP 0x00080000 +#define APP_LOG_BESTAP 0x00100000 + +extern int wc_put_logline( unsigned int Mask, const char * format, ... ); + diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_private.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_private.h new file mode 100755 index 00000000..fb1c37a4 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwcf_private.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _LIBBRIDGE_PRIVATE_H +#define _LIBBRIDGE_PRIVATE_H + +#include +#include +#include +#include + +#define MAX_BRIDGES 1024 +#define MAX_PORTS 1024 + +#define SYSFS_CLASS_NET "/sys/class/net/" +#define SYSFS_PATH_MAX 256 + +#define dprintf(fmt,arg...) + +extern int br_socket_fd; + +static inline unsigned long __tv_to_jiffies(const struct timeval *tv) +{ + unsigned long long jif; + + jif = 1000000ULL * tv->tv_sec + tv->tv_usec; + + return jif/10000; +} + +static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) +{ + unsigned long long tvusec; + + tvusec = 10000ULL*jiffies; + tv->tv_sec = tvusec/1000000; + tv->tv_usec = tvusec - 1000000 * tv->tv_sec; +} +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/libwebsockets.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwebsockets.h new file mode 100644 index 00000000..1ab4e98b --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/libwebsockets.h @@ -0,0 +1,4197 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C +#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C + +#ifdef __cplusplus +#include +#include +#ifdef MBED_OPERATORS +#include "mbed-drivers/mbed.h" +#include "sal-iface-eth/EthernetInterface.h" +#include "sockets/TCPListener.h" +#include "sal-stack-lwip/lwipv4_init.h" + +namespace { +} +using namespace mbed::Sockets::v0; + + +struct sockaddr_in; +struct lws; + +class lws_conn { + public: + lws_conn(): + ts(NULL), + wsi(NULL), + writeable(1), + awaiting_on_writeable(0) + { + } + +public: + void set_wsi(struct lws *_wsi) { wsi = _wsi; } + int actual_onRX(Socket *s); + void onRX(Socket *s); + void onError(Socket *s, socket_error_t err); + void onDisconnect(TCPStream *s); + void onSent(Socket *s, uint16_t len); + void serialized_writeable(struct lws *wsi); + +public: + TCPStream *ts; + +public: + struct lws *wsi; + char writeable; + char awaiting_on_writeable; +}; + +class lws_conn_listener : lws_conn { +public: + lws_conn_listener(): + srv(SOCKET_STACK_LWIP_IPV4) + { + srv.setOnError(TCPStream::ErrorHandler_t(this, + &lws_conn_listener::onError)); + } + + void start(const uint16_t port); /**< start listening */ + +protected: + void onRX(Socket *s); /**< incoming data ready */ + void onError(Socket *s, socket_error_t err); /**< if error occurs */ + void onIncoming(TCPListener *s, void *impl); /**< new connection */ + void onDisconnect(TCPStream *s); /**< disconnection */ + +public: + TCPListener srv; +}; + +#endif + +extern "C" { +#else +#include +#endif + +#if defined(MBED_OPERATORS) || defined(LWS_WITH_ESP8266) +struct sockaddr_in; +#define LWS_POSIX 0 +#else +#define LWS_POSIX 1 +#endif + +#include "websocket/lws_config.h" + +#if defined(WIN32) || defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#ifndef _WIN32_WCE +#include +#else +#define _O_RDONLY 0x0000 +#define O_RDONLY _O_RDONLY +#endif + +// Visual studio older than 2015 and WIN_CE has only _stricmp +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) +#define strcasecmp _stricmp +#elif !defined(__MINGW32__) +#define strcasecmp stricmp +#endif +#define getdtablesize() 30000 + +#define LWS_INLINE __inline +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED + +#ifdef LWS_DLL +#ifdef LWS_INTERNAL +#define LWS_EXTERN extern __declspec(dllexport) +#else +#define LWS_EXTERN extern __declspec(dllimport) +#endif +#else +#define LWS_EXTERN +#endif + +#define LWS_INVALID_FILE INVALID_HANDLE_VALUE +#define LWS_O_RDONLY _O_RDONLY + +#if !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1900) /* Visual Studio 2015 already defines this in */ +#define lws_snprintf _snprintf +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#else /* NOT WIN32 */ +#include + +#if defined(__NetBSD__) || defined(__FreeBSD__) +#include +#endif + +#define LWS_INLINE inline +#define LWS_O_RDONLY O_RDONLY + +#if !defined(MBED_OPERATORS) && !defined(LWS_WITH_ESP8266) +#include +#include +#define LWS_INVALID_FILE -1 +#else +#define getdtablesize() (20) +#define LWS_INVALID_FILE NULL +#endif + +#if defined(__GNUC__) + +/* warn_unused_result attribute only supported by GCC 3.4 or later */ +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define LWS_WARN_UNUSED_RESULT +#endif + +#define LWS_VISIBLE __attribute__((visibility("default"))) +#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) +#else +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#endif + +#if defined(__ANDROID__) +#include +#define getdtablesize() sysconf(_SC_OPEN_MAX) +#endif + +#endif + +#ifdef LWS_USE_LIBEV +#include +#endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV +#include +#ifdef LWS_HAVE_UV_VERSION_H +#include +#endif +#endif /* LWS_USE_LIBUV */ + +#ifndef LWS_EXTERN +#define LWS_EXTERN extern +#endif + +#ifdef _WIN32 +#define random rand +#else +#include +#include +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include +#include +#else +#include +#include +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_USE_POLARSSL) +#include +struct lws_polarssl_context { + x509_crt ca; /**< ca */ + x509_crt certificate; /**< cert */ + rsa_context key; /**< key */ +}; +typedef struct lws_polarssl_context SSL_CTX; +typedef ssl_context SSL; +#else +#if defined(LWS_USE_MBEDTLS) +#include +#else +#include +#include +#endif /* not USE_MBEDTLS */ +#endif /* not USE_POLARSSL */ +#endif /* not USE_WOLFSSL */ +#endif + + +#define CONTEXT_PORT_NO_LISTEN -1 + +/** \defgroup log Logging + * + * ##Logging + * + * Lws provides flexible and filterable logging facilities, which can be + * used inside lws and in user code. + * + * Log categories may be individually filtered bitwise, and directed to built-in + * sinks for syslog-compatible logging, or a user-defined function. + */ +///@{ + +enum lws_log_levels { + LLL_ERR = 1 << 0, + LLL_WARN = 1 << 1, + LLL_NOTICE = 1 << 2, + LLL_INFO = 1 << 3, + LLL_DEBUG = 1 << 4, + LLL_PARSER = 1 << 5, + LLL_HEADER = 1 << 6, + LLL_EXT = 1 << 7, + LLL_CLIENT = 1 << 8, + LLL_LATENCY = 1 << 9, + + LLL_COUNT = 10 /* set to count of valid flags */ +}; + +LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...); +LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl); +/** + * lwsl_timestamp: generate logging timestamp string + * + * \param level: logging level + * \param p: char * buffer to take timestamp + * \param len: length of p + * + * returns length written in p + */ +LWS_VISIBLE LWS_EXTERN int +lwsl_timestamp(int level, char *p, int len); + +#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) + +#if !defined(LWS_WITH_NO_LOGS) +/* notice, warn and log are always compiled in */ +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +/* + * weaker logging can be deselected at configure time using --disable-debug + * that gets rid of the overhead of checking while keeping _warn and _err + * active + */ + +#if defined(LWS_WITH_ESP8266) +#undef _DEBUG +#endif + +#ifdef _DEBUG +#if defined(LWS_WITH_NO_LOGS) +/* notice, warn and log are always compiled in */ +//#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__) +#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__) +#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__) +#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__) +#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) +#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) +#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) +/** + * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only) + * + * \param buf: buffer start to dump + * \param len: length of buffer to dump + */ +LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len); + +#else /* no debug */ +#if defined(LWS_WITH_NO_LOGS) +//#define lwsl_err(...) do {} while(0) +#define lwsl_warn(...) do {} while(0) +#define lwsl_notice(...) do {} while(0) +#endif +#define lwsl_info(...) do {} while(0) +#define lwsl_debug(...) do {} while(0) +#define lwsl_parser(...) do {} while(0) +#define lwsl_header(...) do {} while(0) +#define lwsl_ext(...) do {} while(0) +#define lwsl_client(...) do {} while(0) +#define lwsl_latency(...) do {} while(0) +#define lwsl_hexdump(a, b) + +#endif + +/** + * lws_set_log_level() - Set the logging bitfield + * \param level: OR together the LLL_ debug contexts you want output from + * \param log_emit_function: NULL to leave it as it is, or a user-supplied + * function to perform log string emission instead of + * the default stderr one. + * + * log level defaults to "err", "warn" and "notice" contexts enabled and + * emission on stderr. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_log_level(int level, + void (*log_emit_function)(int level, const char *line)); + +/** + * lwsl_emit_syslog() - helper log emit function writes to system log + * + * \param level: one of LLL_ log level indexes + * \param line: log string + * + * You use this by passing the function pointer to lws_set_log_level(), to set + * it as the log emit function, it is not called directly. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_emit_syslog(int level, const char *line); + +///@} + + +#include + +#ifndef lws_container_of +#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) +#endif + + +struct lws; +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +/* api change list for user code to test against */ + +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG + +/* the struct lws_protocols has the id field present */ +#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD + +/* you can call lws_get_peer_write_allowance */ +#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE + +/* extra parameter introduced in 917f43ab821 */ +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN + +/* File operations stuff exists */ +#define LWS_FEATURE_FOPS + + +#if defined(_WIN32) +typedef SOCKET lws_sockfd_type; +typedef HANDLE lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct lws_pollfd { + lws_sockfd_type fd; /**< file descriptor */ + SHORT events; /**< which events to respond to */ + SHORT revents; /**< which events happened */ +}; +#define LWS_POLLHUP (FD_CLOSE) +#define LWS_POLLIN (FD_READ | FD_ACCEPT) +#define LWS_POLLOUT (FD_WRITE) +#else + +#if defined(MBED_OPERATORS) +/* it's a class lws_conn * */ +typedef void * lws_sockfd_type; +typedef void * lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +struct lws; + +void * mbed3_create_tcp_stream_socket(void); +void mbed3_delete_tcp_stream_socket(void *sockfd); +void mbed3_tcp_stream_bind(void *sock, int port, struct lws *); +void mbed3_tcp_stream_accept(void *sock, struct lws *); +#else +#if defined(LWS_WITH_ESP8266) + +#include +#include + +typedef struct espconn * lws_sockfd_type; +typedef void * lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +struct lws_vhost; + +lws_sockfd_type esp8266_create_tcp_listen_socket(struct lws_vhost *vh); +void esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi); + +#include +#include +#include "ets_sys.h" + +int ets_snprintf(char *str, size_t size, const char *format, ...); +#define snprintf ets_snprintf + +typedef os_timer_t uv_timer_t; +typedef void uv_cb_t(uv_timer_t *); + +void os_timer_disarm(void *); +void os_timer_setfn(os_timer_t *, os_timer_func_t *, void *); + +void ets_timer_arm_new(os_timer_t *, int, int, int); + +//void os_timer_arm(os_timer_t *, int, int); + +#define UV_VERSION_MAJOR 1 + +#define lws_uv_getloop(a, b) (NULL) + +static inline void uv_timer_init(void *l, uv_timer_t *t) +{ + (void)l; + memset(t, 0, sizeof(*t)); + os_timer_disarm(t); +} + +static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +{ + os_timer_setfn(t, (os_timer_func_t *)cb, t); + /* ms, repeat */ + os_timer_arm(t, first, !!rep); +} + +static inline void uv_timer_stop(uv_timer_t *t) +{ + os_timer_disarm(t); +} + +#else +typedef int lws_sockfd_type; +typedef int lws_filefd_type; +#define lws_sockfd_valid(sfd) (sfd >= 0) +#endif +#endif + +#define lws_pollfd pollfd +#define LWS_POLLHUP (POLLHUP|POLLERR) +#define LWS_POLLIN (POLLIN) +#define LWS_POLLOUT (POLLOUT) +#endif + +/** struct lws_pollargs - argument structure for all external poll related calls + * passed in via 'in' */ +struct lws_pollargs { + lws_sockfd_type fd; /**< applicable socket descriptor */ + int events; /**< the new event mask */ + int prev_events; /**< the previous event mask */ +}; + +struct lws_tokens; +struct lws_token_limits; + +/*! \defgroup wsclose Websocket Close + * + * ##Websocket close frame control + * + * When we close a ws connection, we can send a reason code and a short + * UTF-8 description back with the close packet. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_close_status - RFC6455 close status codes */ +enum lws_close_status { + LWS_CLOSE_STATUS_NOSTATUS = 0, + LWS_CLOSE_STATUS_NORMAL = 1000, + /**< 1000 indicates a normal closure, meaning that the purpose for + which the connection was established has been fulfilled. */ + LWS_CLOSE_STATUS_GOINGAWAY = 1001, + /**< 1001 indicates that an endpoint is "going away", such as a server + going down or a browser having navigated away from a page. */ + LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002, + /**< 1002 indicates that an endpoint is terminating the connection due + to a protocol error. */ + LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, + /**< 1003 indicates that an endpoint is terminating the connection + because it has received a type of data it cannot accept (e.g., an + endpoint that understands only text data MAY send this if it + receives a binary message). */ + LWS_CLOSE_STATUS_RESERVED = 1004, + /**< Reserved. The specific meaning might be defined in the future. */ + LWS_CLOSE_STATUS_NO_STATUS = 1005, + /**< 1005 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that no status + code was actually present. */ + LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, + /**< 1006 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed abnormally, e.g., without sending or + receiving a Close control frame. */ + LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007, + /**< 1007 indicates that an endpoint is terminating the connection + because it has received data within a message that was not + consistent with the type of the message (e.g., non-UTF-8 [RFC3629] + data within a text message). */ + LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008, + /**< 1008 indicates that an endpoint is terminating the connection + because it has received a message that violates its policy. This + is a generic status code that can be returned when there is no + other more suitable status code (e.g., 1003 or 1009) or if there + is a need to hide specific details about the policy. */ + LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, + /**< 1009 indicates that an endpoint is terminating the connection + because it has received a message that is too big for it to + process. */ + LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, + /**< 1010 indicates that an endpoint (client) is terminating the + connection because it has expected the server to negotiate one or + more extension, but the server didn't return them in the response + message of the WebSocket handshake. The list of extensions that + are needed SHOULD appear in the /reason/ part of the Close frame. + Note that this status code is not used by the server, because it + can fail the WebSocket handshake instead */ + LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, + /**< 1011 indicates that a server is terminating the connection because + it encountered an unexpected condition that prevented it from + fulfilling the request. */ + LWS_CLOSE_STATUS_TLS_FAILURE = 1015, + /**< 1015 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed due to a failure to perform a TLS handshake + (e.g., the server certificate can't be verified). */ + + /****** add new things just above ---^ ******/ + + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999, +}; + +/** + * lws_close_reason - Set reason and aux data to send with Close packet + * If you are going to return nonzero from the callback + * requesting the connection to close, you can optionally + * call this to set the reason the peer will be told if + * possible. + * + * \param wsi: The websocket connection to set the close reason on + * \param status: A valid close status from websocket standard + * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data + * \param len: Length of data in \param buf to send + */ +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len); + +///@} + +struct lws; +struct lws_context; +/* needed even with extensions disabled for create context */ +struct lws_extension; + +/*! \defgroup usercb User Callback + * + * ##User protocol callback + * + * The protocol callback is the primary way lws interacts with + * user code. For one of a list of a few dozen reasons the callback gets + * called at some event to be handled. + * + * All of the events can be ignored, returning 0 is taken as "OK" and returning + * nonzero in most cases indicates that the connection should be closed. + */ +///@{ + + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_callback_reasons - reason you're getting a protocol callback */ +enum lws_callback_reasons { + LWS_CALLBACK_ESTABLISHED = 0, + /**< (VH) after the server completes a handshake with an incoming + * client. If you built the library with ssl support, in is a + * pointer to the ssl struct associated with the connection or NULL.*/ + LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1, + /**< the request client connection has been unable to complete a + * handshake with the remote server. If in is non-NULL, you can + * find an error string of length len where it points to + * + * Diagnostic strings that may be returned include + * + * "getaddrinfo (ipv6) failed" + * "unknown address family" + * "getaddrinfo (ipv4) failed" + * "set socket opts failed" + * "insert wsi failed" + * "lws_ssl_client_connect1 failed" + * "lws_ssl_client_connect2 failed" + * "Peer hung up" + * "read failed" + * "HS: URI missing" + * "HS: Redirect code but no Location" + * "HS: URI did not parse" + * "HS: Redirect failed" + * "HS: Server did not return 200" + * "HS: OOM" + * "HS: disallowed by client filter" + * "HS: disallowed at ESTABLISHED" + * "HS: ACCEPT missing" + * "HS: ws upgrade response not 101" + * "HS: UPGRADE missing" + * "HS: Upgrade to something other than websocket" + * "HS: CONNECTION missing" + * "HS: UPGRADE malformed" + * "HS: PROTOCOL malformed" + * "HS: Cannot match protocol" + * "HS: EXT: list too big" + * "HS: EXT: failed setting defaults" + * "HS: EXT: failed parsing defaults" + * "HS: EXT: failed parsing options" + * "HS: EXT: Rejects server options" + * "HS: EXT: unknown ext" + * "HS: Accept hash wrong" + * "HS: Rejected by filter cb" + * "HS: OOM" + * "HS: SO_SNDBUF failed" + * "HS: Rejected at CLIENT_ESTABLISHED" + */ + LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2, + /**< this is the last chance for the client user code to examine the + * http headers and decide to reject the connection. If the + * content in the headers is interesting to the + * client (url, etc) it needs to copy it out at + * this point since it will be destroyed before + * the CLIENT_ESTABLISHED call */ + LWS_CALLBACK_CLIENT_ESTABLISHED = 3, + /**< after your client connection completed + * a handshake with the remote server */ + LWS_CALLBACK_CLOSED = 4, + /**< when the websocket session ends */ + LWS_CALLBACK_CLOSED_HTTP = 5, + /**< when a HTTP (non-websocket) session ends */ + LWS_CALLBACK_RECEIVE = 6, + /**< data has appeared for this server endpoint from a + * remote client, it can be found at *in and is + * len bytes long */ + LWS_CALLBACK_RECEIVE_PONG = 7, + /**< servers receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_RECEIVE = 8, + /**< data has appeared from the server for the client connection, it + * can be found at *in and is len bytes long */ + LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9, + /**< clients receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_WRITEABLE = 10, + /**< If you call lws_callback_on_writable() on a connection, you will + * get one of these callbacks coming when the connection socket + * is able to accept another write packet without blocking. + * If it already was able to take another packet without blocking, + * you'll get this callback at the next call to the service loop + * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE + * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */ + LWS_CALLBACK_SERVER_WRITEABLE = 11, + /**< See LWS_CALLBACK_CLIENT_WRITEABLE */ + LWS_CALLBACK_HTTP = 12, + /**< an http request has come from a client that is not + * asking to upgrade the connection to a websocket + * one. This is a chance to serve http content, + * for example, to send a script to the client + * which will then open the websockets connection. + * in points to the URI path requested and + * lws_serve_http_file() makes it very + * simple to send back a file to the client. + * Normally after sending the file you are done + * with the http connection, since the rest of the + * activity will come by websockets from the script + * that was delivered by http, so you will want to + * return 1; to close and free up the connection. */ + LWS_CALLBACK_HTTP_BODY = 13, + /**< the next len bytes data from the http + * request body HTTP connection is now available in in. */ + LWS_CALLBACK_HTTP_BODY_COMPLETION = 14, + /**< the expected amount of http request body has been delivered */ + LWS_CALLBACK_HTTP_FILE_COMPLETION = 15, + /**< a file requested to be sent down http link has completed. */ + LWS_CALLBACK_HTTP_WRITEABLE = 16, + /**< you can write more down the http protocol link now. */ + LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17, + /**< called when a client connects to + * the server at network level; the connection is accepted but then + * passed to this callback to decide whether to hang up immediately + * or not, based on the client IP. in contains the connection + * socket's descriptor. Since the client connection information is + * not available yet, wsi still pointing to the main server socket. + * Return non-zero to terminate the connection before sending or + * receiving anything. Because this happens immediately after the + * network connection from the client, there's no websocket protocol + * selected yet so this callback is issued only to protocol 0. */ + LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18, + /**< called when the request has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the URI, eg, "/" + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the http + * connection to proceed or to kill the connection. */ + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19, + /**< A new client just had + * been connected, accepted, and instantiated into the pool. This + * callback allows setting any relevant property to it. Because this + * happens immediately after the instantiation of a new client, + * there's no websocket protocol selected yet so this callback is + * issued only to protocol 0. Only wsi is defined, pointing to the + * new client, and the return value is ignored. */ + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20, + /**< called when the handshake has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the requested protocol name + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the handshake + * to proceed or to kill the connection. */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to perform extra SSL_CTX_load_verify_locations() or similar + * calls to direct OpenSSL where to find certificates the client + * can use to confirm the remote server identity. user is the + * OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to load extra certifcates into the server which allow it to + * verify the validity of certificates returned by clients. user + * is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, + /**< if the libwebsockets vhost was created with the option + * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this + * callback is generated during OpenSSL verification of the cert + * sent from the client. It is sent to protocol[0] callback as + * no protocol has been negotiated on the connection yet. + * Notice that the libwebsockets context and wsi are both NULL + * during this callback. See + * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html + * to understand more detail about the OpenSSL callback that + * generates this libwebsockets callback and the meanings of the + * arguments passed. In this callback, user is the x509_ctx, + * in is the ssl pointer and len is preverify_ok + * Notice that this callback maintains libwebsocket return + * conventions, return 0 to mean the cert is OK or 1 to fail it. + * This also means that if you don't handle this callback then + * the default callback action of returning 0 allows the client + * certificates. */ + LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, + /**< this callback happens + * when a client handshake is being compiled. user is NULL, + * in is a char **, it's pointing to a char * which holds the + * next location in the header buffer where you can add + * headers, and len is the remaining space in the header buffer, + * which is typically some hundreds of bytes. So, to add a canned + * cookie, your handler code might look similar to: + * + * char **p = (char **)in; + * + * if (len < 100) + * return 1; + * + * *p += sprintf(*p, "Cookie: a=b\x0d\x0a"); + * + * return 0; + * + * Notice if you add anything, you just have to take care about + * the CRLF on the line you added. Obviously this callback is + * optional, if you don't handle it everything is fine. + * + * Notice the callback is coming to protocols[0] all the time, + * because there is no specific protocol negotiated yet. */ + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25, + /**< When the server handshake code + * sees that it does support a requested extension, before + * accepting the extension by additing to the list sent back to + * the client it gives this callback just to check that it's okay + * to use that extension. It calls back to the requested protocol + * and with in being the extension name, len is 0 and user is + * valid. Note though at this time the ESTABLISHED callback hasn't + * happened yet so if you initialize user content there, user + * content during this callback might not be useful for anything. + * Notice this callback comes to protocols[0]. */ + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26, + /**< When a client + * connection is being prepared to start a handshake to a server, + * each supported extension is checked with protocols[0] callback + * with this reason, giving the user code a chance to suppress the + * claim to support that extension by returning non-zero. If + * unhandled, by default 0 will be returned and the extension + * support included in the header to the server. Notice this + * callback comes to protocols[0]. */ + LWS_CALLBACK_PROTOCOL_INIT = 27, + /**< One-time call per protocol, per-vhost using it, so it can + * do initial setup / allocations etc */ + LWS_CALLBACK_PROTOCOL_DESTROY = 28, + /**< One-time call per protocol, per-vhost using it, indicating + * this protocol won't get used at all after this callback, the + * vhost is getting destroyed. Take the opportunity to + * deallocate everything that was allocated by the protocol. */ + LWS_CALLBACK_WSI_CREATE = 29, + /**< outermost (earliest) wsi create notification to protocols[0] */ + LWS_CALLBACK_WSI_DESTROY = 30, + /**< outermost (latest) wsi destroy notification to protocols[0] */ + LWS_CALLBACK_GET_THREAD_ID = 31, + /**< lws can accept callback when writable requests from other + * threads, if you implement this callback and return an opaque + * current thread ID integer. */ + + /* external poll() management support */ + LWS_CALLBACK_ADD_POLL_FD = 32, + /**< lws normally deals with its poll() or other event loop + * internally, but in the case you are integrating with another + * server you will need to have lws sockets share a + * polling array with the other server. This and the other + * POLL_FD related callbacks let you put your specialized + * poll array interface code in the callback for protocol 0, the + * first protocol you support, usually the HTTP protocol in the + * serving case. + * This callback happens when a socket needs to be + * added to the polling loop: in points to a struct + * lws_pollargs; the fd member of the struct is the file + * descriptor, and events contains the active events + * + * If you are using the internal lws polling / event loop + * you can just ignore these callbacks. */ + LWS_CALLBACK_DEL_POLL_FD = 33, + /**< This callback happens when a socket descriptor + * needs to be removed from an external polling array. in is + * again the struct lws_pollargs containing the fd member + * to be removed. If you are using the internal polling + * loop, you can just ignore it. */ + LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34, + /**< This callback happens when lws wants to modify the events for + * a connection. + * in is the struct lws_pollargs with the fd to change. + * The new event mask is in events member and the old mask is in + * the prev_events member. + * If you are using the internal polling loop, you can just ignore + * it. */ + LWS_CALLBACK_LOCK_POLL = 35, + /**< These allow the external poll changes driven + * by lws to participate in an external thread locking + * scheme around the changes, so the whole thing is threadsafe. + * These are called around three activities in the library, + * - inserting a new wsi in the wsi / fd table (len=1) + * - deleting a wsi from the wsi / fd table (len=1) + * - changing a wsi's POLLIN/OUT state (len=0) + * Locking and unlocking external synchronization objects when + * len == 1 allows external threads to be synchronized against + * wsi lifecycle changes if it acquires the same lock for the + * duration of wsi dereference from the other thread context. */ + LWS_CALLBACK_UNLOCK_POLL = 36, + /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */ + + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37, + /**< if configured for including OpenSSL support but no private key + * file has been specified (ssl_private_key_filepath is NULL), this is + * called to allow the user to set the private key directly via + * libopenssl and perform further operations if required; this might be + * useful in situations where the private key is not directly accessible + * by the OS, for example if it is stored on a smartcard. + * user is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38, + /**< The peer has sent an unsolicited Close WS packet. in and + * len are the optional close code (first 2 bytes, network + * order) and the optional additional information which is not + * defined in the standard, and may be a string or non-human- readable data. + * If you return 0 lws will echo the close and then close the + * connection. If you return nonzero lws will just close the + * connection. */ + + LWS_CALLBACK_WS_EXT_DEFAULTS = 39, + /**< */ + + LWS_CALLBACK_CGI = 40, + /**< */ + LWS_CALLBACK_CGI_TERMINATED = 41, + /**< */ + LWS_CALLBACK_CGI_STDIN_DATA = 42, + /**< */ + LWS_CALLBACK_CGI_STDIN_COMPLETED = 43, + /**< */ + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, + /**< */ + LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, + /**< */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, + /**< */ + LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, + /**< */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48, + /**< */ + LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49, + /**< */ + LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50, + /**< */ + LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51, + /**< */ + LWS_CALLBACK_PROCESS_HTML = 52, + /**< */ + LWS_CALLBACK_ADD_HEADERS = 53, + /**< */ + LWS_CALLBACK_SESSION_INFO = 54, + /**< */ + + LWS_CALLBACK_GS_EVENT = 55, + /**< */ + LWS_CALLBACK_HTTP_PMO = 56, + /**< per-mount options for this connection, called before + * the normal LWS_CALLBACK_HTTP when the mount has per-mount + * options + */ + LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57, + /**< when doing an HTTP type client connection, you can call + * lws_client_http_body_pending(wsi, 1) from + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks + * sending the HTTP headers. + * + * From this callback, when you have sent everything, you should let + * lws know by calling lws_client_http_body_pending(wsi, 0) + */ + + /****** add new things just above ---^ ******/ + + LWS_CALLBACK_USER = 1000, + /**< user code can use any including / above without fear of clashes */ +}; + + + +/** + * typedef lws_callback_function() - User server actions + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * This callback is the way the user controls what is served. All the + * protocol detail is hidden and handled by the library. + * + * For each connection / session there is user data allocated that is + * pointed to by "user". You set the size of this user data area when + * the library is initialized with lws_create_server. + */ +typedef int +lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); +///@} + +/*! \defgroup extensions + * + * ##Extension releated functions + * + * Ws defines optional extensions, lws provides the ability to implement these + * in user code if so desired. + * + * We provide one extensions permessage-deflate. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_extension_callback_reasons { + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3, + LWS_EXT_CB_CONSTRUCT = 4, + LWS_EXT_CB_CLIENT_CONSTRUCT = 5, + LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7, + LWS_EXT_CB_DESTROY = 8, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9, + LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10, + LWS_EXT_CB_PACKET_RX_PREPARSE = 11, + LWS_EXT_CB_PACKET_TX_PRESEND = 12, + LWS_EXT_CB_PACKET_TX_DO_SEND = 13, + LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14, + LWS_EXT_CB_FLUSH_PENDING_TX = 15, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17, + LWS_EXT_CB_1HZ = 18, + LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19, + LWS_EXT_CB_IS_WRITEABLE = 20, + LWS_EXT_CB_PAYLOAD_TX = 21, + LWS_EXT_CB_PAYLOAD_RX = 22, + LWS_EXT_CB_OPTION_DEFAULT = 23, + LWS_EXT_CB_OPTION_SET = 24, + LWS_EXT_CB_OPTION_CONFIRM = 25, + LWS_EXT_CB_NAMED_OPTION_SET = 26, + + /****** add new things just above ---^ ******/ +}; + +/** enum lws_ext_options_types */ +enum lws_ext_options_types { + EXTARG_NONE, /**< does not take an argument */ + EXTARG_DEC, /**< requires a decimal argument */ + EXTARG_OPT_DEC /**< may have an optional decimal argument */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_options - Option arguments to the extension. These are + * used in the negotiation at ws upgrade time. + * The helper function lws_ext_parse_options() + * uses these to generate callbacks */ +struct lws_ext_options { + const char *name; /**< Option name, eg, "server_no_context_takeover" */ + enum lws_ext_options_types type; /**< What kind of args the option can take */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_option_arg */ +struct lws_ext_option_arg { + const char *option_name; /**< may be NULL, option_index used then */ + int option_index; /**< argument ordinal to use if option_name missing */ + const char *start; /**< value */ + int len; /**< length of value */ +}; + +/** + * typedef lws_extension_callback_function() - Hooks to allow extensions to operate + * \param context: Websockets context + * \param ext: This extension + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to ptr to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * Each extension that is active on a particular connection receives + * callbacks during the connection lifetime to allow the extension to + * operate on websocket data and manage itself. + * + * Libwebsockets takes care of allocating and freeing "user" memory for + * each active extension on each connection. That is what is pointed to + * by the user parameter. + * + * LWS_EXT_CB_CONSTRUCT: called when the server has decided to + * select this extension from the list provided by the client, + * just before the server will send back the handshake accepting + * the connection with this extension active. This gives the + * extension a chance to initialize its connection context found + * in user. + * + * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT + * but called when client is instantiating this extension. Some + * extensions will work the same on client and server side and then + * you can just merge handlers for both CONSTRUCTS. + * + * LWS_EXT_CB_DESTROY: called when the connection the extension was + * being used on is about to be closed and deallocated. It's the + * last chance for the extension to deallocate anything it has + * allocated in the user data (pointed to by user) before the + * user data is deleted. This same callback is used whether you + * are in client or server instantiation context. + * + * LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on + * a connection, and a packet of data arrived at the connection, + * it is passed to this callback to give the extension a chance to + * change the data, eg, decompress it. user is pointing to the + * extension's private connection context data, in is pointing + * to an lws_tokens struct, it consists of a char * pointer called + * token, and an int called token_len. At entry, these are + * set to point to the received buffer and set to the content + * length. If the extension will grow the content, it should use + * a new buffer allocated in its private user context data and + * set the pointed-to lws_tokens members to point to its buffer. + * + * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as + * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the + * extension a chance to change websocket data just before it will + * be sent out. Using the same lws_token pointer scheme in in, + * the extension can change the buffer and the length to be + * transmitted how it likes. Again if it wants to grow the + * buffer safely, it should copy the data into its own buffer and + * set the lws_tokens token pointer to it. + * + * LWS_EXT_CB_ARGS_VALIDATE: + */ +typedef int +lws_extension_callback_function(struct lws_context *context, + const struct lws_extension *ext, struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len); + +/** struct lws_extension - An extension we support */ +struct lws_extension { + const char *name; /**< Formal extension name, eg, "permessage-deflate" */ + lws_extension_callback_function *callback; /**< Service callback */ + const char *client_offer; /**< String containing exts and options client offers */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_set_extension_option(): set extension option if possible + * + * \param wsi: websocket connection + * \param ext_name: name of ext, like "permessage-deflate" + * \param opt_name: name of option, like "rx_buf_size" + * \param opt_val: value to set option to + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val); + +#ifndef LWS_NO_EXTENSIONS +/* lws_get_internal_extensions() - DEPRECATED + * + * \Deprecated There is no longer a set internal extensions table. The table is provided + * by user code along with application-specific settings. See the test + * client and server for how to do. + */ +static LWS_INLINE LWS_WARN_DEPRECATED const struct lws_extension * +lws_get_internal_extensions(void) { return NULL; } + +/** + * lws_ext_parse_options() - deal with parsing negotiated extension options + * + * \param ext: related extension struct + * \param wsi: websocket connection + * \param ext_user: per-connection extension private data + * \param opts: list of supported options + * \param o: option string to parse + * \param len: length + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, + void *ext_user, const struct lws_ext_options *opts, + const char *o, int len); +#endif + +/** lws_extension_callback_pm_deflate() - extension for RFC7692 + * + * \param context: lws context + * \param ext: related lws_extension struct + * \param wsi: websocket connection + * \param reason: incoming callback reason + * \param user: per-connection extension private data + * \param in: pointer parameter + * \param len: length parameter + * + * Built-in callback implementing RFC7692 permessage-deflate + */ +LWS_EXTERN +int lws_extension_callback_pm_deflate( + struct lws_context *context, const struct lws_extension *ext, + struct lws *wsi, enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len); + +/* + * The internal exts are part of the public abi + * If we add more extensions, publish the callback here ------v + */ +///@} + +/*! \defgroup Protocols-and-Plugins Protocols and Plugins + * \ingroup lwsapi + * + * ##Protocol and protocol plugin -related apis + * + * Protocols bind ws protocol names to a custom callback specific to that + * protocol implementaion. + * + * A list of protocols can be passed in at context creation time, but it is + * also legal to leave that NULL and add the protocols and their callback code + * using plugins. + * + * Plugins are much preferable compared to cut and pasting code into an + * application each time, since they can be used standalone. + */ +///@{ +/** struct lws_protocols - List of protocols and handlers client or server + * supports. */ + +struct lws_protocols { + const char *name; + /**< Protocol name that must match the one given in the client + * Javascript new WebSocket(url, 'protocol') name. */ + lws_callback_function *callback; + /**< The service callback used for this protocol. It allows the + * service action for an entire protocol to be encapsulated in + * the protocol-specific callback */ + size_t per_session_data_size; + /**< Each new connection using this protocol gets + * this much memory allocated on connection establishment and + * freed on connection takedown. A pointer to this per-connection + * allocation is passed into the callback in the 'user' parameter */ + size_t rx_buffer_size; + /**< lws allocates this much space for rx data and informs callback + * when something came. Due to rx flow control, the callback may not + * be able to consume it all without having to return to the event + * loop. That is supported in lws. + * + * This also controls how much may be sent at once at the moment, + * although this is likely to change. + */ + unsigned int id; + /**< ignored by lws, but useful to contain user information bound + * to the selected protocol. For example if this protocol was + * called "myprotocol-v2", you might set id to 2, and the user + * code that acts differently according to the version can do so by + * switch (wsi->protocol->id), user code might use some bits as + * capability flags based on selected protocol version, etc. */ + void *user; /**< ignored by lws, but user code can pass a pointer + here it can later access from the protocol callback */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +struct lws_vhost; + +/** + * lws_vhost_name_to_protocol() - get vhost's protocol object from its name + * + * \param vh: vhost to search + * \param name: protocol name + * + * Returns NULL or a pointer to the vhost's protocol of the requested name + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name); + +/** + * lws_get_protocol() - Returns a protocol pointer from a websocket + * connection. + * \param wsi: pointer to struct websocket you want to know the protocol of + * + * + * Some apis can act on all live connections of a given protocol, + * this is how you can get a pointer to the active protocol if needed. + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_get_protocol(struct lws *wsi); + +/** lws_protocol_get() - deprecated: use lws_get_protocol */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost + * storage + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * \param size: bytes to allocate + * + * Protocols often find it useful to allocate a per-vhost struct, this is a + * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot, + int size); + +/** + * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage + * + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * + * Recover a pointer to the allocated per-vhost storage for the protocol created + * by lws_protocol_vh_priv_zalloc() earlier + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot); + +/** + * lws_finalize_startup() - drop initial process privileges + * + * \param context: lws context + * + * This is called after the end of the vhost protocol initializations, but + * you may choose to call it earlier + */ +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context); + +#ifdef LWS_WITH_PLUGINS + +/* PLUGINS implies LIBUV */ + +#define LWS_PLUGIN_API_MAGIC 180 + +/** struct lws_plugin_capability - how a plugin introduces itself to lws */ +struct lws_plugin_capability { + unsigned int api_magic; /**< caller fills this in, plugin fills rest */ + const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ + int count_protocols; /**< how many protocols */ + const struct lws_extension *extensions; /**< array of extensions provided by plugin */ + int count_extensions; /**< how many extensions */ +}; + +typedef int (*lws_plugin_init_func)(struct lws_context *, + struct lws_plugin_capability *); +typedef int (*lws_plugin_destroy_func)(struct lws_context *); + +/** struct lws_plugin */ +struct lws_plugin { + struct lws_plugin *list; /**< linked list */ +#if (UV_VERSION_MAJOR > 0) + uv_lib_t lib; /**< shared library pointer */ +#else + void *l; /**< so we can compile on ancient libuv */ +#endif + char name[64]; /**< name of the plugin */ + struct lws_plugin_capability caps; /**< plugin capabilities */ +}; + +#endif + +///@} + + +/*! \defgroup generic-sessions plugin: generic-sessions + * \ingroup Protocols-and-Plugins + * + * ##Plugin Generic-sessions related + * + * generic-sessions plugin provides a reusable, generic session and login / + * register / forgot password framework including email verification. + */ +///@{ + +#define LWSGS_EMAIL_CONTENT_SIZE 16384 +/**< Maximum size of email we might send */ + +/* SHA-1 binary and hexified versions */ +/** typedef struct lwsgw_hash_bin */ +typedef struct { unsigned char bin[20]; /**< binary representation of hash */} lwsgw_hash_bin; +/** typedef struct lwsgw_hash */ +typedef struct { char id[41]; /**< ascii hex representation of hash */ } lwsgw_hash; + +/** enum lwsgs_auth_bits */ +enum lwsgs_auth_bits { + LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ + LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ + LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ + LWSGS_AUTH_FORGOT_FLOW = 8, /**< he just completed "forgot password" flow */ +}; + +/** struct lws_session_info - information about user session status */ +struct lws_session_info { + char username[32]; /**< username logged in as, or empty string */ + char email[100]; /**< email address associated with login, or empty string */ + char ip[72]; /**< ip address session was started from */ + unsigned int mask; /**< access rights mask associated with session + * see enum lwsgs_auth_bits */ + char session[42]; /**< session id string, usable as opaque uid when not logged in */ +}; + +/** enum lws_gs_event */ +enum lws_gs_event { + LWSGSE_CREATED, /**< a new user was created */ + LWSGSE_DELETED /**< an existing user was deleted */ +}; + +/** struct lws_gs_event_args */ +struct lws_gs_event_args { + enum lws_gs_event event; /**< which event happened */ + const char *username; /**< which username the event happened to */ + const char *email; /**< the email address of that user */ +}; + +///@} + + +/*! \defgroup context-and-vhost + * \ingroup lwsapi + * + * ##Context and Vhost releated functions + * + * LWS requires that there is one context, in which you may define multiple + * vhosts. Each vhost is a virtual host, with either its own listen port + * or sharing an existing one. Each vhost has its own SSL context that can + * be set up individually or left disabled. + * + * If you don't care about multiple "site" support, you can ignore it and + * lws will create a single default vhost at context creation time. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ + +/** enum lws_context_options - context and vhost options */ +enum lws_context_options { + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = (1 << 1) | + (1 << 12), + /**< (VH) Don't allow the connection unless the client has a + * client cert that we recognize; provides + * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = (1 << 2), + /**< (CTX) Don't try to get the server's hostname */ + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = (1 << 3) | + (1 << 12), + /**< (VH) Allow non-SSL (plaintext) connections on the same + * port as SSL is listening... undermines the security of SSL; + * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_LIBEV = (1 << 4), + /**< (CTX) Use libev event loop */ + LWS_SERVER_OPTION_DISABLE_IPV6 = (1 << 5), + /**< (VH) Disable IPV6 support */ + LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS = (1 << 6), + /**< (VH) Don't load OS CA certs, you will need to load your + * own CA cert(s) */ + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED = (1 << 7), + /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */ + LWS_SERVER_OPTION_VALIDATE_UTF8 = (1 << 8), + /**< (VH) Check UT-8 correctness */ + LWS_SERVER_OPTION_SSL_ECDH = (1 << 9) | + (1 << 12), + /**< (VH) initialize ECDH ciphers */ + LWS_SERVER_OPTION_LIBUV = (1 << 10), + /**< (CTX) Use libuv event loop */ + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS = (1 << 11) | + (1 << 12), + /**< (VH) Use http redirect to force http to https + * (deprecated: use mount redirection) */ + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), + /**< (CTX) Initialize the SSL library at all */ + LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13), + /**< (CTX) Only create the context when calling context + * create api, implies user code will create its own vhosts */ + LWS_SERVER_OPTION_UNIX_SOCK = (1 << 14), + /**< (VH) Use Unix socket */ + LWS_SERVER_OPTION_STS = (1 << 15), + /**< (VH) Send Strict Transport Security header, making + * clients subsequently go to https even if user asked for http */ + LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY = (1 << 16), + /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */ + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE = (1 << 17), + /**< (VH) if set, only ipv6 allowed on the vhost */ + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN = (1 << 18), + /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault + * normally makes the lib spin so you can attach a debugger to it + * even if it happened without a debugger in place. You can disable + * that by giving this option. + */ + + /****** add new things just above ---^ ******/ +}; + +#define lws_check_opt(c, f) (((c) & (f)) == (f)) + +/** struct lws_context_creation_info - parameters to create context and /or vhost with + * + * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS + * is not given, then for backwards compatibility one vhost is created at + * context-creation time using the info from this struct. + * + * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created + * at the same time as the context, they are expected to be created afterwards. + */ +struct lws_context_creation_info { + int port; + /**< VHOST: Port to listen on... you can use CONTEXT_PORT_NO_LISTEN to + * suppress listening on any port, that's what you want if you are + * not running a websocket server at all but just using it as a + * client */ + const char *iface; + /**< VHOST: NULL to bind the listen socket to all interfaces, or the + * interface name, eg, "eth2" + * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is + * the pathname of a UNIX domain socket. you can use the UNIX domain + * sockets in abstract namespace, by prepending an at symbol to the + * socket name. */ + const struct lws_protocols *protocols; + /**< VHOST: Array of structures listing supported protocols and a protocol- + * specific callback for each one. The list is ended with an + * entry that has a NULL callback pointer. */ + const struct lws_extension *extensions; + /**< VHOST: NULL or array of lws_extension structs listing the + * extensions this context supports. */ + const struct lws_token_limits *token_limits; + /**< CONTEXT: NULL or struct lws_token_limits pointer which is initialized + * with a token length limit for each possible WSI_TOKEN_ */ + const char *ssl_private_key_password; + /**< VHOST: NULL or the passphrase needed for the private key */ + const char *ssl_cert_filepath; + /**< VHOST: If libwebsockets was compiled to use ssl, and you want + * to listen using SSL, set to the filepath to fetch the + * server cert from, otherwise NULL for unencrypted */ + const char *ssl_private_key_filepath; + /**< VHOST: filepath to private key if wanting SSL mode; + * if this is set to NULL but sll_cert_filepath is set, the + * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called + * to allow setting of the private key directly via openSSL + * library calls */ + const char *ssl_ca_filepath; + /**< VHOST: CA certificate filepath or NULL */ + const char *ssl_cipher_list; + /**< VHOST: List of valid ciphers to use (eg, + * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" + * or you can leave it as NULL to get "DEFAULT" */ + const char *http_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int http_proxy_port; + /**< VHOST: If http_proxy_address was non-NULL, uses this port */ + int gid; + /**< CONTEXT: group id to change to after setting listen socket, or -1. */ + int uid; + /**< CONTEXT: user id to change to after setting listen socket, or -1. */ + unsigned int options; + /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ + void *user; + /**< CONTEXT: optional user pointer that can be recovered via the context + * pointer using lws_context_user */ + int ka_time; + /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive + * timeout to all libwebsocket sockets, client or server */ + int ka_probes; + /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many + * times to try to get a response from the peer before giving up + * and killing the connection */ + int ka_interval; + /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes + * attempt */ +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *provided_client_ssl_ctx; + /**< CONTEXT: If non-null, swap out libwebsockets ssl + * implementation for the one provided by provided_ssl_ctx. + * Libwebsockets no longer is responsible for freeing the context + * if this option is selected. */ +#else /* maintain structure layout either way */ + void *provided_client_ssl_ctx; /**< dummy if ssl disabled */ +#endif + + short max_http_header_data; + /**< CONTEXT: The max amount of header payload that can be handled + * in an http request (unrecognized header payload is dropped) */ + short max_http_header_pool; + /**< CONTEXT: The max number of connections with http headers that + * can be processed simultaneously (the corresponding memory is + * allocated for the lifetime of the context). If the pool is + * busy new incoming connections must wait for accept until one + * becomes free. */ + + unsigned int count_threads; + /**< CONTEXT: how many contexts to create in an array, 0 = 1 */ + unsigned int fd_limit_per_thread; + /**< CONTEXT: nonzero means restrict each service thread to this + * many fds, 0 means the default which is divide the process fd + * limit by the number of threads. */ + unsigned int timeout_secs; + /**< VHOST: various processes involving network roundtrips in the + * library are protected from hanging forever by timeouts. If + * nonzero, this member lets you set the timeout used in seconds. + * Otherwise a default timeout is used. */ + const char *ecdh_curve; + /**< VHOST: if NULL, defaults to initializing server with "prime256v1" */ + const char *vhost_name; + /**< VHOST: name of vhost, must match external DNS name used to + * access the site, like "warmcat.com" as it's used to match + * Host: header and / or SNI name for SSL. */ + const char * const *plugin_dirs; + /**< CONTEXT: NULL, or NULL-terminated array of directories to + * scan for lws protocol plugins at context creation time */ + const struct lws_protocol_vhost_options *pvo; + /**< VHOST: pointer to optional linked list of per-vhost + * options made accessible to protocols */ + int keepalive_timeout; + /**< VHOST: (default = 0 = 60s) seconds to allow remote + * client to hold on to an idle HTTP/1.1 connection */ + const char *log_filepath; + /**< VHOST: filepath to append logs to... this is opened before + * any dropping of initial privileges */ + const struct lws_http_mount *mounts; + /**< VHOST: optional linked list of mounts for this vhost */ + const char *server_string; + /**< CONTEXT: string used in HTTP headers to identify server + * software, if NULL, "libwebsockets". */ + unsigned int pt_serv_buf_size; + /**< CONTEXT: 0 = default of 4096. This buffer is used by + * various service related features including file serving, it + * defines the max chunk of file that can be sent at once. + * At the risk of lws having to buffer failed large sends, it + * can be increased to, eg, 128KiB to improve throughput. */ + unsigned int max_http_header_data2; + /**< CONTEXT: if max_http_header_data is 0 and this + * is nonzero, this will be used in place of the default. It's + * like this for compatibility with the original short version, + * this is unsigned int length. */ + long ssl_options_set; + /**< VHOST: Any bits set here will be set as SSL options */ + long ssl_options_clear; + /**< VHOST: Any bits set here will be cleared as SSL options */ + unsigned short ws_ping_pong_interval; + /**< CONTEXT: 0 for none, else interval in seconds between sending + * PINGs on idle websocket connections. When the PING is sent, + * the PONG must come within the normal timeout_secs timeout period + * or the connection will be dropped. + * Any RX or TX traffic on the connection restarts the interval timer, + * so a connection which always sends or receives something at intervals + * less than the interval given here will never send PINGs / expect + * PONGs. Conversely as soon as the ws connection is established, an + * idle connection will do the PING / PONG roundtrip as soon as + * ws_ping_pong_interval seconds has passed without traffic + */ + const struct lws_protocol_vhost_options *headers; + /**< VHOST: pointer to optional linked list of per-vhost + * canned headers that are added to server responses */ + + const struct lws_protocol_vhost_options *reject_service_keywords; + /**< CONTEXT: Optional list of keywords and rejection codes + text. + * + * The keywords are checked for existing in the user agent string. + * + * Eg, "badrobot" "404 Not Found" + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[8]; /**< dummy */ +}; + +/** + * lws_create_context() - Create the websocket handler + * \param info: pointer to struct with parameters + * + * This function creates the listening socket (if serving) and takes care + * of all initialization in one step. + * + * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is + * created; you're expected to create your own vhosts afterwards using + * lws_create_vhost(). Otherwise a vhost named "default" is also created + * using the information in the vhost-related members, for compatibility. + * + * After initialization, it returns a struct lws_context * that + * represents this server. After calling, user code needs to take care + * of calling lws_service() with the context pointer to get the + * server's sockets serviced. This must be done in the same process + * context as the initialization call. + * + * The protocol callback functions are called for a handful of events + * including http requests coming in, websocket connections becoming + * established, and data arriving; it's also called periodically to allow + * async transmission. + * + * HTTP requests are sent always to the FIRST protocol in protocol, since + * at that time websocket protocol has not been negotiated. Other + * protocols after the first one never see any HTTP callback activity. + * + * The server created is a simple http server by default; part of the + * websocket standard is upgrading this http connection to a websocket one. + * + * This allows the same server to provide files like scripts and favicon / + * images or whatever over http and dynamic data over websockets all in + * one place; they're all handled in the user callback. + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_create_context(struct lws_context_creation_info *info); + +/** + * lws_context_destroy() - Destroy the websocket context + * \param context: Websocket context + * + * This function closes any active connections and then frees the + * context. After calling this, any further use of the context is + * undefined. + */ +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy(struct lws_context *context); + +/** + * lws_set_proxy() - Setups proxy to lws_context. + * \param vhost: pointer to struct lws_vhost you want set proxy for + * \param proxy: pointer to c string containing proxy in format address:port + * + * Returns 0 if proxy string was parsed and proxy was setup. + * Returns -1 if proxy is NULL or has incorrect format. + * + * This is only required if your OS does not provide the http_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy); + + +struct lws_vhost; + +/** + * lws_create_vhost() - Create a vhost (virtual server context) + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * + * This function creates a virtual server (vhost) using the vhost-related + * members of the info struct. You can create many vhosts inside one context + * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS + */ +LWS_EXTERN LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info); + +/** + * lwsws_get_config_globals() - Parse a JSON server config file + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function prepares a n lws_context_creation_info struct with global + * settings from a file d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** + * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function creates vhosts into a context according to the settings in + *JSON files found in directory d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_vhosts(struct lws_context *context, + struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** lws_vhost_get() - \deprecated deprecated: use lws_get_vhost() */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_get_vhost() - return the vhost a wsi belongs to + * + * \param wsi: which connection + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_get_vhost(struct lws *wsi); + +/** + * lws_json_dump_vhost() - describe vhost state and stats in JSON + * + * \param vh: the vhost + * \param buf: buffer to fill with JSON + * \param len: max length of buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len); + +/** + * lws_json_dump_context() - describe context state and stats in JSON + * + * \param context: the context + * \param buf: buffer to fill with JSON + * \param len: max length of buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_context(const struct lws_context *context, char *buf, int len); + +/** + * lws_context_user() - get the user data associated with the context + * \param context: Websocket context + * + * This returns the optional user allocation that can be attached to + * the context the sockets live in at context_create time. It's a way + * to let all sockets serviced in the same context share data without + * using globals statics in the user code. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_context_user(struct lws_context *context); + +/*! \defgroup vhost-mounts Vhost mounts and options + * \ingroup context-and-vhost-creation + * + * ##Vhost mounts and options + */ +///@{ +/** struct lws_protocol_vhost_options - linked list of per-vhost protocol + * name=value options + * + * This provides a general way to attach a linked-list of name=value pairs, + * which can also have an optional child link-list using the options member. + */ +struct lws_protocol_vhost_options { + const struct lws_protocol_vhost_options *next; /**< linked list */ + const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */ + const char *name; /**< name of name=value pair */ + const char *value; /**< value of name=value pair */ +}; + +/** enum lws_mount_protocols + * This specifies the mount protocol for a mountpoint, whether it is to be + * served from a filesystem, or it is a cgi etc. + */ +enum lws_mount_protocols { + LWSMPRO_HTTP = 0, /**< not supported yet */ + LWSMPRO_HTTPS = 1, /**< not supported yet */ + LWSMPRO_FILE = 2, /**< serve from filesystem directory */ + LWSMPRO_CGI = 3, /**< pass to CGI to handle */ + LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */ + LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */ + LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */ +}; + +/** struct lws_http_mount + * + * arguments for mounting something in a vhost's url namespace + */ +struct lws_http_mount { + const struct lws_http_mount *mount_next; + /**< pointer to next struct lws_http_mount */ + const char *mountpoint; + /**< mountpoint in http pathspace, eg, "/" */ + const char *origin; + /**< path to be mounted, eg, "/var/www/warmcat.com" */ + const char *def; + /**< default target, eg, "index.html" */ + const char *protocol; + /**<"protocol-name" to handle mount */ + + const struct lws_protocol_vhost_options *cgienv; + /**< optional linked-list of cgi options. These are created + * as environment variables for the cgi process + */ + const struct lws_protocol_vhost_options *extra_mimetypes; + /**< optional linked-list of mimetype mappings */ + const struct lws_protocol_vhost_options *interpret; + /**< optional linked-list of files to be interpreted */ + + int cgi_timeout; + /**< seconds cgi is allowed to live, if cgi://mount type */ + int cache_max_age; + /**< max-age for reuse of client cache of files, seconds */ + unsigned int auth_mask; + /**< bits set here must be set for authorized client session */ + + unsigned int cache_reusable:1; /**< set if client cache may reuse this */ + unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */ + unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */ + + unsigned char origin_protocol; /**< one of enum lws_mount_protocols */ + unsigned char mountpoint_len; /**< length of mountpoint string */ +}; +///@} +///@} + +/*! \defgroup client + * \ingroup lwsapi + * + * ##Client releated functions + * */ +///@{ + +/** enum lws_client_connect_ssl_connection_flags - flags that may be used + * with struct lws_client_connect_info ssl_connection member to control if + * and how SSL checks apply to the client connection being created + */ + +enum lws_client_connect_ssl_connection_flags { + LCCSCF_USE_SSL = (1 << 0), + LCCSCF_ALLOW_SELFSIGNED = (1 << 1), + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2), + LCCSCF_ALLOW_EXPIRED = (1 << 3) +}; + +/** struct lws_client_connect_info - parameters to connect with when using + * lws_client_connect_via_info() */ + +struct lws_client_connect_info { + struct lws_context *context; + /**< lws context to create connection in */ + const char *address; + /**< remote address to connect to */ + int port; + /**< remote port to connect to */ + int ssl_connection; + /**< nonzero for ssl */ + const char *path; + /**< uri path */ + const char *host; + /**< content of host header */ + const char *origin; + /**< content of origin header */ + const char *protocol; + /**< list of ws protocols we could accept */ + int ietf_version_or_minus_one; + /**< deprecated: currently leave at 0 or -1 */ + void *userdata; + /**< if non-NULL, use this as wsi user_data instead of malloc it */ + const struct lws_extension *client_exts; + /**< array of extensions that may be used on connection */ + const char *method; + /**< if non-NULL, do this http method instead of ws[s] upgrade. + * use "GET" to be a simple http client connection */ + struct lws *parent_wsi; + /**< if another wsi is responsible for this connection, give it here. + * this is used to make sure if the parent closes so do any + * child connections first. */ + const char *uri_replace_from; + /**< if non-NULL, when this string is found in URIs in + * text/html content-encoding, it's replaced with uri_replace_to */ + const char *uri_replace_to; + /**< see uri_replace_from */ + struct lws_vhost *vhost; + /**< vhost to bind to (used to determine related SSL_CTX) */ + struct lws **pwsi; + /**< if not NULL, store the new wsi here early in the connection + * process. Although we return the new wsi, the call to create the + * client connection does progress the connection somewhat and may + * meet an error that will result in the connection being scrubbed and + * NULL returned. While the wsi exists though, he may process a + * callback like CLIENT_CONNECTION_ERROR with his wsi: this gives the + * user callback a way to identify which wsi it is that faced the error + * even before the new wsi is returned and even if ultimately no wsi + * is returned. + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[4]; /**< dummy */ +}; + +/** + * lws_client_connect_via_info() - Connect to another websocket server + * \param ccinfo: pointer to lws_client_connect_info struct + * + * This function creates a connection to a remote server using the + * information provided in ccinfo. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_client_connect_via_info(struct lws_client_connect_info * ccinfo); + +/** + * lws_client_connect() - Connect to another websocket server + * \deprecated DEPRECATED use lws_client_connect_via_info + * \param clients: Websocket context + * \param address: Remote server address, eg, "myserver.com" + * \param port: Port to connect to on the remote server, eg, 80 + * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self + * signed certs + * \param path: Websocket path on server + * \param host: Hostname on server + * \param origin: Socket origin name + * \param protocol: Comma-separated list of protocols being asked for from + * the server, or just one. The server will pick the one it + * likes best. If you don't want to specify a protocol, which is + * legal, use NULL here. + * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest + * protocol supported, or the specific protocol ordinal + * + * This function creates a connection to a remote server + */ +/* deprecated, use lws_client_connect_via_info() */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect(struct lws_context *clients, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, const char *protocol, + int ietf_version_or_minus_one) LWS_WARN_DEPRECATED; +/* deprecated, use lws_client_connect_via_info() */ +/** + * lws_client_connect_extended() - Connect to another websocket server + * \deprecated DEPRECATED use lws_client_connect_via_info + * \param clients: Websocket context + * \param address: Remote server address, eg, "myserver.com" + * \param port: Port to connect to on the remote server, eg, 80 + * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self + * signed certs + * \param path: Websocket path on server + * \param host: Hostname on server + * \param origin: Socket origin name + * \param protocol: Comma-separated list of protocols being asked for from + * the server, or just one. The server will pick the one it + * likes best. + * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest + * protocol supported, or the specific protocol ordinal + * \param userdata: Pre-allocated user data + * + * This function creates a connection to a remote server + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_extended(struct lws_context *clients, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one, + void *userdata) LWS_WARN_DEPRECATED; + +/** + * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost + * + * \param info: client ssl related info + * \param vhost: which vhost to initialize client ssl operations on + * + * You only need to call this if you plan on using SSL client connections on + * the vhost. For non-SSL client connections, it's not necessary to call this. + * + * The following members of info are used during the call + * + * - options must have LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT set, + * otherwise the call does nothing + * - provided_client_ssl_ctx must be NULL to get a generated client + * ssl context, otherwise you can pass a prepared one in by setting it + * - ssl_cipher_list may be NULL or set to the client valid cipher list + * - ssl_ca_filepath may be NULL or client cert filepath + * - ssl_cert_filepath may be NULL or client cert filepath + * - ssl_private_key_filepath may be NULL or client cert private key + * + * You must create your vhost explicitly if you want to use this, so you have + * a pointer to the vhost. Create the context first with the option flag + * LWS_SERVER_OPTION_EXPLICIT_VHOSTS and then call lws_create_vhost() with + * the same info struct. + */ +LWS_VISIBLE LWS_EXTERN int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); + +LWS_VISIBLE LWS_EXTERN int +lws_http_client_read(struct lws *wsi, char **buf, int *len); + +/** + * lws_http_client_http_response() - get last HTTP response code + * + * \param wsi: client connection + * + * Returns the last server response code, eg, 200 for client http connections. + */ +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send); + +/** + * lws_client_http_body_pending() - control if client connection neeeds to send body + * + * \param wsi: client connection + * \param something_left_to_send: nonzero if need to send more body, 0 (default) + * if nothing more to send + * + * If you will send payload data with your HTTP client connection, eg, for POST, + * when you set the related http headers in + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER callback you should also call + * this API with something_left_to_send nonzero, and call + * lws_callback_on_writable(wsi); + * + * After sending the headers, lws will call your callback with + * LWS_CALLBACK_CLIENT_HTTP_WRITEABLE reason when writable. You can send the + * next part of the http body payload, calling lws_callback_on_writable(wsi); + * if there is more to come, or lws_client_http_body_pending(wsi, 0); to + * let lws know the last part is sent and the connection can move on. + */ + +///@} + +/** \defgroup service Built-in service loop entry + * + * ##Built-in service loop entry + * + * If you're not using libev / libuv, these apis are needed to enter the poll() + * wait in lws and service any connections with pending events. + */ +///@{ + +/** + * lws_service() - Service any pending websocket activity + * \param context: Websocket context + * \param timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * + * This function deals with any pending websocket traffic, for three + * kinds of event. It handles these events on both server and client + * types of connection the same. + * + * 1) Accept new connections to our context's server + * + * 2) Call the receive callback for incoming frame data received by + * server or client connections. + * + * You need to call this service function periodically to all the above + * functions to happen; if your application is single-threaded you can + * just call it in your main event loop. + * + * Alternatively you can fork a new process that asynchronously handles + * calling this service in a loop. In that case you are happy if this + * call blocks your thread until it needs to take care of something and + * would call it with a large nonzero timeout. Your loop then takes no + * CPU while there is nothing happening. + * + * If you are calling it in a single-threaded app, you don't want it to + * wait around blocking other things in your loop from happening, so you + * would call it with a timeout_ms of 0, so it returns immediately if + * nothing is pending, or as soon as it services whatever was pending. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service(struct lws_context *context, int timeout_ms); + +/** + * lws_service() - Service any pending websocket activity + * + * \param context: Websocket context + * \param timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * + * Same as lws_service(), but for a specific thread service index. Only needed + * if you are spawning multiple service threads. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi); + +/** + * lws_cancel_service_pt() - Cancel servicing of pending socket activity + * on one thread + * \param wsi: Cancel service on the thread this wsi is serviced by + * + * This function lets a call to lws_service() waiting for a timeout + * immediately return. + * + * It works by creating a phony event and then swallowing it silently. + * + * The reason it may be needed is when waiting in poll(), changes to + * the event masks are ignored by the OS until poll() is reentered. This + * lets you halt the poll() wait and make the reentry happen immediately + * instead of having the wait out the rest of the poll timeout. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cancel_service_pt(struct lws *wsi); + +/** + * lws_cancel_service() - Cancel wait for new pending socket activity + * \param context: Websocket context + * + * This function let a call to lws_service() waiting for a timeout + * immediately return. + * + * What it basically does is provide a fake event that will be swallowed, + * so the wait in poll() is ended. That's useful because poll() doesn't + * attend to changes in POLLIN/OUT/ERR until it re-enters the wait. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cancel_service(struct lws_context *context); + +/** + * lws_service_fd() - Service polled socket with something waiting + * \param context: Websocket context + * \param pollfd: The pollfd entry describing the socket fd and which events + * happened, or NULL to tell lws to do only timeout servicing. + * + * This function takes a pollfd that has POLLIN or POLLOUT activity and + * services it according to the state of the associated + * struct lws. + * + * The one call deals with all "service" that might happen on a socket + * including listen accepts, http files as well as websocket protocol. + * + * If a pollfd says it has something, you can just pass it to + * lws_service_fd() whether it is a socket handled by lws or not. + * If it sees it is a lws socket, the traffic will be handled and + * pollfd->revents will be zeroed now. + * + * If the socket is foreign to lws, it leaves revents alone. So you can + * see if you should service yourself by checking the pollfd revents + * after letting lws try to service it. + * + * You should also call this with pollfd = NULL to just allow the + * once-per-second global timeout checks; if less than a second since the last + * check it returns immediately then. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); + +/** + * lws_service_fd_tsi() - Service polled socket in specific service thread + * \param context: Websocket context + * \param pollfd: The pollfd entry describing the socket fd and which events + * happened. + * \param tsi: thread service index + * + * Same as lws_service_fd() but used with multiple service threads + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, + int tsi); + +/** + * lws_service_adjust_timeout() - Check for any connection needing forced service + * \param context: Websocket context + * \param timeout_ms: The original poll timeout value. You can just set this + * to 1 if you don't really have a poll timeout. + * \param tsi: thread service index + * + * Under some conditions connections may need service even though there is no + * pending network action on them, this is "forced service". For default + * poll() and libuv / libev, the library takes care of calling this and + * dealing with it for you. But for external poll() integration, you need + * access to the apis. + * + * If anybody needs "forced service", returned timeout is zero. In that case, + * you can call lws_service_tsi() with a timeout of -1 to only service + * guys who need forced service. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi); + +/* Backwards compatibility */ +#define lws_plat_service_tsi lws_service_tsi + +///@} + +/*! \defgroup http HTTP + + Modules related to handling HTTP +*/ +//@{ + +/*! \defgroup httpft HTTP File transfer + * \ingroup http + + APIs for sending local files in response to HTTP requests +*/ +//@{ + +/** + * lws_get_mimetype() - Determine mimetype to use from filename + * + * \param file: filename + * \param m: NULL, or mount context + * + * This uses a canned list of known filetypes first, if no match and m is + * non-NULL, then tries a list of per-mount file suffix to mimtype mappings. + * + * Returns either NULL or a pointer to the mimetype matching the file. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m); + +/** + * lws_serve_http_file() - Send a file back to the client using http + * \param wsi: Websocket instance (available from user callback) + * \param file: The file to issue over http + * \param content_type: The http content type, eg, text/html + * \param other_headers: NULL or pointer to header string + * \param other_headers_len: length of the other headers if non-NULL + * + * This function is intended to be called from the callback in response + * to http requests from the client. It allows the callback to issue + * local files down the http link in a single step. + * + * Returning <0 indicates error and the wsi should be closed. Returning + * >0 indicates the file was completely sent and + * lws_http_transaction_completed() called on the wsi (and close if != 0) + * ==0 indicates the file transfer is started and needs more service later, + * the wsi should be left alone. + */ +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len); +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file_fragment(struct lws *wsi); +//@} + +/*! \defgroup html-chunked-substitution HTML Chunked Substitution + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +enum http_status { + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED, + HTTP_STATUS_PAYMENT_REQUIRED, + HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_NOT_FOUND, + HTTP_STATUS_METHOD_NOT_ALLOWED, + HTTP_STATUS_NOT_ACCEPTABLE, + HTTP_STATUS_PROXY_AUTH_REQUIRED, + HTTP_STATUS_REQUEST_TIMEOUT, + HTTP_STATUS_CONFLICT, + HTTP_STATUS_GONE, + HTTP_STATUS_LENGTH_REQUIRED, + HTTP_STATUS_PRECONDITION_FAILED, + HTTP_STATUS_REQ_ENTITY_TOO_LARGE, + HTTP_STATUS_REQ_URI_TOO_LONG, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, + HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, + HTTP_STATUS_EXPECTATION_FAILED, + + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED, + HTTP_STATUS_BAD_GATEWAY, + HTTP_STATUS_SERVICE_UNAVAILABLE, + HTTP_STATUS_GATEWAY_TIMEOUT, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, +}; + +struct lws_process_html_args { + char *p; /**< pointer to the buffer containing the data */ + int len; /**< length of the original data at p */ + int max_len; /**< maximum length we can grow the data to */ + int final; /**< set if this is the last chunk of the file */ +}; + +typedef const char *(*lws_process_html_state_cb)(void *data, int index); + +struct lws_process_html_state { + char *start; /**< pointer to start of match */ + char swallow[16]; /**< matched character buffer */ + int pos; /**< position in match */ + void *data; /**< opaque pointer */ + const char * const *vars; /**< list of variable names */ + int count_vars; /**< count of variable names */ + + lws_process_html_state_cb replace; /**< called on match to perform substitution */ +}; + +/*! lws_chunked_html_process() - generic chunked substitution + * \param args: buffer to process using chunked encoding + * \param s: current processing state + */ +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s); +//@} + +/** \defgroup HTTP-headers-read HTTP headers: read + * \ingroup http + * + * ##HTTP header releated functions + * + * In lws the client http headers are temporarily stored in a pool, only for the + * duration of the http part of the handshake. It's because in most cases, + * the header content is ignored for the whole rest of the connection lifetime + * and would then just be taking up space needlessly. + * + * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time + * the http headers are still allocated, you can use these apis then to + * look at and copy out interesting header content (cookies, etc) + * + * Notice that the header total length reported does not include a terminating + * '\0', however you must allocate for it when using the _copy apis. So the + * length reported for a header containing "123" is 3, but you must provide + * a buffer of length 4 so that "123\0" may be copied into it, or the copy + * will fail with a nonzero return code. + * + * In the special case of URL arguments, like ?x=1&y=2, the arguments are + * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it + * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total + * length to confirm the method. + * + * For URL arguments, each argument is stored urldecoded in a "fragment", so + * you can use the fragment-aware api lws_hdr_copy_fragment() to access each + * argument in turn: the fragments contain urldecoded strings like x=1 or y=2. + * + * As a convenience, lws has an api that will find the fragment with a + * given name= part, lws_get_urlarg_by_name(). + */ +///@{ + +/** struct lws_tokens + * you need these to look at headers that have been parsed if using the + * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum + * list below is absent, .token = NULL and token_len = 0. Otherwise .token + * points to .token_len chars containing that header content. + */ +struct lws_tokens { + char *token; /**< pointer to start of the token */ + int token_len; /**< length of the token's value */ +}; + +/* enum lws_token_indexes + * these have to be kept in sync with lextable.h / minilex.c + * + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_token_indexes { + WSI_TOKEN_GET_URI = 0, + WSI_TOKEN_POST_URI = 1, + WSI_TOKEN_OPTIONS_URI = 2, + WSI_TOKEN_HOST = 3, + WSI_TOKEN_CONNECTION = 4, + WSI_TOKEN_UPGRADE = 5, + WSI_TOKEN_ORIGIN = 6, + WSI_TOKEN_DRAFT = 7, + WSI_TOKEN_CHALLENGE = 8, + WSI_TOKEN_EXTENSIONS = 9, + WSI_TOKEN_KEY1 = 10, + WSI_TOKEN_KEY2 = 11, + WSI_TOKEN_PROTOCOL = 12, + WSI_TOKEN_ACCEPT = 13, + WSI_TOKEN_NONCE = 14, + WSI_TOKEN_HTTP = 15, + WSI_TOKEN_HTTP2_SETTINGS = 16, + WSI_TOKEN_HTTP_ACCEPT = 17, + WSI_TOKEN_HTTP_AC_REQUEST_HEADERS = 18, + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE = 19, + WSI_TOKEN_HTTP_IF_NONE_MATCH = 20, + WSI_TOKEN_HTTP_ACCEPT_ENCODING = 21, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE = 22, + WSI_TOKEN_HTTP_PRAGMA = 23, + WSI_TOKEN_HTTP_CACHE_CONTROL = 24, + WSI_TOKEN_HTTP_AUTHORIZATION = 25, + WSI_TOKEN_HTTP_COOKIE = 26, + WSI_TOKEN_HTTP_CONTENT_LENGTH = 27, + WSI_TOKEN_HTTP_CONTENT_TYPE = 28, + WSI_TOKEN_HTTP_DATE = 29, + WSI_TOKEN_HTTP_RANGE = 30, + WSI_TOKEN_HTTP_REFERER = 31, + WSI_TOKEN_KEY = 32, + WSI_TOKEN_VERSION = 33, + WSI_TOKEN_SWORIGIN = 34, + + WSI_TOKEN_HTTP_COLON_AUTHORITY = 35, + WSI_TOKEN_HTTP_COLON_METHOD = 36, + WSI_TOKEN_HTTP_COLON_PATH = 37, + WSI_TOKEN_HTTP_COLON_SCHEME = 38, + WSI_TOKEN_HTTP_COLON_STATUS = 39, + + WSI_TOKEN_HTTP_ACCEPT_CHARSET = 40, + WSI_TOKEN_HTTP_ACCEPT_RANGES = 41, + WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN = 42, + WSI_TOKEN_HTTP_AGE = 43, + WSI_TOKEN_HTTP_ALLOW = 44, + WSI_TOKEN_HTTP_CONTENT_DISPOSITION = 45, + WSI_TOKEN_HTTP_CONTENT_ENCODING = 46, + WSI_TOKEN_HTTP_CONTENT_LANGUAGE = 47, + WSI_TOKEN_HTTP_CONTENT_LOCATION = 48, + WSI_TOKEN_HTTP_CONTENT_RANGE = 49, + WSI_TOKEN_HTTP_ETAG = 50, + WSI_TOKEN_HTTP_EXPECT = 51, + WSI_TOKEN_HTTP_EXPIRES = 52, + WSI_TOKEN_HTTP_FROM = 53, + WSI_TOKEN_HTTP_IF_MATCH = 54, + WSI_TOKEN_HTTP_IF_RANGE = 55, + WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE = 56, + WSI_TOKEN_HTTP_LAST_MODIFIED = 57, + WSI_TOKEN_HTTP_LINK = 58, + WSI_TOKEN_HTTP_LOCATION = 59, + WSI_TOKEN_HTTP_MAX_FORWARDS = 60, + WSI_TOKEN_HTTP_PROXY_AUTHENTICATE = 61, + WSI_TOKEN_HTTP_PROXY_AUTHORIZATION = 62, + WSI_TOKEN_HTTP_REFRESH = 63, + WSI_TOKEN_HTTP_RETRY_AFTER = 64, + WSI_TOKEN_HTTP_SERVER = 65, + WSI_TOKEN_HTTP_SET_COOKIE = 66, + WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY = 67, + WSI_TOKEN_HTTP_TRANSFER_ENCODING = 68, + WSI_TOKEN_HTTP_USER_AGENT = 69, + WSI_TOKEN_HTTP_VARY = 70, + WSI_TOKEN_HTTP_VIA = 71, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE = 72, + + WSI_TOKEN_PATCH_URI = 73, + WSI_TOKEN_PUT_URI = 74, + WSI_TOKEN_DELETE_URI = 75, + + WSI_TOKEN_HTTP_URI_ARGS = 76, + WSI_TOKEN_PROXY = 77, + WSI_TOKEN_HTTP_X_REAL_IP = 78, + WSI_TOKEN_HTTP1_0 = 79, + + /****** add new things just above ---^ ******/ + + /* use token storage to stash these internally, not for + * user use */ + + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + _WSI_TOKEN_CLIENT_URI, + _WSI_TOKEN_CLIENT_HOST, + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_METHOD, + + /* always last real token index*/ + WSI_TOKEN_COUNT, + + /* parser state additions, no storage associated */ + WSI_TOKEN_NAME_PART, + WSI_TOKEN_SKIPPING, + WSI_TOKEN_SKIPPING_SAW_CR, + WSI_PARSING_COMPLETE, + WSI_INIT_TOKEN_MUXURL, +}; + +struct lws_token_limits { + unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */ +}; + +/** + * lws_token_to_string() - returns a textual representation of a hdr token index + * + * \param: token index + */ +LWS_VISIBLE LWS_EXTERN const unsigned char * +lws_token_to_string(enum lws_token_indexes token); + + +/** + * lws_hdr_total_length: report length of all fragments of a header totalled up + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h); + +/** + * lws_hdr_fragment_length: report length of a single fragment of a header + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to get the length of + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx); + +/** + * lws_hdr_copy() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * + * copies the whole, aggregated header, even if it was delivered in + * several actual headers piece by piece + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h); + +/** + * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * If the requested fragment index is not present, it fails + * returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to copy + * + * Normally this is only useful + * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS + * fragment 0 will contain "x=1" and fragment 1 "y=2" + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len, + enum lws_token_indexes h, int frag_idx); + +/** + * lws_get_urlarg_by_name() - return pointer to arg value if present + * \param wsi: the connection to check + * \param name: the arg name, like "token=" + * \param buf: the buffer to receive the urlarg (including the name= part) + * \param len: the length of the buffer to receive the urlarg + * + * Returns NULL if not found or a pointer inside buf to just after the + * name= part. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len); +///@} + +/*! \defgroup HTTP-headers-create HTTP headers: create + * + * ## HTTP headers: Create + * + * These apis allow you to create HTTP response headers in a way compatible with + * both HTTP/1.x and HTTP/2. + * + * They each append to a buffer taking care about the buffer end, which is + * passed in as a pointer. When data is written to the buffer, the current + * position p is updated accordingly. + * + * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space + * and fail with nonzero return. + */ +///@{ +/** + * lws_add_http_header_status() - add the HTTP response status code + * + * \param wsi: the connection to check + * \param code: an HTTP code like 200, 404 etc (see enum http_status) + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Adds the initial response code, so should be called first + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +/** + * lws_add_http_header_by_name() - append named header and value + * + * \param wsi: the connection to check + * \param name: the hdr name, like "my-header" + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name: value to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +/** + * lws_add_http_header_by_token() - append given header and value + * + * \param wsi: the connection to check + * \param token: the token index for the hdr + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name=value to the headers, but is able to take advantage of better + * HTTP/2 coding mechanisms where possible. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +/** + * lws_add_http_header_by_name() - append content-length helper + * + * \param wsi: the connection to check + * \param content_length: the content length to use + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends content-length: content_length to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_content_length(struct lws *wsi, + unsigned long content_length, + unsigned char **p, unsigned char *end); +/** + * lws_finalize_http_header() - terminate header block + * + * \param wsi: the connection to check + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Indicates no more headers will be added + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_finalize_http_header(struct lws *wsi, unsigned char **p, + unsigned char *end); +///@} + +/** \defgroup form-parsing Form Parsing + * \ingroup http + * ##POSTed form parsing functions + * + * These lws_spa (stateful post arguments) apis let you parse and urldecode + * POSTed form arguments, both using simple urlencoded and multipart transfer + * encoding. + * + * It's capable of handling file uploads as well a named input parsing, + * and the apis are the same for both form upload styles. + * + * You feed it a list of parameter names and it creates pointers to the + * urldecoded arguments: file upload parameters pass the file data in chunks to + * a user-supplied callback as they come. + * + * Since it's stateful, it handles the incoming data needing more than one + * POST_BODY callback and has no limit on uploaded file size. + */ +///@{ + +/** enum lws_spa_fileupload_states */ +enum lws_spa_fileupload_states { + LWS_UFS_CONTENT, + /**< a chunk of file content has arrived */ + LWS_UFS_FINAL_CONTENT, + /**< the last chunk (possibly zero length) of file content has arrived */ + LWS_UFS_OPEN + /**< a new file is starting to arrive */ +}; + +/** + * lws_spa_fileupload_cb() - callback to receive file upload data + * + * \param data: opt_data pointer set in lws_spa_create + * \param name: name of the form field being uploaded + * \param filename: original filename from client + * \param buf: start of data to receive + * \param len: length of data to receive + * \param state: information about how this call relates to file + * + * Notice name and filename shouldn't be trusted, as they are passed from + * HTTP provided by the client. + */ +typedef int (*lws_spa_fileupload_cb)(void *data, const char *name, + const char *filename, char *buf, int len, + enum lws_spa_fileupload_states state); + +/** struct lws_spa - opaque urldecode parser capable of handling multipart + * and file uploads */ +struct lws_spa; + +/** + * lws_spa_create() - create urldecode parser + * + * \param wsi: lws connection (used to find Content Type) + * \param param_names: array of form parameter names, like "username" + * \param count_params: count of param_names + * \param max_storage: total amount of form parameter values we can store + * \param opt_cb: NULL, or callback to receive file upload data. + * \param opt_data: NULL, or user pointer provided to opt_cb. + * + * Creates a urldecode parser and initializes it. + * + * opt_cb can be NULL if you just want normal name=value parsing, however + * if one or more entries in your form are bulk data (file transfer), you + * can provide this callback and filter on the name callback parameter to + * treat that urldecoded data separately. The callback should return -1 + * in case of fatal error, and 0 if OK. + */ +LWS_VISIBLE LWS_EXTERN struct lws_spa * +lws_spa_create(struct lws *wsi, const char * const *param_names, + int count_params, int max_storage, lws_spa_fileupload_cb opt_cb, + void *opt_data); + +/** + * lws_spa_process() - parses a chunk of input data + * + * \param spa: the parser object previously created + * \param in: incoming, urlencoded data + * \param len: count of bytes valid at \param in + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_process(struct lws_spa *spa, const char *in, int len); + +/** + * lws_spa_finalize() - indicate incoming data completed + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_finalize(struct lws_spa *spa); + +/** + * lws_spa_get_length() - return length of parameter value + * + * \param spa: the parser object previously created + * \param n: parameter ordinal to return length of value for + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_get_length(struct lws_spa *spa, int n); + +/** + * lws_spa_get_string() - return pointer to parameter value + * \param spa: the parser object previously created + * \param n: parameter ordinal to return pointer to value for + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_spa_get_string(struct lws_spa *spa, int n); + +/** + * lws_spa_destroy() - destroy parser object + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_destroy(struct lws_spa *spa); +///@} + +/*! \defgroup urlendec Urlencode and Urldecode + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +/** + * lws_urlencode() - like strncpy but with urlencoding + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because urlencoding expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len); + +/* + * URLDECODE 1 / 2 + * + * This simple urldecode only operates until the first '\0' and requires the + * data to exist all at once + */ +/** + * lws_urldecode() - like strncpy but with urldecoding + * + * \param string: output buffer + * \param escaped: input buffer ('\0' terminated) + * \param len: output buffer max length + * + * This is only useful for '\0' terminated strings + * + * Since urldecoding only shrinks the output string, it is possible to + * do it in-place, ie, string == escaped + */ +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len); +///@} +/** + * lws_return_http_status() - Return simple http status + * \param wsi: Websocket instance (available from user callback) + * \param code: Status index, eg, 404 + * \param html_body: User-readable HTML description < 1KB, or NULL + * + * Helper to report HTTP errors back to the client cleanly and + * consistently + */ +LWS_VISIBLE LWS_EXTERN int +lws_return_http_status(struct lws *wsi, unsigned int code, + const char *html_body); + +/** + * lws_http_redirect() - write http redirect into buffer + * + * \param wsi: websocket connection + * \param code: HTTP response code (eg, 301) + * \param loc: where to redirect to + * \param len: length of loc + * \param p: pointer current position in buffer (updated as we write) + * \param end: pointer to end of buffer + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, + unsigned char **p, unsigned char *end); + +/** + * lws_http_transaction_completed() - wait for new http transaction or close + * \param wsi: websocket connection + * + * Returns 1 if the HTTP connection must close now + * Returns 0 and resets connection to wait for new HTTP header / + * transaction if possible + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi); +///@} + +/*! \defgroup pur Sanitize / purify SQL and JSON helpers + * + * ##Sanitize / purify SQL and JSON helpers + * + * APIs for escaping untrusted JSON and SQL safely before use + */ +//@{ + +/** + * lws_sql_purify() - like strncpy but with escaping for sql quotes + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len); + +/** + * lws_json_purify() - like strncpy but with escaping for json chars + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len); +///@} + +/*! \defgroup ev libev helpers + * + * ##libev helpers + * + * APIs specific to libev event loop itegration + */ +///@{ + +#ifdef LWS_USE_LIBEV +typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); +#endif /* LWS_USE_LIBEV */ + +///@} + +/*! \defgroup uv libuv helpers + * + * ##libuv helpers + * + * APIs specific to libuv event loop itegration + */ +///@{ +#ifdef LWS_USE_LIBUV +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + uv_signal_cb cb); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_uv_sigint_cb(uv_signal_t *watcher, int signum); +#endif /* LWS_USE_LIBUV */ +///@} + +/*! \defgroup timeout Connection timeouts + + APIs related to setting connection timeouts +*/ +//@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum pending_timeout { + NO_PENDING_TIMEOUT = 0, + PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1, + PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3, + PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4, + PENDING_TIMEOUT_AWAITING_PING = 5, + PENDING_TIMEOUT_CLOSE_ACK = 6, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE = 7, + PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8, + PENDING_TIMEOUT_SSL_ACCEPT = 9, + PENDING_TIMEOUT_HTTP_CONTENT = 10, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11, + PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12, + PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13, + PENDING_TIMEOUT_CGI = 14, + PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15, + PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, + PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, + PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, + + /****** add new things just above ---^ ******/ +}; + +/** + * lws_set_timeout() - marks the wsi as subject to a timeout + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + * \param reason: timeout reason + * \param secs: how many seconds + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); +///@} + +/*! \defgroup sending-data Sending data + + APIs related to writing data on a connection +*/ +//@{ +#if !defined(LWS_SIZEOFPTR) +#define LWS_SIZEOFPTR (sizeof (void *)) +#endif +#if !defined(u_int64_t) +#define u_int64_t unsigned long long +#endif + +#if defined(__x86_64__) +#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ +#else +#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */ +#endif +#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \ + ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n)) +#define LWS_PRE _LWS_PAD(4 + 10) +/* used prior to 1.7 and retained for backward compatibility */ +#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE +#define LWS_SEND_BUFFER_POST_PADDING 0 + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_write_protocol { + LWS_WRITE_TEXT = 0, + /**< Send a ws TEXT message,the pointer must have LWS_PRE valid + * memory behind it. The receiver expects only valid utf-8 in the + * payload */ + LWS_WRITE_BINARY = 1, + /**< Send a ws BINARY message, the pointer must have LWS_PRE valid + * memory behind it. Any sequence of bytes is valid */ + LWS_WRITE_CONTINUATION = 2, + /**< Continue a previous ws message, the pointer must have LWS_PRE valid + * memory behind it */ + LWS_WRITE_HTTP = 3, + /**< Send HTTP content */ + + /* LWS_WRITE_CLOSE is handled by lws_close_reason() */ + LWS_WRITE_PING = 5, + LWS_WRITE_PONG = 6, + + /* Same as write_http but we know this write ends the transaction */ + LWS_WRITE_HTTP_FINAL = 7, + + /* HTTP2 */ + + LWS_WRITE_HTTP_HEADERS = 8, + /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP + * payload differently, http 1.x links also handle this correctly. so + * to be compatible with both in the future,header response part should + * be sent using this regardless of http version expected) + */ + + /****** add new things just above ---^ ******/ + + /* flags */ + + LWS_WRITE_NO_FIN = 0x40, + /**< This part of the message is not the end of the message */ + + LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80 + /**< client packet payload goes out on wire unmunged + * only useful for security tests since normal servers cannot + * decode the content if used */ +}; + + +/** + * lws_write() - Apply protocol then write data to client + * \param wsi: Websocket instance (available from user callback) + * \param buf: The data to send. For data being sent on a websocket + * connection (ie, not default http), this buffer MUST have + * LWS_PRE bytes valid BEFORE the pointer. + * This is so the protocol header data can be added in-situ. + * \param len: Count of the data bytes in the payload starting from buf + * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one + * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate + * data on a websockets connection. Remember to allow the extra + * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT + * are used. + * + * This function provides the way to issue data back to the client + * for both http and websocket protocols. + * + * IMPORTANT NOTICE! + * + * When sending with websocket protocol + * + * LWS_WRITE_TEXT, + * LWS_WRITE_BINARY, + * LWS_WRITE_CONTINUATION, + * LWS_WRITE_PING, + * LWS_WRITE_PONG + * + * the send buffer has to have LWS_PRE bytes valid BEFORE + * the buffer pointer you pass to lws_write(). + * + * This allows us to add protocol info before and after the data, and send as + * one packet on the network without payload copying, for maximum efficiency. + * + * So for example you need this kind of code to use lws_write with a + * 128-byte payload + * + * char buf[LWS_PRE + 128]; + * + * // fill your part of the buffer... for example here it's all zeros + * memset(&buf[LWS_PRE], 0, 128); + * + * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT); + * + * When sending HTTP, with + * + * LWS_WRITE_HTTP, + * LWS_WRITE_HTTP_HEADERS + * LWS_WRITE_HTTP_FINAL + * + * there is no protocol data prepended, and don't need to take care about the + * LWS_PRE bytes valid before the buffer pointer. + * + * LWS_PRE is at least the frame nonce + 2 header + 8 length + * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off. + * The example apps no longer use it. + * + * Pad LWS_PRE to the CPU word size, so that word references + * to the address immediately after the padding won't cause an unaligned access + * error. Sometimes for performance reasons the recommended padding is even + * larger than sizeof(void *). + * + * In the case of sending using websocket protocol, be sure to allocate + * valid storage before and after buf as explained above. This scheme + * allows maximum efficiency of sending data and protocol in a single + * packet while not burdening the user code with any protocol knowledge. + * + * Return may be -1 for a fatal error needing connection close, or the + * number of bytes sent. + * + * Truncated Writes + * ================ + * + * The OS may not accept everything you asked to write on the connection. + * + * Posix defines POLLOUT indication from poll() to show that the connection + * will accept more write data, but it doesn't specifiy how much. It may just + * accept one byte of whatever you wanted to send. + * + * LWS will buffer the remainder automatically, and send it out autonomously. + * + * During that time, WRITABLE callbacks will be suppressed. + * + * This is to handle corner cases where unexpectedly the OS refuses what we + * usually expect it to accept. You should try to send in chunks that are + * almost always accepted in order to avoid the inefficiency of the buffering. + */ +LWS_VISIBLE LWS_EXTERN int +lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol protocol); + +/* helper for case where buffer may be const */ +#define lws_write_http(wsi, buf, len) \ + lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) +///@} + +/** \defgroup callback-when-writeable Callback when writeable + * + * ##Callback When Writeable + * + * lws can only write data on a connection when it is able to accept more + * data without blocking. + * + * So a basic requirement is we should only use the lws_write() apis when the + * connection we want to write on says that he can accept more data. + * + * When lws cannot complete your send at the time, it will buffer the data + * and send it in the background, suppressing any further WRITEABLE callbacks + * on that connection until it completes. So it is important to write new + * things in a new writeable callback. + * + * These apis reflect the various ways we can indicate we would like to be + * called back when one or more connections is writeable. + */ +///@{ + +/** + * lws_callback_on_writable() - Request a callback when this socket + * becomes able to be written to without + * blocking + * + * \param wsi: Websocket connection instance to get callback for + * + * - Which: only this wsi + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable(struct lws *wsi); + +/** + * lws_callback_on_writable_all_protocol() - Request a callback for all + * connections on same vhost using the given protocol when it + * becomes possible to write to each socket without + * blocking in turn. + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on ANY VHOST + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_callback_on_writable_all_protocol_vhost() - Request a callback for + * all connections using the given protocol when it + * becomes possible to write to each socket without + * blocking in turn. + * + * \param vhost: Only consider connections on this lws_vhost + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol); + +/** + * lws_callback_all_protocol() - Callback all connections using + * the given protocol with the given reason + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * \param reason: Callback reason index + * + * - Which: connections using this protocol on ALL VHOSTS + * - When: when the individual connection becomes writeable + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason); + +/** + * lws_callback_all_protocol_vhost() - Callback all connections using + * the given protocol with the given reason + * + * \param vh: Vhost whose connections will get callbacks + * \param protocol: Which protocol to match + * \param reason: Callback reason index + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason); + +/** + * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost + * with the given reason + * + * \param wsi: wsi whose vhost will get callbacks + * \param reason: Callback reason index + * \param in: in argument to callback + * \param len: len argument to callback + * + * - Which: connections using this protocol on same VHOST as wsi ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len); + +LWS_VISIBLE LWS_EXTERN int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +/** + * lws_get_socket_fd() - returns the socket file descriptor + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_socket_fd(struct lws *wsi); + +/** + * lws_get_peer_write_allowance() - get the amount of data writeable to peer + * if known + * + * \param wsi: Websocket connection instance + * + * if the protocol does not have any guidance, returns -1. Currently only + * http2 connections get send window information from this API. But your code + * should use it so it can work properly with any protocol. + * + * If nonzero return is the amount of payload data the peer or intermediary has + * reported it has buffer space for. That has NO relationship with the amount + * of buffer space your OS can accept on this connection for a write action. + * + * This number represents the maximum you could send to the peer or intermediary + * on this connection right now without the protocol complaining. + * + * lws manages accounting for send window updates and payload writes + * automatically, so this number reflects the situation at the peer or + * intermediary dynamically. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_get_peer_write_allowance(struct lws *wsi); +///@} + +/** + * lws_rx_flow_control() - Enable and disable socket servicing for + * received packets. + * + * If the output side of a server process becomes choked, this allows flow + * control for the input side. + * + * \param wsi: Websocket connection instance to get callback for + * \param enable: 0 = disable read servicing for this connection, 1 = enable + */ +LWS_VISIBLE LWS_EXTERN int +lws_rx_flow_control(struct lws *wsi, int enable); + +/** + * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive + * + * When the user server code realizes it can accept more input, it can + * call this to have the RX flow restriction removed from all connections using + * the given protocol. + * \param context: lws_context + * \param protocol: all connections using this protocol will be allowed to receive + */ +LWS_VISIBLE LWS_EXTERN void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_remaining_packet_payload() - Bytes to come before "overall" + * rx packet is complete + * \param wsi: Websocket instance (available from user callback) + * + * This function is intended to be called from the callback if the + * user code is interested in "complete packets" from the client. + * libwebsockets just passes through payload as it comes and issues a buffer + * additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE + * callback handler can use this API to find out if the buffer it has just + * been given is the last piece of a "complete packet" from the client -- + * when that is the case lws_remaining_packet_payload() will return + * 0. + * + * Many protocols won't care becuse their packets are always small. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_remaining_packet_payload(struct lws *wsi); + + +/** \defgroup sock-adopt Socket adoption helpers + * ##Socket adoption helpers + * + * When integrating with an external app with its own event loop, these can + * be used to accept connections from someone else's listening socket. + * + * When using lws own event loop, these are not needed. + */ +///@{ + +/** + * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); +/** + * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * \param readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * \param len: The length of the data held at \param readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at \param readbuf first, before reading from + * the socket. + * + * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); +///@} + +/** \defgroup net Network related helper APIs + * ##Network related helper APIs + * + * These wrap miscellaneous useful network-related functions + */ +///@{ + +/** + * lws_canonical_hostname() - returns this host's hostname + * + * This is typically used by client code to fill in the host parameter + * when making a client connection. You can only call it after the context + * has been created. + * + * \param context: Websocket context + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_canonical_hostname(struct lws_context *context); + +/** + * lws_get_peer_addresses() - Get client address information + * \param wsi: Local struct lws associated with + * \param fd: Connection socket descriptor + * \param name: Buffer to take client address name + * \param name_len: Length of client address name buffer + * \param rip: Buffer to take client address IP dotted quad + * \param rip_len: Length of client address IP buffer + * + * This function fills in name and rip with the name and IP of + * the client connected with socket descriptor fd. Names may be + * truncated if there is not enough room. If either cannot be + * determined, they will be returned as valid zero-length strings. + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len); + +/** + * lws_get_peer_simple() - Get client address information without RDNS + * + * \param wsi: Local struct lws associated with + * \param name: Buffer to take client address name + * \param namelen: Length of client address name buffer + * + * This provides a 123.123.123.123 type IP address in name from the + * peer that has connected to wsi + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen); +#ifndef LWS_WITH_ESP8266 +/** + * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct + * + * \param ipv6: Allow IPV6 addresses + * \param ifname: Interface name or IP + * \param addr: struct sockaddr_in * to be written + * \param addrlen: Length of addr + * + * This converts a textual network interface name to a sockaddr usable by + * other network functions + */ +LWS_VISIBLE LWS_EXTERN int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen); +///@} +#endif + +/** \defgroup misc Miscellaneous APIs +* ##Miscellaneous APIs +* +* Various APIs outside of other categories +*/ +///@{ + +/** + * lws_snprintf(): snprintf that truncates the returned length too + * + * \param str: destination buffer + * \param size: bytes left in destination buffer + * \param format: format string + * \param ...: args for format + * + * This lets you correctly truncate buffers by concatenating lengths, if you + * reach the limit the reported length doesn't exceed the limit. + */ +LWS_VISIBLE LWS_EXTERN int +lws_snprintf(char *str, size_t size, const char *format, ...); + +/** + * lws_get_random(): fill a buffer with platform random data + * + * \param context: the lws context + * \param buf: buffer to fill + * \param len: how much to fill + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_random(struct lws_context *context, void *buf, int len); +/** + * lws_daemonize(): fill a buffer with platform random data + * + * \param _lock_path: the filepath to write the lock file + * + * Spawn lws as a background process, taking care of various things + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_daemonize(const char *_lock_path); +/** + * lws_get_library_version(): return string describing the version of lws + * + * On unix, also includes the git describe + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_get_library_version(void); + +/** + * lws_wsi_user() - get the user data associated with the connection + * \param wsi: lws connection + * + * Not normally needed since it's passed into the callback + */ +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi); + +/** + * lws_parse_uri: cut up prot:/ads:port/path into pieces + * Notice it does so by dropping '\0' into input string + * and the leading / on the path is consequently lost + * + * \param p: incoming uri string.. will get written to + * \param prot: result pointer for protocol part (https://) + * \param ads: result pointer for address part + * \param port: result pointer for port part + * \param path: result pointer for path part + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path); + +/** + * lws_now_secs(): return seconds since 1970-1-1 + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void); + +/** + * lws_get_context - Allow geting lws_context from a Websocket connection + * instance + * + * With this function, users can access context in the callback function. + * Otherwise users may have to declare context as a global variable. + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT +lws_get_context(const struct lws *wsi); + +/** + * lws_get_count_threads(): how many service threads the context uses + * + * \param context: the lws context + * + * By default this is always 1, if you asked for more than lws can handle it + * will clip the number of threads. So you can use this to find out how many + * threads are actually in use. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_get_count_threads(struct lws_context *context); + +/** + * lws_get_parent() - get parent wsi or NULL + * \param wsi: lws connection + * + * Specialized wsi like cgi stdin/out/err are associated to a parent wsi, + * this allows you to get their parent. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_parent(const struct lws *wsi); + +/** + * lws_get_child() - get child wsi or NULL + * \param wsi: lws connection + * + * Allows you to find a related wsi from the parent wsi. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_child(const struct lws *wsi); + + +/* + * \deprecated DEPRECATED Note: this is not normally needed as a user api. + * It's provided in case it is + * useful when integrating with other app poll loop service code. + */ +LWS_VISIBLE LWS_EXTERN int +lws_read(struct lws *wsi, unsigned char *buf, size_t len); + +/** + * lws_set_allocator() - custom allocator support + * + * \param realloc + * + * Allows you to replace the allocator (and deallocator) used by lws + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_allocator(void *(*realloc)(void *ptr, size_t size)); +///@} + +/** \defgroup wsstatus Websocket status APIs + * ##Websocket connection status APIs + * + * These provide information about ws connection or message status + */ +///@{ +/** + * lws_send_pipe_choked() - tests if socket is writable or not + * \param wsi: lws connection + * + * Allows you to check if you can write more on the socket + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_send_pipe_choked(struct lws *wsi); + +/** + * lws_is_final_fragment() - tests if last part of ws message + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_final_fragment(struct lws *wsi); + +/** + * lws_get_reserved_bits() - access reserved bits of ws frame + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN unsigned char +lws_get_reserved_bits(struct lws *wsi); + +/** + * lws_partial_buffered() - find out if lws buffered the last write + * \param wsi: websocket connection to check + * + * Returns 1 if you cannot use lws_write because the last + * write on this connection is still buffered, and can't be cleared without + * returning to the service loop and waiting for the connection to be + * writeable again. + * + * If you will try to do >1 lws_write call inside a single + * WRITEABLE callback, you must check this after every write and bail if + * set, ask for a new writeable callback and continue writing from there. + * + * This is never set at the start of a writeable callback, but any write + * may set it. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_partial_buffered(struct lws *wsi); + +/** + * lws_frame_is_binary(): true if the current frame was sent in binary mode + * + * \param wsi: the connection we are inquiring about + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_frame_is_binary(struct lws *wsi); + +/** + * lws_is_ssl() - Find out if connection is using SSL + * \param wsi: websocket connection to check + * + * Returns 0 if the connection is not using SSL, 1 if using SSL and + * using verified cert, and 2 if using SSL but the cert was not + * checked (appears for client wsi told to skip check on connection) + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_ssl(struct lws *wsi); +/** + * lws_is_cgi() - find out if this wsi is running a cgi process + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi); +///@} + + +/** \defgroup sha SHA and B64 helpers + * ##SHA and B64 helpers + * + * These provide SHA-1 and B64 helper apis + */ +///@{ +#ifdef LWS_SHA1_USE_OPENSSL_NAME +#define lws_SHA1 SHA1 +#else +/** + * lws_SHA1(): make a SHA-1 digest of a buffer + * + * \param d: incoming buffer + * \param n: length of incoming buffer + * \param md: buffer for message digest (must be >= 20 bytes) + * + * Reduces any size buffer into a 20-byte SHA-1 hash. + */ +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_SHA1(const unsigned char *d, size_t n, unsigned char *md); +#endif +/** + * lws_b64_encode_string(): encode a string into base 64 + * + * \param in: incoming buffer + * \param in_len: length of incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Encodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size); +/** + * lws_b64_decode_string(): decode a string from base 64 + * + * \param in: incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Decodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_decode_string(const char *in, char *out, int out_size); +///@} + + +/*! \defgroup cgi cgi handling + * + * ##CGI handling + * + * These functions allow low-level control over stdin/out/err of the cgi. + * + * However for most cases, binding the cgi to http in and out, the default + * lws implementation already does the right thing. + */ +#ifdef LWS_WITH_CGI +enum lws_enum_stdinouterr { + LWS_STDIN = 0, + LWS_STDOUT = 1, + LWS_STDERR = 2, +}; + +enum lws_cgi_hdr_state { + LCHS_HEADER, + LCHS_CR1, + LCHS_LF1, + LCHS_CR2, + LCHS_LF2, + LHCS_PAYLOAD, + LCHS_SINGLE_0A, +}; + +struct lws_cgi_args { + struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */ + enum lws_enum_stdinouterr ch; /**< channel index */ + unsigned char *data; /**< for messages with payload */ + enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */ + int len; /**< length */ +}; + + +/** + * lws_cgi: spawn network-connected cgi process + * + * \param wsi: connection to own the process + * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL + * \param script_uri_path_len: how many chars on the left of the uri are the path to the cgi + * \param timeout_secs: seconds script should be allowed to run + * \param mp_cgienv: pvo list with per-vhost cgi options to put in env + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, + int script_uri_path_len, int timeout_secs, + const struct lws_protocol_vhost_options *mp_cgienv); + +/** + * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi); + +/** + * lws_cgi_kill: terminate cgi process associated with wsi + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi); +#endif +///@} + + +/*! \defgroup fops file operation wrapping + * + * ##File operation wrapping + * + * Use these helper functions if you want to access a file from the perspective + * of a specific wsi, which is usually the case. If you just want contextless + * file access, use the fops callbacks directly with NULL wsi instead of these + * helpers. + * + * If so, then it calls the platform handler or user overrides where present + * (as defined in info->fops) + * + * The advantage from all this is user code can be portable for file operations + * without having to deal with differences between platforms. + */ +//@{ + +/** struct lws_plat_file_ops - Platform-specific file operations + * + * These provide platform-agnostic ways to deal with filesystem access in the + * library and in the user code. + */ +struct lws_plat_file_ops { + lws_filefd_type (*open)(struct lws *wsi, const char *filename, + unsigned long *filelen, int flags); + /**< Open file (always binary access if plat supports it) + * filelen is filled on exit to be the length of the file + * flags should be set to O_RDONLY or O_RDWR */ + int (*close)(struct lws *wsi, lws_filefd_type fd); + /**< close file */ + unsigned long (*seek_cur)(struct lws *wsi, lws_filefd_type fd, + long offset_from_cur_pos); + /**< seek from current position */ + int (*read)(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len); + /**< Read from file, on exit *amount is set to amount actually read */ + int (*write)(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len); + /**< Write to file, on exit *amount is set to amount actually written */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_get_fops() - get current file ops + * + * \param context: context + */ +LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT +lws_get_fops(struct lws_context *context); + +/** + * lws_plat_file_open() - file open operations + * + * \param wsi: connection doing the opening + * \param filename: filename to open + * \param filelen: length of file (filled in by call) + * \param flags: open flags + */ +static LWS_INLINE lws_filefd_type LWS_WARN_UNUSED_RESULT +lws_plat_file_open(struct lws *wsi, const char *filename, + unsigned long *filelen, int flags) +{ + return lws_get_fops(lws_get_context(wsi))->open(wsi, filename, + filelen, flags); +} + +/** + * lws_plat_file_close() - close file + * + * \param wsi: connection opened by + * \param fd: file descriptor + */ +static LWS_INLINE int +lws_plat_file_close(struct lws *wsi, lws_filefd_type fd) +{ + return lws_get_fops(lws_get_context(wsi))->close(wsi, fd); +} + +/** + * lws_plat_file_seek_cur() - close file + * + * \param wsi: connection opened by + * \param fd: file descriptor + * \param offset: position to seek to + */ +static LWS_INLINE unsigned long +lws_plat_file_seek_cur(struct lws *wsi, lws_filefd_type fd, long offset) +{ + return lws_get_fops(lws_get_context(wsi))->seek_cur(wsi, fd, offset); +} +/** + * lws_plat_file_read() - read from file + * + * \param wsi: connection opened by + * \param fd: file descriptor + * \param amount: how much to read (rewritten by call) + * \param buf: buffer to write to + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_plat_file_read(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len) +{ + return lws_get_fops(lws_get_context(wsi))->read(wsi, fd, amount, buf, + len); +} +/** + * lws_plat_file_write() - write from file + * + * \param wsi: connection opened by + * \param fd: file descriptor + * \param amount: how much to write (rewritten by call) + * \param buf: buffer to read from + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_plat_file_write(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len) +{ + return lws_get_fops(lws_get_context(wsi))->write(wsi, fd, amount, buf, + len); +} +//@} + +/** \defgroup smtp + * \ingroup lwsapi + * ##SMTP related functions + * + * These apis let you communicate with a local SMTP server to send email from + * lws. It handles all the SMTP sequencing and protocol actions. + * + * Your system should have postfix, sendmail or another MTA listening on port + * 25 and able to send email using the "mail" commandline app. Usually distro + * MTAs are configured for this by default. + * + * It runs via its own libuv events if initialized (which requires giving it + * a libuv loop to attach to). + * + * It operates using three callbacks, on_next() queries if there is a new email + * to send, on_get_body() asks for the body of the email, and on_sent() is + * called after the email is successfully sent. + * + * To use it + * + * - create an lws_email struct + * + * - initialize data, loop, the email_* strings, max_content_size and + * the callbacks + * + * - call lws_email_init() + * + * When you have at least one email to send, call lws_email_check() to + * schedule starting to send it. + */ +//@{ +#ifdef LWS_WITH_SMTP + +/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ +enum lwsgs_smtp_states { + LGSSMTP_IDLE, /**< awaiting new email */ + LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ + LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ + LGSSMTP_SENT_HELO, /**< sent the HELO */ + LGSSMTP_SENT_FROM, /**< sent FROM */ + LGSSMTP_SENT_TO, /**< sent TO */ + LGSSMTP_SENT_DATA, /**< sent DATA request */ + LGSSMTP_SENT_BODY, /**< sent the email body */ + LGSSMTP_SENT_QUIT, /**< sent the session quit */ +}; + +/** struct lws_email - abstract context for performing SMTP operations */ +struct lws_email { + void *data; + /**< opaque pointer set by user code and available to the callbacks */ + uv_loop_t *loop; + /**< the libuv loop we will work on */ + + char email_smtp_ip[32]; /**< Fill before init, eg, "127.0.0.1" */ + char email_helo[32]; /**< Fill before init, eg, "myserver.com" */ + char email_from[100]; /**< Fill before init or on_next */ + char email_to[100]; /**< Fill before init or on_next */ + + unsigned int max_content_size; + /**< largest possible email body size */ + + /* Fill all the callbacks before init */ + + int (*on_next)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when idle, 0 = another email to send, nonzero is idle. + * If you return 0, all of the email_* char arrays must be set + * to something useful. */ + int (*on_sent)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when transfer of the email to the SMTP server was + * successful, your callback would remove the current email + * from its queue */ + int (*on_get_body)(struct lws_email *email, char *buf, int len); + /**< (Fill in before calling lws_email_init) + * called when the body part of the queued email is about to be + * sent to the SMTP server. */ + + + /* private things */ + uv_timer_t timeout_email; /**< private */ + enum lwsgs_smtp_states estate; /**< private */ + uv_connect_t email_connect_req; /**< private */ + uv_tcp_t email_client; /**< private */ + time_t email_connect_started; /**< private */ + char email_buf[256]; /**< private */ + char *content; /**< private */ +}; + +/** + * lws_email_init() - Initialize a struct lws_email + * + * \param email: struct lws_email to init + * \param loop: libuv loop to use + * \param max_content: max email content size + * + * Prepares a struct lws_email for use ending SMTP + */ +LWS_VISIBLE LWS_EXTERN int +lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content); + +/** + * lws_email_check() - Request check for new email + * + * \param email: struct lws_email context to check + * + * Schedules a check for new emails in 1s... call this when you have queued an + * email for send. + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_check(struct lws_email *email); +/** + * lws_email_destroy() - stop using the struct lws_email + * + * \param email: the struct lws_email context + * + * Stop sending email using email and free allocations + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_destroy(struct lws_email *email); + +#endif +//@} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config.h new file mode 100644 index 00000000..447c18a8 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* lws_config.h Generated from lws_config.h.in */ + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif + +#define LWS_INSTALL_DATADIR "/usr/local/share" + +/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +/* #undef USE_WOLFSSL */ + +/* Also define to 1 (in addition to USE_WOLFSSL) when using the + (older) CyaSSL library */ +/* #undef USE_OLD_CYASSL */ + +/* #undef LWS_USE_MBEDTLS */ +/* #undef LWS_USE_POLARSSL */ +/* #undef LWS_WITH_ESP8266 */ + +/* #undef LWS_WITH_PLUGINS */ +/* #undef LWS_WITH_NO_LOGS */ + +/* The Libwebsocket version */ +#define LWS_LIBRARY_VERSION "2.1.0" + +#define LWS_LIBRARY_VERSION_MAJOR 2 +#define LWS_LIBRARY_VERSION_MINOR 1 +#define LWS_LIBRARY_VERSION_PATCH 0 +/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ +#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH + +/* The current git commit hash that we're building from */ +#define LWS_BUILD_HASH "broadcom@localhost.localdomain-v2.0.0-189-g2357f7b" + +/* Build with OpenSSL support */ +#define LWS_OPENSSL_SUPPORT + +/* The client should load and trust CA root certs it finds in the OS */ +#define LWS_SSL_CLIENT_USE_OS_CA_CERTS + +/* Sets the path where the client certs should be installed. */ +#define LWS_OPENSSL_CLIENT_CERTS "../share" + +/* Turn off websocket extensions */ +#define LWS_NO_EXTENSIONS + +/* Enable libev io loop */ +/* #undef LWS_USE_LIBEV */ + +/* Enable libuv io loop */ +/* #undef LWS_USE_LIBUV */ + +/* Build with support for ipv6 */ +/* #undef LWS_USE_IPV6 */ + +/* Build with support for UNIX domain socket */ +/* #undef LWS_USE_UNIX_SOCK */ + +/* Build with support for HTTP2 */ +/* #undef LWS_USE_HTTP2 */ + +/* Turn on latency measuring code */ +/* #undef LWS_LATENCY */ + +/* Don't build the daemonizeation api */ +#define LWS_NO_DAEMONIZE + +/* Build without server support */ +/* #undef LWS_NO_SERVER */ + +/* Build without client support */ +/* #undef LWS_NO_CLIENT */ + +/* If we should compile with MinGW support */ +/* #undef LWS_MINGW_SUPPORT */ + +/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */ +#define LWS_BUILTIN_GETIFADDRS + +/* use SHA1() not internal libwebsockets_SHA1 */ +/* #undef LWS_SHA1_USE_OPENSSL_NAME */ + +/* SSL server using ECDH certificate */ +/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */ +#define LWS_HAVE_SSL_CTX_set1_param +/* #undef LWS_HAVE_X509_VERIFY_PARAM_set1_host */ + +/* #undef LWS_HAVE_UV_VERSION_H */ + +/* CGI apis */ +/* #undef LWS_WITH_CGI */ + +/* whether the Openssl is recent enough, and / or built with, ecdh */ +/* #undef LWS_HAVE_OPENSSL_ECDH_H */ + +/* HTTP Proxy support */ +/* #undef LWS_WITH_HTTP_PROXY */ + +/* Http access log support */ +/* #undef LWS_WITH_ACCESS_LOG */ +/* #undef LWS_WITH_SERVER_STATUS */ + +/* #undef LWS_WITH_STATEFUL_URLDECODE */ + +/* Maximum supported service threads */ +#define LWS_MAX_SMP 32 + +/* Lightweight JSON Parser */ +/* #undef LWS_WITH_LEJP */ + +/* SMTP */ +/* #undef LWS_WITH_SMTP */ + + diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config_private.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config_private.h new file mode 100644 index 00000000..8c2fb54c --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/lws_config_private.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* lws_config_private.h.in. Private compilation options. */ + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif + +/* Define to 1 to use CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +/* #undef USE_CYASSL */ + +/* Define to 1 if you have the `bzero' function. */ +#define LWS_HAVE_BZERO + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#define LWS_HAVE_FORK + +/* Define to 1 if you have the `getenv’ function. */ +#define LWS_HAVE_GETENV + +/* Define to 1 if you have the header file. */ +/* #undef LWS_HAVE_IN6ADDR_H */ + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_INTTYPES_H + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef LWS_HAVE_LIBSSL */ + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define LWS_HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#define LWS_HAVE_MEMSET + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_NETINET_IN_H + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define LWS_HAVE_REALLOC + +/* Define to 1 if you have the `socket' function. */ +#define LWS_HAVE_SOCKET + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#define LWS_HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_SYS_PRCTL_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +/* #undef LWS_HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#define LWS_HAVE_VFORK + +/* Define to 1 if you have the header file. */ +/* #undef LWS_HAVE_VFORK_H */ + +/* Define to 1 if `fork' works. */ +#define LWS_HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#define LWS_HAVE_WORKING_VFORK + +/* Define to 1 if execvpe() exists */ +#define LWS_HAVE_EXECVPE + +/* Define to 1 if you have the header file. */ +#define LWS_HAVE_ZLIB_H + +#define LWS_HAVE_GETLOADAVG + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR // We're not using libtool + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to 1 if we have getifaddrs */ +/* #undef LWS_HAVE_GETIFADDRS */ + +/* Define if the inline keyword doesn't exist. */ +/* #undef inline */ + + diff --git a/feeds/wlan-ap/libwebsocket/src/include/websocket/private-libwebsockets.h b/feeds/wlan-ap/libwebsocket/src/include/websocket/private-libwebsockets.h new file mode 100644 index 00000000..4c60ded7 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/include/websocket/private-libwebsockets.h @@ -0,0 +1,1943 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "lws_config.h" +#include "lws_config_private.h" + + +#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK) +#define _GNU_SOURCE +#endif + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#if defined(LWS_WITH_ESP8266) +#include +#define assert(n) + +/* rom-provided stdc functions for free, ensure use these instead of libc ones */ + +int ets_vsprintf(char *str, const char *format, va_list argptr); +int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); +int ets_snprintf(char *str, size_t size, const char *format, ...); +int ets_sprintf(char *str, const char *format, ...); +int os_printf_plus(const char *format, ...); +#undef malloc +#undef realloc +#undef free +void *pvPortMalloc(size_t s, const char *f, int line); +#define malloc(s) pvPortMalloc(s, "", 0) +void *pvPortRealloc(void *p, size_t s, const char *f, int line); +#define realloc(p, s) pvPortRealloc(p, s, "", 0) +void vPortFree(void *p, const char *f, int line); +#define free(p) vPortFree(p, "", 0) +#undef memcpy +void *ets_memcpy(void *dest, const void *src, size_t n); +#define memcpy ets_memcpy +void *ets_memset(void *dest, int v, size_t n); +#define memset ets_memset +char *ets_strcpy(char *dest, const char *src); +#define strcpy ets_strcpy +char *ets_strncpy(char *dest, const char *src, size_t n); +#define strncpy ets_strncpy +char *ets_strstr(const char *haystack, const char *needle); +#define strstr ets_strstr +int ets_strcmp(const char *s1, const char *s2); +int ets_strncmp(const char *s1, const char *s2, size_t n); +#define strcmp ets_strcmp +#define strncmp ets_strncmp +size_t ets_strlen(const char *s); +#define strlen ets_strlen +void *ets_memmove(void *dest, const void *src, size_t n); +#define memmove ets_memmove +char *ets_strchr(const char *s, int c); +#define strchr_ets_strchr +#undef _DEBUG +#include + +#else +#define STORE_IN_ROM +#include +#endif +#if LWS_MAX_SMP > 1 +#include +#endif + +#ifdef LWS_HAVE_SYS_STAT_H +#include +#endif + +#include + +#define wlog(...) wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_1), __VA_ARGS__ ) +#define wdbg1(...) wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_2), __VA_ARGS__ ) +#define wdbg2(...) wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_3), __VA_ARGS__ ) +#define wdbg3(...) wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_4), __VA_ARGS__ ) +#define wdbg( level, ... ) wc_put_logline( level, __VA_ARGS__ ) + + +#if defined(WIN32) || defined(_WIN32) +#if (WINVER < 0x0501) +#undef WINVER +#undef _WIN32_WINNT +#define WINVER 0x0501 +#define _WIN32_WINNT WINVER +#endif +#define LWS_NO_DAEMONIZE +#define LWS_ERRNO WSAGetLastError() +#define LWS_EAGAIN WSAEWOULDBLOCK +#define LWS_EALREADY WSAEALREADY +#define LWS_EINPROGRESS WSAEINPROGRESS +#define LWS_EINTR WSAEINTR +#define LWS_EISCONN WSAEISCONN +#define LWS_EWOULDBLOCK WSAEWOULDBLOCK +#define MSG_NOSIGNAL 0 +#define SHUT_RDWR SD_BOTH +#define SOL_TCP IPPROTO_TCP +#define SHUT_WR SD_SEND + +#define compatible_close(fd) closesocket(fd) +#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1 +#define lws_socket_is_valid(x) (!!x) +#define LWS_SOCK_INVALID 0 +#include +#include +#include +#include +#ifdef LWS_HAVE_IN6ADDR_H +#include +#endif +#include +#include + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) +#define vsnprintf _vsnprintf +#else +#ifdef LWS_HAVE__VSNPRINTF +#define vsnprintf _vsnprintf +#endif +#endif + +#ifdef LWS_HAVE__SNPRINTF +#define lws_snprintf _snprintf +#endif + +#else /* not windows --> */ + +#include +#include +#include +#include +#ifndef MBED_OPERATORS +#ifndef __cplusplus +#include +#endif +#include +#include +#ifdef LWS_WITH_ESP8266 +#include +#define vsnprintf ets_vsnprintf +#define snprintf ets_snprintf +#define sprintf ets_sprintf +#else +#include +#endif +#ifdef LWS_WITH_HTTP_PROXY +#include +#include +#endif +#if defined(LWS_BUILTIN_GETIFADDRS) + #include +#else + #if !defined(LWS_WITH_ESP8266) + #include + #endif +#endif +#if defined (__ANDROID__) +#include +#include +#elif defined (__sun) +#include +#else +#if !defined(LWS_WITH_ESP8266) +#include +#endif +#endif +#include +#if !defined(LWS_WITH_ESP8266) +#include +#include +#include +#include +#include +#include +#endif +#ifdef LWS_USE_LIBEV +#include +#endif +#ifdef LWS_USE_LIBUV +#include +#endif + +#endif /* MBED */ + +#ifndef LWS_NO_FORK +#ifdef LWS_HAVE_SYS_PRCTL_H +#include +#endif +#endif + +#include + +#define LWS_ERRNO errno +#define LWS_EAGAIN EAGAIN +#define LWS_EALREADY EALREADY +#define LWS_EINPROGRESS EINPROGRESS +#define LWS_EINTR EINTR +#define LWS_EISCONN EISCONN +#define LWS_EWOULDBLOCK EWOULDBLOCK + +#define lws_set_blocking_send(wsi) + +#if defined(MBED_OPERATORS) || defined(LWS_WITH_ESP8266) +#define lws_socket_is_valid(x) ((x) != NULL) +#define LWS_SOCK_INVALID (NULL) +struct lws; +const char * +lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen); +#else +#define lws_socket_is_valid(x) (x >= 0) +#define LWS_SOCK_INVALID (-1) +#endif +#endif + +#ifndef LWS_HAVE_BZERO +#ifndef bzero +#define bzero(b, len) (memset((b), '\0', (len)), (void) 0) +#endif +#endif + +#ifndef LWS_HAVE_STRERROR +#define strerror(x) "" +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include +#include +#else +#include +#include +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_USE_POLARSSL) +#include +#include +#include +#include +#include +#define SSL_ERROR_WANT_READ POLARSSL_ERR_NET_WANT_READ +#define SSL_ERROR_WANT_WRITE POLARSSL_ERR_NET_WANT_WRITE +#define OPENSSL_VERSION_NUMBER 0x10002000L +#else +#if defined(LWS_USE_MBEDTLS) +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#ifdef LWS_HAVE_OPENSSL_ECDH_H +#include +#endif +#include +#endif /* not USE_MBEDTLS */ +#endif /* not USE_POLARSSL */ +#endif /* not USE_WOLFSSL */ +#endif + +#include "libwebsockets.h" +#if defined(WIN32) || defined(_WIN32) +#else +static inline int compatible_close(int fd) { return close(fd); } +#endif + +#if defined(WIN32) || defined(_WIN32) +#include +#endif + +#if defined(MBED_OPERATORS) +#undef compatible_close +#define compatible_close(fd) mbed3_delete_tcp_stream_socket(fd) +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + +#if defined(LWS_WITH_ESP8266) +#undef compatible_close +#define compatible_close(fd) { fd->state=ESPCONN_CLOSE; espconn_delete(fd); } +lws_sockfd_type +esp8266_create_tcp_stream_socket(void); +void +esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi); +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + + +#if defined(WIN32) || defined(_WIN32) + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#ifndef u_int64_t +typedef unsigned __int64 u_int64_t; +#endif + +#undef __P +#ifndef __P +#if __STDC__ +#define __P(protos) protos +#else +#define __P(protos) () +#endif +#endif + +#else + +#include +#include + +#if defined(__APPLE__) +#include +#elif defined(__FreeBSD__) +#include +#elif defined(__linux__) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__QNX__) + #include + #if defined(__LITTLEENDIAN__) + #define BYTE_ORDER __LITTLEENDIAN__ + #define LITTLE_ENDIAN __LITTLEENDIAN__ + #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */ + #endif + #if defined(__BIGENDIAN__) + #define BYTE_ORDER __BIGENDIAN__ + #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */ + #define BIG_ENDIAN __BIGENDIAN__ + #endif +#endif + +#if defined(__sun) && defined(__GNUC__) +# define BYTE_ORDER __BYTE_ORDER__ +# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +# define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif + +#if !defined(BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER +#endif +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __BIG_ENDIAN +#endif + +#endif + +/* + * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, + * but happily have something equivalent in the SO_NOSIGPIPE flag. + */ +#ifdef __APPLE__ +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +/* + * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in + * POSIX 2008. + */ +#ifdef __sun +#define MSG_NOSIGNAL 0 +#endif + +#ifdef _WIN32 +#ifndef FD_HASHTABLE_MODULUS +#define FD_HASHTABLE_MODULUS 32 +#endif +#endif + +#ifndef LWS_DEF_HEADER_LEN +#define LWS_DEF_HEADER_LEN 4096 +#endif +#ifndef LWS_DEF_HEADER_POOL +#define LWS_DEF_HEADER_POOL 4 +#endif +#ifndef LWS_MAX_PROTOCOLS +#define LWS_MAX_PROTOCOLS 5 +#endif +#ifndef LWS_MAX_EXTENSIONS_ACTIVE +#define LWS_MAX_EXTENSIONS_ACTIVE 2 +#endif +#ifndef LWS_MAX_EXT_OFFERS +#define LWS_MAX_EXT_OFFERS 8 +#endif +#ifndef SPEC_LATEST_SUPPORTED +#define SPEC_LATEST_SUPPORTED 13 +#endif +#ifndef AWAITING_TIMEOUT +#define AWAITING_TIMEOUT 20 +#endif +#ifndef CIPHERS_LIST_STRING +#define CIPHERS_LIST_STRING "DEFAULT" +#endif +#ifndef LWS_SOMAXCONN +#define LWS_SOMAXCONN SOMAXCONN +#endif + +#define MAX_WEBSOCKET_04_KEY_LEN 128 + +#ifndef SYSTEM_RANDOM_FILEPATH +#define SYSTEM_RANDOM_FILEPATH "/dev/urandom" +#endif + +enum lws_websocket_opcodes_07 { + LWSWSOPC_CONTINUATION = 0, + LWSWSOPC_TEXT_FRAME = 1, + LWSWSOPC_BINARY_FRAME = 2, + + LWSWSOPC_NOSPEC__MUX = 7, + + /* control extensions 8+ */ + + LWSWSOPC_CLOSE = 8, + LWSWSOPC_PING = 9, + LWSWSOPC_PONG = 0xa, +}; + + +enum lws_connection_states { + LWSS_HTTP, + LWSS_HTTP_ISSUING_FILE, + LWSS_HTTP_HEADERS, + LWSS_HTTP_BODY, + LWSS_DEAD_SOCKET, + LWSS_ESTABLISHED, + LWSS_CLIENT_HTTP_ESTABLISHED, + LWSS_CLIENT_UNCONNECTED, + LWSS_RETURNED_CLOSE_ALREADY, + LWSS_AWAITING_CLOSE_ACK, + LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE, + LWSS_SHUTDOWN, + + LWSS_HTTP2_AWAIT_CLIENT_PREFACE, + LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS, + LWSS_HTTP2_ESTABLISHED, + + LWSS_CGI, +}; + +enum http_version { + HTTP_VERSION_1_0, + HTTP_VERSION_1_1, + HTTP_VERSION_2 +}; + +enum http_connection_type { + HTTP_CONNECTION_CLOSE, + HTTP_CONNECTION_KEEP_ALIVE +}; + +enum lws_pending_protocol_send { + LWS_PPS_NONE, + LWS_PPS_HTTP2_MY_SETTINGS, + LWS_PPS_HTTP2_ACK_SETTINGS, + LWS_PPS_HTTP2_PONG, +}; + +enum lws_rx_parse_state { + LWS_RXPS_NEW, + + LWS_RXPS_04_mask_1, + LWS_RXPS_04_mask_2, + LWS_RXPS_04_mask_3, + + LWS_RXPS_04_FRAME_HDR_1, + LWS_RXPS_04_FRAME_HDR_LEN, + LWS_RXPS_04_FRAME_HDR_LEN16_2, + LWS_RXPS_04_FRAME_HDR_LEN16_1, + LWS_RXPS_04_FRAME_HDR_LEN64_8, + LWS_RXPS_04_FRAME_HDR_LEN64_7, + LWS_RXPS_04_FRAME_HDR_LEN64_6, + LWS_RXPS_04_FRAME_HDR_LEN64_5, + LWS_RXPS_04_FRAME_HDR_LEN64_4, + LWS_RXPS_04_FRAME_HDR_LEN64_3, + LWS_RXPS_04_FRAME_HDR_LEN64_2, + LWS_RXPS_04_FRAME_HDR_LEN64_1, + + LWS_RXPS_07_COLLECT_FRAME_KEY_1, + LWS_RXPS_07_COLLECT_FRAME_KEY_2, + LWS_RXPS_07_COLLECT_FRAME_KEY_3, + LWS_RXPS_07_COLLECT_FRAME_KEY_4, + + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED +}; + +#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32 + +enum connection_mode { + LWSCM_HTTP_SERVING, + LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */ + LWSCM_PRE_WS_SERVING_ACCEPT, + + LWSCM_WS_SERVING, + LWSCM_WS_CLIENT, + + LWSCM_HTTP2_SERVING, + + /* transient, ssl delay hiding */ + LWSCM_SSL_ACK_PENDING, + LWSCM_SSL_INIT, + + /* special internal types */ + LWSCM_SERVER_LISTENER, + LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */ + + /* HTTP Client related */ + LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP, + LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */ + LWSCM_WSCL_WAITING_CONNECT, + LWSCM_WSCL_WAITING_PROXY_REPLY, + LWSCM_WSCL_ISSUE_HANDSHAKE, + LWSCM_WSCL_ISSUE_HANDSHAKE2, + LWSCM_WSCL_ISSUE_HTTP_BODY, + LWSCM_WSCL_WAITING_SSL, + LWSCM_WSCL_WAITING_SERVER_REPLY, + LWSCM_WSCL_WAITING_EXTENSION_CONNECT, + LWSCM_WSCL_PENDING_CANDIDATE_CHILD, + + /****** add new things just above ---^ ******/ + + +}; + +enum { + LWS_RXFLOW_ALLOW = (1 << 0), + LWS_RXFLOW_PENDING_CHANGE = (1 << 1), +}; + +/* this is not usable directly by user code any more, lws_close_reason() */ +#define LWS_WRITE_CLOSE 4 + +struct lws_protocols; +struct lws; + +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + +struct lws_io_watcher { +#ifdef LWS_USE_LIBEV + ev_io ev_watcher; +#endif +#ifdef LWS_USE_LIBUV + uv_poll_t uv_watcher; +#endif + struct lws_context *context; +}; + +struct lws_signal_watcher { +#ifdef LWS_USE_LIBEV + ev_signal ev_watcher; +#endif +#ifdef LWS_USE_LIBUV + uv_signal_t uv_watcher; +#endif + struct lws_context *context; +}; +#endif + +#ifdef _WIN32 +#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) +struct lws_fd_hashtable { + struct lws **wsi; + int length; +}; +#endif + +/* + * This is totally opaque to code using the library. It's exported as a + * forward-reference pointer-only declaration; the user can use the pointer with + * other APIs to get information out of it. + */ + +struct lws_fragments { + unsigned int offset; + unsigned short len; + unsigned char nfrag; /* which ah->frag[] continues this content, or 0 */ +}; + +/* + * these are assigned from a pool held in the context. + * Both client and server mode uses them for http header analysis + */ + +struct allocated_headers { + struct lws *wsi; /* owner */ + char *data; /* prepared by context init to point to dedicated storage */ + /* + * the randomly ordered fragments, indexed by frag_index and + * lws_fragments->nfrag for continuation. + */ + struct lws_fragments frags[WSI_TOKEN_COUNT * 2]; + time_t assigned; + /* + * for each recognized token, frag_index says which frag[] his data + * starts in (0 means the token did not appear) + * the actual header data gets dumped as it comes in, into data[] + */ + unsigned char frag_index[WSI_TOKEN_COUNT]; + unsigned char rx[2048]; + unsigned int rxpos; + unsigned int rxlen; + unsigned int pos; + + unsigned int http_response; + +#ifndef LWS_NO_CLIENT + char initial_handshake_hash_base64[30]; +#endif + + unsigned char in_use; + unsigned char nfrag; +}; + +/* + * so we can have n connections being serviced simultaneously, + * these things need to be isolated per-thread. + */ + +struct lws_context_per_thread { +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; +#endif + struct lws_pollfd *fds; +#if defined(LWS_WITH_ESP8266) + struct lws **lws_vs_fds_index; +#endif + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + struct lws *timeout_list; +#ifdef LWS_USE_LIBUV + struct lws_context *context; +#endif +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi_list; +#endif + void *http_header_data; + struct allocated_headers *ah_pool; + struct lws *ah_wait_list; + int ah_wait_list_length; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *pending_read_list; /* linked list */ +#endif +#ifndef LWS_NO_SERVER + struct lws *wsi_listening; +#endif +#if defined(LWS_USE_LIBEV) + struct ev_loop *io_loop_ev; +#endif +#if defined(LWS_USE_LIBUV) + uv_loop_t *io_loop_uv; + uv_signal_t signals[8]; + uv_timer_t uv_timeout_watcher; + uv_idle_t uv_idle; +#endif +#if defined(LWS_USE_LIBEV) + struct lws_io_watcher w_accept; +#endif +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + struct lws_signal_watcher w_sigint; + unsigned char ev_loop_foreign:1; +#endif + + unsigned long count_conns; + /* + * usable by anything in the service code, but only if the scope + * does not last longer than the service action (since next service + * of any socket can likewise use it and overwrite) + */ + unsigned char *serv_buf; +#ifdef _WIN32 + WSAEVENT *events; +#else + lws_sockfd_type dummy_pipe_fds[2]; +#endif + unsigned int fds_count; + + short ah_count_in_use; + unsigned char tid; + unsigned char lock_depth; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * hierarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + +struct lws_vhost { +#if !defined(LWS_WITH_ESP8266) + char http_proxy_address[128]; + char proxy_basic_auth_token[128]; +#endif +#if defined(LWS_WITH_ESP8266) + /* listen sockets need a place to hang their hat */ + esp_tcp tcp; +#endif + struct lws_context *context; + struct lws_vhost *vhost_next; + const struct lws_http_mount *mount_list; + struct lws *lserv_wsi; + const char *name; + const char *iface; + const struct lws_protocols *protocols; + void **protocol_vh_privs; + const struct lws_protocol_vhost_options *pvo; + const struct lws_protocol_vhost_options *headers; + struct lws **same_vh_protocol_list; +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *ssl_ctx; + SSL_CTX *ssl_client_ctx; +#endif +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *extensions; +#endif + unsigned long long rx, tx; + unsigned long conn, trans, ws_upgrades, http2_upgrades; + + int listen_port; + unsigned int http_proxy_port; + unsigned int options; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + int keepalive_timeout; +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + +#ifdef LWS_OPENSSL_SUPPORT + int use_ssl; + int allow_non_ssl_on_ssl_port; + unsigned int user_supplied_ssl_ctx:1; +#endif + + unsigned char default_protocol_index; +}; + +/* + * the rest is managed per-context, that includes + * + * - processwide single fd -> wsi lookup + * - contextwide headers pool + */ + +struct lws_context { + time_t last_timeout_check_s; + time_t last_ws_ping_pong_check_s; + time_t time_up; + struct lws_plat_file_ops fops; + struct lws_context_per_thread pt[LWS_MAX_SMP]; +#ifdef _WIN32 +/* different implementation between unix and windows */ + struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; +#else +#if defined(LWS_WITH_ESP8266) + struct espconn **connpool; /* .reverse points to the wsi */ + void *rxd; + int rxd_len; + os_timer_t to_timer; +#else + struct lws **lws_lookup; /* fd to wsi */ +#endif +#endif + struct lws_vhost *vhost_list; + struct lws_plugin *plugin_list; + const struct lws_token_limits *token_limits; + void *user_space; + const char *server_string; + const struct lws_protocol_vhost_options *reject_service_keywords; + +#if defined(LWS_USE_LIBEV) + lws_ev_signal_cb_t * lws_ev_sigint_cb; +#endif +#if defined(LWS_USE_LIBUV) + uv_signal_cb lws_uv_sigint_cb; +#endif + char canonical_hostname[128]; +#ifdef LWS_LATENCY + unsigned long worst_latency; + char worst_latency_info[256]; +#endif + + int max_fds; +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + int use_ev_sigint; +#endif + int started_with_parent; + int uid, gid; + + int fd_random; +#ifdef LWS_OPENSSL_SUPPORT +#define lws_ssl_anybody_has_buffered_read(w) \ + (w->vhost->use_ssl && \ + w->context->pt[(int)w->tsi].pending_read_list) +#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \ + (/*c->use_ssl && */ \ + c->pt[(int)t].pending_read_list) +#else +#define lws_ssl_anybody_has_buffered_read(ctx) (0) +#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0) +#endif + int count_wsi_allocated; + int count_cgi_spawned; + unsigned int options; + unsigned int fd_limit_per_thread; + unsigned int timeout_secs; + unsigned int pt_serv_buf_size; + int max_http_header_data; + + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; + int service_tid_detected; + + short max_http_header_pool; + short count_threads; + short plugin_protocol_count; + short plugin_extension_count; + short server_string_len; + unsigned short ws_ping_pong_interval; + + unsigned int being_destroyed:1; + unsigned int requested_kill:1; + unsigned int protocol_init_done:1; +}; + +#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] +#define lws_get_vh_protocol(vh, x) vh->protocols[x] + +LWS_EXTERN void +lws_close_free_wsi_final(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_init(struct lws_context * context, const char * const *d); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_destroy(struct lws_context * context); + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi); + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), + + LWS_EV_PREPARE_DELETION = (1 << 31), +}; + +#if defined(LWS_USE_LIBEV) +LWS_EXTERN void +lws_libev_accept(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN void +lws_libev_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libev_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libev_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libev_run(const struct lws_context *context, int tsi); +#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV) +LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); +#else +#define lws_libev_accept(_a, _b) ((void) 0) +#define lws_libev_io(_a, _b) ((void) 0) +#define lws_libev_init_fd_table(_a) (0) +#define lws_libev_run(_a, _b) ((void) 0) +#define lws_libev_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBEV_ENABLED(context) (0) +#if LWS_POSIX +#define lws_feature_status_libev(_a) \ + lwsl_notice("libev support not compiled in\n") +#else +#define lws_feature_status_libev(_a) +#endif +#endif + +#if defined(LWS_USE_LIBUV) +LWS_EXTERN void +lws_libuv_accept(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN void +lws_libuv_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libuv_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libuv_destroyloop(struct lws_context *context, int tsi); +#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) +LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info); +#else +#define lws_libuv_accept(_a, _b) ((void) 0) +#define lws_libuv_io(_a, _b) ((void) 0) +#define lws_libuv_init_fd_table(_a) (0) +#define lws_libuv_run(_a, _b) ((void) 0) +#define lws_libuv_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBUV_ENABLED(context) (0) +#if LWS_POSIX +#define lws_feature_status_libuv(_a) \ + lwsl_notice("libuv support not compiled in\n") +#else +#define lws_feature_status_libuv(_a) +#endif +#endif + + +#ifdef LWS_USE_IPV6 +#define LWS_IPV6_ENABLED(vh) \ + (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ + !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +#ifdef LWS_USE_UNIX_SOCK +#define LWS_UNIX_SOCK_ENABLED(vhost) \ + (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) +#else +#define LWS_UNIX_SOCK_ENABLED(vhost) (0) +#endif +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; + +/* notice that these union members: + * + * hdr + * http + * http2 + * + * all have a pointer to allocated_headers struct as their first member. + * + * It means for allocated_headers access, the three union paths can all be + * used interchangeably to access the same data + */ + + +#ifndef LWS_NO_CLIENT +struct client_info_stash { + char address[256]; + char path[4096]; + char host[256]; + char origin[256]; + char protocol[256]; + char method[16]; +}; +#endif + +struct _lws_header_related { + /* MUST be first in struct */ + struct allocated_headers *ah; + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + enum uri_path_states ups; + enum uri_esc_states ues; + short lextable_pos; + unsigned int current_token_limit; +#ifndef LWS_NO_CLIENT + unsigned short c_port; +#endif + char esc_stash; + char post_literal_equal; + unsigned char parser_state; /* enum lws_token_indexes */ + char redirects; +}; + +struct _lws_http_mode_related { + /* MUST be first in struct */ + struct allocated_headers *ah; /* mirroring _lws_header_related */ + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + struct lws *new_wsi_list; + unsigned long filepos; + unsigned long filelen; + lws_filefd_type fd; + + enum http_version request_version; + enum http_connection_type connection_type; + unsigned int content_length; + unsigned int content_remain; +}; + +#ifdef LWS_USE_HTTP2 + +enum lws_http2_settings { + LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE = 1, + LWS_HTTP2_SETTINGS__ENABLE_PUSH, + LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS, + LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE, + LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE, + LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE, + + LWS_HTTP2_SETTINGS__COUNT /* always last */ +}; + +enum lws_http2_wellknown_frame_types { + LWS_HTTP2_FRAME_TYPE_DATA, + LWS_HTTP2_FRAME_TYPE_HEADERS, + LWS_HTTP2_FRAME_TYPE_PRIORITY, + LWS_HTTP2_FRAME_TYPE_RST_STREAM, + LWS_HTTP2_FRAME_TYPE_SETTINGS, + LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE, + LWS_HTTP2_FRAME_TYPE_PING, + LWS_HTTP2_FRAME_TYPE_GOAWAY, + LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE, + LWS_HTTP2_FRAME_TYPE_CONTINUATION, + + LWS_HTTP2_FRAME_TYPE_COUNT /* always last */ +}; + +enum lws_http2_flags { + LWS_HTTP2_FLAG_END_STREAM = 1, + LWS_HTTP2_FLAG_END_HEADERS = 4, + LWS_HTTP2_FLAG_PADDED = 8, + LWS_HTTP2_FLAG_PRIORITY = 0x20, + + LWS_HTTP2_FLAG_SETTINGS_ACK = 1, +}; + +#define LWS_HTTP2_STREAM_ID_MASTER 0 +#define LWS_HTTP2_FRAME_HEADER_LENGTH 9 +#define LWS_HTTP2_SETTINGS_LENGTH 6 + +struct http2_settings { + unsigned int setting[LWS_HTTP2_SETTINGS__COUNT]; +}; + +enum http2_hpack_state { + + /* optional before first header block */ + HPKS_OPT_PADDING, + HKPS_OPT_E_DEPENDENCY, + HKPS_OPT_WEIGHT, + + /* header block */ + HPKS_TYPE, + + HPKS_IDX_EXT, + + HPKS_HLEN, + HPKS_HLEN_EXT, + + HPKS_DATA, + + /* optional after last header block */ + HKPS_OPT_DISCARD_PADDING, +}; + +enum http2_hpack_type { + HPKT_INDEXED_HDR_7, + HPKT_INDEXED_HDR_6_VALUE_INCR, + HPKT_LITERAL_HDR_VALUE_INCR, + HPKT_INDEXED_HDR_4_VALUE, + HPKT_LITERAL_HDR_VALUE, + HPKT_SIZE_5 +}; + +struct hpack_dt_entry { + int token; /* additions that don't map to a token are ignored */ + int arg_offset; + int arg_len; +}; + +struct hpack_dynamic_table { + struct hpack_dt_entry *entries; + char *args; + int pos; + int next; + int num_entries; + int args_length; +}; + +struct _lws_http2_related { + /* + * having this first lets us also re-use all HTTP union code + * and in turn, http_mode_related has allocated headers in right + * place so we can use the header apis on the wsi directly still + */ + struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */ + + struct http2_settings my_settings; + struct http2_settings peer_settings; + + struct lws *parent_wsi; + struct lws *next_child_wsi; + + struct hpack_dynamic_table *hpack_dyn_table; + struct lws *stream_wsi; + unsigned char ping_payload[8]; + unsigned char one_setting[LWS_HTTP2_SETTINGS_LENGTH]; + + unsigned int count; + unsigned int length; + unsigned int stream_id; + enum http2_hpack_state hpack; + enum http2_hpack_type hpack_type; + unsigned int header_index; + unsigned int hpack_len; + unsigned int hpack_e_dep; + int tx_credit; + unsigned int my_stream_id; + unsigned int child_count; + int my_priority; + + unsigned int END_STREAM:1; + unsigned int END_HEADERS:1; + unsigned int send_END_STREAM:1; + unsigned int GOING_AWAY; + unsigned int requested_POLLOUT:1; + unsigned int waiting_tx_credit:1; + unsigned int huff:1; + unsigned int value:1; + + unsigned short round_robin_POLLOUT; + unsigned short count_POLLOUT_children; + unsigned short hpack_pos; + + unsigned char type; + unsigned char flags; + unsigned char frame_state; + unsigned char padding; + unsigned char hpack_m; + unsigned char initialized; +}; + +#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.http2.parent_wsi) + +#endif + +struct _lws_websocket_related { + /* cheapest way to deal with ah overlap with ws union transition */ + struct _lws_header_related hdr; + char *rx_ubuf; + unsigned int rx_ubuf_alloc; + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + time_t time_next_ping_check; + size_t rx_packet_length; + unsigned int rx_ubuf_head; + unsigned char mask[4]; + /* Also used for close content... control opcode == < 128 */ + unsigned char ping_payload_buf[128 - 3 + LWS_PRE]; + + unsigned char ping_payload_len; + unsigned char mask_idx; + unsigned char opcode; + unsigned char rsv; + unsigned char rsv_first_msg; + /* zero if no info, or length including 2-byte close code */ + unsigned char close_in_ping_buffer_len; + unsigned char utf8; + unsigned char stashed_write_type; + unsigned char tx_draining_stashed_wp; + + unsigned int final:1; + unsigned int frame_is_binary:1; + unsigned int all_zero_nonce:1; + unsigned int this_frame_masked:1; + unsigned int inside_frame:1; /* next write will be more of frame */ + unsigned int clean_buffer:1; /* buffer not rewritten by extension */ + unsigned int payload_is_close:1; /* process as PONG, but it is close */ + unsigned int ping_pending_flag:1; + unsigned int continuation_possible:1; + unsigned int owed_a_fin:1; + unsigned int check_utf8:1; + unsigned int defeat_check_utf8:1; + unsigned int pmce_compressed_message:1; + unsigned int stashed_write_pending:1; + unsigned int rx_draining_ext:1; + unsigned int tx_draining_ext:1; + unsigned int send_check_ping:1; +}; + +#ifdef LWS_WITH_CGI + +/* wsi who is master of the cgi points to an lws_cgi */ + +struct lws_cgi { + struct lws_cgi *cgi_list; + struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */ + struct lws *wsi; /* owner */ + unsigned long content_length; + unsigned long content_length_seen; + int pipe_fds[3][2]; + int pid; + + unsigned int being_closed:1; +}; +#endif + +signed char char_to_hex(const char c); + +#ifndef LWS_NO_CLIENT +enum lws_chunk_parser { + ELCP_HEX, + ELCP_CR, + ELCP_CONTENT, + ELCP_POST_CR, + ELCP_POST_LF, +}; +#endif + +struct lws_rewrite; + +#ifdef LWS_WITH_ACCESS_LOG +struct lws_access_log { + char *header_log; + char *user_agent; + unsigned long sent; + int response; +}; +#endif + +struct lws { + + /* structs */ + /* members with mutually exclusive lifetimes are unionized */ + + union u { + struct _lws_http_mode_related http; +#ifdef LWS_USE_HTTP2 + struct _lws_http2_related http2; +#endif + struct _lws_header_related hdr; + struct _lws_websocket_related ws; + } u; + + /* lifetime members */ + +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + struct lws_io_watcher w_read; +#endif +#if defined(LWS_USE_LIBEV) + struct lws_io_watcher w_write; +#endif + time_t pending_timeout_limit; + + /* pointers */ + + struct lws_context *context; + struct lws_vhost *vhost; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi; /* wsi being cgi master have one of these */ +#endif + const struct lws_protocols *protocol; + struct lws **same_vh_protocol_prev, *same_vh_protocol_next; + struct lws *timeout_list; + struct lws **timeout_list_prev; +#ifdef LWS_WITH_ACCESS_LOG + struct lws_access_log access_log; +#endif + void *user_space; + /* rxflow handling */ + unsigned char *rxflow_buffer; + /* truncated send handling */ + unsigned char *trunc_alloc; /* non-NULL means buffering in progress */ + +#if defined (LWS_WITH_ESP8266) + void *premature_rx; + unsigned short prem_rx_size, prem_rx_pos; +#endif + +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE]; + void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE]; +#endif +#ifdef LWS_OPENSSL_SUPPORT + SSL *ssl; +#if !defined(LWS_USE_POLARSSL) && !defined(LWS_USE_MBEDTLS) + BIO *client_bio; +#endif + struct lws *pending_read_list_prev, *pending_read_list_next; +#endif +#ifdef LWS_WITH_HTTP_PROXY + struct lws_rewrite *rw; +#endif +#ifdef LWS_LATENCY + unsigned long action_start; + unsigned long latency_start; +#endif + /* pointer / int */ + lws_sockfd_type sock; + + /* ints */ + int position_in_fds_table; + int rxflow_len; + int rxflow_pos; + unsigned int trunc_alloc_len; /* size of malloc */ + unsigned int trunc_offset; /* where we are in terms of spilling */ + unsigned int trunc_len; /* how much is buffered */ +#ifndef LWS_NO_CLIENT + int chunk_remaining; +#endif + unsigned int cache_secs; + + unsigned int hdr_parsing_completed:1; + unsigned int http2_substream:1; + unsigned int listener:1; + unsigned int user_space_externally_allocated:1; + unsigned int socket_is_permanently_unusable:1; + unsigned int rxflow_change_to:2; + unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */ + unsigned int conn_stat_done:1; + unsigned int cache_reuse:1; + unsigned int cache_revalidate:1; + unsigned int cache_intermediaries:1; + unsigned int favoured_pollin:1; + unsigned int sending_chunked:1; + unsigned int already_did_cce:1; + unsigned int told_user_closed:1; +#if defined(LWS_WITH_ESP8266) + unsigned int pending_send_completion:3; + unsigned int close_is_pending_send_completion:1; +#endif +#ifdef LWS_WITH_ACCESS_LOG + unsigned int access_log_pending:1; +#endif +#ifndef LWS_NO_CLIENT + unsigned int do_ws:1; /* whether we are doing http or ws flow */ + unsigned int chunked:1; /* if the clientside connection is chunked */ + unsigned int client_rx_avail:1; + unsigned int client_http_body_pending:1; +#endif +#ifdef LWS_WITH_HTTP_PROXY + unsigned int perform_rewrite:1; +#endif +#ifndef LWS_NO_EXTENSIONS + unsigned int extension_data_pending:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int use_ssl:3; + unsigned int upgraded:1; +#endif +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int redirect_to_https:1; +#endif + + /* chars */ +#ifndef LWS_NO_EXTENSIONS + unsigned char count_act_ext; +#endif + unsigned char ietf_spec_revision; + char mode; /* enum connection_mode */ + char state; /* enum lws_connection_states */ + char state_pre_close; + char lws_rx_parse_state; /* enum lws_rx_parse_state */ + char rx_frame_type; /* enum lws_write_protocol */ + char pending_timeout; /* enum pending_timeout */ + char pps; /* enum lws_pending_protocol_send */ + char tsi; /* thread service index we belong to */ + char protocol_interpret_idx; +#ifdef LWS_WITH_CGI + char cgi_channel; /* which of stdin/out/err */ + char hdr_state; +#endif +#ifndef LWS_NO_CLIENT + char chunk_parser; /* enum lws_chunk_parser */ +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +}; + +LWS_EXTERN int log_level; + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface); + +LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status); + +LWS_EXTERN int +remove_wsi_socket_from_fds(struct lws *wsi); +LWS_EXTERN int +lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); + +#ifndef LWS_LATENCY +static inline void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion) { + do { + (void)context; (void)wsi; (void)action; (void)ret; + (void)completion; + } while (0); +} +static inline void +lws_latency_pre(struct lws_context *context, struct lws *wsi) { + do { (void)context; (void)wsi; } while (0); +} +#else +#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0) +extern void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion); +#endif + +LWS_EXTERN void +lws_set_protocol_write_pending(struct lws *wsi, + enum lws_pending_protocol_send pend); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_action(struct lws *wsi); + +LWS_EXTERN int +lws_b64_selftest(void); + +LWS_EXTERN int +lws_service_flag_pending(struct lws_context *context, int tsi); + +#if defined(_WIN32) || defined(MBED_OPERATORS) || defined(LWS_WITH_ESP8266) +LWS_EXTERN struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd); + +LWS_EXTERN int +insert_wsi(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd); +#else +#define wsi_from_fd(A,B) A->lws_lookup[B] +#define insert_wsi(A,B) assert(A->lws_lookup[B->sock] == 0); A->lws_lookup[B->sock]=B +#define delete_from_fd(A,B) A->lws_lookup[B]=0 +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); + + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_service_timeout_check(struct lws *wsi, unsigned int sec); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_2(struct lws *wsi); + +LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT +lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, + const char *path, const char *host); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_create_new_server_wsi(struct lws_vhost *vhost); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_generate_client_handshake(struct lws *wsi, char *pkt); + +LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +LWS_EXTERN struct lws * +lws_client_connect_via_info2(struct lws *wsi); + +/* + * EXTENSIONS + */ + +#ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct lws_context *context); +LWS_EXTERN int +lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r, + void *v, size_t len); + +LWS_EXTERN int +lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len); +LWS_EXTERN int +lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason, + void *arg, int len); + +#else +#define lws_any_extension_handled(_a, _b, _c, _d) (0) +#define lws_ext_cb_active(_a, _b, _c, _d) (0) +#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_interpret_server_handshake(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_union_transition(struct lws *wsi, enum connection_mode mode); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len); +#ifdef LWS_USE_HTTP2 +LWS_EXTERN struct lws *lws_http2_get_network_wsi(struct lws *wsi); +struct lws * lws_http2_get_nth_child(struct lws *wsi, int n); +LWS_EXTERN int +lws_http2_interpret_settings_payload(struct http2_settings *settings, + unsigned char *buf, int len); +LWS_EXTERN void lws_http2_init(struct http2_settings *settings); +LWS_EXTERN int +lws_http2_parser(struct lws *wsi, unsigned char c); +LWS_EXTERN int lws_http2_do_pps_send(struct lws_context *context, + struct lws *wsi); +LWS_EXTERN int lws_http2_frame_write(struct lws *wsi, int type, int flags, + unsigned int sid, unsigned int len, + unsigned char *buf); +LWS_EXTERN struct lws * +lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid); +LWS_EXTERN int lws_hpack_interpret(struct lws *wsi, + unsigned char c); +LWS_EXTERN int +lws_add_http2_header_by_name(struct lws *wsi, + const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +LWS_EXTERN int +lws_add_http2_header_by_token(struct lws *wsi, + enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +LWS_EXTERN int +lws_add_http2_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +LWS_EXTERN +void lws_http2_configure_if_upgraded(struct lws *wsi); +#else +#define lws_http2_configure_if_upgraded(x) +#endif + +LWS_EXTERN int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd); + +LWS_EXTERN int +lws_plat_check_connection_error(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice); + +LWS_EXTERN int +lws_header_table_detach(struct lws *wsi, int autoservice); + +LWS_EXTERN void +lws_header_table_reset(struct lws *wsi, int autoservice); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ensure_user_space(struct lws *wsi); + +LWS_EXTERN int +lws_change_pollfd(struct lws *wsi, int _and, int _or); + +#ifndef LWS_NO_SERVER +int lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +LWS_EXTERN struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername); +LWS_EXTERN int +handshake_0405(struct lws_context *context, struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info); +#else +#define lws_context_init_server(_a, _b) (0) +#define lws_interpret_incoming_packet(_a, _b, _c) (0) +#define lws_server_get_canonical_hostname(_a, _b) +#endif + +#ifndef LWS_NO_DAEMONIZE +LWS_EXTERN int get_daemonize_pid(); +#else +#define get_daemonize_pid() (0) +#endif + +#if !defined(MBED_OPERATORS) && !defined(LWS_WITH_ESP8266) +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen); +#endif +LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); + +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, + LWS_SSL_CAPABLE_MORE_SERVICE = -2, +}; + +#ifndef LWS_OPENSSL_SUPPORT +#define LWS_SSL_ENABLED(context) (0) +#define lws_context_init_server_ssl(_a, _b) (0) +#define lws_ssl_destroy(_a) +#define lws_context_init_http2_ssl(_a) +#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl +#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl +#define lws_ssl_pending lws_ssl_pending_no_ssl +#define lws_server_socket_service_ssl(_b, _c) (0) +#define lws_ssl_close(_a) (0) +#define lws_ssl_context_destroy(_a) +#define lws_ssl_SSL_CTX_destroy(_a) +#define lws_ssl_remove_wsi_from_buffered_list(_a) +#define lws_context_init_ssl_library(_a) +#else +#define LWS_SSL_ENABLED(context) (context->use_ssl) +LWS_EXTERN int openssl_websocket_private_data_index; +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending(struct lws *wsi); +LWS_EXTERN int +lws_context_init_ssl_library(struct lws_context_creation_info *info); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN int +lws_ssl_close(struct lws *wsi); +LWS_EXTERN void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); +LWS_EXTERN void +lws_ssl_context_destroy(struct lws_context *context); +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_bio_create(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect1(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect2(struct lws *wsi); +LWS_EXTERN void +lws_ssl_elaborate_error(void); +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +#else +#define lws_context_init_server_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_ssl_destroy(struct lws_vhost *vhost); + +/* HTTP2-related */ + +#ifdef LWS_USE_HTTP2 +LWS_EXTERN void +lws_context_init_http2_ssl(struct lws_vhost *vhost); +#else +#define lws_context_init_http2_ssl(_a) +#endif +#endif + +#if LWS_MAX_SMP > 1 +static LWS_INLINE void +lws_pt_mutex_init(struct lws_context_per_thread *pt) +{ + pthread_mutex_init(&pt->lock, NULL); +} + +static LWS_INLINE void +lws_pt_mutex_destroy(struct lws_context_per_thread *pt) +{ + pthread_mutex_destroy(&pt->lock); +} + +static LWS_INLINE void +lws_pt_lock(struct lws_context_per_thread *pt) +{ + if (!pt->lock_depth++) + pthread_mutex_lock(&pt->lock); +} + +static LWS_INLINE void +lws_pt_unlock(struct lws_context_per_thread *pt) +{ + if (!(--pt->lock_depth)) + pthread_mutex_unlock(&pt->lock); +} +#else +#define lws_pt_mutex_init(_a) (void)(_a) +#define lws_pt_mutex_destroy(_a) (void)(_a) +#define lws_pt_lock(_a) (void)(_a) +#define lws_pt_unlock(_a) (void)(_a) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending_no_ssl(struct lws *wsi); + +#ifdef LWS_WITH_HTTP_PROXY +struct lws_rewrite { + hubbub_parser *parser; + hubbub_parser_optparams params; + const char *from, *to; + int from_len, to_len; + unsigned char *p, *end; + struct lws *wsi; +}; +static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len) +{ + if (s->len != len) + return 1; + + return strncmp((const char *)s->ptr, p, len); +} +typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw); +LWS_EXTERN struct lws_rewrite * +lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to); +LWS_EXTERN void +lws_rewrite_destroy(struct lws_rewrite *r); +LWS_EXTERN int +lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len); +#endif + +#ifndef LWS_NO_CLIENT +LWS_EXTERN int lws_client_socket_service(struct lws_context *context, + struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi); +#ifdef LWS_OPENSSL_SUPPORT +LWS_EXTERN int +lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +#else + #define lws_context_init_client_ssl(_a, _b) (0) +#endif +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#define lws_handshake_client(_a, _b, _c) (0) +#endif + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi); + +LWS_EXTERN int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); + +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN int +_lws_server_listen_accept_flow_control(struct lws *twsi, int on); +#else +#define lws_server_socket_service(_a, _b, _c) (0) +#define lws_handshake_server(_a, _b, _c) (0) +#define _lws_server_listen_accept_flow_control(a, b) (0) +#endif + +#ifdef LWS_WITH_ACCESS_LOG +LWS_EXTERN int +lws_access_log(struct lws *wsi); +#else +#define lws_access_log(_a) +#endif + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt); + +int +lws_protocol_init(struct lws_context *context); + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p); + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len); + +/* + * custom allocator + */ +LWS_EXTERN void * +lws_realloc(void *ptr, size_t size); + +LWS_EXTERN void * LWS_WARN_UNUSED_RESULT +lws_zalloc(size_t size); + +#define lws_malloc(S) lws_realloc(NULL, S) +#define lws_free(P) lws_realloc(P, 0) +#define lws_free_set_NULL(P) do { lws_realloc(P, 0); (P) = NULL; } while(0) + +/* lws_plat_ */ +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct lws_context *context, + struct lws *wsi); +LWS_EXTERN void +lws_plat_service_periodic(struct lws_context *context); + +LWS_EXTERN int +lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pfd); +LWS_EXTERN int +lws_plat_context_early_init(void); +LWS_EXTERN void +lws_plat_context_early_destroy(struct lws_context *context); +LWS_EXTERN void +lws_plat_context_late_destroy(struct lws_context *context); +LWS_EXTERN int +lws_poll_listen_fd(struct lws_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct lws_context *context, int timeout_ms); +LWS_EXTERN LWS_VISIBLE int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); +LWS_EXTERN int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info); +LWS_EXTERN void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info); +LWS_EXTERN unsigned long long +time_in_microseconds(void); +LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); + +LWS_EXTERN void a2w_logfr_and_reboot( char * Buf ); +#ifdef __cplusplus +}; +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/libwebsockets.c b/feeds/wlan-ap/libwebsocket/src/libwebsockets.c new file mode 100755 index 00000000..c8c6535e --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/libwebsockets.c @@ -0,0 +1,2668 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" +#include "websocket/libwcf_debug.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +#if defined(WIN32) || defined(_WIN32) +#else +#include +#endif + +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr; + +static const char * const log_level_names[] = { + "ERR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "PARSER", + "HEADER", + "EXTENSION", + "CLIENT", + "LATENCY", +}; + +void a2w_logfr_and_reboot( char * Buf ) +{ + char buf[200]; + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf)-1, "websocket requested reboot (%s)", Buf); + wc_put_logline( (WRITE_TO_FR | APP_LOG_WEBSOCK | DEBUG_LEVEL_1), "%s", buf ); + sleep(1); + system( "/splashbin/reboot" ); +} + +void +lws_free_wsi(struct lws *wsi) +{ + if (!wsi) + return; + + /* Protocol user data may be allocated either internally by lws + * or by specified the user. + * We should only free what we allocated. */ + if (wsi->protocol && wsi->protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) + lws_free(wsi->user_space); + + lws_free_set_NULL(wsi->rxflow_buffer); + lws_free_set_NULL(wsi->trunc_alloc); + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("ah det due to close\n"); + lws_header_table_detach(wsi, 0); + + wsi->context->count_wsi_allocated--; + lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, + wsi->context->count_wsi_allocated); + + lws_free(wsi); +} + +static void +lws_remove_from_timeout_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!wsi->timeout_list_prev) /* ie, not part of the list */ + return; + + lws_pt_lock(pt); + /* if we have a next guy, set his prev to our prev */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev; + /* set our prev guy to our next guy instead of us */ + *wsi->timeout_list_prev = wsi->timeout_list; + + /* we're out of the list, we should not point anywhere any more */ + wsi->timeout_list_prev = NULL; + wsi->timeout_list = NULL; + lws_pt_unlock(pt); +} + +LWS_VISIBLE void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + time_t now; + + lws_pt_lock(pt); + + time(&now); + + if (reason && !wsi->timeout_list_prev) { + /* our next guy is current first guy */ + wsi->timeout_list = pt->timeout_list; + /* if there is a next guy, set his prev ptr to our next ptr */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = &wsi->timeout_list; + /* our prev ptr is first ptr */ + wsi->timeout_list_prev = &pt->timeout_list; + /* set the first guy to be us */ + *wsi->timeout_list_prev = wsi; + } + + lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs); + wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout = reason; + + lws_pt_unlock(pt); + + if (!reason) + lws_remove_from_timeout_list(wsi); +} + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) +{ + struct lws_context_per_thread *pt; + struct lws **pwsi, *wsi1, *wsi2; + struct lws_context *context; + struct lws_tokens eff_buf; + int n, m, ret; + + if (!wsi) + return; + + lws_access_log(wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->premature_rx) + lws_free(wsi->premature_rx); + + if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) { + lwsl_notice("delaying close\n"); + wsi->close_is_pending_send_completion = 1; + return; + } +#endif + + if (wsi->u.hdr.ah) + /* we're closing, losing some rx is OK */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + //lwsl_notice("%s: closing %p: close child %p\n", + // __func__, wsi, wsi2); + wsi1 = wsi2->sibling_list; + //lwsl_notice("%s: closing %p: next sibling %p\n", + // __func__, wsi2, wsi1); + wsi2->parent = NULL; + /* stop it doing shutdown processing */ + wsi2->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi2, reason); + wsi2 = wsi1; + } + wsi->child_list = NULL; + } + +#ifdef LWS_WITH_CGI + if (wsi->mode == LWSCM_CGI) { + /* we are not a network connection, but a handler for CGI io */ + if (wsi->parent && wsi->parent->cgi) + /* end the binding between us and master */ + wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; + wsi->socket_is_permanently_unusable = 1; + + lwsl_debug("------ %s: detected cgi fdhandler wsi %p\n", __func__, wsi); + goto just_kill_connection; + } + + if (wsi->cgi) { + struct lws_cgi **pcgi = &pt->cgi_list; + /* remove us from the cgi list */ + lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->cgi); + while (*pcgi) { + if (*pcgi == wsi->cgi) { + /* drop us from the pt cgi list */ + *pcgi = (*pcgi)->cgi_list; + break; + } + pcgi = &(*pcgi)->cgi_list; + } + /* we have a cgi going, we must kill it */ + wsi->cgi->being_closed = 1; + lws_cgi_kill(wsi); + } +#endif + + if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED && + wsi->u.http.fd != LWS_INVALID_FILE) { + lws_plat_file_close(wsi, wsi->u.http.fd); + wsi->u.http.fd = LWS_INVALID_FILE; + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->socket_is_permanently_unusable || + reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || + wsi->state == LWSS_SHUTDOWN) + goto just_kill_connection; + + wsi->state_pre_close = wsi->state; + + switch (wsi->state_pre_close) { + case LWSS_DEAD_SOCKET: + return; + + /* we tried the polite way... */ + case LWSS_AWAITING_CLOSE_ACK: + goto just_kill_connection; + + case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE: + if (wsi->trunc_len) { + lws_callback_on_writable(wsi); + return; + } + lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + goto just_kill_connection; + default: + if (wsi->trunc_len) { + lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); + return; + } + break; + } + + if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) + goto just_kill_connection; + + if (wsi->mode == LWSCM_HTTP_SERVING) { + if (wsi->user_space) + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) { + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + /* + * are his extensions okay with him closing? Eg he might be a mux + * parent and just his ch1 aspect is closing? + */ + + if (lws_ext_cb_active(wsi, + LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { + lwsl_ext("extension vetoed close\n"); + return; + } + + /* + * flush any tx pending from extensions, since we may send close packet + * if there are problems with send, just nuke the connection + */ + + do { + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* show every extension the new incoming data */ + + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0); + if (m < 0) { + lwsl_ext("Extension reports fatal error\n"); + goto just_kill_connection; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) + if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len) != + eff_buf.token_len) { + lwsl_debug("close: ext spill failed\n"); + goto just_kill_connection; + } + } while (ret); + + /* + * signal we are closing, lws_write will + * add any necessary version-specific stuff. If the write fails, + * no worries we are closing anyway. If we didn't initiate this + * close, then our state has been changed to + * LWSS_RETURNED_CLOSE_ALREADY and we will skip this. + * + * Likewise if it's a second call to close this connection after we + * sent the close indication to the peer already, we are in state + * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time. + */ + + if (wsi->state_pre_close == LWSS_ESTABLISHED && + (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */ + (reason != LWS_CLOSE_STATUS_NOSTATUS && + (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) { + lwsl_debug("sending close indication...\n"); + + /* if no prepared close reason, use 1000 and no aux data */ + if (!wsi->u.ws.close_in_ping_buffer_len) { + wsi->u.ws.close_in_ping_buffer_len = 2; + wsi->u.ws.ping_payload_buf[LWS_PRE] = + (reason >> 16) & 0xff; + wsi->u.ws.ping_payload_buf[LWS_PRE + 1] = + reason & 0xff; + } + +#if defined (LWS_WITH_ESP8266) + wsi->close_is_pending_send_completion = 1; +#endif + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.close_in_ping_buffer_len, + LWS_WRITE_CLOSE); + if (n >= 0) { + /* + * we have sent a nice protocol level indication we + * now wish to close, we should not send anything more + */ + wsi->state = LWSS_AWAITING_CLOSE_ACK; + + /* + * ...and we should wait for a reply for a bit + * out of politeness + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1); + lwsl_debug("sent close indication, awaiting ack\n"); + + return; + } + + lwsl_info("close: sending close packet failed, hanging up\n"); + + /* else, the send failed and we should just hang up */ + } + +just_kill_connection: + + if (wsi->parent) { + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + //lwsl_notice("%s: detach %p from parent %p\n", + // __func__, wsi, wsi->parent); + *pwsi = wsi->sibling_list; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (*pwsi) + lwsl_err("%s: failed to detach from parent\n", + __func__); + } + +#if LWS_POSIX + /* + * Testing with ab shows that we have to stage the socket close when + * the system is under stress... shutdown any further TX, change the + * state to one that won't emit anything more, and wait with a timeout + * for the POLLIN to show a zero-size rx before coming back and doing + * the actual close. + */ + if (wsi->state != LWSS_SHUTDOWN && + wsi->state != LWSS_CLIENT_UNCONNECTED && + reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && + !wsi->socket_is_permanently_unusable) { + lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, wsi->sock, wsi->state); + n = shutdown(wsi->sock, SHUT_WR); + if (n) + lwsl_debug("closing: shutdown (state %d) ret %d\n", wsi->state, LWS_ERRNO); + +// This causes problems with disconnection when the events are half closing connection +// FD_READ | FD_CLOSE (33) +#ifndef _WIN32_WCE + /* libuv: no event available to guarantee completion */ + if (!LWS_LIBUV_ENABLED(context)) { + + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + wsi->state = LWSS_SHUTDOWN; + lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, + context->timeout_secs); + return; + } +#endif + } +#endif + + lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, + wsi, wsi->sock); + +#ifdef LWS_WITH_HTTP_PROXY + if (wsi->rw) { + lws_rewrite_destroy(wsi->rw); + wsi->rw = NULL; + } +#endif + /* + * we won't be servicing or receiving anything further from this guy + * delete socket from the internal poll list if still present + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + + lws_remove_from_timeout_list(wsi); + + /* checking return redundant since we anyway close */ + if (wsi->sock != LWS_SOCK_INVALID) + remove_wsi_socket_from_fds(wsi); + +#if defined(LWS_WITH_ESP8266) + espconn_disconnect(wsi->sock); +#endif + + wsi->state = LWSS_DEAD_SOCKET; + + lws_free_set_NULL(wsi->rxflow_buffer); + + if (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->mode == LWSCM_WS_SERVING || + wsi->mode == LWSCM_WS_CLIENT) { + + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + } + + if (wsi->u.ws.tx_draining_ext) { + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + } + lws_free_set_NULL(wsi->u.ws.rx_ubuf); + + if (wsi->trunc_alloc) + /* not going to be completed... nuke it */ + lws_free_set_NULL(wsi->trunc_alloc); + + wsi->u.ws.ping_payload_len = 0; + wsi->u.ws.ping_pending_flag = 0; + } + + /* tell the user it's all over for this guy */ + + if (wsi->protocol && wsi->protocol->callback && + ((wsi->state_pre_close == LWSS_ESTABLISHED) || + (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) || + (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) || + (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) || + (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) || + (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) { + + if (wsi->user_space) { + lwsl_debug("%s: doing LWS_CALLBACK_HTTP_DROP_PROTOCOL for %p prot %s", __func__, wsi, wsi->protocol->name); + wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + } + lwsl_debug("calling back CLOSED\n"); + wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED, + wsi->user_space, NULL, 0); + } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_debug("calling back CLOSED_HTTP\n"); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0 ); + } else if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) && + !wsi->already_did_cce) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, NULL, 0); + } else + lwsl_debug("not calling back closed mode=%d state=%d\n", + wsi->mode, wsi->state_pre_close); + + /* deallocate any active extension contexts */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) + lwsl_warn("extension destruction failed\n"); + /* + * inform all extensions in case they tracked this guy out of band + * even though not active on him specifically + */ + if (lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0) + lwsl_warn("ext destroy wsi failed\n"); + + wsi->socket_is_permanently_unusable = 1; + +#ifdef LWS_USE_LIBUV + if (LWS_LIBUV_ENABLED(context)) { + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + /* libuv has to do his own close handle processing asynchronously */ + lws_libuv_closehandle(wsi); + + return; + } +#endif + + lws_close_free_wsi_final(wsi); +} + +void +lws_close_free_wsi_final(struct lws *wsi) +{ + int n; + + if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) { +#if LWS_POSIX + //lwsl_err("*** closing sockfd %d\n", wsi->sock); + n = compatible_close(wsi->sock); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); + +#else + compatible_close(wsi->sock); + (void)n; +#endif + wsi->sock = LWS_SOCK_INVALID; + } + + /* outermost destroy notification for wsi (user_space still intact) */ + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->user_space, NULL, 0); + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + for (n = 0; n < 6; n++) + if (wsi->cgi->pipe_fds[n / 2][n & 1] >= 0) + close(wsi->cgi->pipe_fds[n / 2][n & 1]); + + lws_free(wsi->cgi); + } +#endif + + lws_free_wsi(wsi); +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, sl = strlen(name); + + while (lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { + + if (!strncmp(buf, name, sl)) + return buf + sl; + + n++; + } + + return NULL; +} + +#if LWS_POSIX +LWS_VISIBLE int +interface_to_sa(struct lws_vhost *vh, const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ + int ipv6 = 0; +#ifdef LWS_USE_IPV6 + ipv6 = LWS_IPV6_ENABLED(vh); +#endif + (void)vh; + + return lws_interface_to_sa(ipv6, ifname, addr, addrlen); +} +#endif + +#if LWS_POSIX +static int +lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + struct addrinfo ai, *res; + struct sockaddr_in addr4; + + if (rip) + rip[0] = '\0'; + name[0] = '\0'; + addr4.sin_family = AF_UNSPEC; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(vh)) { + if (!lws_plat_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ads)->sin6_addr, rip, rip_len)) { + lwsl_err("inet_ntop", strerror(LWS_ERRNO)); + return -1; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in6), name, + name_len, NULL, 0, 0); + + return 0; + } else +#endif + { + struct addrinfo *result; + + memset(&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_CANONNAME; + + if (getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in), + name, name_len, NULL, 0, 0)) + return -1; + + if (!rip) + return 0; + + if (getaddrinfo(name, NULL, &ai, &result)) + return -1; + + res = result; + while (addr4.sin_family == AF_UNSPEC && res) { + switch (res->ai_family) { + case AF_INET: + addr4.sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; + addr4.sin_family = AF_INET; + break; + } + + res = res->ai_next; + } + freeaddrinfo(result); + } + + if (addr4.sin_family == AF_UNSPEC) + return -1; + + if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) + return -1; + + return 0; +#else + (void)vh; + (void)ads; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + + return -1; +#endif +} +#endif + +LWS_VISIBLE const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ +#if LWS_POSIX + socklen_t len, olen; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + int af = AF_INET; + void *p, *q; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + af = AF_INET6; + q = &sin6.sin6_addr; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + q = &sin4.sin_addr; + } + + olen = len; + if (getpeername(wsi->sock, p, &len) < 0 || len > olen) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + return NULL; + } + + return lws_plat_inet_ntop(af, q, name, namelen); +#else +#if defined(LWS_WITH_ESP8266) + return lws_plat_get_peer_simple(wsi, name, namelen); +#else + return NULL; +#endif +#endif +} + +LWS_VISIBLE int +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + socklen_t len; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + struct lws_context *context = wsi->context; + int ret = -1; + void *p; + + rip[0] = '\0'; + name[0] = '\0'; + + lws_latency_pre(context, wsi); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); + +bail: + lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); + return ret; +#else + (void)wsi; + (void)fd; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; +#endif +} + +LWS_EXTERN void * +lws_context_user(struct lws_context *context) +{ + return context->user_space; +} + +LWS_VISIBLE struct lws_vhost * +lws_vhost_get(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context *context = vh->context; + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost == vh && wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) +{ + int n; + + for (n = 0; n < wsi->vhost->count_protocols; n++) + if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) + return 1; + + return 0; +} + +/** + * lws_now_secs() - seconds since 1970-1-1 + * + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + + +#if LWS_POSIX + +LWS_VISIBLE int +lws_get_socket_fd(struct lws *wsi) +{ + return wsi->sock; +} + +#endif + +#ifdef LWS_LATENCY +void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completed) +{ + unsigned long long u; + char buf[256]; + + u = time_in_microseconds(); + + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; + } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); + + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif + +LWS_VISIBLE int +lws_rx_flow_control(struct lws *wsi, int enable) +{ + if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) + return 0; + + lwsl_info("%s: (0x%p, %d)\n", __func__, wsi, enable); + wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable; + + return 0; +} + +LWS_VISIBLE void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + const struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + unsigned int n, m = context->count_threads; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); + } + pt++; + } +} + +LWS_VISIBLE extern const char * +lws_canonical_hostname(struct lws_context *context) +{ + return (const char *)context->canonical_hostname; +} + +int user_callback_handle_rxflow(lws_callback_function callback_function, + struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + + n = callback_function(wsi, reason, user, in, len); + if (!n) + n = _lws_rx_flow_control(wsi); + + return n; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +LWS_VISIBLE int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +{ +#if !defined(LWS_WITH_ESP8266) + char *p; + char authstring[96]; + + if (!proxy) + return -1; + + p = strchr(proxy, '@'); + if (p) { /* auth is around */ + + if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + strncpy(authstring, proxy, p - proxy); + // null termination not needed on input + if (lws_b64_encode_string(authstring, (p - proxy), + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) + goto auth_too_long; + + lwsl_info(" Proxy auth in use\n"); + + proxy = p + 1; + } else + vhost->proxy_basic_auth_token[0] = '\0'; + + strncpy(vhost->http_proxy_address, proxy, + sizeof(vhost->http_proxy_address) - 1); + vhost->http_proxy_address[ + sizeof(vhost->http_proxy_address) - 1] = '\0'; + + p = strchr(vhost->http_proxy_address, ':'); + if (!p && !vhost->http_proxy_port) { + lwsl_err("http_proxy needs to be ads:port\n"); + + return -1; + } else { + if (p) { + *p = '\0'; + vhost->http_proxy_port = atoi(p + 1); + } + } + + lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address, + vhost->http_proxy_port); + + return 0; + +auth_too_long: + lwsl_err("proxy auth too long\n"); +#endif + return -1; +} + +LWS_VISIBLE const struct lws_protocols * +lws_get_protocol(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_is_final_fragment(struct lws *wsi) +{ + lwsl_info("%s: final %d, rx pk length %d, draining %d", __func__, + wsi->u.ws.final, wsi->u.ws.rx_packet_length, + wsi->u.ws.rx_draining_ext); + return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext; +} + +LWS_VISIBLE unsigned char +lws_get_reserved_bits(struct lws *wsi) +{ + return wsi->u.ws.rsv; +} + +int +lws_ensure_user_space(struct lws *wsi) +{ + lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol); + if (!wsi->protocol) + return 1; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size); + if (wsi->user_space == NULL) { + lwsl_err("Out of memory for conn user space\n"); + return 1; + } + } else + lwsl_info("%s: %p protocol pss %u, user_space=%d\n", + __func__, wsi, wsi->protocol->per_session_data_size, + wsi->user_space); + return 0; +} + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) +{ + time_t o_now = time(NULL); + unsigned long long now; + struct tm *ptm = NULL; +#ifndef WIN32 + struct tm tm; +#endif + int n; + +#ifndef _WIN32_WCE +#ifdef WIN32 + ptm = localtime(&o_now); +#else + if (localtime_r(&o_now, &tm)) + ptm = &tm; +#endif +#endif + p[0] = '\0'; + for (n = 0; n < LLL_COUNT; n++) { + if (level != (1 << n)) + continue; + now = time_in_microseconds() / 100; + if (ptm) + n = lws_snprintf(p, len, + "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + (int)(now % 10000), log_level_names[n]); + else + n = lws_snprintf(p, len, "[%llu:%04d] %s: ", + (unsigned long long) now / 10000, + (int)(now % 10000), log_level_names[n]); + return n; + } + + return 0; +} + +LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) +{ +#if !defined(LWS_WITH_ESP8266) + char buf[50]; + + lwsl_timestamp(level, buf, sizeof(buf)); +#if 0 + fprintf(stderr, "%s%s", buf, line); +#else + wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_1), "%s%s", buf, line); +#endif +#endif +} + +LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) +{ +#if defined(LWS_WITH_ESP8266) + char buf[128]; +#else + char buf[256]; +#endif + unsigned int n; + + if (!(log_level & filter)) + return; + + n = vsnprintf(buf, sizeof(buf) - 1, format, vl); + (void)n; +#if defined(LWS_WITH_ESP8266) + buf[sizeof(buf) - 1] = '\0'; +#else + /* vnsprintf returns what it would have written, even if truncated */ + if (n > sizeof(buf) - 1) + n = sizeof(buf) - 1; + if (n > 0) + buf[n] = '\0'; +#endif + + lwsl_emit(filter, buf); +} + +LWS_VISIBLE void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _lws_logv(filter, format, ap); + va_end(ap); +} + +LWS_VISIBLE void lws_set_log_level(int level, + void (*func)(int level, const char *line)) +{ + log_level = level; + if (func) + lwsl_emit = func; +} + +LWS_VISIBLE int +lws_is_ssl(struct lws *wsi) +{ +#ifdef LWS_OPENSSL_SUPPORT + return wsi->use_ssl; +#else + (void)wsi; + return 0; +#endif +} + +LWS_VISIBLE int +lws_partial_buffered(struct lws *wsi) +{ + return !!wsi->trunc_len; +} + +void lws_set_protocol_write_pending(struct lws *wsi, + enum lws_pending_protocol_send pend) +{ + lwsl_info("setting pps %d\n", pend); + + if (wsi->pps) + lwsl_err("pps overwrite\n"); + wsi->pps = pend; + lws_rx_flow_control(wsi, 0); + lws_callback_on_writable(wsi); +} + +LWS_VISIBLE size_t +lws_get_peer_write_allowance(struct lws *wsi) +{ +#ifdef LWS_USE_HTTP2 + /* only if we are using HTTP2 on this connection */ + if (wsi->mode != LWSCM_HTTP2_SERVING) + return -1; + /* user is only interested in how much he can send, or that he can't */ + if (wsi->u.http2.tx_credit <= 0) + return 0; + + return wsi->u.http2.tx_credit; +#else + (void)wsi; + return -1; +#endif +} + +LWS_VISIBLE void +lws_union_transition(struct lws *wsi, enum connection_mode mode) +{ + lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode); + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->mode = mode; +} + +LWS_VISIBLE struct lws_plat_file_ops * +lws_get_fops(struct lws_context *context) +{ + return &context->fops; +} + +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_get_context(const struct lws *wsi) +{ + return wsi->context; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_count_threads(struct lws_context *context) +{ + return context->count_threads; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len) +{ + unsigned char *p, *start; + int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE; + + assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT); + + start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE]; + + *p++ = (((int)status) >> 8) & 0xff; + *p++ = ((int)status) & 0xff; + + if (buf) + while (len-- && p < start + budget) + *p++ = *buf++; + + wsi->u.ws.close_in_ping_buffer_len = p - start; +} + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi) +{ + /* there is no pending change */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) { + lwsl_debug("%s: no pending change\n", __func__); + return 0; + } + + /* stuff is still buffered, not ready to really accept new input */ + if (wsi->rxflow_buffer) { + /* get ourselves called back to deal with stashed buffer */ + lws_callback_on_writable(wsi); + return 0; + } + + /* pending is cleared, we can change rxflow state */ + + wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + +LWS_EXTERN int +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) +{ + static const unsigned char e0f4[] = { + 0xa0 | ((2 - 1) << 2) | 1, /* e0 */ + 0x80 | ((4 - 1) << 2) | 1, /* e1 */ + 0x80 | ((4 - 1) << 2) | 1, /* e2 */ + 0x80 | ((4 - 1) << 2) | 1, /* e3 */ + 0x80 | ((4 - 1) << 2) | 1, /* e4 */ + 0x80 | ((4 - 1) << 2) | 1, /* e5 */ + 0x80 | ((4 - 1) << 2) | 1, /* e6 */ + 0x80 | ((4 - 1) << 2) | 1, /* e7 */ + 0x80 | ((4 - 1) << 2) | 1, /* e8 */ + 0x80 | ((4 - 1) << 2) | 1, /* e9 */ + 0x80 | ((4 - 1) << 2) | 1, /* ea */ + 0x80 | ((4 - 1) << 2) | 1, /* eb */ + 0x80 | ((4 - 1) << 2) | 1, /* ec */ + 0x80 | ((2 - 1) << 2) | 1, /* ed */ + 0x80 | ((4 - 1) << 2) | 1, /* ee */ + 0x80 | ((4 - 1) << 2) | 1, /* ef */ + 0x90 | ((3 - 1) << 2) | 2, /* f0 */ + 0x80 | ((4 - 1) << 2) | 2, /* f1 */ + 0x80 | ((4 - 1) << 2) | 2, /* f2 */ + 0x80 | ((4 - 1) << 2) | 2, /* f3 */ + 0x80 | ((1 - 1) << 2) | 2, /* f4 */ + + 0, /* s0 */ + 0x80 | ((4 - 1) << 2) | 0, /* s2 */ + 0x80 | ((4 - 1) << 2) | 1, /* s3 */ + }; + unsigned char s = *state; + + while (len--) { + unsigned char c = *buf++; + + if (!s) { + if (c >= 0x80) { + if (c < 0xc2 || c > 0xf4) + return 1; + if (c < 0xe0) + s = 0x80 | ((4 - 1) << 2); + else + s = e0f4[c - 0xe0]; + } + } else { + if (c < (s & 0xf0) || + c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) + return 1; + s = e0f4[21 + (s & 3)]; + } + } + + *state = s; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path) +{ + const char *end; + static const char *slash = "/"; + + /* cut up the location into address, port and path */ + *prot = p; + while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) + p++; + if (!*p) { + end = p; + p = (char *)*prot; + *prot = end; + } else { + *p = '\0'; + p += 3; + } + *ads = p; + if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) + *port = 80; + else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) + *port = 443; + + while (*p && *p != ':' && *p != '/') + p++; + if (*p == ':') { + *p++ = '\0'; + *port = atoi(p); + while (*p && *p != '/') + p++; + } + *path = slash; + if (*p) { + *p++ = '\0'; + if (*p) + *path = p; + } + + return 0; +} + +#ifdef LWS_NO_EXTENSIONS + +/* we need to provide dummy callbacks for internal exts + * so user code runs when faced with a lib compiled with + * extensions disabled. + */ + +int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + (void)context; + (void)ext; + (void)wsi; + (void)reason; + (void)user; + (void)in; + (void)len; + + return 0; +} +#endif + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface) +{ +#if LWS_POSIX +#ifdef LWS_USE_UNIX_SOCK + struct sockaddr_un serv_unix; +#endif +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; + socklen_t len = sizeof(struct sockaddr); + int n; + struct sockaddr_in sin; + struct sockaddr *v; + +#ifdef LWS_USE_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_unix; + n = sizeof(struct sockaddr_un); + bzero((char *) &serv_unix, sizeof(serv_unix)); + serv_unix.sun_family = AF_UNIX; + if (sizeof(serv_unix.sun_path) <= strlen(iface)) { + lwsl_err("\"%s\" too long for UNIX domain socket\n", + iface); + return -1; + } + strcpy(serv_unix.sun_path, iface); + if (serv_unix.sun_path[0] == '@') + serv_unix.sun_path[0] = '\0'; + + } else +#endif +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } + + serv_addr4.sin_port = htons(port); + } /* ipv4 */ + + n = bind(sockfd, v, n); +#ifdef LWS_USE_UNIX_SOCK + if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { + lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", + sockfd, iface, n, LWS_ERRNO); + return -1; + } else +#endif + if (n < 0) { + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); + return -1; + } + + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else + port = ntohs(sin.sin_port); +#endif + + return port; +} + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi) +{ + if (!wsi->context->ws_ping_pong_interval) + return; + if (wsi->state != LWSS_ESTABLISHED) + return; + + wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() + + wsi->context->ws_ping_pong_interval; +} + +static const char *hex = "0123456789ABCDEF"; + +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 2) { + if (*p == '\'') { + *q++ = '\''; + *q++ = '\''; + len --; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + if (!p) { + escaped[0] = '\0'; + return escaped; + } + + while (*p && len-- > 6) { + if (*p == '\"' || *p == '\\' || *p < 0x20) { + *q++ = '\\'; + *q++ = 'u'; + *q++ = '0'; + *q++ = '0'; + *q++ = hex[((*p) >> 4) & 15]; + *q++ = hex[(*p) & 15]; + len -= 5; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 3) { + if (*p == ' ') { + *q++ = '+'; + p++; + continue; + } + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z')) { + *q++ = *p++; + continue; + } + *q++ = '%'; + *q++ = hex[(*p >> 4) & 0xf]; + *q++ = hex[*p & 0xf]; + + len -= 2; + p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len) +{ + int state = 0, n; + char sum = 0; + + while (*escaped && len) { + switch (state) { + case 0: + if (*escaped == '%') { + state++; + escaped++; + continue; + } + if (*escaped == '+') { + escaped++; + *string++ = ' '; + len--; + continue; + } + *string++ = *escaped++; + len--; + break; + case 1: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + sum = n << 4; + state++; + break; + + case 2: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + *string++ = sum | n; + len--; + state = 0; + break; + } + + } + *string = '\0'; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context) +{ + struct lws_context_creation_info info; + + info.uid = context->uid; + info.gid = context->gid; + + if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(&info); + + return 0; +} + +int +lws_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + unsigned int n; + + if (!size) + return 0; + + va_start(ap, format); + n = vsnprintf(str, size, format, ap); + va_end(ap); + + if (n >= size) + return size; + + return n; +} + + +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi) { +#ifdef LWS_WITH_CGI + return !!wsi->cgi; +#else + return 0; +#endif +} + +#ifdef LWS_WITH_CGI + +static int +urlencode(const char *in, int inlen, char *out, int outlen) +{ + char *start = out, *end = out + outlen; + + while (inlen-- && out < end - 4) { + if ((*in >= 'A' && *in <= 'Z') || + (*in >= 'a' && *in <= 'z') || + (*in >= '0' && *in <= '9') || + *in == '-' || + *in == '_' || + *in == '.' || + *in == '~') { + *out++ = *in++; + continue; + } + if (*in == ' ') { + *out++ = '+'; + in++; + continue; + } + *out++ = '%'; + *out++ = hex[(*in) >> 4]; + *out++ = hex[(*in++) & 15]; + } + *out = '\0'; + + if (out >= end - 4) + return -1; + + return out - start; +} + +static struct lws * +lws_create_basic_wsi(struct lws_context *context, int tsi) +{ + struct lws *new_wsi; + + if ((unsigned int)context->pt[tsi].fds_count == + context->fd_limit_per_thread - 1) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws)); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = tsi; + new_wsi->context = context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_CGI; + new_wsi->mode = LWSCM_CGI; + new_wsi->hdr_parsing_completed = 0; + new_wsi->position_in_fds_table = -1; + + /* + * these can only be set once the protocol is known + * we set an unestablished connection's protocol pointer + * to the start of the defauly vhost supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = context->vhost_list->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->sock = LWS_SOCK_INVALID; + context->count_wsi_allocated++; + + return new_wsi; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len, + int timeout_secs, const struct lws_protocol_vhost_options *mp_cgienv) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *env_array[30], cgi_path[400], e[1024], *p = e, + *end = p + sizeof(e) - 1, tok[256], *t; + struct lws_cgi *cgi; + int n, m, i, uritok = -1; + + /* + * give the master wsi a cgi struct + */ + + wsi->cgi = lws_zalloc(sizeof(*wsi->cgi)); + if (!wsi->cgi) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + cgi = wsi->cgi; + cgi->wsi = wsi; /* set cgi's owning wsi */ + + /* create pipes for [stdin|stdout] and [stderr] */ + + for (n = 0; n < 3; n++) + if (pipe(cgi->pipe_fds[n]) == -1) + goto bail1; + + /* create cgi wsis for each stdin/out/err fd */ + + for (n = 0; n < 3; n++) { + cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi); + if (!cgi->stdwsi[n]) + goto bail2; + cgi->stdwsi[n]->cgi_channel = n; + cgi->stdwsi[n]->vhost = wsi->vhost; + +// lwsl_err("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, wsi, n, +// cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]); + + /* read side is 0, stdin we want the write side, others read */ + cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)]; + if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) { + lwsl_err("%s: setting NONBLOCK failed\n", __func__); + goto bail2; + } + } + + for (n = 0; n < 3; n++) { + lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->sock); + if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n])) + goto bail3; + cgi->stdwsi[n]->parent = wsi; + cgi->stdwsi[n]->sibling_list = wsi->child_list; + wsi->child_list = cgi->stdwsi[n]; + } + + lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT); + lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN); + lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN); + + lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__, + cgi->stdwsi[LWS_STDIN]->sock, cgi->stdwsi[LWS_STDOUT]->sock, + cgi->stdwsi[LWS_STDERR]->sock); + + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs); + + /* the cgi stdout is always sending us http1.x header data first */ + wsi->hdr_state = LCHS_HEADER; + + /* add us to the pt list of active cgis */ + lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->cgi); + cgi->cgi_list = pt->cgi_list; + pt->cgi_list = cgi; + + /* prepare his CGI env */ + + n = 0; + + if (lws_is_ssl(wsi)) + env_array[n++] = "HTTPS=ON"; + if (wsi->u.hdr.ah) { + static const unsigned char meths[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + }; + static const char * const meth_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", + }; + + for (m = 0; m < ARRAY_SIZE(meths); m++) + if (lws_hdr_total_length(wsi, meths[m]) >= + script_uri_path_len) { + uritok = meths[m]; + break; + } + + if (uritok < 0) + goto bail3; + + lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s", + lws_hdr_simple_ptr(wsi, uritok)); + cgi_path[sizeof(cgi_path) - 1] = '\0'; + env_array[n++] = cgi_path; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s", + meth_names[m]); + p++; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "QUERY_STRING="); + /* dump the individual URI Arg parameters */ + m = 0; + while (1) { + i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok), + WSI_TOKEN_HTTP_URI_ARGS, m); + if (i < 0) + break; + t = tok; + while (*t && *t != '=' && p < end - 4) + *p++ = *t++; + if (*t == '=') + *p++ = *t++; + i = urlencode(t, i- (t - tok), p, end - p); + if (i > 0) { + p += i; + *p++ = '&'; + } + m++; + } + if (m) + p--; + *p++ = '\0'; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "PATH_INFO=%s", + lws_hdr_simple_ptr(wsi, uritok) + + script_uri_path_len); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_REFERER=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_HOST=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_COOKIE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "USER_AGENT=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT)); + p++; + } + if (uritok == WSI_TOKEN_POST_URI) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)); + p++; + } + } + env_array[n++] = p; + p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1; + + while (mp_cgienv) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name, + mp_cgienv->value); + lwsl_debug(" Applying mount-specific cgi env '%s'\n", + env_array[n - 1]); + p++; + mp_cgienv = mp_cgienv->next; + } + + env_array[n++] = "SERVER_SOFTWARE=libwebsockets"; + env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin"; + env_array[n] = NULL; + +#if 0 + for (m = 0; m < n; m++) + lwsl_err(" %s\n", env_array[m]); +#endif + + /* + * Actually having made the env, as a cgi we don't need the ah + * any more + */ + if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen) + lws_header_table_detach(wsi, 0); + + /* we are ready with the redirection pipes... run the thing */ +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + cgi->pid = fork(); +#else + cgi->pid = vfork(); +#endif + if (cgi->pid < 0) { + lwsl_err("fork failed, errno %d", errno); + goto bail3; + } + +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + setpgrp(); /* stops on-daemonized main processess getting SIGINT from TTY */ + + if (cgi->pid) { + /* we are the parent process */ + wsi->context->count_cgi_spawned++; + lwsl_debug("%s: cgi %p spawned PID %d\n", __func__, cgi, cgi->pid); + return 0; + } + + /* somewhere we can at least read things and enter it */ + if (chdir("/tmp")) + lwsl_notice("%s: Failed to chdir\n", __func__); + + /* We are the forked process, redirect and kill inherited things. + * + * Because of vfork(), we cannot do anything that changes pages in + * the parent environment. Stuff that changes kernel state for the + * process is OK. Stuff that happens after the execvpe() is OK. + */ + + for (n = 0; n < 3; n++) + if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) { + lwsl_err("%s: stdin dup2 failed\n", __func__); + goto bail3; + } + +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + for (m = 0; m < n; m++) { + p = strchr(env_array[m], '='); + *p++ = '\0'; + setenv(env_array[m], p, 1); + } + execvp(exec_array[0], (char * const *)&exec_array[0]); +#else + execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]); +#endif + + exit(1); + +bail3: + /* drop us from the pt cgi list */ + pt->cgi_list = cgi->cgi_list; + + while (--n >= 0) + remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]); +bail2: + for (n = 0; n < 3; n++) + if (wsi->cgi->stdwsi[n]) + lws_free_wsi(cgi->stdwsi[n]); + +bail1: + for (n = 0; n < 3; n++) { + if (cgi->pipe_fds[n][0]) + close(cgi->pipe_fds[n][0]); + if (cgi->pipe_fds[n][1]) + close(cgi->pipe_fds[n][1]); + } + + lws_free_set_NULL(wsi->cgi); + + lwsl_err("%s: failed\n", __func__); + + return -1; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi) +{ + int n, m, match = 0, lp = 0; + static const char * const content_length = "content-length: "; + char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, + *end = &buf[sizeof(buf) - 1 - LWS_PRE], c, l[12]; + + if (!wsi->cgi) + return -1; + + while (wsi->hdr_state != LHCS_PAYLOAD) { + /* we have to separate header / finalize and + * payload chunks, since they need to be + * handled separately + */ + n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1); + if (n < 0) { + if (errno != EAGAIN) { + lwsl_debug("%s: read says %d\n", __func__, n); + return -1; + } + else + n = 0; + } + if (n) { + lwsl_debug("-- 0x%02X %c\n", (unsigned char)c, c); + switch (wsi->hdr_state) { + case LCHS_HEADER: + if (!content_length[match] && + (c >= '0' && c <= '9') && + lp < sizeof(l) - 1) { + l[lp++] = c; + l[lp] = '\0'; + wsi->cgi->content_length = atol(l); + } + if (tolower(c) == content_length[match]) + match++; + else + match = 0; + + /* some cgi only send us \x0a for EOL */ + if (c == '\x0a') { + wsi->hdr_state = LCHS_SINGLE_0A; + *p++ = '\x0d'; + } + *p++ = c; + if (c == '\x0d') { + wsi->hdr_state = LCHS_LF1; + break; + } + + break; + case LCHS_LF1: + *p++ = c; + if (c == '\x0a') { + wsi->hdr_state = LCHS_CR2; + break; + } + /* we got \r[^\n]... it's unreasonable */ + lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, (unsigned char)c); + return -1; + + case LCHS_CR2: + if (c == '\x0d') { + /* drop the \x0d */ + wsi->hdr_state = LCHS_LF2; + break; + } + wsi->hdr_state = LCHS_HEADER; + match = 0; + *p++ = c; + break; + case LCHS_LF2: + case LCHS_SINGLE_0A: + m = wsi->hdr_state; + if (c == '\x0a') { + lwsl_debug("Content-Length: %ld\n", wsi->cgi->content_length); + wsi->hdr_state = LHCS_PAYLOAD; + /* drop the \0xa ... finalize will add it if needed */ + if (lws_finalize_http_header(wsi, + (unsigned char **)&p, + (unsigned char *)end)) + return -1; + break; + } + if (m == LCHS_LF2) + /* we got \r\n\r[^\n]... it's unreasonable */ + return -1; + /* we got \x0anext header, it's reasonable */ + *p++ = c; + wsi->hdr_state = LCHS_HEADER; + break; + case LHCS_PAYLOAD: + break; + } + } + + /* ran out of input, ended the headers, or filled up the headers buf */ + if (!n || wsi->hdr_state == LHCS_PAYLOAD || (p + 4) == end) { + + m = lws_write(wsi, (unsigned char *)start, + p - start, LWS_WRITE_HTTP_HEADERS); + if (m < 0) { + lwsl_debug("%s: write says %d\n", __func__, m); + return -1; + } + /* writeability becomes uncertain now we wrote + * something, we must return to the event loop + */ + + return 0; + } + } + + n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), + start, sizeof(buf) - LWS_PRE); + + if (n < 0 && errno != EAGAIN) { + lwsl_debug("%s: stdout read says %d\n", __func__, n); + return -1; + } + if (n > 0) { + m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP); + //lwsl_notice("write %d\n", m); + if (m < 0) { + lwsl_debug("%s: stdout write says %d\n", __func__, m); + return -1; + } + wsi->cgi->content_length_seen += m; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi) +{ + struct lws_cgi_args args; + int status, n; + + lwsl_debug("%s: %p\n", __func__, wsi); + + if (!wsi->cgi) + return 0; + + if (wsi->cgi->pid > 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) { + lwsl_debug("%s: PID %d reaped\n", __func__, + wsi->cgi->pid); + goto handled; + } + /* kill the process group */ + n = kill(-wsi->cgi->pid, SIGTERM); + lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n", __func__, + wsi->cgi->pid, n, errno); + if (n < 0) { + /* + * hum seen errno=3 when process is listed in ps, + * it seems we don't always retain process grouping + * + * Direct these fallback attempt to the exact child + */ + n = kill(wsi->cgi->pid, SIGTERM); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGPIPE); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGKILL); + if (n < 0) + lwsl_err("%s: SIGKILL PID %d failed errno %d (maybe zombie)\n", + __func__, wsi->cgi->pid, errno); + } + } + } + /* He could be unkillable because he's a zombie */ + n = 1; + while (n > 0) { + n = waitpid(-wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", __func__, n); + if (n <= 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", __func__, n); + } + } + } + +handled: + args.stdwsi = &wsi->cgi->stdwsi[0]; + + if (wsi->cgi->pid != -1 && user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_CGI_TERMINATED, + wsi->user_space, + (void *)&args, 0)) { + wsi->cgi->pid = -1; + if (!wsi->cgi->being_closed) + lws_close_free_wsi(wsi, 0); + } + + return 0; +} + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt) +{ + struct lws_cgi **pcgi, *cgi = NULL; + int status, n = 1; + + while (n > 0) { + /* find finished guys but don't reap yet */ + n = waitpid(-1, &status, WNOHANG | WNOWAIT); + if (n <= 0) + continue; + lwsl_debug("%s: observed PID %d terminated\n", __func__, n); + + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) { + lwsl_debug("%s: wsi %p: expected content length seen: %ld\n", + __func__, cgi->wsi, cgi->content_length_seen); + } + + /* reap it */ + waitpid(n, &status, WNOHANG); + /* + * he's already terminated so no need for kill() + * but we should do the terminated cgi callback + * and close him if he's not already closing + */ + if (n == cgi->pid) { + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, n); + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + cgi = NULL; + } + /* if not found on the cgi list, as he's one of ours, reap */ + if (!cgi) { + lwsl_debug("%s: reading PID %d although no cgi match\n", + __func__, n); + waitpid(n, &status, WNOHANG); + } + } + +/* disable this to confirm timeout cgi cleanup flow */ +#if 1 + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) { + lwsl_debug("%s: wsi %p: expected content length seen: %ld\n", + __func__, cgi->wsi, cgi->content_length_seen); + } + + /* reap it */ + if (waitpid(cgi->pid, &status, WNOHANG) > 0) { + + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, cgi->pid); + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + } +#endif + + /* general anti zombie defence */ + n = waitpid(-1, &status, WNOHANG); + //if (n > 0) + // lwsl_notice("%s: anti-zombie wait says %d\n", __func__, n); + + return 0; +} +#endif + +#ifdef LWS_NO_EXTENSIONS +LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + return -1; +} +#endif + +#ifdef LWS_WITH_ACCESS_LOG +int +lws_access_log(struct lws *wsi) +{ + char *p = wsi->access_log.user_agent, ass[512]; + int l; + + if (!wsi->access_log_pending) + return 0; + + if (!wsi->access_log.header_log) + return 0; + + if (!p) + p = ""; + + l = lws_snprintf(ass, sizeof(ass) - 1, "%s %d %lu %s\n", + wsi->access_log.header_log, + wsi->access_log.response, wsi->access_log.sent, p); + + if (wsi->vhost->log_fd != (int)LWS_INVALID_FILE) { + if (write(wsi->vhost->log_fd, ass, l) != l) + lwsl_err("Failed to write log\n"); + } else + lwsl_err("%s", ass); + + if (wsi->access_log.header_log) { + lws_free(wsi->access_log.header_log); + wsi->access_log.header_log = NULL; + } + if (wsi->access_log.user_agent) { + lws_free(wsi->access_log.user_agent); + wsi->access_log.user_agent = NULL; + } + wsi->access_log_pending = 0; + + return 0; +} +#endif + +#ifdef LWS_WITH_SERVER_STATUS + +LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) +{ + static const char * const prots[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" + }; + char *orig = buf, *end = buf + len - 1, first = 1; + int n = 0; + + if (len < 100) + return 0; + + buf += lws_snprintf(buf, end - buf, + "{\n \"name\":\"%s\",\n" + " \"port\":\"%d\",\n" + " \"use_ssl\":\"%d\",\n" + " \"sts\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"conn\":\"%lu\",\n" + " \"trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"http2_upg\":\"%lu\"" + , + vh->name, vh->listen_port, +#ifdef LWS_OPENSSL_SUPPORT + vh->use_ssl, +#else + 0, +#endif + !!(vh->options & LWS_SERVER_OPTION_STS), + vh->rx, vh->tx, vh->conn, vh->trans, vh->ws_upgrades, + vh->http2_upgrades + ); + + if (vh->mount_list) { + const struct lws_http_mount *m = vh->mount_list; + + buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); + while (m) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"mountpoint\":\"%s\",\n" + " \"origin\":\"%s%s\",\n" + " \"cache_max_age\":\"%d\",\n" + " \"cache_reuse\":\"%d\",\n" + " \"cache_revalidate\":\"%d\",\n" + " \"cache_intermediaries\":\"%d\"\n" + , + m->mountpoint, + prots[m->origin_protocol], + m->origin, + m->cache_max_age, + m->cache_reusable, + m->cache_revalidate, + m->cache_intermediaries); + if (m->def) + buf += lws_snprintf(buf, end - buf, + ",\n \"default\":\"%s\"", + m->def); + buf += lws_snprintf(buf, end - buf, "\n }"); + first = 0; + m = m->mount_next; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + if (vh->protocols) { + n = 0; + first = 1; + + buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); + while (n < vh->count_protocols) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"%s\":{\n" + " \"status\":\"ok\"\n }\n }" + , + vh->protocols[n].name); + first = 0; + n++; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + buf += lws_snprintf(buf, end - buf, "\n}"); + + return buf - orig; +} + + +LWS_EXTERN LWS_VISIBLE int +lws_json_dump_context(const struct lws_context *context, char *buf, int len) +{ + char *orig = buf, *end = buf + len - 1, first = 1; + const struct lws_vhost *vh = context->vhost_list; + +#ifdef LWS_WITH_CGI + struct lws_cgi * const *pcgi; +#endif + const struct lws_context_per_thread *pt; + time_t t = time(NULL); + int listening = 0, cgi_count = 0, n; + + buf += lws_snprintf(buf, end - buf, "{ " + "\"version\":\"%s\",\n" + "\"uptime\":\"%ld\",\n" + "\"cgi_spawned\":\"%d\",\n" + "\"pt_fd_max\":\"%d\",\n" + "\"ah_pool_max\":\"%d\",\n" + "\"wsi_alive\":\"%d\",\n", + lws_get_library_version(), + (unsigned long)(t - context->time_up), + context->count_cgi_spawned, + context->fd_limit_per_thread, + context->max_http_header_pool, + context->count_wsi_allocated); +#ifdef LWS_HAVE_GETLOADAVG + { + double d[3]; + int m; + + m = getloadavg(d, 3); + for (n = 0; n < m; n++) { + buf += lws_snprintf(buf, end - buf, + "\"l%d\":\"%.2f\",\n", + n + 1, d[n]); + } + } +#endif + + buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + if (n) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n" + " \"fds_count\":\"%d\",\n" + " \"ah_pool_inuse\":\"%d\",\n" + " \"ah_wait_list\":\"%d\"\n" + " }", + pt->fds_count, + pt->ah_count_in_use, + pt->ah_wait_list_length); + } + + buf += lws_snprintf(buf, end - buf, "], \"vhosts\":[\n "); + + while (vh) { + if (!first) + if(buf != end) + *buf++ = ','; + buf += lws_json_dump_vhost(vh, buf, end - buf); + first = 0; + if (vh->lserv_wsi) + listening++; + vh = vh->vhost_next; + } + + buf += lws_snprintf(buf, end - buf, "],\n\"listen_wsi\":\"%d\"", + listening); + +#ifdef LWS_WITH_CGI + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + pcgi = &pt->cgi_list; + + while (*pcgi) { + pcgi = &(*pcgi)->cgi_list; + + cgi_count++; + } + } +#endif + buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", + cgi_count); + + buf += lws_snprintf(buf, end - buf, "}\n "); + + return buf - orig; +} + +#endif diff --git a/feeds/wlan-ap/libwebsocket/src/lws-plat-unix.c b/feeds/wlan-ap/libwebsocket/src/lws-plat-unix.c new file mode 100644 index 00000000..0badf827 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/lws-plat-unix.c @@ -0,0 +1,730 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#include +#include + +#ifdef LWS_WITH_PLUGINS +#include +#endif +#include + + +/* + * included from libwebsockets.c for unix builds + */ + +unsigned long long time_in_microseconds(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec; +} + +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ + return read(context->fd_random, (char *)buf, len); +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + struct lws_pollfd fds; + + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi->trunc_len) + return 1; + + fds.fd = wsi->sock; + fds.events = POLLOUT; + fds.revents = 0; + + if (poll(&fds, 1, 0) != 1) + return 1; + + if ((fds.revents & POLLOUT) == 0) + return 1; + + /* okay to send another packet without blocking */ + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ + return poll(fd, 1, 0); +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char buf = 0; + + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + char buf = 0, m = context->count_threads; + + while (m--) { + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); + pt++; + } +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + int syslog_level = LOG_DEBUG; + + switch (level) { + case LLL_ERR: + syslog_level = LOG_ERR; + break; + case LLL_WARN: + syslog_level = LOG_WARNING; + break; + case LLL_NOTICE: + syslog_level = LOG_NOTICE; + break; + case LLL_INFO: + syslog_level = LOG_INFO; + break; + } + syslog(syslog_level, "%s", line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + int n = -1, m, c; + char buf; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + if (timeout_ms < 0) + goto faked_service; + + lws_libev_run(context, tsi); + lws_libuv_run(context, tsi); + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = + context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + } + context->service_tid = context->service_tid_detected; + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + n = poll(pt->fds, pt->fds_count, timeout_ms); + +#ifdef LWS_OPENSSL_SUPPORT + if (!pt->rx_draining_ext_list && + !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) { +#else + if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { +#endif + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } + +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; + + if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { + if (read(pt->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) +{ + int optval = 1; + socklen_t optlen = sizeof(optval); + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&optval, optlen) < 0) + return 1; + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) + + /* + * didn't find a way to set these per-socket, need to + * tune kernel systemwide values + */ +#else + /* set the keepalive conditions we want on it too */ + optval = vhost->ka_time; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_probes; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *)&optval, optlen) < 0) + return 1; +#endif + } + + /* Disable Nagle */ + optval = 1; +#if defined (__sun) + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#elif !defined(__APPLE__) && \ + !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \ + !defined(__NetBSD__) && \ + !defined(__OpenBSD__) + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#else + tcp_proto = getprotobyname("TCP"); + if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0) + return 1; +#endif + + /* We are nonblocking... */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + return 1; + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ + if (info->gid != -1) + if (setgid(info->gid)) + lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO)); + + if (info->uid != -1) { + struct passwd *p = getpwuid(info->uid); + + if (p) { + initgroups(p->pw_name, info->gid); + if (setuid(info->uid)) + lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); + else + lwsl_notice("Set privs to user '%s'\n", p->pw_name); + } else + lwsl_warn("getpwuid: unable to find uid %d", info->uid); + } +} + +#ifdef LWS_WITH_PLUGINS + +#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0 + +/* libuv.c implements these in a cross-platform way */ + +#else + +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} + +LWS_VISIBLE int +lws_plat_plugins_init(struct lws_context * context, const char * const *d) +{ + struct lws_plugin_capability lcaps; + struct lws_plugin *plugin; + lws_plugin_init_func initfunc; + struct dirent **namelist; + int n, i, m, ret = 0; + char path[256]; + void *l; + + lwsl_notice(" Plugins:\n"); + + while (d && *d) { + n = scandir(*d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; + } + + for (i = 0; i < n; i++) { + if (strlen(namelist[i]->d_name) < 7) + goto inval; + + lwsl_notice(" %s\n", namelist[i]->d_name); + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, + namelist[i]->d_name); + l = dlopen(path, RTLD_NOW); + if (!l) { + lwsl_err("Error loading DSO: %s\n", dlerror()); + while (i++ < n) + free(namelist[i]); + goto bail; + } + /* we could open it, can we get his init function? */ + m = lws_snprintf(path, sizeof(path) - 1, "init_%s", + namelist[i]->d_name + 3 /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ + initfunc = dlsym(l, path); + if (!initfunc) { + lwsl_err("Failed to get init on %s: %s", + namelist[i]->d_name, dlerror()); + dlclose(l); + } + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Initializing %s failed %d\n", + namelist[i]->d_name, m); + dlclose(l); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin)); + if (!plugin) { + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->l = l; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + + free(namelist[i]); + continue; + + skip: + dlclose(l); + inval: + free(namelist[i]); + } + free(namelist); + d++; + } + +bail: + free(namelist); + + return ret; +} + +LWS_VISIBLE int +lws_plat_plugins_destroy(struct lws_context * context) +{ + struct lws_plugin *plugin = context->plugin_list, *p; + lws_plugin_destroy_func func; + char path[256]; + int m; + + if (!plugin) + return 0; + + lwsl_notice("%s\n", __func__); + + while (plugin) { + p = plugin; + m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3); + path[m - 3] = '\0'; + func = dlsym(plugin->l, path); + if (!func) { + lwsl_err("Failed to get destroy on %s: %s", + plugin->name, dlerror()); + goto next; + } + m = func(context); + if (m) + lwsl_err("Initializing %s failed %d\n", + plugin->name, m); +next: + dlclose(p->l); + plugin = p->list; + p->list = NULL; + free(p); + } + + context->plugin_list = NULL; + + return 0; +} + +#endif +#endif + + +#if 0 +static void +sigabrt_handler(int x) +{ + printf("%s\n", __func__); + //*(char *)0 = 0; +} +#endif + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + signal(SIGPIPE, SIG_IGN); + +// signal(SIGABRT, sigabrt_handler); + + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int m = context->count_threads; + +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_plat_plugins_destroy(context); +#endif + + if (context->lws_lookup) + lws_free(context->lws_lookup); + + while (m--) { + close(pt->dummy_pipe_fds[0]); + close(pt->dummy_pipe_fds[1]); + pt++; + } + close(context->fd_random); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + int rc = -1; + + struct ifaddrs *ifr; + struct ifaddrs *ifc; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif + + getifaddrs(&ifr); + for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { + if (!ifc->ifa_addr) + continue; + + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + + if (strcmp(ifc->ifa_name, ifname)) + continue; + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_USE_IPV6 + if (ipv6) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr[10] = 0xff; + addr6->sin6_addr.s6_addr[11] = 0xff; + memcpy(&addr6->sin6_addr.s6_addr[12], + &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_USE_IPV6 + case AF_INET6: + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: + continue; + } + rc = 0; + } + + freeifaddrs(ifr); + + if (rc == -1) { + /* check if bind to IP address */ +#ifdef LWS_USE_IPV6 + if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) + rc = 0; + else +#endif + if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) + rc = 0; + } + + return rc; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + + pt->fds[pt->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ + /* if our parent went down, don't linger around */ + if (context->started_with_parent && + kill(context->started_with_parent, 0) < 0) + kill(getpid(), SIGTERM); +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + return inet_ntop(af, src, dst, cnt); +} + +static lws_filefd_type +_lws_plat_file_open(struct lws *wsi, const char *filename, + unsigned long *filelen, int flags) +{ + struct stat stat_buf; + int ret = open(filename, flags, 0664); + + if (ret < 0) + return LWS_INVALID_FILE; + + if (fstat(ret, &stat_buf) < 0) { + close(ret); + return LWS_INVALID_FILE; + } + *filelen = stat_buf.st_size; + return ret; +} + +static int +_lws_plat_file_close(struct lws *wsi, lws_filefd_type fd) +{ + return close(fd); +} + +unsigned long +_lws_plat_file_seek_cur(struct lws *wsi, lws_filefd_type fd, long offset) +{ + return lseek(fd, offset, SEEK_CUR); +} + +static int +_lws_plat_file_read(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len) +{ + long n; + + n = read((int)fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + + *amount = n; + + return 0; +} + +static int +_lws_plat_file_write(struct lws *wsi, lws_filefd_type fd, unsigned long *amount, + unsigned char *buf, unsigned long len) +{ + long n; + + n = write((int)fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + + *amount = n; + + return 0; +} + + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads, fd; + + /* master context has the global fd lookup array */ + context->lws_lookup = lws_zalloc(sizeof(struct lws *) * + context->max_fds); + if (context->lws_lookup == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_notice(" mem: platform fd map: %5u bytes\n", + sizeof(struct lws *) * context->max_fds); + fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); + + context->fd_random = fd; + if (context->fd_random < 0) { + lwsl_err("Unable to open random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, context->fd_random); + return 1; + } + + if (!lws_libev_init_fd_table(context) && + !lws_libuv_init_fd_table(context)) { + /* otherwise libev handled it instead */ + + while (n--) { + if (pipe(pt->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + return 1; + } + + /* use the read end of pipe as first item */ + pt->fds[0].fd = pt->dummy_pipe_fds[0]; + pt->fds[0].events = LWS_POLLIN; + pt->fds[0].revents = 0; + pt->fds_count = 1; + pt++; + } + } + + context->fops.open = _lws_plat_file_open; + context->fops.close = _lws_plat_file_close; + context->fops.seek_cur = _lws_plat_file_seek_cur; + context->fops.read = _lws_plat_file_read; + context->fops.write = _lws_plat_file_write; + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} diff --git a/feeds/wlan-ap/libwebsocket/src/output.c b/feeds/wlan-ap/libwebsocket/src/output.c new file mode 100644 index 00000000..83cc1c72 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/output.c @@ -0,0 +1,700 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +static int +lws_0405_frame_mask_generate(struct lws *wsi) +{ +#if 0 + wsi->u.ws.mask[0] = 0; + wsi->u.ws.mask[1] = 0; + wsi->u.ws.mask[2] = 0; + wsi->u.ws.mask[3] = 0; +#else + int n; + /* fetch the per-frame nonce */ + + n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4); + if (n != 4) { + lwsl_parser("Unable to read from random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, n); + return 1; + } +#endif + /* start masking from first byte of masking key buffer */ + wsi->u.ws.mask_idx = 0; + + return 0; +} + +#ifdef _DEBUG + +LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len) +{ + unsigned char *buf = (unsigned char *)vbuf; + unsigned int n, m, start; + char line[80]; + char *p; + + lwsl_parser("\n"); + + for (n = 0; n < len;) { + start = n; + p = line; + + p += sprintf(p, "%04X: ", start); + + for (m = 0; m < 16 && n < len; m++) + p += sprintf(p, "%02X ", buf[n++]); + while (m++ < 16) + p += sprintf(p, " "); + + p += sprintf(p, " "); + + for (m = 0; m < 16 && (start + m) < len; m++) { + if (buf[start + m] >= ' ' && buf[start + m] < 127) + *p++ = buf[start + m]; + else + *p++ = '.'; + } + while (m++ < 16) + *p++ = ' '; + + *p++ = '\n'; + *p = '\0'; + lwsl_debug("%s", line); + } + lwsl_debug("\n"); +} + +#endif + +/* + * notice this returns number of bytes consumed, or -1 + */ + +int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) +{ + struct lws_context *context = lws_get_context(wsi); + size_t real_len = len; + unsigned int n; + int m; + + if (!len) + return 0; + /* just ignore sends after we cleared the truncation buffer */ + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + !wsi->trunc_len) + return len; + + if (wsi->trunc_len && (buf < wsi->trunc_alloc || + buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) { + char dump[20]; + strncpy(dump, (char *)buf, sizeof(dump) - 1); + dump[sizeof(dump) - 1] = '\0'; +#if defined(LWS_WITH_ESP8266) + lwsl_err("****** %p: Sending new %d (%s), pending truncated ...\n", + wsi, len, dump); +#else + lwsl_err("****** %p: Sending new %d (%s), pending truncated ...\n" + " It's illegal to do an lws_write outside of\n" + " the writable callback: fix your code", + wsi, len, dump); +#endif + a2w_logfr_and_reboot( "truncated" ); +#if 0 + assert(0); +#endif + return -1; + } + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len); + if (m < 0) + return -1; + if (m) /* handled */ { + n = m; + goto handle_truncated_send; + } + + if (!lws_socket_is_valid(wsi->sock)) + lwsl_warn("** error invalid sock but expected to send\n"); + + /* limit sending */ + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + n += LWS_PRE + 4; + if (n > len) + n = len; +#if defined(LWS_WITH_ESP8266) + if (wsi->pending_send_completion) { + n = 0; + goto handle_truncated_send; + } +#endif + + /* nope, send it on the socket directly */ + lws_latency_pre(context, wsi); + n = lws_ssl_capable_write(wsi, buf, n); + lws_latency(context, wsi, "send lws_issue_raw", n, n == len); + + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + /* we're going to close, let close know sends aren't possible */ + wsi->socket_is_permanently_unusable = 1; + return -1; + case LWS_SSL_CAPABLE_MORE_SERVICE: + /* nothing got sent, not fatal, retry the whole thing later */ + n = 0; + break; + } + +handle_truncated_send: + /* + * we were already handling a truncated send? + */ + if (wsi->trunc_len) { + lwsl_info("%p partial adv %d (vs %d)\n", wsi, n, real_len); + wsi->trunc_offset += n; + wsi->trunc_len -= n; + + if (!wsi->trunc_len) { + lwsl_info("***** %x partial send completed\n", wsi); + /* done with it, but don't free it */ + n = real_len; + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + lwsl_info("***** %x signalling to close now\n", wsi); + return -1; /* retry closing now */ + } + } + /* always callback on writeable */ + lws_callback_on_writable(wsi); + + return n; + } + + if ((unsigned int)n == real_len) + /* what we just sent went out cleanly */ + return n; + + /* + * Newly truncated send. Buffer the remainder (it will get + * first priority next time the socket is writable) + */ + lwsl_notice("%p new partial sent %d from %d total\n", wsi, n, real_len); + + /* + * - if we still have a suitable malloc lying around, use it + * - or, if too small, reallocate it + * - or, if no buffer, create it + */ + if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) { + lws_free(wsi->trunc_alloc); + + wsi->trunc_alloc_len = real_len - n; + wsi->trunc_alloc = lws_malloc(real_len - n); + if (!wsi->trunc_alloc) { + lwsl_err("truncated send: unable to malloc %d\n", + real_len - n); + return -1; + } + } + wsi->trunc_offset = 0; + wsi->trunc_len = real_len - n; + memcpy(wsi->trunc_alloc, buf + n, real_len - n); + + /* since something buffered, force it to get another chance to send */ + lws_callback_on_writable(wsi); + + return real_len; +} + +LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol wp) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int masked7 = (wsi->mode == LWSCM_WS_CLIENT); + unsigned char is_masked_bit = 0; + unsigned char *dropmask = NULL; + struct lws_tokens eff_buf; + int pre = 0, n; + size_t orig_len = len; + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent += len; +#endif + if (wsi->vhost) + wsi->vhost->tx += len; + + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + /* remove us from the list */ + struct lws **w = &pt->tx_draining_ext_list; + lwsl_debug("%s: TX EXT DRAINING: Remove from list\n", __func__); + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) | + LWS_WRITE_CONTINUATION; + + lwsl_ext("FORCED draining wp to 0x%02X\n", wp); + } + + lws_restart_ws_ping_pong_timer(wsi); + + if (wp == LWS_WRITE_HTTP || + wp == LWS_WRITE_HTTP_FINAL || + wp == LWS_WRITE_HTTP_HEADERS) + goto send_raw; + + /* if not in a state to send stuff, then just send nothing */ + + if (wsi->state != LWSS_ESTABLISHED && + ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY && + wsi->state != LWSS_AWAITING_CLOSE_ACK) || + wp != LWS_WRITE_CLOSE)) + return 0; + + /* if we are continuing a frame that already had its header done */ + + if (wsi->u.ws.inside_frame) { + lwsl_debug("INSIDE FRAME\n"); + goto do_more_inside_frame; + } + + wsi->u.ws.clean_buffer = 1; + + /* + * give a chance to the extensions to modify payload + * the extension may decide to produce unlimited payload erratically + * (eg, compression extension), so we require only that if he produces + * something, it will be a complete fragment of the length known at + * the time (just the fragment length known), and if he has + * more we will come back next time he is writeable and allow him to + * produce more fragments until he's drained. + * + * This allows what is sent each time it is writeable to be limited to + * a size that can be sent without partial sends or blocking, allows + * interleaving of control frames and other connection service. + */ + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + switch ((int)wp) { + case LWS_WRITE_PING: + case LWS_WRITE_PONG: + case LWS_WRITE_CLOSE: + break; + default: + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp); + if (n < 0) + return -1; + + if (n && eff_buf.token_len) { + /* extension requires further draining */ + wsi->u.ws.tx_draining_ext = 1; + wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list; + pt->tx_draining_ext_list = wsi; + /* we must come back to do more */ + lws_callback_on_writable(wsi); + /* + * keep a copy of the write type for the overall + * action that has provoked generation of these + * fragments, so the last guy can use its FIN state. + */ + wsi->u.ws.tx_draining_stashed_wp = wp; + /* this is definitely not actually the last fragment + * because the extension asserted he has more coming + * So make sure this intermediate one doesn't go out + * with a FIN. + */ + wp |= LWS_WRITE_NO_FIN; + } + + if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) { + wsi->u.ws.stashed_write_pending = 0; + wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type; + } + } + + /* + * an extension did something we need to keep... for example, if + * compression extension, it has already updated its state according + * to this being issued + */ + if ((char *)buf != eff_buf.token) { + /* + * ext might eat it, but no have anything to issue yet + * in that case we have to follow his lead, but stash and + * replace the write type that was lost here the first time. + */ + if (len && !eff_buf.token_len) { + if (!wsi->u.ws.stashed_write_pending) + wsi->u.ws.stashed_write_type = (char)wp & 0x3f; + wsi->u.ws.stashed_write_pending = 1; + return len; + } + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + } + + buf = (unsigned char *)eff_buf.token; + len = eff_buf.token_len; + + switch (wsi->ietf_spec_revision) { + case 13: + if (masked7) { + pre += 4; + dropmask = &buf[0 - pre]; + is_masked_bit = 0x80; + } + + switch (wp & 0xf) { + case LWS_WRITE_TEXT: + n = LWSWSOPC_TEXT_FRAME; + break; + case LWS_WRITE_BINARY: + n = LWSWSOPC_BINARY_FRAME; + break; + case LWS_WRITE_CONTINUATION: + n = LWSWSOPC_CONTINUATION; + break; + + case LWS_WRITE_CLOSE: + n = LWSWSOPC_CLOSE; + break; + case LWS_WRITE_PING: + n = LWSWSOPC_PING; + break; + case LWS_WRITE_PONG: + n = LWSWSOPC_PONG; + break; + default: + lwsl_warn("lws_write: unknown write opc / wp\n"); + return -1; + } + + if (!(wp & LWS_WRITE_NO_FIN)) + n |= 1 << 7; + + if (len < 126) { + pre += 2; + buf[-pre] = n; + buf[-pre + 1] = (unsigned char)(len | is_masked_bit); + } else { + if (len < 65536) { + pre += 4; + buf[-pre] = n; + buf[-pre + 1] = 126 | is_masked_bit; + buf[-pre + 2] = (unsigned char)(len >> 8); + buf[-pre + 3] = (unsigned char)len; + } else { + pre += 10; + buf[-pre] = n; + buf[-pre + 1] = 127 | is_masked_bit; +#if defined __LP64__ + buf[-pre + 2] = (len >> 56) & 0x7f; + buf[-pre + 3] = len >> 48; + buf[-pre + 4] = len >> 40; + buf[-pre + 5] = len >> 32; +#else + buf[-pre + 2] = 0; + buf[-pre + 3] = 0; + buf[-pre + 4] = 0; + buf[-pre + 5] = 0; +#endif + buf[-pre + 6] = (unsigned char)(len >> 24); + buf[-pre + 7] = (unsigned char)(len >> 16); + buf[-pre + 8] = (unsigned char)(len >> 8); + buf[-pre + 9] = (unsigned char)len; + } + } + break; + } + +do_more_inside_frame: + + /* + * Deal with masking if we are in client -> server direction and + * the wp demands it + */ + + if (masked7) { + if (!wsi->u.ws.inside_frame) + if (lws_0405_frame_mask_generate(wsi)) { + lwsl_err("frame mask generation failed\n"); + return -1; + } + + /* + * in v7, just mask the payload + */ + if (dropmask) { /* never set if already inside frame */ + for (n = 4; n < (int)len + 4; n++) + dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + /* copy the frame nonce into place */ + memcpy(dropmask, wsi->u.ws.mask, 4); + } + } + +send_raw: + switch ((int)wp) { + case LWS_WRITE_CLOSE: +/* lwsl_hexdump(&buf[-pre], len); */ + case LWS_WRITE_HTTP: + case LWS_WRITE_HTTP_FINAL: + case LWS_WRITE_HTTP_HEADERS: + case LWS_WRITE_PONG: + case LWS_WRITE_PING: +#ifdef LWS_USE_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) { + unsigned char flags = 0; + + n = LWS_HTTP2_FRAME_TYPE_DATA; + if (wp == LWS_WRITE_HTTP_HEADERS) { + n = LWS_HTTP2_FRAME_TYPE_HEADERS; + flags = LWS_HTTP2_FLAG_END_HEADERS; + if (wsi->u.http2.send_END_STREAM) + flags |= LWS_HTTP2_FLAG_END_STREAM; + } + + if ((wp == LWS_WRITE_HTTP || + wp == LWS_WRITE_HTTP_FINAL) && + wsi->u.http.content_length) { + wsi->u.http.content_remain -= len; + lwsl_info("%s: content_remain = %lu\n", __func__, + wsi->u.http.content_remain); + if (!wsi->u.http.content_remain) { + lwsl_info("%s: selecting final write mode\n", __func__); + wp = LWS_WRITE_HTTP_FINAL; + } + } + + if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) { + lwsl_info("%s: setting END_STREAM\n", __func__); + flags |= LWS_HTTP2_FLAG_END_STREAM; + } + + return lws_http2_frame_write(wsi, n, flags, + wsi->u.http2.my_stream_id, len, buf); + } +#endif + return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre); + default: + break; + } + + /* + * give any active extensions a chance to munge the buffer + * before send. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + * + * callback returns 1 in case it wants to spill more buffers + * + * This takes care of holding the buffer if send is incomplete, ie, + * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with + * the buffer). If wsi->u.ws.clean_buffer is 1, it will instead + * return to the user code how much OF THE USER BUFFER was consumed. + */ + + n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre); + wsi->u.ws.inside_frame = 1; + if (n <= 0) + return n; + + if (n == (int)len + pre) { + /* everything in the buffer was handled (or rebuffered...) */ + wsi->u.ws.inside_frame = 0; + return orig_len; + } + + /* + * it is how many bytes of user buffer got sent... may be < orig_len + * in which case callback when writable has already been arranged + * and user code can call lws_write() again with the rest + * later. + */ + + return n - pre; +} + +LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_process_html_args args; + unsigned long amount, poss; + unsigned char *p = pt->serv_buf; + int n, m; + + // lwsl_notice("%s (trunc len %d)\n", __func__, wsi->trunc_len); + + while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) { + if (wsi->trunc_len) { + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s: closing\n", __func__); + return -1; + } + continue; + } + + if (wsi->u.http.filepos == wsi->u.http.filelen) + goto all_sent; + + poss = context->pt_serv_buf_size; + + if (wsi->sending_chunked) { + /* we need to drop the chunk size in here */ + p += 10; + /* allow for the chunk to grow by 128 in translation */ + poss -= 10 + 128; + } + + if (lws_plat_file_read(wsi, wsi->u.http.fd, &amount, p, poss) < 0) + return -1; /* caller will close */ + + //lwsl_notice("amount %ld\n", amount); + + n = (int)amount; + if (n) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + context->timeout_secs); + + if (wsi->sending_chunked) { + args.p = (char *)p; + args.len = n; + args.max_len = poss + 128; + args.final = wsi->u.http.filepos + n == + wsi->u.http.filelen; + if (user_callback_handle_rxflow( + wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi, + LWS_CALLBACK_PROCESS_HTML, + wsi->user_space, &args, 0) < 0) + return -1; + n = args.len; + p = (unsigned char *)args.p; + } + + m = lws_write(wsi, p, n, + wsi->u.http.filepos == wsi->u.http.filelen ? + LWS_WRITE_HTTP_FINAL : + LWS_WRITE_HTTP + ); + if (m < 0) + return -1; + + wsi->u.http.filepos += amount; + if (m != n) { + /* adjust for what was not sent */ + if (lws_plat_file_seek_cur(wsi, wsi->u.http.fd, + m - n) == + (unsigned long)-1) + return -1; + } + } +all_sent: + if (!wsi->trunc_len && + wsi->u.http.filepos == wsi->u.http.filelen) { + wsi->state = LWSS_HTTP; + /* we might be in keepalive, so close it off here */ + lws_plat_file_close(wsi, wsi->u.http.fd); + wsi->u.http.fd = LWS_INVALID_FILE; + + lwsl_debug("file completed\n"); + + if (wsi->protocol->callback) + /* ignore callback returned value */ + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_HTTP_FILE_COMPLETION, + wsi->user_space, NULL, 0) < 0) + return -1; + + return 1; /* >0 indicates completed */ + } + } + + lws_callback_on_writable(wsi); + + return 0; /* indicates further processing must be done */ +} + +#if LWS_POSIX +LWS_VISIBLE int +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + int n; + + n = recv(wsi->sock, (char *)buf, len, 0); + if (n >= 0) { + if (wsi->vhost) + wsi->vhost->rx += n; + lws_restart_ws_ping_pong_timer(wsi); + return n; + } +#if LWS_POSIX + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) + return LWS_SSL_CAPABLE_MORE_SERVICE; +#endif + lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + int n = 0; + +#if LWS_POSIX + n = send(wsi->sock, (char *)buf, len, MSG_NOSIGNAL); +// lwsl_info("%s: sent len %d result %d", __func__, len, n); + if (n >= 0) + return n; + + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) { + if (LWS_ERRNO == LWS_EWOULDBLOCK) + lws_set_blocking_send(wsi); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } +#else + (void)n; + (void)wsi; + (void)buf; + (void)len; + // !!! +#endif + + lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", len, wsi->sock, n, LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} +#endif +LWS_VISIBLE int +lws_ssl_pending_no_ssl(struct lws *wsi) +{ + (void)wsi; + return 0; +} diff --git a/feeds/wlan-ap/libwebsocket/src/parsers.c b/feeds/wlan-ap/libwebsocket/src/parsers.c new file mode 100644 index 00000000..6a4d77f5 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/parsers.c @@ -0,0 +1,1503 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +const unsigned char lextable[] = { + #include "websocket/lextable.h" +}; + +#define FAIL_CHAR 0x08 + +int LWS_WARN_UNUSED_RESULT +lextable_decode(int pos, char c) +{ + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } + + if (lextable[pos] == FAIL_CHAR) + return -1; + + /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } +} + +// doesn't scrub the ah rxbuffer by default, parent must do if needed + +void +lws_header_table_reset(struct lws *wsi, int autoservice) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt; + struct lws_pollfd *pfd; + + /* if we have the idea we're resetting 'our' ah, must be bound to one */ + assert(ah); + /* ah also concurs with ownership */ + assert(ah->wsi == wsi); + + /* init the ah to reflect no headers or data have appeared yet */ + memset(ah->frag_index, 0, sizeof(ah->frag_index)); + ah->nfrag = 0; + ah->pos = 0; + ah->http_response = 0; + + /* since we will restart the ah, our new headers are not completed */ + // wsi->hdr_parsing_completed = 0; + + /* + * if we inherited pending rx (from socket adoption deferred + * processing), apply and free it. + */ + if (wsi->u.hdr.preamble_rx) { + memcpy(ah->rx, wsi->u.hdr.preamble_rx, + wsi->u.hdr.preamble_rx_len); + ah->rxlen = wsi->u.hdr.preamble_rx_len; + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + if (autoservice) { + lwsl_notice("%s: calling service on readbuf ah\n", __func__); + + pt = &wsi->context->pt[(int)wsi->tsi]; + + /* unlike a normal connect, we have the headers already + * (or the first part of them anyway) + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + lws_service_fd_tsi(wsi->context, pfd, wsi->tsi); + } + } +} + +int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi; + int n; + + lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__, (void *)wsi, + (void *)wsi->u.hdr.ah, wsi->tsi, pt->ah_count_in_use); + + /* if we are already bound to one, just clear it down */ + if (wsi->u.hdr.ah) { + lwsl_info("cleardown\n"); + goto reset; + } + + lws_pt_lock(pt); + pwsi = &pt->ah_wait_list; + while (*pwsi) { + if (*pwsi == wsi) { + /* if already waiting on list, if no new ah just ret */ + if (pt->ah_count_in_use == + context->max_http_header_pool) { + lwsl_notice("%s: no free ah to attach\n", __func__); + goto bail; + } + /* new ah.... remove ourselves from waiting list */ + *pwsi = wsi->u.hdr.ah_wait_list; /* set our prev to our next */ + wsi->u.hdr.ah_wait_list = NULL; /* no next any more */ + pt->ah_wait_list_length--; + break; + } + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + /* + * pool is all busy... add us to waiting list and return that we + * weren't able to deliver it right now + */ + if (pt->ah_count_in_use == context->max_http_header_pool) { + lwsl_notice("%s: adding %p to ah waiting list\n", __func__, wsi); + wsi->u.hdr.ah_wait_list = pt->ah_wait_list; + pt->ah_wait_list = wsi; + pt->ah_wait_list_length++; + + /* we cannot accept input then */ + + _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa); + goto bail; + } + + for (n = 0; n < context->max_http_header_pool; n++) + if (!pt->ah_pool[n].in_use) + break; + + /* if the count of in use said something free... */ + assert(n != context->max_http_header_pool); + + wsi->u.hdr.ah = &pt->ah_pool[n]; + wsi->u.hdr.ah->in_use = 1; + pt->ah_pool[n].wsi = wsi; /* mark our owner */ + pt->ah_count_in_use++; + + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + + lwsl_info("%s: wsi %p: ah %p: count %d (on exit)\n", __func__, + (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use); + + lws_pt_unlock(pt); + +reset: + + /* and reset the rx state */ + wsi->u.hdr.ah->rxpos = 0; + wsi->u.hdr.ah->rxlen = 0; + + lws_header_table_reset(wsi, autoservice); + time(&wsi->u.hdr.ah->assigned); + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) + if (!lws_client_connect_via_info2(wsi)) + /* our client connect has failed, the wsi + * has been closed + */ + return -1; +#endif + + return 0; + +bail: + lws_pt_unlock(pt); + + return 1; +} + +int lws_header_table_detach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi; + time_t now; + + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, wsi->tsi, + pt->ah_count_in_use); + + if (wsi->u.hdr.preamble_rx) + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + /* may not be detached while he still has unprocessed rx */ + if (ah && ah->rxpos != ah->rxlen) { + lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d\n", __func__, wsi, + ah->rxpos, ah->rxlen); + assert(ah->rxpos == ah->rxlen); + + return 0; + } + + lws_pt_lock(pt); + + pwsi = &pt->ah_wait_list; + if (!ah) { /* remove from wait list if none attached */ + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: wsi %p, remv wait\n", + __func__, wsi); + *pwsi = wsi->u.hdr.ah_wait_list; + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + goto bail; + } + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + /* no ah, not on list... no more business here */ + goto bail; + } + /* we did have an ah attached */ + time(&now); + if (ah->assigned && now - ah->assigned > 3) { + /* + * we're detaching the ah, but it was held an + * unreasonably long time + */ + lwsl_notice("%s: wsi %p: ah held %ds, " + "ah.rxpos %d, ah.rxlen %d, mode/state %d %d," + "wsi->more_rx_waiting %d\n", __func__, wsi, + (int)(now - ah->assigned), + ah->rxpos, ah->rxlen, wsi->mode, wsi->state, + wsi->more_rx_waiting); + } + + ah->assigned = 0; + + /* if we think we're detaching one, there should be one in use */ + assert(pt->ah_count_in_use > 0); + /* and this specific one should have been in use */ + assert(ah->in_use); + wsi->u.hdr.ah = NULL; + ah->wsi = NULL; /* no owner */ + + /* oh there is nobody on the waiting list... leave it at that then */ + if (!*pwsi) { + ah->in_use = 0; + pt->ah_count_in_use--; + + goto bail; + } + + /* somebody else on same tsi is waiting, give it to oldest guy */ + + lwsl_info("pt wait list %p\n", *pwsi); + while ((*pwsi)->u.hdr.ah_wait_list) + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + + wsi = *pwsi; + lwsl_info("last wsi in wait list %p\n", wsi); + + wsi->u.hdr.ah = ah; + ah->wsi = wsi; /* new owner */ + /* and reset the rx state */ + ah->rxpos = 0; + ah->rxlen = 0; + lws_header_table_reset(wsi, autoservice); + time(&wsi->u.hdr.ah->assigned); + + /* clients acquire the ah and then insert themselves in fds table... */ + if (wsi->position_in_fds_table != -1) { + lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi); + + /* he has been stuck waiting for an ah, but now his wait is over, + * let him progress + */ + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + } + + /* point prev guy to next guy in list instead */ + *pwsi = wsi->u.hdr.ah_wait_list; + /* the guy who got one is out of the list */ + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) + if (!lws_client_connect_via_info2(wsi)) { + /* our client connect has failed, the wsi + * has been closed + */ + lws_pt_unlock(pt); + + return -1; + } +#endif + + assert(!!pt->ah_wait_list_length == !!(int)(long)pt->ah_wait_list); +bail: + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, wsi->tsi, + pt->ah_count_in_use); + lws_pt_unlock(pt); + + return 0; +} + +LWS_VISIBLE int +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx) +{ + int n; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + if (!frag_idx) + return wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (frag_idx-- && n); + + return 0; +} + +LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + int len = 0; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + len += wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + + return len; +} + +LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h, int frag_idx) +{ + int n = 0; + int f = wsi->u.hdr.ah->frag_index[h]; + + if (!f) + return -1; + + while (n < frag_idx) { + f = wsi->u.hdr.ah->frags[f].nfrag; + if (!f) + return -1; + n++; + } + + if (wsi->u.hdr.ah->frags[f].len >= len) + return -1; + + memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset, + wsi->u.hdr.ah->frags[f].len); + dst[wsi->u.hdr.ah->frags[f].len] = '\0'; + + return wsi->u.hdr.ah->frags[f].len; +} + +LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h) +{ + int toklen = lws_hdr_total_length(wsi, h); + int n; + + if (toklen >= len) + return -1; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + + do { + strcpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset]); + dst += wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + + return toklen; +} + +char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return NULL; + + return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset; +} + +int LWS_WARN_UNUSED_RESULT +lws_pos_in_bounds(struct lws *wsi) +{ + if (wsi->u.hdr.ah->pos < (unsigned int)wsi->context->max_http_header_data) + return 0; + + if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) { + lwsl_err("Ran out of header data space\n"); + return 1; + } + + /* + * with these tests everywhere, it should never be able to exceed + * the limit, only meet the limit + */ + + lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos, + wsi->context->max_http_header_data); + assert(0); + + return 1; +} + +int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s) +{ + wsi->u.hdr.ah->nfrag++; + if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) { + lwsl_warn("More hdr frags than we can deal with, dropping\n"); + return -1; + } + + wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag; + + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0; + + do { + if (lws_pos_in_bounds(wsi)) + return -1; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s; + if (*s) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + } while (*s++); + + return 0; +} + +signed char char_to_hex(const char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int LWS_WARN_UNUSED_RESULT +issue_char(struct lws *wsi, unsigned char c) +{ + unsigned short frag_len; + + if (lws_pos_in_bounds(wsi)) + return -1; + + frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len; + /* + * If we haven't hit the token limit, just copy the character into + * the header + */ + if (frag_len < wsi->u.hdr.current_token_limit) { + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; + if (c) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + return 0; + } + + /* Insert a null character when we *hit* the limit: */ + if (frag_len == wsi->u.hdr.current_token_limit) { + if (lws_pos_in_bounds(wsi)) + return -1; + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0'; + lwsl_warn("header %i exceeds limit %d\n", + wsi->u.hdr.parser_state, + wsi->u.hdr.current_token_limit); + } + + return 1; +} + +int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c) +{ + static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + }; + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context *context = wsi->context; + unsigned int n, m, enc = 0; + + assert(wsi->u.hdr.ah); + + switch (wsi->u.hdr.parser_state) { + default: + + lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c); + + /* collect into malloc'd buffers */ + /* optional initial space swallow */ + if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len && + c == ' ') + break; + + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (wsi->u.hdr.parser_state == methods[m]) + break; + if (m == ARRAY_SIZE(methods)) + /* it was not any of the methods */ + goto check_eol; + + /* special URI processing... end at space */ + + if (c == ' ') { + /* enforce starting with / */ + if (!ah->frags[ah->nfrag].len) + if (issue_char(wsi, '/') < 0) + return -1; + + if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + } + + /* begin parsing HTTP version: */ + if (issue_char(wsi, '\0') < 0) + return -1; + wsi->u.hdr.parser_state = WSI_TOKEN_HTTP; + goto start_fragment; + } + + /* + * PRIORITY 1 + * special URI processing... convert %xx + */ + + switch (wsi->u.hdr.ues) { + case URIES_IDLE: + if (c == '%') { + wsi->u.hdr.ues = URIES_SEEN_PERCENT; + goto swallow; + } + break; + case URIES_SEEN_PERCENT: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + wsi->u.hdr.esc_stash = c; + wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1; + goto swallow; + + case URIES_SEEN_PERCENT_H1: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) | + char_to_hex(c); + enc = 1; + wsi->u.hdr.ues = URIES_IDLE; + break; + } + + /* + * PRIORITY 2 + * special URI processing... + * convert /.. or /... or /../ etc to / + * convert /./ to / + * convert // or /// etc to / + * leave /.dir or whatever alone + */ + + switch (wsi->u.hdr.ups) { + case URIPS_IDLE: + if (!c) + return -1; + /* genuine delimiter */ + if ((c == '&' || c == ';') && !enc) { + if (issue_char(wsi, c) < 0) + return -1; + /* swallow the terminator */ + ah->frags[ah->nfrag].len--; + /* link to next fragment */ + ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + /* start next fragment after the & */ + wsi->u.hdr.post_literal_equal = 0; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + goto swallow; + } + /* uriencoded = in the name part, disallow */ + if (c == '=' && enc && + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] && + !wsi->u.hdr.post_literal_equal) + c = '_'; + + /* after the real =, we don't care how many = */ + if (c == '=' && !enc) + wsi->u.hdr.post_literal_equal = 1; + + /* + to space */ + if (c == '+' && !enc) + c = ' '; + /* issue the first / always */ + if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_SEEN_SLASH: + /* swallow subsequent slashes */ + if (c == '/') + goto swallow; + /* track and swallow the first . after / */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT; + goto swallow; + } + wsi->u.hdr.ups = URIPS_IDLE; + break; + case URIPS_SEEN_SLASH_DOT: + /* swallow second . */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT; + goto swallow; + } + /* change /./ to / */ + if (c == '/') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + goto swallow; + } + /* it was like /.dir ... regurgitate the . */ + wsi->u.hdr.ups = URIPS_IDLE; + if (issue_char(wsi, '.') < 0) + return -1; + break; + + case URIPS_SEEN_SLASH_DOT_DOT: + + /* /../ or /..[End of URI] --> backup to last / */ + if (c == '/' || c == '?') { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + if (ah->frags[ah->nfrag].len > 1) + break; + goto swallow; + } + + /* /..[^/] ... regurgitate and allow */ + + if (issue_char(wsi, '.') < 0) + return -1; + if (issue_char(wsi, '.') < 0) + return -1; + wsi->u.hdr.ups = URIPS_IDLE; + break; + } + + if (c == '?' && !enc && + !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */ + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + /* seal off uri header */ + if (issue_char(wsi, '\0') < 0) + return -1; + + /* move to using WSI_TOKEN_HTTP_URI_ARGS */ + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + + wsi->u.hdr.post_literal_equal = 0; + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag; + wsi->u.hdr.ups = URIPS_IDLE; + goto swallow; + } + +check_eol: + /* bail at EOL */ + if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE && + c == '\x0d') { + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + c = '\0'; + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + lwsl_parser("*\n"); + } + + n = issue_char(wsi, c); + if ((int)n < 0) + return -1; + if (n > 0) + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + +swallow: + /* per-protocol end of headers management */ + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + break; + + /* collecting and checking a name part */ + case WSI_TOKEN_NAME_PART: + lwsl_parser("WSI_TOKEN_NAME_PART '%c' (mode=%d)\n", c, wsi->mode); + + wsi->u.hdr.lextable_pos = + lextable_decode(wsi->u.hdr.lextable_pos, c); + /* + * Server needs to look out for unknown methods... + */ + if (wsi->u.hdr.lextable_pos < 0 && + wsi->mode == LWSCM_HTTP_SERVING) { + /* this is not a header we know about */ + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (ah->frag_index[methods[m]]) { + /* + * already had the method, no idea what + * this crap from the client is, ignore + */ + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + /* + * hm it's an unknown http method from a client in fact, + * treat as dangerous + */ + if (m == ARRAY_SIZE(methods)) { + lwsl_info("Unknown method - dropping\n"); + goto forbid; + } + break; + } + /* + * ...otherwise for a client, let him ignore unknown headers + * coming from the server + */ + if (wsi->u.hdr.lextable_pos < 0) { + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + + if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) { + /* terminal state */ + + n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) | + lextable[wsi->u.hdr.lextable_pos + 1]; + + lwsl_parser("known hdr %d\n", n); + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (n == methods[m] && + ah->frag_index[methods[m]]) { + lwsl_warn("Duplicated method\n"); + return -1; + } + + /* + * WSORIGIN is protocol equiv to ORIGIN, + * JWebSocket likes to send it, map to ORIGIN + */ + if (n == WSI_TOKEN_SWORIGIN) + n = WSI_TOKEN_ORIGIN; + + wsi->u.hdr.parser_state = (enum lws_token_indexes) + (WSI_TOKEN_GET_URI + n); + + if (context->token_limits) + wsi->u.hdr.current_token_limit = + context->token_limits->token_limit[ + wsi->u.hdr.parser_state]; + else + wsi->u.hdr.current_token_limit = + wsi->context->max_http_header_data; + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + + goto start_fragment; + } + break; + +start_fragment: + ah->nfrag++; +excessive: + if (ah->nfrag == ARRAY_SIZE(ah->frags)) { + lwsl_warn("More hdr frags than we can deal with\n"); + return -1; + } + + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + + n = ah->frag_index[wsi->u.hdr.parser_state]; + if (!n) { /* first fragment */ + ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag; + break; + } + /* continuation */ + while (ah->frags[n].nfrag) + n = ah->frags[n].nfrag; + ah->frags[n].nfrag = ah->nfrag; + + if (issue_char(wsi, ' ') < 0) + return -1; + break; + + /* skipping arg part of a name we didn't recognize */ + case WSI_TOKEN_SKIPPING: + lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c); + + if (c == '\x0d') + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + break; + + case WSI_TOKEN_SKIPPING_SAW_CR: + lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (c == '\x0a') { + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + } else + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + /* we're done, ignore anything else */ + + case WSI_PARSING_COMPLETE: + lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c); + break; + } + + return 0; + +set_parsing_complete: + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) + wsi->ietf_spec_revision = + atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); + + lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision); + } + wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE; + wsi->hdr_parsing_completed = 1; + + return 0; + +forbid: + lwsl_notice(" forbidding on uri sanitation\n"); + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + return -1; +} + +LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi) +{ + return wsi->u.ws.frame_is_binary; +} + +int +lws_rx_sm(struct lws *wsi, unsigned char c) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int callback_action = LWS_CALLBACK_RECEIVE; + int ret = 0, n, rx_draining_ext = 0; + struct lws_tokens eff_buf; + + if (wsi->socket_is_permanently_unusable) + return -1; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + + eff_buf.token = NULL; + eff_buf.token_len = 0; + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + rx_draining_ext = 1; + lwsl_err("%s: doing draining flow\n", __func__); + + goto drain_extension; + } + switch (wsi->ietf_spec_revision) { + case 13: + /* + * no prepended frame key any more + */ + wsi->u.ws.all_zero_nonce = 1; + goto handle_first; + + default: + lwsl_warn("lws_rx_sm: unknown spec version %d\n", + wsi->ietf_spec_revision); + break; + } + break; + case LWS_RXPS_04_mask_1: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2; + break; + case LWS_RXPS_04_mask_2: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3; + break; + case LWS_RXPS_04_mask_3: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + /* + * start from the zero'th byte in the XOR key buffer since + * this is the start of a frame with a new key + */ + + wsi->u.ws.mask_idx = 0; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; + break; + + /* + * 04 logical framing from the spec (all this is masked when incoming + * and has to be unmasked) + * + * We ignore the possibility of extension data because we don't + * negotiate any extensions at the moment. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|R| Payload len | Extended payload length | + * |I|S|S|S| (4) |S| (7) | (16/63) | + * |N|V|V|V| |V| | (if payload len==126/127) | + * | |1|2|3| |4| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | | Extension data | + * +-------------------------------+ - - - - - - - - - - - - - - - + + * : : + * +---------------------------------------------------------------+ + * : Application data : + * +---------------------------------------------------------------+ + * + * We pass payload through to userland as soon as we get it, ignoring + * FIN. It's up to userland to buffer it up if it wants to see a + * whole unfragmented block of the original size (which may be up to + * 2^63 long!) + */ + + case LWS_RXPS_04_FRAME_HDR_1: +handle_first: + + wsi->u.ws.opcode = c & 0xf; + wsi->u.ws.rsv = c & 0x70; + wsi->u.ws.final = !!((c >> 7) & 1); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.frame_is_binary = + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; + break; + case 127: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c & 0x7f; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.ws.rx_packet_length = 0; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_7: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_6: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_5: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_4: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_3: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_2: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_1: + wsi->u.ws.rx_packet_length |= ((size_t)c); + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_2: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_3: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_4: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + wsi->u.ws.mask_idx = 0; + if (wsi->u.ws.rx_packet_length == 0) { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.rx_ubuf_head + LWS_PRE >= + wsi->u.ws.rx_ubuf_alloc) { + lwsl_err("Attempted overflow \n"); + return -1; + } + if (wsi->u.ws.all_zero_nonce) + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = c; + else + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = + c ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + else + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != + wsi->protocol->rx_buffer_size) + break; + + /* spill because we filled our rx buffer */ +spill: + /* + * is this frame a control packet we should take care of at this + * layer? If so service it and hide it from the user callback + */ + + lwsl_parser("spill on %s\n", wsi->protocol->name); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_CLOSE: + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen client close ack\n"); + return -1; + } + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + /* if he sends us 2 CLOSE, kill him */ + return -1; + + if (lws_partial_buffered(wsi)) { + /* + * if we're in the middle of something, + * we can't do a normal close response and + * have to just close our end. + */ + wsi->socket_is_permanently_unusable = 1; + lwsl_parser("Closing on peer close due to Pending tx\n"); + return -1; + } + + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head)) + return -1; + + lwsl_parser("server sees client close packet\n"); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* deal with the close packet contents as a PONG */ + wsi->u.ws.payload_is_close = 1; + goto process_as_ping; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } +process_as_ping: + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + return 0; + + case LWSWSOPC_PONG: + lwsl_info("received pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("received expected PONG on wsi %p\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_RECEIVE_PONG; + break; + + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + case LWSWSOPC_CONTINUATION: + break; + + default: + lwsl_parser("passing opc %x up to exts\n", + wsi->u.ws.opcode); + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) + /* not handle or fail */ + lwsl_ext("ext opc opcode 0x%x unknown\n", + wsi->u.ws.opcode); + + wsi->u.ws.rx_ubuf_head = 0; + return 0; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + +drain_extension: + lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + if (n < 0) { + /* + * we may rely on this to get RX, just drop connection + */ + wsi->socket_is_permanently_unusable = 1; + return -1; + } + + if (rx_draining_ext && eff_buf.token_len == 0) + goto already_done; + + if (n && eff_buf.token_len) { + /* extension had more... main loop will come back */ + wsi->u.ws.rx_draining_ext = 1; + wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list; + pt->rx_draining_ext_list = wsi; + } + + if (eff_buf.token_len > 0 || + callback_action == LWS_CALLBACK_RECEIVE_PONG) { + eff_buf.token[eff_buf.token_len] = '\0'; + + if (wsi->protocol->callback) { + + if (callback_action == LWS_CALLBACK_RECEIVE_PONG) + lwsl_info("Doing pong callback\n"); + + ret = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, + eff_buf.token, + eff_buf.token_len); + } + else + lwsl_err("No callback on payload spill!\n"); + } + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + } + + return ret; + +illegal_ctl_length: + + lwsl_warn("Control frame with xtended length is illegal\n"); + /* kill the connection */ + return -1; +} + +LWS_VISIBLE size_t +lws_remaining_packet_payload(struct lws *wsi) +{ + return wsi->u.ws.rx_packet_length; +} + +/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much + * to expect in that state and can deal with it in bulk more efficiently. + */ + +int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, + size_t *len) +{ + unsigned char *buffer = *buf, mask[4]; + int buffer_size, n; + unsigned int avail; + char *rx_ubuf; + + if (wsi->protocol->rx_buffer_size) + buffer_size = wsi->protocol->rx_buffer_size; + else + buffer_size = wsi->context->pt_serv_buf_size; + avail = buffer_size - wsi->u.ws.rx_ubuf_head; + + /* do not consume more than we should */ + if (avail > wsi->u.ws.rx_packet_length) + avail = wsi->u.ws.rx_packet_length; + + /* do not consume more than what is in the buffer */ + if (avail > *len) + avail = *len; + + /* we want to leave 1 byte for the parser to handle properly */ + if (avail <= 1) + return 0; + + avail--; + rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head; + if (wsi->u.ws.all_zero_nonce) + memcpy(rx_ubuf, buffer, avail); + else { + + for (n = 0; n < 4; n++) + mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3]; + + /* deal with 4-byte chunks using unwrapped loop */ + n = avail >> 2; + while (n--) { + *(rx_ubuf++) = *(buffer++) ^ mask[0]; + *(rx_ubuf++) = *(buffer++) ^ mask[1]; + *(rx_ubuf++) = *(buffer++) ^ mask[2]; + *(rx_ubuf++) = *(buffer++) ^ mask[3]; + } + /* and the remaining bytes bytewise */ + for (n = 0; n < (int)(avail & 3); n++) + *(rx_ubuf++) = *(buffer++) ^ mask[n]; + + wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3; + } + + (*buf) += avail; + wsi->u.ws.rx_ubuf_head += avail; + wsi->u.ws.rx_packet_length -= avail; + *len -= avail; + + return avail; +} diff --git a/feeds/wlan-ap/libwebsocket/src/pollfd.c b/feeds/wlan-ap/libwebsocket/src/pollfd.c new file mode 100644 index 00000000..5bfc6f1e --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/pollfd.c @@ -0,0 +1,426 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + int ret = 0, pa_events = 1; + struct lws_pollfd *pfd; + int sampled_tid, tid; + + if (!wsi || wsi->position_in_fds_table < 0) + return 0; + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + assert(wsi->position_in_fds_table >= 0 && + wsi->position_in_fds_table < pt->fds_count); + + pfd = &pt->fds[wsi->position_in_fds_table]; + pa->fd = wsi->sock; + pa->prev_events = pfd->events; + pa->events = pfd->events = (pfd->events & ~_and) | _or; + + //lwsl_notice("%s: wsi %p, posin %d. from %d -> %d\n", __func__, wsi, wsi->position_in_fds_table, pa->prev_events, pa->events); + + + if (wsi->http2_substream) + return 0; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, + wsi->user_space, (void *)pa, 0)) { + ret = -1; + goto bail; + } + + if (_and & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ); + } + if (_or & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + } + if (_and & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + } + if (_or & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); + } + + /* + * if we changed something in this pollfd... + * ... and we're running in a different thread context + * than the service thread... + * ... and the service thread is waiting ... + * then cancel it to force a restart with our changed events + */ +#if LWS_POSIX + pa_events = pa->prev_events != pa->events; +#endif + + if (pa_events) { + + if (lws_plat_change_pollfd(context, wsi, pfd)) { + lwsl_info("%s failed\n", __func__); + ret = -1; + goto bail; + } + + sampled_tid = context->service_tid; + if (sampled_tid) { + tid = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + if (tid == -1) { + ret = -1; + goto bail; + } + if (tid != sampled_tid) + lws_cancel_service_pt(wsi); + } + } +bail: + return ret; +} + +int +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_pollargs pa = { wsi->sock, LWS_POLLIN, 0 }; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int ret = 0; +#ifndef LWS_NO_SERVER + struct lws_pollargs pa1; +#endif + + lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n", + __func__, wsi, wsi->tsi, wsi->sock, pt->fds_count); + + if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { + lwsl_err("Too many fds (%d vs %d)\n", context->max_fds, + context->fd_limit_per_thread ); + return 1; + } + +#if !defined(_WIN32) && !defined(MBED_OPERATORS) && !defined(LWS_WITH_ESP8266) + if (wsi->sock >= context->max_fds) { + lwsl_err("Socket fd %d is too high (%d)\n", + wsi->sock, context->max_fds); + return 1; + } +#endif + + assert(wsi); + assert(wsi->vhost); + assert(lws_socket_is_valid(wsi->sock)); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + return -1; + + lws_pt_lock(pt); + pt->count_conns++; + insert_wsi(context, wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->position_in_fds_table == -1) +#endif + wsi->position_in_fds_table = pt->fds_count; + + // lwsl_notice("%s: %p: setting posinfds %d\n", __func__, wsi, wsi->position_in_fds_table); + + pt->fds[wsi->position_in_fds_table].fd = wsi->sock; +#if LWS_POSIX + pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN; +#else + pt->fds[wsi->position_in_fds_table].events = 0; // LWS_POLLIN; +#endif + pa.events = pt->fds[pt->fds_count].events; + + lws_plat_insert_socket_into_fds(context, wsi); + + /* external POLL support via protocol 0 */ + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + /* if no more room, defeat accepts on this thread */ + if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1) + _lws_change_pollfd(pt->wsi_listening, LWS_POLLIN, 0, &pa1); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + ret = -1; + + return ret; +} + +int +remove_wsi_socket_from_fds(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_pollargs pa = { wsi->sock, 0, 0 }; +#if !defined(LWS_WITH_ESP8266) +#ifndef LWS_NO_SERVER + struct lws_pollargs pa1; +#endif + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws *end_wsi; + int v; +#endif + int m, ret = 0; + +#if !defined(_WIN32) && !defined(MBED_OPERATORS) && !defined(LWS_WITH_ESP8266) + if (wsi->sock > context->max_fds) { + lwsl_err("fd %d too high (%d)\n", wsi->sock, context->max_fds); + return 1; + } +#endif + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + return -1; + + /* + * detach ourselves from vh protocol list if we're on one + * A -> B -> C + * A -> C , or, B -> C, or A -> B + */ + lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi); + if (wsi->same_vh_protocol_prev) { + assert (*(wsi->same_vh_protocol_prev) == wsi); + lwsl_info("have prev %p, setting him to our next %p\n", + wsi->same_vh_protocol_prev, + wsi->same_vh_protocol_next); + + /* guy who pointed to us should point to our next */ + *(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next; + } //else + //lwsl_err("null wsi->prev\n"); + /* our next should point back to our prev */ + if (wsi->same_vh_protocol_next) { + lwsl_info("have next %p\n"); + wsi->same_vh_protocol_next->same_vh_protocol_prev = + wsi->same_vh_protocol_prev; + } //else + //lwsl_err("null wsi->next\n"); + + /* the guy who is to be deleted's slot index in pt->fds */ + m = wsi->position_in_fds_table; + +#if !defined(LWS_WITH_ESP8266) + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION); + + lws_pt_lock(pt); + + lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", + __func__, wsi, wsi->sock, wsi->position_in_fds_table, + pt->fds_count, pt->fds[pt->fds_count].fd); + + /* have the last guy take up the now vacant slot */ + pt->fds[m] = pt->fds[pt->fds_count - 1]; +#endif + /* this decrements pt->fds_count */ + lws_plat_delete_socket_from_fds(context, wsi, m); +#if !defined(LWS_WITH_ESP8266) + v = (int) pt->fds[m].fd; + /* end guy's "position in fds table" is now the deletion guy's old one */ + end_wsi = wsi_from_fd(context, v); + if (!end_wsi) { + lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", (int)pt->fds[m].fd, m, pt->fds_count); + assert(0); + } else + end_wsi->position_in_fds_table = m; + + /* deletion guy's lws_lookup entry needs nuking */ + delete_from_fd(context, wsi->sock); + /* removed wsi has no position any more */ + wsi->position_in_fds_table = -1; + + /* remove also from external POLL support via protocol 0 */ + if (lws_socket_is_valid(wsi->sock)) + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + if (!context->being_destroyed) + /* if this made some room, accept connects on this thread */ + if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1) + _lws_change_pollfd(pt->wsi_listening, 0, LWS_POLLIN, &pa1); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + ret = -1; +#endif + return ret; +} + +int +lws_change_pollfd(struct lws *wsi, int _and, int _or) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + struct lws_pollargs pa; + int ret = 0; + + if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) + return 1; + + context = lws_get_context(wsi); + if (!context) + return 1; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + return -1; + + pt = &context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt); + ret = _lws_change_pollfd(wsi, _and, _or, &pa); + lws_pt_unlock(pt); + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + ret = -1; + + return ret; +} + +LWS_VISIBLE int +lws_callback_on_writable(struct lws *wsi) +{ +#ifdef LWS_USE_HTTP2 + struct lws *network_wsi, *wsi2; + int already; +#endif + + if (wsi->state == LWSS_SHUTDOWN) + return 0; + + if (wsi->socket_is_permanently_unusable) + return 0; + +#ifdef LWS_USE_HTTP2 + lwsl_info("%s: %p\n", __func__, wsi); + + if (wsi->mode != LWSCM_HTTP2_SERVING) + goto network_sock; + + if (wsi->u.http2.requested_POLLOUT) { + lwsl_info("already pending writable\n"); + return 1; + } + + if (wsi->u.http2.tx_credit <= 0) { + /* + * other side is not able to cope with us sending + * anything so no matter if we have POLLOUT on our side. + * + * Delay waiting for our POLLOUT until peer indicates he has + * space for more using tx window command in http2 layer + */ + lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi, + wsi->u.http2.tx_credit); + wsi->u.http2.waiting_tx_credit = 1; + return 0; + } + + network_wsi = lws_http2_get_network_wsi(wsi); + already = network_wsi->u.http2.requested_POLLOUT; + + /* mark everybody above him as requesting pollout */ + + wsi2 = wsi; + while (wsi2) { + wsi2->u.http2.requested_POLLOUT = 1; + lwsl_info("mark %p pending writable\n", wsi2); + wsi2 = wsi2->u.http2.parent_wsi; + } + + /* for network action, act only on the network wsi */ + + wsi = network_wsi; + if (already) + return 1; +network_sock: +#endif + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0)) + return 1; + + if (wsi->position_in_fds_table < 0) { + lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock); + return -1; + } + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + return -1; + + return 1; +} + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol) +{ + struct lws *wsi; + + if (protocol < vhost->protocols || + protocol >= (vhost->protocols + vhost->count_protocols)) { + lwsl_err("%s: protocol is not from vhost\n", __func__); + + return -1; + } + + wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols]; + //lwsl_notice("%s: protocol %p, start wsi %p\n", __func__, protocol, wsi); + while (wsi) { + //lwsl_notice("%s: protocol %p, this wsi %p (wsi->protocol=%p)\n", + // __func__, protocol, wsi, wsi->protocol); + assert(wsi->protocol == protocol); + assert(*wsi->same_vh_protocol_prev == wsi); + if (wsi->same_vh_protocol_next) { + // lwsl_err("my next says %p\n", wsi->same_vh_protocol_next); + // lwsl_err("my next's prev says %p\n", + // wsi->same_vh_protocol_next->same_vh_protocol_prev); + assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == &wsi->same_vh_protocol_next); + } + //lwsl_notice(" apv: %p\n", wsi); + lws_callback_on_writable(wsi); + wsi = wsi->same_vh_protocol_next; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + struct lws_vhost *vhost = context->vhost_list; + int n; + + while (vhost) { + for (n = 0; n < vhost->count_protocols; n++) + if (protocol->callback == + vhost->protocols[n].callback && + !strcmp(protocol->name, vhost->protocols[n].name)) + break; + if (n != vhost->count_protocols) + lws_callback_on_writable_all_protocol_vhost( + vhost, &vhost->protocols[n]); + + vhost = vhost->vhost_next; + } + + return 0; +} diff --git a/feeds/wlan-ap/libwebsocket/src/server-handshake.c b/feeds/wlan-ap/libwebsocket/src/server-handshake.c new file mode 100644 index 00000000..f0fcbb9d --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/server-handshake.c @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } +#ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE int +lws_extension_server_handshake(struct lws *wsi, char **p) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + const struct lws_extension *ext; + char ext_name[128]; + int ext_count = 0; + int more = 1; + char ignore; + int n, m; + char *c; + + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) + return 0; + + /* + * break down the list of client extensions + * and go through them + */ + + if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size, + WSI_TOKEN_EXTENSIONS) < 0) + return 1; + + c = (char *)pt->serv_buf; + lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); + wsi->count_act_ext = 0; + n = 0; + ignore = 0; + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') + ignore = 1; + if (ignore || *c == ' ') { + c++; + continue; + } + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check a client's extension against our support */ + + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } +#if 0 + m = ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_ARGS_VALIDATE, + NULL, start + n, 0); + if (m) { + ext++; + continue; + } +#endif + /* + * oh, we do support this one he asked for... but let's + * ask user code if it's OK to apply it on this + * particular connection + protocol + */ + m = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means go ahead and allow + * the extension, it's what we get if the callback is + * unhandled + */ + + if (m) { + ext++; + continue; + } + + /* apply it */ + + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->count_act_ext] = ext; + + /* allow him to construct his context */ + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_CONSTRUCT, + (void *)&wsi->act_ext_user[wsi->count_act_ext], + NULL, 0)) { + lwsl_notice("ext %s failed construction\n", ext_name); + ext_count--; + ext++; + continue; + } + + if (ext_count > 1) + *(*p)++ = ','; + else + LWS_CPYAPP(*p, "\x0d\x0aSec-WebSocket-Extensions: "); + *p += sprintf(*p, "%s", ext_name); + + wsi->count_act_ext++; + lwsl_parser("count_act_ext <- %d\n", wsi->count_act_ext); + + ext++; + } + + n = 0; + } + + return 0; +} +#endif +int +handshake_0405(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + unsigned char hash[20]; + int n, accept_len; + char *response; + char *p; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || + !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { + lwsl_parser("handshake_04 missing pieces\n"); + /* completed header processing, but missing some bits */ + goto bail; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) { + lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN); + goto bail; + } + + /* + * since key length is restricted above (currently 128), cannot + * overflow + */ + n = sprintf((char *)pt->serv_buf, + "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); + + lws_SHA1(pt->serv_buf, n, hash); + + accept_len = lws_b64_encode_string((char *)hash, 20, (char *)pt->serv_buf, + context->pt_serv_buf_size); + if (accept_len < 0) { + lwsl_warn("Base64 encoded hash too long\n"); + goto bail; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) + goto bail; + + /* create the response packet */ + + /* make a buffer big enough for everything */ + + response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE; + p = response; + LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Upgrade: WebSocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Accept: "); + strcpy(p, (char *)pt->serv_buf); + p += accept_len; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) { + LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: "); + n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL); + if (n < 0) + goto bail; + p += n; + } + +#ifndef LWS_NO_EXTENSIONS + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + if (lws_extension_server_handshake(wsi, &p)) + goto bail; +#endif + + //LWS_CPYAPP(p, "\x0d\x0a""An-unknown-header: blah"); + + /* end of response packet */ + + LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a"); + + if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX, + response, p - response)) { + + /* okay send the handshake response accepting the connection */ + + lwsl_parser("issuing resp pkt %d len\n", (int)(p - response)); +#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266) + fwrite(response, 1, p - response, stderr); +#endif + n = lws_write(wsi, (unsigned char *)response, + p - response, LWS_WRITE_HTTP_HEADERS); + if (n != (p - response)) { + lwsl_debug("handshake_0405: ERROR writing to socket\n"); + goto bail; + } + + } + + /* alright clean up and set ourselves into established state */ + + wsi->state = LWSS_ESTABLISHED; + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + + { + const char * uri_ptr = + lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); + int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); + const struct lws_http_mount *hit = + lws_find_mount(wsi, uri_ptr, uri_len); + if (hit && hit->cgienv && + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + } + + return 0; + + +bail: + /* caller will free up his parsing allocations */ + return -1; +} + diff --git a/feeds/wlan-ap/libwebsocket/src/server.c b/feeds/wlan-ap/libwebsocket/src/server.c new file mode 100644 index 00000000..20b6202a --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/server.c @@ -0,0 +1,2751 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#if defined (LWS_WITH_ESP8266) +#undef memcpy +void *memcpy(void *dest, const void *src, size_t n) +{ + return ets_memcpy(dest, src, n); +} +#endif + +int +lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if LWS_POSIX + int n, opt = 1, limit = 1; +#endif + lws_sockfd_type sockfd; + struct lws_vhost *vh; + struct lws *wsi; + int m = 0; + + /* set up our external listening socket we serve on */ + + if (info->port == CONTEXT_PORT_NO_LISTEN) + return 0; + + vh = vhost->context->vhost_list; + while (vh) { + if (vh->listen_port == info->port) { + if ((!info->iface && !vh->iface) || + (info->iface && vh->iface && + !strcmp(info->iface, vh->iface))) { + vhost->listen_port = info->port; + vhost->iface = info->iface; + lwsl_notice(" using listen skt from vhost %s\n", + vh->name); + return 0; + } + } + vh = vh->vhost_next; + } + +#if LWS_POSIX +#if defined(__linux__) + limit = vhost->context->count_threads; +#endif + + for (m = 0; m < limit; m++) { +#ifdef LWS_USE_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + else +#endif +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(vhost)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd == -1) { +#else +#if defined(LWS_WITH_ESP8266) + sockfd = esp8266_create_tcp_listen_socket(vhost); + if (!lws_sockfd_valid(sockfd)) { + +#else + sockfd = mbed3_create_tcp_stream_socket(); + if (!lws_sockfd_valid(sockfd)) { +#endif +#endif + lwsl_err("ERROR opening socket\n"); + return 1; + } + +#if LWS_POSIX + /* + * allow us to restart even if old sockets in TIME_WAIT + */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&opt, sizeof(opt)) < 0) { + compatible_close(sockfd); + return 1; + } + +#if defined(LWS_USE_IPV6) && defined(IPV6_V6ONLY) + if (LWS_IPV6_ENABLED(vhost)) { + if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) { + int value = (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0; + if (setsockopt(sockfd, SOL_IPV6, IPV6_V6ONLY, + (const void*)&value, sizeof(value)) < 0) { + compatible_close(sockfd); + return 1; + } + } + } +#endif + +#if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1 + if (vhost->context->count_threads > 1) + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, + (const void *)&opt, sizeof(opt)) < 0) { + compatible_close(sockfd); + return 1; + } +#endif +#endif + lws_plat_set_socket_options(vhost, sockfd); + +#if LWS_POSIX + n = lws_socket_bind(vhost, sockfd, info->port, info->iface); + if (n < 0) + goto bail; + info->port = n; +#endif + vhost->listen_port = info->port; + vhost->iface = info->iface; + + wsi = lws_zalloc(sizeof(struct lws)); + if (wsi == NULL) { + lwsl_err("Out of mem\n"); + goto bail; + } + wsi->context = vhost->context; + wsi->sock = sockfd; + wsi->mode = LWSCM_SERVER_LISTENER; + wsi->protocol = vhost->protocols; + wsi->tsi = m; + wsi->vhost = vhost; + wsi->listener = 1; + + vhost->context->pt[m].wsi_listening = wsi; + if (insert_wsi_socket_into_fds(vhost->context, wsi)) + goto bail; + + vhost->context->count_wsi_allocated++; + vhost->lserv_wsi = wsi; + +#if LWS_POSIX + listen(wsi->sock, LWS_SOMAXCONN); + } /* for each thread able to independently listen */ +#else +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_bind(wsi->sock, info->port, wsi); +#else + mbed3_tcp_stream_bind(wsi->sock, info->port, wsi); +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { +#ifdef LWS_USE_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + lwsl_notice(" Listening on \"%s\"\n", info->iface); + else +#endif + lwsl_notice(" Listening on port %d\n", info->port); + } + + return 0; + +bail: + compatible_close(sockfd); + + return 1; +} + +int +_lws_server_listen_accept_flow_control(struct lws *twsi, int on) +{ + struct lws_context_per_thread *pt = &twsi->context->pt[(int)twsi->tsi]; + struct lws *wsi = pt->wsi_listening; + int n; + + if (!wsi || twsi->context->being_destroyed) + return 0; + + lwsl_debug("%s: Thr %d: LISTEN wsi %p: state %d\n", + __func__, twsi->tsi, (void *)wsi, on); + + if (on) + n = lws_change_pollfd(wsi, 0, LWS_POLLIN); + else + n = lws_change_pollfd(wsi, LWS_POLLIN, 0); + + return n; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername) +{ + struct lws_vhost *vhost = context->vhost_list; + const char *p; + int n, m, colon; + + n = strlen(servername); + colon = n; + p = strchr(servername, ':'); + if (p) + colon = p - servername; + + /* first try exact matches */ + + while (vhost) { + if (port == vhost->listen_port && + !strncmp(vhost->name, servername, colon)) { + lwsl_info("SNI: Found: %s\n", servername); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* + * if no exact matches, try matching *.vhost-name + * unintentional matches are possible but resolve to x.com for *.x.com + * which is reasonable. If exact match exists we already chose it and + * never reach here. SSL will still fail it if the cert doesn't allow + * *.x.com. + */ + + vhost = context->vhost_list; + while (vhost) { + m = strlen(vhost->name); + if (port == vhost->listen_port && + m <= (colon - 2) && + servername[colon - m - 1] == '.' && + !strncmp(vhost->name, servername + colon - m, m)) { + lwsl_info("SNI: Found %s on wildcard: %s\n", + servername, vhost->name); + return vhost; + } + vhost = vhost->vhost_next; + } + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) +{ + int n; + + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(name, vh->protocols[n].name)) + return &vh->protocols[n]; + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m) +{ + int n = strlen(file); + const struct lws_protocol_vhost_options *pvo = NULL; + + if (m) + pvo = m->extra_mimetypes; + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".gif")) + return "image/gif"; + + if (!strcmp(&file[n - 3], ".js")) + return "text/javascript"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 4], ".jpg")) + return "image/jpeg"; + + if (!strcmp(&file[n - 3], ".gz")) + return "application/gzip"; + + if (!strcmp(&file[n - 4], ".JPG")) + return "image/jpeg"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + if (!strcmp(&file[n - 4], ".css")) + return "text/css"; + + if (!strcmp(&file[n - 4], ".txt")) + return "text/plain"; + + if (!strcmp(&file[n - 4], ".svg")) + return "image/svg+xml"; + + if (!strcmp(&file[n - 4], ".ttf")) + return "application/x-font-ttf"; + + if (!strcmp(&file[n - 5], ".woff")) + return "application/font-woff"; + + if (!strcmp(&file[n - 4], ".xml")) + return "application/xml"; + + while (pvo) { + if (pvo->name[0] == '*') /* ie, match anything */ + return pvo->value; + + if (!strcmp(&file[n - strlen(pvo->name)], pvo->name)) + return pvo->value; + + pvo = pvo->next; + } + + return NULL; +} + +static int +lws_http_serve(struct lws *wsi, char *uri, const char *origin, + const struct lws_http_mount *m) +{ + const struct lws_protocol_vhost_options *pvo = m->interpret; + struct lws_process_html_args args; + const char *mimetype; +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + struct stat st; + int spin = 0; +#endif + char path[256], sym[512]; + unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p; + unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE; +#if !defined(WIN32) && LWS_POSIX + size_t len; +#endif + int n; + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri); + +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + do { + spin++; + + if (stat(path, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } + + lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode); +#if !defined(WIN32) && LWS_POSIX + if ((S_IFMT & st.st_mode) == S_IFLNK) { + len = readlink(path, sym, sizeof(sym) - 1); + if (len) { + lwsl_err("Failed to read link %s\n", path); + goto bail; + } + sym[len] = '\0'; + lwsl_debug("symlink %s -> %s\n", path, sym); + lws_snprintf(path, sizeof(path) - 1, "%s", sym); + } +#endif + if ((S_IFMT & st.st_mode) == S_IFDIR) { + lwsl_debug("default filename append to dir\n"); + lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html", + origin, uri); + } + + } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5); + + if (spin == 5) + lwsl_err("symlink loop %s \n", path); + + n = sprintf(sym, "%08lX%08lX", (unsigned long)st.st_size, + (unsigned long)st.st_mtime); + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) { + /* + * he thinks he has some version of it already, + * check if the tag matches + */ + if (!strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH))) { + + lwsl_debug("%s: ETAG match %s %s\n", __func__, + uri, origin); + + /* we don't need to send the payload */ + if (lws_add_http_header_status(wsi, 304, &p, end)) + return -1; + + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + n = lws_write(wsi, start, p - start, + LWS_WRITE_HTTP_HEADERS); + if (n != (p - start)) { + lwsl_err("_write returned %d from %d\n", n, p - start); + return -1; + } + + return lws_http_transaction_completed(wsi); + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; +#endif + + mimetype = lws_get_mimetype(path, m); + if (!mimetype) { + lwsl_err("unknown mimetype for %s\n", path); + goto bail; + } + if (!mimetype[0]) + lwsl_debug("sending no mimetype for %s\n", path); + + wsi->sending_chunked = 0; + + /* + * check if this is in the list of file suffixes to be interpreted by + * a protocol + */ + while (pvo) { + n = strlen(path); + if (n > (int)strlen(pvo->name) && + !strcmp(&path[n - strlen(pvo->name)], pvo->name)) { + wsi->sending_chunked = 1; + wsi->protocol_interpret_idx = (char)(long)pvo->value; + lwsl_info("want %s interpreted by %s\n", path, + wsi->vhost->protocols[(int)(long)(pvo->value)].name); + wsi->protocol = &wsi->vhost->protocols[(int)(long)(pvo->value)]; + if (lws_ensure_user_space(wsi)) + return -1; + break; + } + pvo = pvo->next; + } + + if (m->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, m->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + args.p = (char *)p; + args.max_len = end - p; + if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS, + wsi->user_space, &args, 0)) + return -1; + p = (unsigned char *)args.p; + } + + n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start); + + if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) + return -1; /* error or can't reuse connection: close the socket */ + + return 0; +bail: + + return -1; +} + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len) +{ + const struct lws_http_mount *hm, *hit = NULL; + int best = 0; + + hm = wsi->vhost->mount_list; + while (hm) { + if (uri_len >= hm->mountpoint_len && + !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) && + (uri_ptr[hm->mountpoint_len] == '\0' || + uri_ptr[hm->mountpoint_len] == '/' || + hm->mountpoint_len == 1) + ) { + if (hm->origin_protocol == LWSMPRO_CALLBACK || + ((hm->origin_protocol == LWSMPRO_CGI || + lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || + hm->protocol) && + hm->mountpoint_len > best)) { + best = hm->mountpoint_len; + hit = hm; + } + } + hm = hm->mount_next; + } + + return hit; +} + +int +lws_http_action(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + enum http_connection_type connection_type; + enum http_version request_version; + char content_length_str[32]; + struct lws_process_html_args args; + const struct lws_http_mount *hit = NULL; + unsigned int n, count = 0; + char http_version_str[10]; + char http_conn_str[20]; + int http_version_len; + char *uri_ptr = NULL, *s; + int uri_len = 0; + int meth = -1; + + static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, +#ifdef LWS_USE_HTTP2 + WSI_TOKEN_HTTP_COLON_PATH, +#endif + }; +#if defined(_DEBUG) || defined(LWS_WITH_ACCESS_LOG) + static const char * const method_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", +#ifdef LWS_USE_HTTP2 + ":path", +#endif + }; +#endif + static const char * const oprot[] = { + "http://", "https://" + }; + + /* it's not websocket.... shall we accept it as http? */ + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) + count++; + if (!count) { + lwsl_warn("Missing URI in HTTP request\n"); + goto bail_nuke_ah; + } + + if (count != 1) { + lwsl_warn("multiple methods?\n"); + goto bail_nuke_ah; + } + + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) { + uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]); + uri_len = lws_hdr_total_length(wsi, methods[n]); + lwsl_info("Method: %s request for '%s'\n", + method_names[n], uri_ptr); + meth = n; + break; + } + + (void)meth; + + /* we insist on absolute paths */ + + if (uri_ptr[0] != '/') { + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + + goto bail_nuke_ah; + } + + /* HTTP header had a content length? */ + + wsi->u.http.content_length = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)) + wsi->u.http.content_length = 100 * 1024 * 1024; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + lws_hdr_copy(wsi, content_length_str, + sizeof(content_length_str) - 1, + WSI_TOKEN_HTTP_CONTENT_LENGTH); + wsi->u.http.content_length = atoi(content_length_str); + } + + if (wsi->http2_substream) { + wsi->u.http.request_version = HTTP_VERSION_2; + } else { + /* http_version? Default to 1.0, override with token: */ + request_version = HTTP_VERSION_1_0; + + /* Works for single digit HTTP versions. : */ + http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP); + if (http_version_len > 7) { + lws_hdr_copy(wsi, http_version_str, + sizeof(http_version_str) - 1, WSI_TOKEN_HTTP); + if (http_version_str[5] == '1' && http_version_str[7] == '1') + request_version = HTTP_VERSION_1_1; + } + wsi->u.http.request_version = request_version; + + /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */ + if (request_version == HTTP_VERSION_1_1) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + connection_type = HTTP_CONNECTION_CLOSE; + + /* Override default if http "Connection:" header: */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { + lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1, + WSI_TOKEN_CONNECTION); + http_conn_str[sizeof(http_conn_str) - 1] = '\0'; + if (!strcasecmp(http_conn_str, "keep-alive")) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + if (!strcasecmp(http_conn_str, "close")) + connection_type = HTTP_CONNECTION_CLOSE; + } + wsi->u.http.connection_type = connection_type; + } + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION, + wsi->user_space, uri_ptr, uri_len); + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + /* + * if there is content supposed to be coming, + * put a timeout on it having arrived + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); +#ifdef LWS_OPENSSL_SUPPORT + if (wsi->redirect_to_https) { + /* + * we accepted http:// only so we could redirect to + * https://, so issue the redirect. Create the redirection + * URI from the host: header and ignore the path part + */ + unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, + *end = p + 512; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) + goto bail_nuke_ah; + + n = sprintf((char *)end, "https://%s/", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } +#endif + +#ifdef LWS_WITH_ACCESS_LOG + /* + * Produce Apache-compatible log string for wsi, like this: + * + * 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800] + * "GET /aep-screen.png HTTP/1.1" + * 200 152987 "https://libwebsockets.org/index.html" + * "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36" + * + */ + { + static const char * const hver[] = { + "http/1.0", "http/1.1", "http/2" + }; +#ifdef LWS_USE_IPV6 + char ads[INET6_ADDRSTRLEN]; +#else + char ads[INET_ADDRSTRLEN]; +#endif + char da[64]; + const char *pa, *me; + struct tm *tmp; + time_t t = time(NULL); + int l = 256; + + if (wsi->access_log_pending) + lws_access_log(wsi); + + wsi->access_log.header_log = lws_malloc(l); + if (wsi->access_log.header_log) { + + tmp = localtime(&t); + if (tmp) + strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp); + else + strcpy(da, "01/Jan/1970:00:00:00 +0000"); + + pa = lws_get_peer_simple(wsi, ads, sizeof(ads)); + if (!pa) + pa = "(unknown)"; + + if (meth >= 0) + me = method_names[meth]; + else + me = "unknown"; + + lws_snprintf(wsi->access_log.header_log, l, + "%s - - [%s] \"%s %s %s\"", + pa, da, me, uri_ptr, + hver[wsi->u.http.request_version]); + + l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT); + if (l) { + wsi->access_log.user_agent = lws_malloc(l + 2); + if (wsi->access_log.user_agent) + lws_hdr_copy(wsi, wsi->access_log.user_agent, + l + 1, WSI_TOKEN_HTTP_USER_AGENT); + else + lwsl_err("OOM getting user agent\n"); + } + wsi->access_log_pending = 1; + } + } +#endif + + /* can we serve it from the mount list? */ + + hit = lws_find_mount(wsi, uri_ptr, uri_len); + if (!hit) { + /* deferred cleanup and reset to protocols[0] */ + + lwsl_info("no hit\n"); + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + + goto after; + } + + s = uri_ptr + hit->mountpoint_len; + + args.p = uri_ptr; + args.len = uri_len; + args.max_len = hit->auth_mask; + args.final = 0; /* used to signal callback dealt with it */ + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS, + wsi->user_space, &args, 0); + if (n) { + lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED, + NULL); + goto bail_nuke_ah; + } + if (args.final) /* callback completely handled it well */ + return 0; + + /* + * if we have a mountpoint like https://xxx.com/yyy + * there is an implied / at the end for our purposes since + * we can only mount on a "directory". + * + * But if we just go with that, the browser cannot understand + * that he is actually looking down one "directory level", so + * even though we give him /yyy/abc.html he acts like the + * current directory level is /. So relative urls like "x.png" + * wrongly look outside the mountpoint. + * + * Therefore if we didn't come in on a url with an explicit + * / at the end, we must redirect to add it so the browser + * understands he is one "directory level" down. + */ + if ((hit->mountpoint_len > 1 || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (*s != '/' || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (hit->origin_protocol != LWSMPRO_CGI && + hit->origin_protocol != LWSMPRO_CALLBACK //&& + //hit->protocol == NULL + )) { + unsigned char *start = pt->serv_buf + LWS_PRE, + *p = start, *end = p + 512; + + lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin); + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) + goto bail_nuke_ah; + + /* > at start indicates deal with by redirect */ + if (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS) + n = lws_snprintf((char *)end, 256, "%s%s", + oprot[hit->origin_protocol & 1], + hit->origin); + else + n = lws_snprintf((char *)end, 256, + "%s%s%s/", oprot[lws_is_ssl(wsi)], + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), + uri_ptr); + + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } + + /* + * A particular protocol callback is mounted here? + * + * For the duration of this http transaction, bind us to the + * associated protocol + */ + if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) { + const struct lws_protocols *pp; + const char *name = hit->origin; + if (hit->protocol) + name = hit->protocol; + + pp = lws_vhost_name_to_protocol(wsi->vhost, name); + if (!pp) { + n = -1; + lwsl_err("Unable to find plugin '%s'\n", + hit->origin); + return 1; + } + + if (lws_bind_protocol(wsi, pp)) + return 1; + + if (hit->cgienv && wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + goto after; + } + } + +#ifdef LWS_WITH_CGI + /* did we hit something with a cgi:// origin? */ + if (hit->origin_protocol == LWSMPRO_CGI) { + const char *cmd[] = { + NULL, /* replace with cgi path */ + NULL + }; + unsigned char *p, *end, buffer[1024]; + + lwsl_debug("%s: cgi\n", __func__); + cmd[0] = hit->origin; + + n = 5; + if (hit->cgi_timeout) + n = hit->cgi_timeout; + + n = lws_cgi(wsi, cmd, hit->mountpoint_len, n, + hit->cgienv); + if (n) { + lwsl_err("%s: cgi failed\n"); + return -1; + } + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + + if (lws_add_http_header_status(wsi, 200, &p, end)) + return 1; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, + (unsigned char *)"close", 5, &p, end)) + return 1; + n = lws_write(wsi, buffer + LWS_PRE, + p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + + goto deal_body; + } +#endif + + n = strlen(s); + if (s[0] == '\0' || (n == 1 && s[n - 1] == '/')) + s = (char *)hit->def; + if (!s) + s = "index.html"; + + wsi->cache_secs = hit->cache_max_age; + wsi->cache_reuse = hit->cache_reusable; + wsi->cache_revalidate = hit->cache_revalidate; + wsi->cache_intermediaries = hit->cache_intermediaries; + + n = lws_http_serve(wsi, s, hit->origin, hit); + if (n) { + /* + * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + */ + if (hit->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, hit->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + + n = pp->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + } else + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + } + +after: + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + +#ifdef LWS_WITH_CGI +deal_body: +#endif + /* + * If we're not issuing a file, check for content_length or + * HTTP keep-alive. No keep-alive header allocation for + * ISSUING_FILE, as this uses HTTP/1.0. + * + * In any case, return 0 and let lws_read decide how to + * proceed based on state + */ + if (wsi->state != LWSS_HTTP_ISSUING_FILE) + /* Prepare to read body if we have a content length: */ + if (wsi->u.http.content_length > 0) + wsi->state = LWSS_HTTP_BODY; + + return 0; + +bail_nuke_ah: + /* we're closing, losing some rx is OK */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + // lwsl_notice("%s: drop1\n", __func__); + lws_header_table_detach(wsi, 1); + + return 1; +} + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p) +{ +// if (wsi->protocol == p) +// return 0; + + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + if (!wsi->user_space_externally_allocated) + lws_free_set_NULL(wsi->user_space); + + wsi->protocol = p; + if (!p) + return 0; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL, + wsi->user_space, NULL, 0)) + return 1; + + return 0; +} + + +int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct _lws_header_related hdr; + struct allocated_headers *ah; + int protocol_len, n = 0, hit; + char protocol_list[128]; + char protocol_name[64]; + char *p; + + if (len >= 10000000) { + lwsl_err("%s: assert: len %ld\n", __func__, (long)len); + assert(0); + } + + if (!wsi->u.hdr.ah) { + lwsl_err("%s: assert: NULL ah\n", __func__); + assert(0); + } + + while (len--) { + wsi->more_rx_waiting = !!len; + + if (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode); + goto bail_nuke_ah; + } + + if (lws_parse(wsi, *(*buf)++)) { + lwsl_info("lws_parse failed\n"); + goto bail_nuke_ah; + } + + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + continue; + + lwsl_parser("%s: lws_parse sees parsing complete\n", __func__); + lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + /* check for unwelcome guests */ + + if (wsi->context->reject_service_keywords) { + const struct lws_protocol_vhost_options *rej = + wsi->context->reject_service_keywords; + char ua[384], *msg = NULL; + + if (!lws_hdr_copy(wsi, ua, sizeof(ua) - 1, + WSI_TOKEN_HTTP_USER_AGENT)) { + ua[sizeof(ua) - 1] = '\0'; + while (rej) { + if (strstr(ua, rej->name)) { + msg = strchr(rej->value, ' '); + if (msg) + msg++; + lws_return_http_status(wsi, atoi(rej->value), msg); + + goto bail_nuke_ah; + } + rej = rej->next; + } + } + } + + /* select vhost */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + struct lws_vhost *vhost = lws_select_vhost( + context, wsi->vhost->listen_port, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + if (vhost) + wsi->vhost = vhost; + } else + lwsl_info("no host\n"); + + wsi->vhost->trans++; + if (!wsi->conn_stat_done) { + wsi->vhost->conn++; + wsi->conn_stat_done = 1; + } + + wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT; + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* is this websocket protocol or normal http 1.0? */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "websocket")) { + wsi->vhost->ws_upgrades++; + lwsl_info("Upgrade to ws\n"); + goto upgrade_ws; + } +#ifdef LWS_USE_HTTP2 + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "h2c")) { + wsi->vhost->http2_upgrades++; + lwsl_info("Upgrade to h2c\n"); + goto upgrade_h2c; + } +#endif + lwsl_info("Unknown upgrade\n"); + /* dunno what he wanted to upgrade to */ + goto bail_nuke_ah; + } + + /* no upgrade ack... he remained as HTTP */ + + lwsl_info("No upgrade\n"); + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED); + wsi->state = LWSS_HTTP; + wsi->u.http.fd = LWS_INVALID_FILE; + + /* expose it at the same offset as u.hdr */ + wsi->u.http.ah = ah; + lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, + (void *)wsi->u.hdr.ah); + + n = lws_http_action(wsi); + + return n; + +#ifdef LWS_USE_HTTP2 +upgrade_h2c: + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) { + lwsl_info("missing http2_settings\n"); + goto bail_nuke_ah; + } + + lwsl_info("h2c upgrade...\n"); + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS); + /* convert the peer's HTTP-Settings */ + n = lws_b64_decode_string(p, protocol_list, + sizeof(protocol_list)); + if (n < 0) { + lwsl_parser("HTTP2_SETTINGS too long\n"); + return 1; + } + + /* adopt the header info */ + + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP2_SERVING); + + /* http2 union member has http union struct at start */ + wsi->u.http.ah = ah; + + lws_http2_init(&wsi->u.http2.peer_settings); + lws_http2_init(&wsi->u.http2.my_settings); + + /* HTTP2 union */ + + lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings, + (unsigned char *)protocol_list, n); + + strcpy(protocol_list, + "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Upgrade: h2c\x0d\x0a\x0d\x0a"); + n = lws_issue_raw(wsi, (unsigned char *)protocol_list, + strlen(protocol_list)); + if (n != strlen(protocol_list)) { + lwsl_debug("http2 switch: ERROR writing to socket\n"); + return 1; + } + + wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE; + + return 0; +#endif + +upgrade_ws: + if (!wsi->protocol) + lwsl_err("NULL protocol at lws_read\n"); + + /* + * It's websocket + * + * Select the first protocol we support from the list + * the client sent us. + * + * Copy it to remove header fragmentation + */ + + if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1, + WSI_TOKEN_PROTOCOL) < 0) { + lwsl_err("protocol list too long"); + goto bail_nuke_ah; + } + + protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + protocol_list[protocol_len] = '\0'; + p = protocol_list; + hit = 0; + + while (*p && !hit) { + n = 0; + while (n < sizeof(protocol_name) - 1 && *p && *p !=',') + protocol_name[n++] = *p++; + protocol_name[n] = '\0'; + if (*p) + p++; + + lwsl_info("checking %s\n", protocol_name); + + n = 0; + while (wsi->vhost->protocols[n].callback) { + if (wsi->vhost->protocols[n].name && + !strcmp(wsi->vhost->protocols[n].name, + protocol_name)) { + wsi->protocol = &wsi->vhost->protocols[n]; + hit = 1; + break; + } + + n++; + } + } + + /* we didn't find a protocol he wanted? */ + + if (!hit) { + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) { + lwsl_info("No protocol from \"%s\" supported\n", + protocol_list); + goto bail_nuke_ah; + } + /* + * some clients only have one protocol and + * do not send the protocol list header... + * allow it and match to the vhost's default + * protocol (which itself defaults to zero) + */ + lwsl_info("defaulting to prot handler %d\n", + wsi->vhost->default_protocol_index); + n = 0; + wsi->protocol = &wsi->vhost->protocols[ + (int)wsi->vhost->default_protocol_index]; + } + + /* allocate wsi->user storage */ + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* + * Give the user code a chance to study the request and + * have the opportunity to deny it + */ + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, + wsi->user_space, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { + lwsl_warn("User code denied connection\n"); + goto bail_nuke_ah; + } + + /* + * Perform the handshake according to the protocol version the + * client announced + */ + + switch (wsi->ietf_spec_revision) { + case 13: + lwsl_parser("lws_parse calling handshake_04\n"); + if (handshake_0405(context, wsi)) { + lwsl_info("hs0405 has failed the connection\n"); + goto bail_nuke_ah; + } + break; + + default: + lwsl_info("Unknown client spec version %d\n", + wsi->ietf_spec_revision); + goto bail_nuke_ah; + } + + /* + * stitch protocol choice into the vh protocol linked list + * We always insert ourselves at the start of the list + * + * X <-> B + * X <-> pAn <-> pB + */ + //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n", + // __func__, + // wsi->vhost->same_vh_protocol_list[n], + // wsi->same_vh_protocol_prev); + wsi->same_vh_protocol_prev = /* guy who points to us */ + &wsi->vhost->same_vh_protocol_list[n]; + wsi->same_vh_protocol_next = /* old first guy is our next */ + wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; + + + + /* we are upgrading to ws, so http/1.1 and keepalive + + * pipelined header considerations about keeping the ah around + * no longer apply. However it's common for the first ws + * protocol data to have been coalesced with the browser + * upgrade request and to already be in the ah rx buffer. + */ + + lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n", + __func__, wsi, wsi->u.hdr.ah->rxpos, + wsi->u.hdr.ah->rxlen); + lws_pt_lock(pt); + hdr = wsi->u.hdr; + + lws_union_transition(wsi, LWSCM_WS_SERVING); + /* + * first service is WS mode will notice this, use the RX and + * then detach the ah (caution: we are not in u.hdr union + * mode any more then... ah_temp member is at start the same + * though) + * + * Because rxpos/rxlen shows something in the ah, we will get + * service guaranteed next time around the event loop + * + * All union members begin with hdr, so we can use it even + * though we transitioned to ws union mode (the ah detach + * code uses it anyway). + */ + wsi->u.hdr = hdr; + lws_pt_unlock(pt); + + lws_restart_ws_ping_pong_timer(wsi); + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, use + * a big default for compatibility + */ + + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + return 1; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_debug("Allocating RX buffer %d\n", n); +#if LWS_POSIX + if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, + (const char *)&n, sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + return 1; + } +#endif + + lwsl_parser("accepted v%02d connection\n", + wsi->ietf_spec_revision); + + /* notify user code that we're ready to roll */ + + if (wsi->protocol->callback) + if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, + wsi->user_space, +#ifdef LWS_OPENSSL_SUPPORT + wsi->ssl, +#else + NULL, +#endif + 0)) + return 1; + + /* !!! drop ah unreservedly after ESTABLISHED */ + if (!wsi->more_rx_waiting) { + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + + //lwsl_notice("%p: dropping ah EST\n", wsi); + lws_header_table_detach(wsi, 1); + } + + return 0; + } /* while all chars are handled */ + + return 0; + +bail_nuke_ah: + /* drop the header info */ + /* we're closing, losing some rx is OK */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + //lwsl_notice("%s: drop2\n", __func__); + lws_header_table_detach(wsi, 1); + + return 1; +} + +static int +lws_get_idlest_tsi(struct lws_context *context) +{ + unsigned int lowest = ~0; + int n = 0, hit = -1; + + for (; n < context->count_threads; n++) { + if ((unsigned int)context->pt[n].fds_count != + context->fd_limit_per_thread - 1 && + (unsigned int)context->pt[n].fds_count < lowest) { + lowest = context->pt[n].fds_count; + hit = n; + } + } + + return hit; +} + +struct lws * +lws_create_new_server_wsi(struct lws_vhost *vhost) +{ + struct lws *new_wsi; + int n = lws_get_idlest_tsi(vhost->context); + + if (n < 0) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws)); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = n; + lwsl_info("Accepted %p to tsi %d\n", new_wsi, new_wsi->tsi); + + new_wsi->vhost = vhost; + new_wsi->context = vhost->context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_HTTP; + new_wsi->mode = LWSCM_HTTP_SERVING; + new_wsi->hdr_parsing_completed = 0; + +#ifdef LWS_OPENSSL_SUPPORT + new_wsi->use_ssl = LWS_SSL_ENABLED(vhost); +#endif + + /* + * these can only be set once the protocol is known + * we set an unestablished connection's protocol pointer + * to the start of the supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = vhost->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->sock = LWS_SOCK_INVALID; + vhost->context->count_wsi_allocated++; + + /* + * outermost create notification for wsi + * no user_space because no protocol selection + */ + vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, + NULL, NULL, 0); + + return new_wsi; +} + +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi) +{ + int n = NO_PENDING_TIMEOUT; + + lws_access_log(wsi); + + lwsl_info("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_HTTP; + wsi->mode = LWSCM_HTTP_SERVING; + wsi->u.http.content_length = 0; + wsi->u.http.content_remain = 0; + wsi->hdr_parsing_completed = 0; +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent = 0; +#endif + + if (wsi->vhost->keepalive_timeout) + n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE; + lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout); + + /* + * We already know we are on http1.1 / keepalive and the next thing + * coming will be another header set. + * + * If there is no pending rx and we still have the ah, drop it and + * reacquire a new ah when the new headers start to arrive. (Otherwise + * we needlessly hog an ah indefinitely.) + * + * However if there is pending rx and we know from the keepalive state + * that is already at least the start of another header set, simply + * reset the existing header table and keep it. + */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + if (!wsi->more_rx_waiting) { + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + lws_header_table_detach(wsi, 1); + } else + lws_header_table_reset(wsi, 1); + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +} + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) +{ + struct lws_context *context = vh->context; + struct lws *new_wsi = lws_create_new_server_wsi(vh); + + if (!new_wsi) { + compatible_close(accept_fd); + return NULL; + } + + //lwsl_notice("%s: new wsi %p, sockfd %d, cb %p\n", __func__, new_wsi, accept_fd, context->vhost_list->protocols[0].callback); + + new_wsi->sock = accept_fd; + + /* the transport is accepted... give him time to negotiate */ + lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + +#if LWS_POSIX == 0 +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_accept(accept_fd, new_wsi); +#else + mbed3_tcp_stream_accept(accept_fd, new_wsi); +#endif +#endif + /* + * A new connection was accepted. Give the user a chance to + * set properties of the newly created wsi. There's no protocol + * selected yet so we issue this to protocols[0] + */ + if ((context->vhost_list->protocols[0].callback)(new_wsi, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0)) { + /* force us off the timeout list by hand */ + lws_set_timeout(new_wsi, NO_PENDING_TIMEOUT, 0); + compatible_close(new_wsi->sock); + lws_free(new_wsi); + return NULL; + } + + lws_libev_accept(new_wsi, new_wsi->sock); + lws_libuv_accept(new_wsi, new_wsi->sock); + + if (!LWS_SSL_ENABLED(new_wsi->vhost)) { + if (insert_wsi_socket_into_fds(context, new_wsi)) { + lwsl_err("%s: fail inserting socket\n", __func__); + goto fail; + } + } else { + new_wsi->mode = LWSCM_SSL_INIT; + if (lws_server_socket_service_ssl(new_wsi, accept_fd)) { + lwsl_err("%s: fail ssl negotiation\n", __func__); + goto fail; + } + } + + if (!lws_header_table_attach(new_wsi, 0)) + lwsl_debug("Attached ah immediately\n"); + + return new_wsi; + +fail: + lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) +{ + return lws_adopt_socket_vhost(context->vhost_list, accept_fd); +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + struct lws *wsi = lws_adopt_socket(context, accept_fd); + struct lws_context_per_thread *pt; + struct allocated_headers *ah; + struct lws_pollfd *pfd; + + if (!wsi) + return NULL; + + if (!readbuf) + return wsi; + + if (len > sizeof(ah->rx)) { + lwsl_err("%s: rx in too big\n", __func__); + goto bail; + } + + /* + * we can't process the initial read data until we can attach an ah. + * + * if one is available, get it and place the data in his ah rxbuf... + * wsi with ah that have pending rxbuf get auto-POLLIN service. + * + * no autoservice because we didn't get a chance to attach the + * readbuf data to wsi or ah yet, and we will do it next if we get + * the ah. + */ + if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) { + ah = wsi->u.hdr.ah; + memcpy(ah->rx, readbuf, len); + ah->rxpos = 0; + ah->rxlen = len; + + lwsl_notice("%s: calling service on readbuf ah\n", __func__); + pt = &context->pt[(int)wsi->tsi]; + + /* unlike a normal connect, we have the headers already + * (or the first part of them anyway). + * libuv won't come back and service us without a network + * event, so we need to do the header service right here. + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + if (lws_service_fd_tsi(context, pfd, wsi->tsi)) + /* service closed us */ + return NULL; + + return wsi; + } + lwsl_err("%s: deferring handling ah\n", __func__); + /* + * hum if no ah came, we are on the wait list and must defer + * dealing with this until the ah arrives. + * + * later successful lws_header_table_attach() will apply the + * below to the rx buffer (via lws_header_table_reset()). + */ + wsi->u.hdr.preamble_rx = lws_malloc(len); + if (!wsi->u.hdr.preamble_rx) { + lwsl_err("OOM\n"); + goto bail; + } + memcpy(wsi->u.hdr.preamble_rx, readbuf, len); + wsi->u.hdr.preamble_rx_len = len; + + return wsi; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +LWS_VISIBLE int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + lws_sockfd_type accept_fd = LWS_SOCK_INVALID; + struct allocated_headers *ah; +#if LWS_POSIX + struct sockaddr_in cli_addr; + socklen_t clilen; +#endif + int n, len; + + // lwsl_notice("%s: mode %d\n", __func__, wsi->mode); + + switch (wsi->mode) { + + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_HTTP2_SERVING: + + /* handle http headers coming in */ + + /* pending truncated sends have uber priority */ + + if (wsi->trunc_len) { + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) + goto fail; + /* + * we can't afford to allow input processing to send + * something new, so spin around he event loop until + * he doesn't have any partials + */ + break; + } + + /* any incoming data ready? */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + goto try_pollout; + + /* + * If we previously just did POLLIN when IN and OUT were + * signalled (because POLLIN processing may have used up + * the POLLOUT), don't let that happen twice in a row... + * next time we see the situation favour POLLOUT + */ +#if !defined(LWS_WITH_ESP8266) + if (wsi->favoured_pollin && + (pollfd->revents & pollfd->events & LWS_POLLOUT)) { + wsi->favoured_pollin = 0; + goto try_pollout; + } +#endif + /* these states imply we MUST have an ah attached */ + + if (wsi->state == LWSS_HTTP || + wsi->state == LWSS_HTTP_ISSUING_FILE || + wsi->state == LWSS_HTTP_HEADERS) { + if (!wsi->u.hdr.ah) { + + //lwsl_err("wsi %p: missing ah\n", wsi); + /* no autoservice beacuse we will do it next */ + if (lws_header_table_attach(wsi, 0)) { + lwsl_err("wsi %p: failed to acquire ah\n", wsi); + goto try_pollout; + } + } + ah = wsi->u.hdr.ah; + + //lwsl_notice("%s: %p: rxpos:%d rxlen:%d\n", __func__, wsi, + // ah->rxpos, ah->rxlen); + + /* if nothing in ah rx buffer, get some fresh rx */ + if (ah->rxpos == ah->rxlen) { + ah->rxlen = lws_ssl_capable_read(wsi, ah->rx, + sizeof(ah->rx)); + ah->rxpos = 0; + //lwsl_notice("%s: wsi %p, ah->rxlen = %d\r\n", + // __func__, wsi, ah->rxlen); + switch (ah->rxlen) { + case 0: + lwsl_info("%s: read 0 len\n", __func__); + /* lwsl_info(" state=%d\n", wsi->state); */ +// if (!wsi->hdr_parsing_completed) +// lws_header_table_detach(wsi); + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + ah->rxlen = ah->rxpos = 0; + goto try_pollout; + } + } + + if (!(ah->rxpos != ah->rxlen && ah->rxlen)) { + lwsl_err("%s: assert: rxpos %d, rxlen %d\n", + __func__, ah->rxpos, ah->rxlen); + + assert(0); + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + n = lws_read(wsi, ah->rx + ah->rxpos, + ah->rxlen - ah->rxpos); + if (n < 0) /* we closed wsi */ + return 1; + if (wsi->u.hdr.ah) { + if ( wsi->u.hdr.ah->rxlen) + wsi->u.hdr.ah->rxpos += n; + + lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n", __func__, wsi, wsi->u.hdr.ah->rxpos, wsi->u.hdr.ah->rxlen); + + if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen && + (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED && + wsi->mode != LWSCM_HTTP2_SERVING)) + lws_header_table_detach(wsi, 1); + } + break; + } + + goto try_pollout; + } + + len = lws_ssl_capable_read(wsi, pt->serv_buf, + context->pt_serv_buf_size); + lwsl_notice("%s: wsi %p read %d\r\n", __func__, wsi, len); + switch (len) { + case 0: + lwsl_info("%s: read 0 len\n", __func__); + /* lwsl_info(" state=%d\n", wsi->state); */ +// if (!wsi->hdr_parsing_completed) +// lws_header_table_detach(wsi); + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + goto try_pollout; + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + /* + * this may want to send + * (via HTTP callback for example) + */ + n = lws_read(wsi, pt->serv_buf, len); + if (n < 0) /* we closed wsi */ + return 1; + /* + * he may have used up the + * writability above, if we will defer POLLOUT + * processing in favour of POLLIN, note it + */ + if (pollfd->revents & LWS_POLLOUT) + wsi->favoured_pollin = 1; + break; + } + +try_pollout: + + /* this handles POLLOUT for http serving fragments */ + + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + /* one shot */ + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_notice("%s a\n", __func__); + goto fail; + } + + if (!wsi->hdr_parsing_completed) + break; + + if (wsi->state != LWSS_HTTP_ISSUING_FILE) { + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_WRITEABLE, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("writeable_fail\n"); + goto fail; + } + break; + } + + /* >0 == completion, <0 == error */ + n = lws_serve_http_file_fragment(wsi); + if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) { + lwsl_info("completed\n"); + goto fail; + } + + break; + + case LWSCM_SERVER_LISTENER: + +#if LWS_POSIX + /* pollin means a client has connected to us then */ + + do { + if (!(pollfd->revents & LWS_POLLIN) || !(pollfd->events & LWS_POLLIN)) + break; + + /* listen socket got an unencrypted connection... */ + + clilen = sizeof(cli_addr); + lws_latency_pre(context, wsi); + accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, + &clilen); + lws_latency(context, wsi, "listener accept", accept_fd, + accept_fd >= 0); + if (accept_fd < 0) { + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK) { + lwsl_err("accept asks to try again\n"); + break; + } + lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO)); + break; + } + + lws_plat_set_socket_options(wsi->vhost, accept_fd); + + lwsl_debug("accepted new conn port %u on fd=%d\n", + ntohs(cli_addr.sin_port), accept_fd); + +#else + /* not very beautiful... */ + accept_fd = (lws_sockfd_type)pollfd; +#endif + /* + * look at who we connected to and give user code a chance + * to reject based on client IP. There's no protocol selected + * yet so we issue this to protocols[0] + */ + if ((wsi->vhost->protocols[0].callback)(wsi, + LWS_CALLBACK_FILTER_NETWORK_CONNECTION, + NULL, (void *)(long)accept_fd, 0)) { + lwsl_debug("Callback denied network connection\n"); + compatible_close(accept_fd); + break; + } + + if (!lws_adopt_socket_vhost(wsi->vhost, accept_fd)) + /* already closed cleanly as necessary */ + return 1; + +#if LWS_POSIX + } while (pt->fds_count < context->fd_limit_per_thread - 1 && + lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0); +#endif + return 0; + + default: + break; + } + + if (!lws_server_socket_service_ssl(wsi, accept_fd)) + return 0; + +fail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; +} + +LWS_VISIBLE int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len) +{ + static const char * const intermediates[] = { "private", "public" }; + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char cache_control[50], *cc = "no-store"; + unsigned char *response = pt->serv_buf + LWS_PRE; + unsigned char *p = response; + unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; + int ret = 0, cclen = 8; + + wsi->u.http.fd = lws_plat_file_open(wsi, file, &wsi->u.http.filelen, + O_RDONLY); + + if (wsi->u.http.fd == LWS_INVALID_FILE) { + lwsl_err("Unable to open '%s'\n", file); + + return -1; + } + + if (lws_add_http_header_status(wsi, 200, &p, end)) + return -1; + if (content_type && content_type[0]) { + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)content_type, + strlen(content_type), &p, end)) + return -1; + } + + if (!wsi->sending_chunked) { + if (lws_add_http_header_content_length(wsi, wsi->u.http.filelen, &p, end)) + return -1; + } else { + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", + 7, &p, end)) + return -1; + } + + if (wsi->cache_secs && wsi->cache_reuse) { + if (wsi->cache_revalidate) { + cc = cache_control; + cclen = sprintf(cache_control, "%s max-age: %u", + intermediates[wsi->cache_intermediaries], + wsi->cache_secs); + } else { + cc = "no-cache"; + cclen = 8; + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL, + (unsigned char *)cc, cclen, &p, end)) + return -1; + + if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, + (unsigned char *)"keep-alive", 10, &p, end)) + return -1; + + if (other_headers) { + if ((end - p) < other_headers_len) + return -1; + memcpy(p, other_headers, other_headers_len); + p += other_headers_len; + } + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); + if (ret != (p - response)) { + lwsl_err("_write returned %d from %d\n", ret, (p - response)); + return -1; + } + + wsi->u.http.filepos = 0; + wsi->state = LWSS_HTTP_ISSUING_FILE; + + return lws_serve_http_file_fragment(wsi); +} + +int +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + lwsl_parser("%s: received %d byte packet\n", __func__, (int)len); +#if 0 + lwsl_hexdump(*buf, len); +#endif + + /* let the rx protocol state machine have as much as it needs */ + + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) { + lws_rxflow_cache(wsi, *buf, 0, len); + lwsl_parser("%s: cached %d\n", __func__, len); + return 1; + } + + if (wsi->u.ws.rx_draining_ext) { + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) + wsi->rxflow_pos++; + + /* consume payload bytes efficiently */ + if (wsi->lws_rx_parse_state == + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) { + m = lws_payload_until_length_exhausted(wsi, buf, &len); + if (wsi->rxflow_buffer) + wsi->rxflow_pos += m; + } + + /* process the byte */ + m = lws_rx_sm(wsi, *(*buf)++); + if (m < 0) + return -1; + len--; + } + + lwsl_parser("%s: exit with %d unused\n", __func__, (int)len); + + return 0; +} + +LWS_VISIBLE void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) + return; +#if LWS_POSIX + /* find canonical hostname */ + gethostname((char *)context->canonical_hostname, + sizeof(context->canonical_hostname) - 1); + + lwsl_notice(" canonical_hostname = %s\n", context->canonical_hostname); +#else + (void)context; +#endif +} + +#define LWS_MAX_ELEM_NAME 32 + +enum urldecode_stateful { + US_NAME, + US_IDLE, + US_PC1, + US_PC2, + + MT_LOOK_BOUND_IN, + MT_HNAME, + MT_DISP, + MT_TYPE, + MT_IGNORE1, + MT_IGNORE2, +}; + +static const char * const mp_hdr[] = { + "content-disposition: ", + "content-type: ", + "\x0d\x0a" +}; + +typedef int (*lws_urldecode_stateful_cb)(void *data, + const char *name, char **buf, int len, int final); + +struct lws_urldecode_stateful { + char *out; + void *data; + char name[LWS_MAX_ELEM_NAME]; + char temp[LWS_MAX_ELEM_NAME]; + char content_type[32]; + char content_disp[32]; + char content_disp_filename[256]; + char mime_boundary[128]; + int out_len; + int pos; + int hdr_idx; + int mp; + + unsigned int multipart_form_data:1; + unsigned int inside_quote:1; + unsigned int subname:1; + unsigned int boundary_real_crlf:1; + + enum urldecode_stateful state; + + lws_urldecode_stateful_cb output; +}; + +static struct lws_urldecode_stateful * +lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data, + lws_urldecode_stateful_cb output) +{ + struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s)); + char buf[200], *p; + int m = 0; + + if (!s) + return NULL; + + s->out = out; + s->out_len = out_len; + s->output = output; + s->pos = 0; + s->mp = 0; + s->state = US_NAME; + s->name[0] = '\0'; + s->data = data; + + if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) { + /* multipart/form-data; boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */ + + if (!strncmp(buf, "multipart/form-data", 19)) { + s->multipart_form_data = 1; + s->state = MT_LOOK_BOUND_IN; + s->mp = 2; + p = strstr(buf, "boundary="); + if (p) { + p += 9; + s->mime_boundary[m++] = '\x0d'; + s->mime_boundary[m++] = '\x0a'; + s->mime_boundary[m++] = '-'; + s->mime_boundary[m++] = '-'; + while (m < sizeof(s->mime_boundary) - 1 && + *p && *p != ' ') + s->mime_boundary[m++] = *p++; + + s->mime_boundary[m] = '\0'; + + lwsl_notice("boundary '%s'\n", s->mime_boundary); + } + } + } + + return s; +} + +static int +lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in, int len) +{ + int n, m, hit = 0; + char sum = 0, c, was_end = 0; + + while (len--) { + if (s->pos == s->out_len - s->mp - 1) { + if (s->output(s->data, s->name, &s->out, s->pos, 0)) + return -1; + + was_end = s->pos; + s->pos = 0; + } + switch (s->state) { + + /* states for url arg style */ + + case US_NAME: + s->inside_quote = 0; + if (*in == '=') { + s->name[s->pos] = '\0'; + s->pos = 0; + s->state = US_IDLE; + in++; + continue; + } + if (*in == '&') { + s->name[s->pos] = '\0'; + if (s->output(s->data, s->name, &s->out, s->pos, 1)) + return -1; + s->pos = 0; + s->state = US_IDLE; + in++; + continue; + } + if (s->pos >= sizeof(s->name) - 1) { + lwsl_notice("Name too long\n"); + return -1; + } + s->name[s->pos++] = *in++; + break; + case US_IDLE: + if (*in == '%') { + s->state++; + in++; + continue; + } + if (*in == '&') { + s->out[s->pos] = '\0'; + if (s->output(s->data, s->name, &s->out, s->pos, 1)) + return -1; + s->pos = 0; + s->state = US_NAME; + in++; + continue; + } + if (*in == '+') { + in++; + s->out[s->pos++] = ' '; + continue; + } + s->out[s->pos++] = *in++; + break; + case US_PC1: + n = char_to_hex(*in); + if (n < 0) + return -1; + + in++; + sum = n << 4; + s->state++; + break; + + case US_PC2: + n = char_to_hex(*in); + if (n < 0) + return -1; + + in++; + s->out[s->pos++] = sum | n; + s->state = US_IDLE; + break; + + + /* states for multipart / mime style */ + + case MT_LOOK_BOUND_IN: +retry_as_first: + if (*in == s->mime_boundary[s->mp] && + s->mime_boundary[s->mp]) { + in++; + s->mp++; + if (!s->mime_boundary[s->mp]) { + s->mp = 0; + s->state = MT_IGNORE1; + + if (s->pos || was_end) + if (s->output(s->data, s->name, + &s->out, s->pos, 1)) + return -1; + + s->pos = 0; + + s->content_disp[0] = '\0'; + s->name[0] = '\0'; + s->content_disp_filename[0] = '\0'; + s->boundary_real_crlf = 1; + } + continue; + } + if (s->mp) { + n = 0; + if (!s->boundary_real_crlf) + n = 2; + + memcpy(s->out + s->pos, s->mime_boundary + n, s->mp - n); + s->pos += s->mp; + s->mp = 0; + goto retry_as_first; + } + + s->out[s->pos++] = *in; + in++; + s->mp = 0; + break; + + case MT_HNAME: + m = 0; + c =*in; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + for (n = 0; n < ARRAY_SIZE(mp_hdr); n++) + if (c == mp_hdr[n][s->mp]) { + m++; + hit = n; + } + in++; + if (!m) { + s->mp = 0; + continue; + } + + s->mp++; + if (m != 1) + continue; + + if (mp_hdr[hit][s->mp]) + continue; + + s->mp = 0; + s->temp[0] = '\0'; + s->subname = 0; + + if (hit == 2) + s->state = MT_LOOK_BOUND_IN; + else + s->state += hit + 1; + break; + + case MT_DISP: + /* form-data; name="file"; filename="t.txt" */ + + if (*in == '\x0d') { +// lwsl_notice("disp: '%s', '%s', '%s'\n", +// s->content_disp, s->name, +// s->content_disp_filename); + + if (s->content_disp_filename[0]) + if (s->output(s->data, s->name, + &s->out, s->pos, LWS_UFS_OPEN)) + return -1; + s->state = MT_IGNORE2; + goto done; + } + if (*in == ';') { + s->subname = 1; + s->temp[0] = '\0'; + s->mp = 0; + goto done; + } + + if (*in == '\"') { + s->inside_quote ^= 1; + goto done; + } + + if (s->subname) { + if (*in == '=') { + s->temp[s->mp] = '\0'; + s->subname = 0; + s->mp = 0; + goto done; + } + if (s->mp < sizeof(s->temp) - 1 && + (*in != ' ' || s->inside_quote)) + s->temp[s->mp++] = *in; + goto done; + } + + if (!s->temp[0]) { + if (s->mp < sizeof(s->content_disp) - 1) + s->content_disp[s->mp++] = *in; + s->content_disp[s->mp] = '\0'; + goto done; + } + + if (!strcmp(s->temp, "name")) { + if (s->mp < sizeof(s->name) - 1) + s->name[s->mp++] = *in; + s->name[s->mp] = '\0'; + goto done; + } + + if (!strcmp(s->temp, "filename")) { + if (s->mp < sizeof(s->content_disp_filename) - 1) + s->content_disp_filename[s->mp++] = *in; + s->content_disp_filename[s->mp] = '\0'; + goto done; + } +done: + in++; + break; + + case MT_TYPE: + if (*in == '\x0d') + s->state = MT_IGNORE2; + else { + if (s->mp < sizeof(s->content_type) - 1) + s->content_type[s->mp++] = *in; + s->content_type[s->mp] = '\0'; + } + in++; + break; + + case MT_IGNORE1: + if (*in == '\x0d') + s->state = MT_IGNORE2; + in++; + break; + + case MT_IGNORE2: + s->mp = 0; + if (*in == '\x0a') + s->state = MT_HNAME; + in++; + break; + } + } + + return 0; +} + +static int +lws_urldecode_s_destroy(struct lws_urldecode_stateful *s) +{ + int ret = 0; + + if (s->state != US_IDLE) + ret = -1; + + if (!ret) + if (s->output(s->data, s->name, &s->out, s->pos, 1)) + ret = -1; + + lws_free(s); + + return ret; +} + +struct lws_spa { + struct lws_urldecode_stateful *s; + lws_spa_fileupload_cb opt_cb; + const char * const *param_names; + int count_params; + char **params; + int *param_length; + void *opt_data; + + char *storage; + char *end; + int max_storage; +}; + +static int +lws_urldecode_spa_lookup(struct lws_spa *spa, + const char *name) +{ + int n; + + for (n = 0; n < spa->count_params; n++) + if (!strcmp(spa->param_names[n], name)) + return n; + + return -1; +} + +static int +lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len, + int final) +{ + struct lws_spa *spa = + (struct lws_spa *)data; + int n; + + if (spa->s->content_disp_filename[0]) { + if (spa->opt_cb) { + n = spa->opt_cb(spa->opt_data, name, + spa->s->content_disp_filename, + *buf, len, final); + + if (n < 0) + return -1; + } + return 0; + } + n = lws_urldecode_spa_lookup(spa, name); + + if (n == -1 || !len) /* unrecognized */ + return 0; + + if (!spa->params[n]) + spa->params[n] = *buf; + + if ((*buf) + len >= spa->end) { + lwsl_notice("%s: exceeded storage\n", __func__); + return -1; + } + + spa->param_length[n] += len; + + /* move it on inside storage */ + (*buf) += len; + *((*buf)++) = '\0'; + + spa->s->out_len -= len + 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN struct lws_spa * +lws_spa_create(struct lws *wsi, const char * const *param_names, + int count_params, int max_storage, + lws_spa_fileupload_cb opt_cb, void *opt_data) +{ + struct lws_spa *spa = lws_zalloc(sizeof(*spa)); + + if (!spa) + return NULL; + + spa->param_names = param_names; + spa->count_params = count_params; + spa->max_storage = max_storage; + spa->opt_cb = opt_cb; + spa->opt_data = opt_data; + + spa->storage = lws_malloc(max_storage); + if (!spa->storage) + goto bail2; + spa->end = spa->storage + max_storage - 1; + + spa->params = lws_zalloc(sizeof(char *) * count_params); + if (!spa->params) + goto bail3; + + spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa, + lws_urldecode_spa_cb); + if (!spa->s) + goto bail4; + + spa->param_length = lws_zalloc(sizeof(int) * count_params); + if (!spa->param_length) + goto bail5; + + lwsl_notice("%s: Created SPA %p\n", __func__, spa); + + return spa; + +bail5: + lws_urldecode_s_destroy(spa->s); +bail4: + lws_free(spa->params); +bail3: + lws_free(spa->storage); +bail2: + lws_free(spa); + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_process(struct lws_spa *ludspa, const char *in, int len) +{ + if (!ludspa) { + lwsl_err("%s: NULL spa\n"); + return -1; + } + return lws_urldecode_s_process(ludspa->s, in, len); +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_get_length(struct lws_spa *ludspa, int n) +{ + if (n >= ludspa->count_params) + return 0; + + return ludspa->param_length[n]; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_spa_get_string(struct lws_spa *ludspa, int n) +{ + if (n >= ludspa->count_params) + return NULL; + + return ludspa->params[n]; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_finalize(struct lws_spa *spa) +{ + if (spa->s) { + lws_urldecode_s_destroy(spa->s); + spa->s = NULL; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_spa_destroy(struct lws_spa *spa) +{ + int n = 0; + + lwsl_notice("%s: destroy spa %p\n", __func__, spa); + + if (spa->s) + lws_urldecode_s_destroy(spa->s); + + lwsl_debug("%s\n", __func__); + + lws_free(spa->param_length); + lws_free(spa->params); + lws_free(spa->storage); + lws_free(spa); + + return n; +} + +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s) +{ + char *sp, buffer[32]; + const char *pc; + int old_len, n; + + /* do replacements */ + sp = args->p; + old_len = args->len; + args->len = 0; + s->start = sp; + while (sp < args->p + old_len) { + + if (args->len + 7 >= args->max_len) { + lwsl_err("Used up interpret padding\n"); + return -1; + } + + if ((!s->pos && *sp == '$') || s->pos) { + int hits = 0, hit = 0; + + if (!s->pos) + s->start = sp; + s->swallow[s->pos++] = *sp; + if (s->pos == sizeof(s->swallow) - 1) + goto skip; + for (n = 0; n < s->count_vars; n++) + if (!strncmp(s->swallow, s->vars[n], s->pos)) { + hits++; + hit = n; + } + if (!hits) { +skip: + s->swallow[s->pos] = '\0'; + memcpy(s->start, s->swallow, s->pos); + args->len++; + s->pos = 0; + sp = s->start + 1; + continue; + } + if (hits == 1 && s->pos == strlen(s->vars[hit])) { + pc = s->replace(s->data, hit); + if (!pc) + pc = "NULL"; + n = strlen(pc); + s->swallow[s->pos] = '\0'; + if (n != s->pos) { + memmove(s->start + n, + s->start + s->pos, + old_len - (sp - args->p)); + old_len += (n - s->pos) + 1; + } + memcpy(s->start, pc, n); + args->len++; + sp = s->start + 1; + + s->pos = 0; + } + sp++; + continue; + } + + args->len++; + sp++; + } + + /* no space left for final chunk trailer */ + if (args->final && args->len + 7 >= args->max_len) + return -1; + + n = sprintf(buffer, "%X\x0d\x0a", args->len); + + args->p -= n; + memcpy(args->p, buffer, n); + args->len += n; + + if (args->final) { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '0'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 7; + } else { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 2; + } + + return 0; +} diff --git a/feeds/wlan-ap/libwebsocket/src/service.c b/feeds/wlan-ap/libwebsocket/src/service.c new file mode 100644 index 00000000..a04dc55f --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/service.c @@ -0,0 +1,1128 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +static int +lws_calllback_as_writeable(struct lws *wsi) +{ + int n; + + switch (wsi->mode) { + case LWSCM_WS_CLIENT: + n = LWS_CALLBACK_CLIENT_WRITEABLE; + break; + case LWSCM_WSCL_ISSUE_HTTP_BODY: + n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE; + break; + case LWSCM_WS_SERVING: + n = LWS_CALLBACK_SERVER_WRITEABLE; + break; + default: + n = LWS_CALLBACK_HTTP_WRITEABLE; + break; + } + lwsl_debug("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space); + return user_callback_handle_rxflow(wsi->protocol->callback, + wsi, (enum lws_callback_reasons) n, + wsi->user_space, NULL, 0); +} + +int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) +{ + int write_type = LWS_WRITE_PONG; + struct lws_tokens eff_buf; +#ifdef LWS_USE_HTTP2 + struct lws *wsi2; +#endif + int ret, m, n; + + //lwsl_err("%s: %p\n", __func__, wsi); + + /* + * user callback is lowest priority to get these notifications + * actually, since other pending things cannot be disordered + */ + + /* Priority 1: pending truncated sends are incomplete ws fragments + * If anything else sent first the protocol would be + * corrupted. + */ + if (wsi->trunc_len) { + if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s signalling to close\n", __func__); + return -1; + } + /* leave POLLOUT active either way */ + return 0; + } else + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) + return -1; /* retry closing now */ + + if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY) + goto user_service; + + +#ifdef LWS_USE_HTTP2 + /* Priority 2: protocol packets + */ + if (wsi->pps) { + lwsl_info("servicing pps %d\n", wsi->pps); + switch (wsi->pps) { + case LWS_PPS_HTTP2_MY_SETTINGS: + case LWS_PPS_HTTP2_ACK_SETTINGS: + lws_http2_do_pps_send(lws_get_context(wsi), wsi); + break; + default: + break; + } + wsi->pps = LWS_PPS_NONE; + lws_rx_flow_control(wsi, 1); + + return 0; /* leave POLLOUT active */ + } +#endif + +#ifdef LWS_WITH_CGI + if (wsi->cgi) + goto user_service_go_again; +#endif + + /* Priority 3: pending control packets (pong or close) + */ + if ((wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.ping_pending_flag) || + (wsi->state == LWSS_RETURNED_CLOSE_ALREADY && + wsi->u.ws.payload_is_close)) { + + if (wsi->u.ws.payload_is_close) + write_type = LWS_WRITE_CLOSE; + + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.ping_payload_len, write_type); + if (n < 0) + return -1; + + /* well he is sent, mark him done */ + wsi->u.ws.ping_pending_flag = 0; + if (wsi->u.ws.payload_is_close) + /* oh... a close frame was it... then we are done */ + return -1; + + /* otherwise for PING, leave POLLOUT active either way */ + return 0; + } + + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + wsi->u.ws.send_check_ping) { + + lwsl_info("issuing ping on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 0; + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + 0, LWS_WRITE_PING); + if (n < 0) + return -1; + + /* + * we apparently were able to send the PING in a reasonable time + * now reset the clock on our peer to be able to send the + * PONG in a reasonable time. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG, + wsi->context->timeout_secs); + + return 0; + } + + /* Priority 4: if we are closing, not allowed to send more data frags + * which means user callback or tx ext flush banned now + */ + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + goto user_service; + + /* Priority 5: Tx path extension with more to send + * + * These are handled as new fragments each time around + * So while we must block new writeable callback to enforce + * payload ordering, but since they are always complete + * fragments control packets can interleave OK. + */ + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + lwsl_ext("SERVICING TX EXT DRAINING\n"); + if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) + return -1; + /* leave POLLOUT active */ + return 0; + } + + /* Priority 6: user can get the callback + */ + m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0); + if (m) + return -1; +#ifndef LWS_NO_EXTENSIONS + if (!wsi->extension_data_pending) + goto user_service; +#endif + /* + * check in on the active extensions, see if they + * had pending stuff to spill... they need to get the + * first look-in otherwise sequence will be disordered + * + * NULL, zero-length eff_buf means just spill pending + */ + + ret = 1; + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* give every extension a chance to spill */ + + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_PACKET_TX_PRESEND, + &eff_buf, 0); + if (m < 0) { + lwsl_err("ext reports fatal error\n"); + return -1; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they gave us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from POLLOUT spill\n"); + return -1; + } + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %s\n", + eff_buf.token_len, n); + return -1; + } + } else + continue; + + /* no extension has more to spill */ + + if (!ret) + continue; + + /* + * There's more to spill from an extension, but we just sent + * something... did that leave the pipe choked? + */ + + if (!lws_send_pipe_choked(wsi)) + /* no we could add more */ + continue; + + lwsl_info("choked in POLLOUT service\n"); + + /* + * Yes, he's choked. Leave the POLLOUT masked on so we will + * come back here when he is unchoked. Don't call the user + * callback to enforce ordering of spilling, he'll get called + * when we come back here and there's nothing more to spill. + */ + + return 0; + } +#ifndef LWS_NO_EXTENSIONS + wsi->extension_data_pending = 0; +#endif +user_service: + /* one shot */ + + if (pollfd) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + + if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY && + !wsi->hdr_parsing_completed) + return 0; + +#ifdef LWS_WITH_CGI +user_service_go_again: +#endif + +#ifdef LWS_USE_HTTP2 + /* + * we are the 'network wsi' for potentially many muxed child wsi with + * no network connection of their own, who have to use us for all their + * network actions. So we use a round-robin scheme to share out the + * POLLOUT notifications to our children. + * + * But because any child could exhaust the socket's ability to take + * writes, we can only let one child get notified each time. + * + * In addition children may be closed / deleted / added between POLLOUT + * notifications, so we can't hold pointers + */ + + if (wsi->mode != LWSCM_HTTP2_SERVING) { + lwsl_info("%s: non http2\n", __func__); + goto notify; + } + + wsi->u.http2.requested_POLLOUT = 0; + if (!wsi->u.http2.initialized) { + lwsl_info("pollout on uninitialized http2 conn\n"); + return 0; + } + + lwsl_info("%s: doing children\n", __func__); + + wsi2 = wsi; + do { + wsi2 = wsi2->u.http2.next_child_wsi; + lwsl_info("%s: child %p\n", __func__, wsi2); + if (!wsi2) + continue; + if (!wsi2->u.http2.requested_POLLOUT) + continue; + wsi2->u.http2.requested_POLLOUT = 0; + if (lws_calllback_as_writeable(wsi2)) { + lwsl_debug("Closing POLLOUT child\n"); + lws_close_free_wsi(wsi2, LWS_CLOSE_STATUS_NOSTATUS); + } + wsi2 = wsi; + } while (wsi2 != NULL && !lws_send_pipe_choked(wsi)); + + lwsl_info("%s: completed\n", __func__); + + return 0; +notify: +#endif + return lws_calllback_as_writeable(wsi); +} + +int +lws_service_timeout_check(struct lws *wsi, unsigned int sec) +{ +//#if LWS_POSIX + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n = 0; +//#endif + + (void)n; + + /* + * if extensions want in on it (eg, we are a mux parent) + * give them a chance to service child timeouts + */ + if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0) + return 0; + + if (!wsi->pending_timeout) + return 0; + + /* + * if we went beyond the allowed time, kill the + * connection + */ + if ((time_t)sec > wsi->pending_timeout_limit) { +//#if LWS_POSIX + if (wsi->sock != LWS_SOCK_INVALID && wsi->position_in_fds_table >= 0) + n = pt->fds[wsi->position_in_fds_table].events; + + /* no need to log normal idle keepalive timeout */ + if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) + lwsl_notice("wsi %p: TIMEDOUT WAITING on %d (did hdr %d, ah %p, wl %d, pfd events %d)\n", + (void *)wsi, wsi->pending_timeout, + wsi->hdr_parsing_completed, wsi->u.hdr.ah, + pt->ah_wait_list_length, n); +//#endif + /* + * Since he failed a timeout, he already had a chance to do + * something and was unable to... that includes situations like + * half closed connections. So process this "failed timeout" + * close as a violent death and don't try to do protocol + * cleanup like flush partials. + */ + wsi->socket_is_permanently_unusable = 1; + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)"Timed out waiting SSL", 21); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; + } + + return 0; +} + +int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len) +{ + /* his RX is flowcontrolled, don't send remaining now */ + if (wsi->rxflow_buffer) { + /* rxflow while we were spilling prev rxflow */ + lwsl_info("stalling in existing rxflow buf\n"); + return 1; + } + + /* a new rxflow, buffer it and warn caller */ + lwsl_info("new rxflow input buffer len %d\n", len - n); + wsi->rxflow_buffer = lws_malloc(len - n); + if (!wsi->rxflow_buffer) + return -1; + wsi->rxflow_len = len - n; + wsi->rxflow_pos = 0; + memcpy(wsi->rxflow_buffer, buf + n, len - n); + + return 0; +} + +/* this is used by the platform service code to stop us waiting for network + * activity in poll() when we have something that already needs service + */ + +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + int n; + + /* Figure out if we really want to wait in poll() + * We only need to wait if really nothing already to do and we have + * to wait for something from network + */ + + /* 1) if we know we are draining rx ext, do not wait in poll */ + if (pt->rx_draining_ext_list) + return 0; + +#ifdef LWS_OPENSSL_SUPPORT + /* 2) if we know we have non-network pending data, do not wait in poll */ + if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { + lwsl_info("ssl buffered read\n"); + return 0; + } +#endif + + /* 3) if any ah has pending rx, do not wait in poll */ + for (n = 0; n < context->max_http_header_pool; n++) + if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) { + /* any ah with pending rx must be attached to someone */ + if (!pt->ah_pool[n].wsi) { + lwsl_err("%s: assert: no wsi attached to ah\n", __func__); + assert(0); + } + return 0; + } + + return timeout_ms; +} + +/* + * guys that need POLLIN service again without waiting for network action + * can force POLLIN here if not flowcontrolled, so they will get service. + * + * Return nonzero if anybody got their POLLIN faked + */ +int +lws_service_flag_pending(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *wsi_next; +#endif + struct lws *wsi; + int forced = 0; + int n; + + /* POLLIN faking */ + + /* + * 1) For all guys with already-available ext data to drain, if they are + * not flowcontrolled, fake their POLLIN status + */ + wsi = pt->rx_draining_ext_list; + while (wsi) { + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & + LWS_POLLIN) + forced = 1; + wsi = wsi->u.ws.rx_draining_ext_list; + } + +#ifdef LWS_OPENSSL_SUPPORT + /* + * 2) For all guys with buffered SSL read data already saved up, if they + * are not flowcontrolled, fake their POLLIN status so they'll get + * service to use up the buffered incoming data, even though their + * network socket may have nothing + */ + wsi = pt->pending_read_list; + while (wsi) { + wsi_next = wsi->pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + /* + * he's going to get serviced now, take him off the + * list of guys with buffered SSL. If he still has some + * at the end of the service, he'll get put back on the + * list then. + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + } + + wsi = wsi_next; + } +#endif + /* + * 3) For any wsi who have an ah with pending RX who did not + * complete their current headers, and are not flowcontrolled, + * fake their POLLIN status so they will be able to drain the + * rx buffered in the ah + */ + for (n = 0; n < context->max_http_header_pool; n++) + if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen && + !pt->ah_pool[n].wsi->hdr_parsing_completed) { + pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].revents |= + pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].events & + LWS_POLLIN; + if (pt->fds[pt->ah_pool[n].wsi->position_in_fds_table].revents & + LWS_POLLIN) + forced = 1; + } + + return forced; +} + +#ifndef LWS_NO_CLIENT + +LWS_VISIBLE int +lws_http_client_read(struct lws *wsi, char **buf, int *len) +{ + int rlen, n; + + + + rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len); + if (rlen < 0) + return -1; + + *len = rlen; + if (rlen == 0) + return 0; + +// lwsl_err("%s: read %d\n", __func__, rlen); + + /* allow the source to signal he has data again next time */ + wsi->client_rx_avail = 0; + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + /* + * server may insist on transfer-encoding: chunked, + * so http client must deal with it + */ +spin_chunks: + while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) { + switch (wsi->chunk_parser) { + case ELCP_HEX: + if ((*buf)[0] == '\x0d') { + wsi->chunk_parser = ELCP_CR; + break; + } + n = char_to_hex((*buf)[0]); + if (n < 0) + return -1; + wsi->chunk_remaining <<= 4; + wsi->chunk_remaining |= n; + break; + case ELCP_CR: + if ((*buf)[0] != '\x0a') + return -1; + wsi->chunk_parser = ELCP_CONTENT; + lwsl_info("chunk %d\n", wsi->chunk_remaining); + if (wsi->chunk_remaining) + break; + lwsl_info("final chunk\n"); + goto completed; + + case ELCP_CONTENT: + break; + + case ELCP_POST_CR: + if ((*buf)[0] != '\x0d') + return -1; + + wsi->chunk_parser = ELCP_POST_LF; + break; + + case ELCP_POST_LF: + if ((*buf)[0] != '\x0a') + return -1; + + wsi->chunk_parser = ELCP_HEX; + wsi->chunk_remaining = 0; + break; + } + (*buf)++; + (*len)--; + } + + if (wsi->chunked && !wsi->chunk_remaining) + return 0; + + if (wsi->u.http.content_remain && + (int)wsi->u.http.content_remain < *len) + n = wsi->u.http.content_remain; + else + n = *len; + + if (wsi->chunked && wsi->chunk_remaining && + wsi->chunk_remaining < n) + n = wsi->chunk_remaining; + +#ifdef LWS_WITH_HTTP_PROXY + /* hubbub */ + if (wsi->perform_rewrite) + lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n); + else +#endif + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + wsi->user_space, *buf, n)) + return -1; + + if (wsi->chunked && wsi->chunk_remaining) { + (*buf) += n; + wsi->chunk_remaining -= n; + *len -= n; + } + + if (wsi->chunked && !wsi->chunk_remaining) + wsi->chunk_parser = ELCP_POST_CR; + + if (wsi->chunked && *len) { + goto spin_chunks; + } + + if (wsi->chunked) + return 0; + + wsi->u.http.content_remain -= n; + if (wsi->u.http.content_remain || !wsi->u.http.content_length) + return 0; + +completed: + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) + return -1; + + if (lws_http_transaction_completed_client(wsi)) + return -1; + + return 0; +} +#endif + +LWS_VISIBLE int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + lws_sockfd_type our_fd = 0, tmp_fd; + struct lws_tokens eff_buf; + unsigned int pending = 0; + struct lws *wsi, *wsi1; + char draining_flow = 0; + int timed_out = 0; + time_t now; + int n = 0, m; + int more; + + if (!context->protocol_init_done) + lws_protocol_init(context); + + time(&now); + + /* + * handle case that system time was uninitialized when lws started + * at boot, and got initialized a little later + */ + if (context->time_up < 1464083026 && now > 1464083026) + context->time_up = now; + + /* TODO: if using libev, we should probably use timeout watchers... */ + if (context->last_timeout_check_s != now) { + context->last_timeout_check_s = now; + + lws_plat_service_periodic(context); + + /* global timeout check once per second */ + + if (pollfd) + our_fd = pollfd->fd; + + wsi = context->pt[tsi].timeout_list; + while (wsi) { + /* we have to take copies, because he may be deleted */ + wsi1 = wsi->timeout_list; + tmp_fd = wsi->sock; + if (lws_service_timeout_check(wsi, (unsigned int)now)) { + /* he did time out... */ + if (tmp_fd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + /* he's gone, no need to mark as handled */ + } + wsi = wsi1; + } +#ifdef LWS_WITH_CGI + lws_cgi_kill_terminated(pt); +#endif +#if 0 + { + char s[300], *p = s; + + for (n = 0; n < context->count_threads; n++) + p += sprintf(p, " %7lu (%5d), ", + context->pt[n].count_conns, + context->pt[n].fds_count); + + lwsl_notice("load: %s\n", s); + } +#endif + } + + /* + * at intervals, check for ws connections needing ping-pong checks + */ + + if (context->ws_ping_pong_interval && + context->last_ws_ping_pong_check_s < now + 10) { + context->last_ws_ping_pong_check_s = now; + + struct lws_vhost *vh = context->vhost_list; + while (vh) { + for (n = 0; n < vh->count_protocols; n++) { + wsi = vh->same_vh_protocol_list[n]; + + while (wsi) { + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + !wsi->u.ws.send_check_ping && + wsi->u.ws.time_next_ping_check && + wsi->u.ws.time_next_ping_check < now) { + + lwsl_info("requesting ping-pong on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 1; + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING, + context->timeout_secs); + lws_callback_on_writable(wsi); + wsi->u.ws.time_next_ping_check = now + + wsi->context->ws_ping_pong_interval; + } + wsi = wsi->same_vh_protocol_next; + } + } + vh = vh->vhost_next; + } + } + + /* the socket we came to service timed out, nothing to do */ + if (timed_out) + return 0; + + /* just here for timeout management? */ + if (!pollfd) + return 0; + + /* no, here to service a socket descriptor */ + wsi = wsi_from_fd(context, pollfd->fd); + if (!wsi) + /* not lws connection ... leave revents alone and return */ + return 0; + + /* + * so that caller can tell we handled, past here we need to + * zero down pollfd->revents after handling + */ + +#if LWS_POSIX + /* handle session socket closed */ + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + wsi->socket_is_permanently_unusable = 1; + lwsl_debug("Session Socket %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto close_and_handled; + } + +#ifdef _WIN32 + if (pollfd->revents & LWS_POLLOUT) + wsi->sock_send_blocking = FALSE; +#endif + +#endif + + lwsl_debug("fd=%d, revents=%d\n", pollfd->fd, pollfd->revents); + + /* okay, what we came here to do... */ + + switch (wsi->mode) { + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_CLIENT: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_SERVER_LISTENER: + case LWSCM_SSL_ACK_PENDING: + if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) + goto handled; + +#ifdef LWS_WITH_CGI + if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) { + n = lws_handle_POLLOUT_event(wsi, pollfd); + if (n) + goto close_and_handled; + goto handled; + } +#endif + n = lws_server_socket_service(context, wsi, pollfd); + if (n) /* closed by above */ + return 1; + goto handled; + + case LWSCM_WS_SERVING: + case LWSCM_WS_CLIENT: + case LWSCM_HTTP2_SERVING: + case LWSCM_HTTP_CLIENT_ACCEPTED: + + /* 1: something requested a callback when it was OK to write */ + + if ((pollfd->revents & LWS_POLLOUT) && + (wsi->state == LWSS_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS || + wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) && + lws_handle_POLLOUT_event(wsi, pollfd)) { + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lwsl_info("lws_service_fd: closing\n"); + goto close_and_handled; + } + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * we stopped caring about anything except control + * packets. Force flow control off, defeat tx + * draining. + */ + lws_rx_flow_control(wsi, 1); + wsi->u.ws.tx_draining_ext = 0; + } + + if (wsi->u.ws.tx_draining_ext) + /* we cannot deal with new RX until the TX ext + * path has been drained. It's because new + * rx will, eg, crap on the wsi rx buf that + * may be needed to retain state. + * + * TX ext drain path MUST go through event loop + * to avoid blocking. + */ + break; + + if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) + /* We cannot deal with any kind of new RX + * because we are RX-flowcontrolled. + */ + break; + + /* 2: RX Extension needs to be drained + */ + + if (wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.rx_draining_ext) { + + lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__); +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_WS_CLIENT) { + n = lws_client_rx_sm(wsi, 0); + if (n < 0) + /* we closed wsi */ + n = 0; + } else +#endif + n = lws_rx_sm(wsi, 0); + + goto handled; + } + + if (wsi->u.ws.rx_draining_ext) + /* + * We have RX EXT content to drain, but can't do it + * right now. That means we cannot do anything lower + * priority either. + */ + break; + + /* 3: RX Flowcontrol buffer needs to be drained + */ + + if (wsi->rxflow_buffer) { + lwsl_info("draining rxflow (len %d)\n", + wsi->rxflow_len - wsi->rxflow_pos + ); + /* well, drain it */ + eff_buf.token = (char *)wsi->rxflow_buffer + + wsi->rxflow_pos; + eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos; + draining_flow = 1; + goto drain; + } + + /* 4: any incoming (or ah-stashed incoming rx) data ready? + * notice if rx flow going off raced poll(), rx flow wins + */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; + +read: + /* all the union members start with hdr, so even in ws mode + * we can deal with the ah via u.hdr + */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi); + eff_buf.token_len = wsi->u.hdr.ah->rxlen - + wsi->u.hdr.ah->rxpos; + eff_buf.token = (char *)wsi->u.hdr.ah->rx + + wsi->u.hdr.ah->rxpos; + } else { + if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) { + eff_buf.token_len = lws_ssl_capable_read(wsi, + pt->serv_buf, pending ? pending : + context->pt_serv_buf_size); + switch (eff_buf.token_len) { + case 0: + lwsl_info("%s: zero length read\n", __func__); + goto close_and_handled; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lwsl_info("SSL Capable more service\n"); + n = 0; + goto handled; + case LWS_SSL_CAPABLE_ERROR: + lwsl_info("Closing when error\n"); + goto close_and_handled; + } + + eff_buf.token = (char *)pt->serv_buf; + } + } + +drain: +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED && + !wsi->told_user_closed) { + + /* + * simply mark ourselves as having readable data + * and turn off our POLLIN + */ + wsi->client_rx_avail = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + + /* let user code know, he'll usually ask for writeable + * callback and drain / re-enable it there + */ + if (user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP, + wsi->user_space, NULL, 0)) + goto close_and_handled; + } +#endif + /* + * give any active extensions a chance to munge the buffer + * before parse. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + */ + do { + more = 0; + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE, + &eff_buf, 0); + if (m < 0) + goto close_and_handled; + if (m) + more = 1; + + /* service incoming data */ + + if (eff_buf.token_len) { + /* + * if draining from rxflow buffer, not + * critical to track what was used since at the + * use it bumps wsi->rxflow_pos. If we come + * around again it will pick up from where it + * left off. + */ + n = lws_read(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + /* we closed wsi */ + n = 0; + goto handled; + } + } + + eff_buf.token = NULL; + eff_buf.token_len = 0; + } while (more); + + if (wsi->u.hdr.ah) { + lwsl_notice("%s: %p: detaching\n", + __func__, wsi); + /* show we used all the pending rx up */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + /* we can run the normal ah detach flow despite + * being in ws union mode, since all union members + * start with hdr */ + lws_header_table_detach(wsi, 0); + } + + pending = lws_ssl_pending(wsi); + if (pending) { + pending = pending > context->pt_serv_buf_size ? + context->pt_serv_buf_size : pending; + goto read; + } + + if (draining_flow && wsi->rxflow_buffer && + wsi->rxflow_pos == wsi->rxflow_len) { + lwsl_info("flow buffer: drained\n"); + lws_free_set_NULL(wsi->rxflow_buffer); + /* having drained the rxflow buffer, can rearm POLLIN */ +#ifdef LWS_NO_SERVER + n = +#endif + _lws_rx_flow_control(wsi); + /* n ignored, needed for NO_SERVER case */ + } + + break; +#ifdef LWS_WITH_CGI + case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data... + * do the callback on our master wsi + */ + { + struct lws_cgi_args args; + + if (wsi->cgi_channel >= LWS_STDOUT && + !(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; + if (wsi->cgi_channel == LWS_STDIN && + !(pollfd->revents & pollfd->events & LWS_POLLOUT)) + break; + + if (wsi->cgi_channel == LWS_STDIN) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + + args.ch = wsi->cgi_channel; + args.stdwsi = &wsi->parent->cgi->stdwsi[0]; + args.hdr_state = wsi->hdr_state; + + //lwsl_err("CGI LWS_STDOUT waiting wsi %p mode %d state %d\n", + // wsi->parent, wsi->parent->mode, wsi->parent->state); + + if (user_callback_handle_rxflow( + wsi->parent->protocol->callback, + wsi->parent, LWS_CALLBACK_CGI, + wsi->parent->user_space, + (void *)&args, 0)) + return 1; + + break; + } +#endif + default: +#ifdef LWS_NO_CLIENT + break; +#else + if ((pollfd->revents & LWS_POLLOUT) && + lws_handle_POLLOUT_event(wsi, pollfd)) + goto close_and_handled; + + n = lws_client_socket_service(context, wsi, pollfd); + if (n) + return 1; + goto handled; +#endif + } + + n = 0; + goto handled; + +close_and_handled: + lwsl_debug("Close and handled\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + /* + * pollfd may point to something else after the close + * due to pollfd swapping scheme on delete on some platforms + * we can't clear revents now because it'd be the wrong guy's revents + */ + return 1; + +handled: + pollfd->revents = 0; + return n; +} + +LWS_VISIBLE int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) +{ + return lws_service_fd_tsi(context, pollfd, 0); +} + +LWS_VISIBLE int +lws_service(struct lws_context *context, int timeout_ms) +{ + return lws_plat_service(context, timeout_ms); +} + +LWS_VISIBLE int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + return _lws_plat_service_tsi(context, timeout_ms, tsi); +} + diff --git a/feeds/wlan-ap/libwebsocket/src/sha-1.c b/feeds/wlan-ap/libwebsocket/src/sha-1.c new file mode 100644 index 00000000..e9509571 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/sha-1.c @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#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*/ diff --git a/feeds/wlan-ap/libwebsocket/src/ssl-client.c b/feeds/wlan-ap/libwebsocket/src/ssl-client.c new file mode 100644 index 00000000..2df2b586 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/ssl-client.c @@ -0,0 +1,436 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +extern int lws_ssl_get_error(struct lws *wsi, int n); + +int +lws_ssl_client_bio_create(struct lws *wsi) +{ +#if defined(LWS_USE_POLARSSL) + return 0; +#else +#if defined(LWS_USE_MBEDTLS) +#else + struct lws_context *context = wsi->context; + const char *hostname = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST); + + (void)hostname; + + wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); + if (!wsi->ssl) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); + lws_decode_ssl_error(); + return -1; + } + +#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host + X509_VERIFY_PARAM *param; + (void)param; + + if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { + param = SSL_get0_param(wsi->ssl); + /* Enable automatic hostname checks */ + X509_VERIFY_PARAM_set_hostflags(param, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + X509_VERIFY_PARAM_set1_host(param, hostname, 0); + /* Configure a non-zero callback if desired */ + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, 0); + } +#endif + +#ifndef USE_WOLFSSL + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#else +#ifdef WOLFSSL_SNI_HOST_NAME + wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#endif +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +#endif +#endif + +#ifdef USE_WOLFSSL + /* + * wolfSSL/CyaSSL does certificate verification differently + * from OpenSSL. + * If we should ignore the certificate, we need to set + * this before SSL_new and SSL_connect is called. + * Otherwise the connect will simply fail with error code -155 + */ +#ifdef USE_OLD_CYASSL + if (wsi->use_ssl == 2) + CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#else + if (wsi->use_ssl == 2) + wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#endif +#endif /* USE_WOLFSSL */ + + wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE); + SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else + BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ +#endif + + SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, + context); + + return 0; +#endif +#endif +} + +int +lws_ssl_client_connect1(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + int n = 0; + + lws_latency_pre(context, wsi); +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_connect(wsi->ssl); +#endif +#endif + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) + goto some_wait; + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("%s: WANT_WRITE... retrying\n", __func__); + lws_callback_on_writable(wsi); +some_wait: + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = ERR_get_error(); + + if (n != SSL_ERROR_NONE) { + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + lwsl_err("SSL connect error %lu: %s\n", + n, ERR_error_string(n, sb)); + return -1; + } +#endif +#endif + } + + return 1; +} + +int +lws_ssl_client_connect2(struct lws *wsi) +{ + struct lws_context *context = wsi->context; +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; +#endif +#endif + int n = 0; + + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { + lws_latency_pre(context, wsi); +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_connect(wsi->ssl); +#endif +#endif + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) { + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("SSL_connect WANT_WRITE... retrying\n"); + lws_callback_on_writable(wsi); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = ERR_get_error(); + if (n != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + n, ERR_error_string(n, sb)); + return -1; + } +#endif +#endif + } + } + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else +#ifndef USE_WOLFSSL + /* + * See comment above about wolfSSL certificate + * verification + */ + lws_latency_pre(context, wsi); + n = SSL_get_verify_result(wsi->ssl); + lws_latency(context, wsi, + "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); + + if (n != X509_V_OK) { + if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) { + lwsl_notice("accepting self-signed certificate\n"); + } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID || + n == X509_V_ERR_CERT_HAS_EXPIRED) && + wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) { + lwsl_notice("accepting expired certificate\n"); + } else { + lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lws_ssl_elaborate_error(); + return -1; + } + } +#endif /* USE_WOLFSSL */ +#endif +#endif + + return 1; +} + + +int lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if defined(LWS_USE_POLARSSL) + return 0; +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_METHOD *method; + struct lws wsi; + int error; + int n; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return 0; + + if (info->provided_client_ssl_ctx) { + /* use the provided OpenSSL context if given one */ + vhost->ssl_client_ctx = info->provided_client_ssl_ctx; + /* nothing for lib to delete */ + vhost->user_supplied_ssl_ctx = 1; + + return 0; + } + + if (info->port != CONTEXT_PORT_NO_LISTEN) + return 0; + + /* basic openssl init already happened in context init */ + + method = (SSL_METHOD *)TLS_client_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + /* create context */ + vhost->ssl_client_ctx = SSL_CTX_new(method); + if (!vhost->ssl_client_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(vhost->ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, + info->ssl_cipher_list); + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!info->ssl_ca_filepath) { + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, NULL, + LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err( + "Unable to load SSL Client certs from %s " + "(set by --with-client-cert-dir= " + "in configure) -- client ssl isn't " + "going to work", LWS_OPENSSL_CLIENT_CERTS); + } else + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, info->ssl_ca_filepath, + NULL)) + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work", info->ssl_ca_filepath); + else + lwsl_info("loaded ssl_ca_filepath\n"); + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (info->ssl_cert_filepath) { + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx, + info->ssl_cert_filepath); + if (n != 1) { + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + } + if (info->ssl_private_key_filepath) { + lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info); + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx, + info->ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s' %lu: %s\n", + info->ssl_private_key_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + } + + /* + * give him a fake wsi with context set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = vhost->context; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, + vhost->ssl_client_ctx, NULL, 0); + + return 0; +#endif +#endif +} diff --git a/feeds/wlan-ap/libwebsocket/src/ssl-server.c b/feeds/wlan-ap/libwebsocket/src/ssl-server.c new file mode 100644 index 00000000..85f40387 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/ssl-server.c @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct lws_vhost *vh; + struct lws wsi; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + vh = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + /* + * give him a fake wsi with context set, so he can use lws_get_context() + * in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vh; + wsi.context = vh->context; + + n = vh->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + return !n; +} + +static int +lws_context_ssl_init_ecdh(struct lws_vhost *vhost) +{ +#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT + EC_KEY *EC_key = NULL; + EVP_PKEY *pkey; + int KeyType; + X509 *x; + + if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) + return 0; + + lwsl_notice(" Using ECDH certificate support\n"); + + /* Get X509 certificate from ssl context */ + x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0); + if (!x) { + lwsl_err("%s: x is NULL\n", __func__); + return 1; + } + /* Get the public key from certificate */ + pkey = X509_get_pubkey(x); + if (!pkey) { + lwsl_err("%s: pkey is NULL\n", __func__); + + return 1; + } + /* Get the key type */ + KeyType = EVP_PKEY_type(pkey->type); + + if (EVP_PKEY_EC != KeyType) { + lwsl_notice("Key type is not EC\n"); + return 0; + } + /* Get the key */ + EC_key = EVP_PKEY_get1_EC_KEY(pkey); + /* Set ECDH parameter */ + if (!EC_key) { + lwsl_err("%s: ECDH key is NULL \n", __func__); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key); + EC_KEY_free(EC_key); +#endif + return 0; +} + +static int +lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#ifdef LWS_HAVE_OPENSSL_ECDH_H + EC_KEY *ecdh; + int ecdh_nid; + const char *ecdh_curve = "prime256v1"; + + if (info->ecdh_curve) + ecdh_curve = info->ecdh_curve; + + ecdh_nid = OBJ_sn2nid(ecdh_curve); + if (NID_undef == ecdh_nid) { + lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); + return 1; + } + + ecdh = EC_KEY_new_by_curve_name(ecdh_nid); + if (NULL == ecdh) { + lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + + lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); +#else + lwsl_notice(" OpenSSL doesn't support ECDH\n"); +#endif + return 0; +} + +#ifndef OPENSSL_NO_TLSEXT +static int +lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) +{ + struct lws_context *context; + struct lws_vhost *vhost, *vh; + const char *servername; + int port; + + if (!ssl) + return SSL_TLSEXT_ERR_NOACK; + + context = (struct lws_context *)SSL_CTX_get_ex_data( + SSL_get_SSL_CTX(ssl), + openssl_SSL_CTX_private_data_index); + + /* + * We can only get ssl accepted connections by using a vhost's ssl_ctx + * find out which listening one took us and only match vhosts on the + * same port. + */ + vh = context->vhost_list; + while (vh) { + if (vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + break; + vh = vh->vhost_next; + } + + assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */ + port = vh->listen_port; + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + + if (servername) { + vhost = lws_select_vhost(context, port, servername); + if (vhost) { + lwsl_debug("SNI: Found: %s (port %d)\n", + servername, port); + SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); + return SSL_TLSEXT_ERR_OK; + } + lwsl_err("SNI: Unknown ServerName: %s\n", servername); + } + + return SSL_TLSEXT_ERR_OK; +} +#endif + +#endif +#endif + +LWS_VISIBLE int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context *context = vhost->context; + struct lws wsi; + int error; + int n; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + vhost->use_ssl = 0; + return 0; + } + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + + vhost->use_ssl = info->ssl_cert_filepath != NULL; + + if (vhost->use_ssl && info->ssl_cipher_list) + lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list); + + if (vhost->use_ssl) + lwsl_notice(" Using SSL mode\n"); + else + lwsl_notice(" Using non-SSL mode\n"); + } + + /* + * give him a fake wsi with context + vhost set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = context; + + (void)n; + (void)error; + +#if defined(LWS_USE_POLARSSL) + lwsl_notice(" Compiled with PolarSSL support\n"); + + vhost->ssl_ctx = lws_zalloc(sizeof (*vhost->ssl_ctx)); + + /* Load the trusted CA */ + + if (info->ssl_ca_filepath) { + n = x509_crt_parse_file(&vhost->ssl_ctx->ca, + info->ssl_ca_filepath); + + if (n < 0) { +// error_strerror(ret, errorbuf, sizeof(errorbuf)); + lwsl_err("%s: Failed to load ca cert\n", __func__); + return -1; + } + } + + /* Load our cert */ + + if (info->ssl_cert_filepath) { + n = x509_crt_parse_file(&vhost->ssl_ctx->certificate, + info->ssl_cert_filepath); + + if (n < 0) { +// error_strerror(ret, errorbuf, sizeof(errorbuf)); + lwsl_err("%s: Failed to load cert\n", __func__); + return -1; + } + } + + /* Load cert private key */ + + if (info->ssl_private_key_filepath) { + pk_context pk; + pk_init(&pk); + n = pk_parse_keyfile(&pk, info->ssl_private_key_filepath, + info->ssl_private_key_password); + + if (!n && !pk_can_do(&pk, POLARSSL_PK_RSA)) + n = POLARSSL_ERR_PK_TYPE_MISMATCH; + + if (!n) + rsa_copy(&vhost->ssl_ctx->key, pk_rsa(pk)); + else + rsa_free(&vhost->ssl_ctx->key); + pk_free(&pk); + + if (n) { + //error_strerror(ret, errorbuf, sizeof(errorbuf)); + lwsl_err("%s: error reading private key\n", __func__); + + return -1; + } + } +#else +#if defined(LWS_USE_MBEDTLS) + lwsl_notice(" Compiled with mbedTLS support\n"); +#else + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + * + * SSLv23_server_method() is the openssl method for "allow all TLS + * versions", compared to e.g. TLSv1_2_server_method() which only allows + * tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options() + */ + + { + SSL_METHOD *method; + + method = (SSL_METHOD *)TLS_server_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + } + + /* associate the lws context with the SSL_CTX */ + + SSL_CTX_set_ex_data(vhost->ssl_ctx, + openssl_SSL_CTX_private_data_index, vhost->context); + + /* Disable SSLv2 and SSLv3 */ + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_ctx, + info->ssl_cipher_list); + + /* as a server, are we requiring clients to identify themselves? */ + + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) { + int verify_options = SSL_VERIFY_PEER; + + if (!lws_check_opt(info->options, + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) + verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + SSL_CTX_set_session_id_context(vhost->ssl_ctx, + (unsigned char *)context, sizeof(void *)); + + /* absolutely require the client cert */ + + SSL_CTX_set_verify(vhost->ssl_ctx, + verify_options, OpenSSL_verify_callback); + } + +#ifndef OPENSSL_NO_TLSEXT + SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx, + lws_ssl_server_name_cb); +#endif + + /* + * give user code a chance to load certs into the server + * allowing it to verify incoming client certs + */ + + if (info->ssl_ca_filepath && + !SSL_CTX_load_verify_locations(vhost->ssl_ctx, + info->ssl_ca_filepath, NULL)) { + lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__); + } + + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh_curve(info, vhost)) + return -1; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + vhost->ssl_ctx, NULL, 0); + } + + if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT)) + /* Normally SSL listener rejects non-ssl, optionally allow */ + vhost->allow_non_ssl_on_ssl_port = 1; + + if (info->ssl_options_set) + SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set); + if (info->ssl_options_clear) + SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear); + + lwsl_info(" SSL options 0x%X\n", + SSL_CTX_get_options(vhost->ssl_ctx)); + + if (vhost->use_ssl) { + /* openssl init for server sockets */ + + /* set the local certificate from CertFile */ + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx, + info->ssl_cert_filepath); + if (n != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + lws_ssl_bind_passphrase(vhost->ssl_ctx, info); + + if (info->ssl_private_key_filepath != NULL) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + info->ssl_private_key_filepath, error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + } else + if (vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } + + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + + if (lws_context_ssl_init_ecdh(vhost)) + return 1; + + /* + * SSL is happy and has a cert it's content with + * If we're supporting HTTP2, initialize that + */ + + lws_context_init_http2_ssl(vhost); + } + +#endif +#endif + + return 0; +} + diff --git a/feeds/wlan-ap/libwebsocket/src/ssl.c b/feeds/wlan-ap/libwebsocket/src/ssl.c new file mode 100644 index 00000000..918ddf67 --- /dev/null +++ b/feeds/wlan-ap/libwebsocket/src/ssl.c @@ -0,0 +1,694 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "websocket/private-libwebsockets.h" + +#if defined(LWS_USE_POLARSSL) +static const int ciphers[] = +{ + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + 0 +}; + +static int urandom_bytes(void *ctx, unsigned char *dest, size_t len) +{ + int cur; + int fd = open("/dev/urandom", O_RDONLY); + + while (len) { + cur = read(fd, dest, len); + if (cur < 0) + continue; + len -= cur; + } + close(fd); + + return 0; +} + +static void pssl_debug(void *ctx, int level, const char *str) +{ + lwsl_err("PolarSSL [level %d]: %s", level, str); +} + +#endif + +int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +int lws_ssl_get_error(struct lws *wsi, int n) +{ +#if defined(LWS_USE_POLARSSL) +#define ERR_error_string(a, b) "" + return n; +#else +#if defined(LWS_USE_MBEDTLS) + return n; +#else + return SSL_get_error(wsi->ssl, n); +#endif +#endif +} + +void +lws_ssl_elaborate_error(void) +{ +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + + char buf[256]; + u_long err; + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, buf, sizeof(buf)); + lwsl_err("*** %s\n", buf); + } +#endif +#endif +} + + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else +static int +lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata) +{ + struct lws_context_creation_info * info = + (struct lws_context_creation_info *)userdata; + + strncpy(buf, info->ssl_private_key_password, size); + buf[size - 1] = '\0'; + + return strlen(buf); +} +#endif +#endif + +void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info) +{ + if (!info->ssl_private_key_password) + return; +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + /* + * password provided, set ssl callback and user data + * for checking password which will be trigered during + * SSL_CTX_use_PrivateKey_file function + */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info); + SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb); +#endif +#endif +} + +int +lws_context_init_ssl_library(struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_notice(" Compiled with CyaSSL support\n"); +#else + lwsl_notice(" Compiled with wolfSSL support\n"); +#endif +#else +#if defined(LWS_USE_POLARSSL) + lwsl_notice(" Compiled with PolarSSL support\n"); +#else +#if defined(LWS_USE_MBEDTLS) + lwsl_notice(" Compiled with mbedTLS support\n"); +#else + lwsl_notice(" Compiled with OpenSSL support\n"); +#endif +#endif +#endif + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* basic openssl init */ + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_library_init(); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); +#endif +#endif + + return 0; +} + + +LWS_VISIBLE void +lws_ssl_destroy(struct lws_vhost *vhost) +{ + if (!lws_check_opt(vhost->context->options, + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return; + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); + +#if (OPENSSL_VERSION_NUMBER < 0x10100006L) +#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if (OPENSSL_VERSION_NUMBER >= 0x10100005L) && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +#endif +} + +LWS_VISIBLE void +lws_decode_ssl_error(void) +{ +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + char buf[256]; + u_long err; + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, buf, sizeof(buf)); + lwsl_err("*** %lu %s\n", err, buf); + } +#endif +#endif +} + +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + if (!wsi->pending_read_list_prev && + !wsi->pending_read_list_next && + pt->pending_read_list != wsi) + /* we are not on the list */ + return; + + /* point previous guy's next to our next */ + if (!wsi->pending_read_list_prev) + pt->pending_read_list = wsi->pending_read_list_next; + else + wsi->pending_read_list_prev->pending_read_list_next = + wsi->pending_read_list_next; + + /* point next guy's previous to our previous */ + if (wsi->pending_read_list_next) + wsi->pending_read_list_next->pending_read_list_prev = + wsi->pending_read_list_prev; + + wsi->pending_read_list_prev = NULL; + wsi->pending_read_list_next = NULL; +} + +LWS_VISIBLE int +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n = 0; + + if (!wsi->ssl) + return lws_ssl_capable_read_no_ssl(wsi, buf, len); + +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_read(wsi->ssl, buf, len); +#endif +#endif + + /* manpage: returning 0 means connection shut down */ + if (!n) + return LWS_SSL_CAPABLE_ERROR; + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) + return LWS_SSL_CAPABLE_MORE_SERVICE; + + return LWS_SSL_CAPABLE_ERROR; + } + + if (wsi->vhost) + wsi->vhost->rx += n; + + lws_restart_ws_ping_pong_timer(wsi); + + /* + * if it was our buffer that limited what we read, + * check if SSL has additional data pending inside SSL buffers. + * + * Because these won't signal at the network layer with POLLIN + * and if we don't realize, this data will sit there forever + */ + if (n != len) + goto bail; + if (!wsi->ssl) + goto bail; +#if defined(LWS_USE_POLARSSL) + if (ssl_get_bytes_avail(wsi->ssl) <= 0) + goto bail; +#else +#if defined(LWS_USE_MBEDTLS) +#else + if (!SSL_pending(wsi->ssl)) + goto bail; +#endif +#endif + if (wsi->pending_read_list_next) + return n; + if (wsi->pending_read_list_prev) + return n; + if (pt->pending_read_list == wsi) + return n; + + /* add us to the linked list of guys with pending ssl */ + if (pt->pending_read_list) + pt->pending_read_list->pending_read_list_prev = wsi; + + wsi->pending_read_list_next = pt->pending_read_list; + wsi->pending_read_list_prev = NULL; + pt->pending_read_list = wsi; + + return n; +bail: + lws_ssl_remove_wsi_from_buffered_list(wsi); + + return n; +} + +LWS_VISIBLE int +lws_ssl_pending(struct lws *wsi) +{ + if (!wsi->ssl) + return 0; +#if defined(LWS_USE_POLARSSL) + return ssl_get_bytes_avail(wsi->ssl) > 0; +#else +#if defined(LWS_USE_MBEDTLS) + return ssl_get_bytes_avail(wsi->ssl) > 0;; +#else + return SSL_pending(wsi->ssl); +#endif +#endif +} + +LWS_VISIBLE int +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) +{ + int n; + + if (!wsi->ssl) + return lws_ssl_capable_write_no_ssl(wsi, buf, len); + +#if defined(LWS_USE_POLARSSL) + n = ssl_write(wsi->ssl, buf, len); +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_write(wsi->ssl, buf, len); +#endif +#endif + if (n > 0) + return n; + + n = lws_ssl_get_error(wsi, n); + if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) { + if (n == SSL_ERROR_WANT_WRITE) + lws_set_blocking_send(wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_close(struct lws *wsi) +{ + int n; + + if (!wsi->ssl) + return 0; /* not handled */ + +#if defined(LWS_USE_POLARSSL) + ssl_close_notify(wsi->ssl); + (void)n; /* we need to close the fd? */ + ssl_free(wsi->ssl); +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_get_fd(wsi->ssl); + SSL_shutdown(wsi->ssl); + compatible_close(n); + SSL_free(wsi->ssl); +#endif +#endif + wsi->ssl = NULL; + + return 1; /* handled */ +} + +/* leave all wsi close processing to the caller */ + +LWS_VISIBLE int +lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n, m; +#if !defined(USE_WOLFSSL) && !defined(LWS_USE_POLARSSL) && !defined(LWS_USE_MBEDTLS) + BIO *bio; +#endif + + if (!LWS_SSL_ENABLED(wsi->vhost)) + return 0; + + switch (wsi->mode) { + case LWSCM_SSL_INIT: + + if (wsi->ssl) + lwsl_err("%s: leaking ssl\n", __func__); + if (accept_fd == LWS_SOCK_INVALID) + assert(0); + +#if defined(LWS_USE_POLARSSL) + { + ssl_session *ssn; + int rc; + + wsi->ssl = lws_zalloc(sizeof(ssl_context)); + ssn = lws_zalloc(sizeof(ssl_session)); + + rc = ssl_init(wsi->ssl); + if (rc) { + lwsl_err("ssl_init failed\n"); + goto fail; + } + + ssl_set_endpoint(wsi->ssl, SSL_IS_SERVER); + ssl_set_authmode(wsi->ssl, SSL_VERIFY_OPTIONAL); + ssl_set_rng(wsi->ssl, urandom_bytes, NULL); + ssl_set_dbg(wsi->ssl, pssl_debug, NULL); + ssl_set_bio(wsi->ssl, net_recv, &wsi->sock, net_send, &wsi->sock); + + ssl_set_ciphersuites(wsi->ssl, ciphers); + + ssl_set_session(wsi->ssl, ssn); + + ssl_set_ca_chain(wsi->ssl, &wsi->vhost->ssl_ctx->ca, + NULL, NULL); + + ssl_set_own_cert_rsa(wsi->ssl, + &wsi->vhost->ssl_ctx->certificate, + &wsi->vhost->ssl_ctx->key); + +// ssl_set_dh_param(wsi->ssl, my_dhm_P, my_dhm_G); + + lwsl_err("%s: polarssl init done\n", __func__); + } +#else +#if defined(LWS_USE_MBEDTLS) +#else + wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); + if (wsi->ssl == NULL) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); + lws_decode_ssl_error(); + if (accept_fd != LWS_SOCK_INVALID) + compatible_close(accept_fd); + goto fail; + } + + SSL_set_ex_data(wsi->ssl, + openssl_websocket_private_data_index, wsi->vhost); + + SSL_set_fd(wsi->ssl, accept_fd); +#endif +#endif + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else +#if defined(LWS_USE_POLARSSL) + +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + bio = SSL_get_rbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); + bio = SSL_get_wbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); +#endif +#endif +#endif + + /* + * we are not accepted yet, but we need to enter ourselves + * as a live connection. That way we can retry when more + * pieces come if we're not sorted yet + */ + + wsi->mode = LWSCM_SSL_ACK_PENDING; + if (insert_wsi_socket_into_fds(context, wsi)) { + lwsl_err("%s: failed to insert into fds\n", __func__); + goto fail; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, + context->timeout_secs); + + lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); + + /* fallthru */ + + case LWSCM_SSL_ACK_PENDING: + + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_err("%s: lws_change_pollfd failed\n", __func__); + goto fail; + } + + lws_latency_pre(context, wsi); + + n = recv(wsi->sock, (char *)pt->serv_buf, context->pt_serv_buf_size, + MSG_PEEK); + + /* + * optionally allow non-SSL connect on SSL listening socket + * This is disabled by default, if enabled it goes around any + * SSL-level access control (eg, client-side certs) so leave + * it disabled unless you know it's not a problem for you + */ + + if (wsi->vhost->allow_non_ssl_on_ssl_port) { + if (n >= 1 && pt->serv_buf[0] >= ' ') { + /* + * TLS content-type for Handshake is 0x16, and + * for ChangeCipherSpec Record, it's 0x14 + * + * A non-ssl session will start with the HTTP + * method in ASCII. If we see it's not a legit + * SSL handshake kill the SSL for this + * connection and try to handle as a HTTP + * connection upgrade directly. + */ + wsi->use_ssl = 0; +#if defined(LWS_USE_POLARSSL) + ssl_close_notify(wsi->ssl); + ssl_free(wsi->ssl); +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_shutdown(wsi->ssl); + SSL_free(wsi->ssl); +#endif +#endif + wsi->ssl = NULL; + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) + wsi->redirect_to_https = 1; + goto accepted; + } + if (!n) /* + * connection is gone, or nothing to read + * if it's gone, we will timeout on + * PENDING_TIMEOUT_SSL_ACCEPT + */ + break; + if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK)) { + /* + * well, we get no way to know ssl or not + * so go around again waiting for something + * to come and give us a hint, or timeout the + * connection. + */ + m = SSL_ERROR_WANT_READ; + goto go_again; + } + } + + /* normal SSL connection processing path */ +#if defined(LWS_USE_POLARSSL) + n = ssl_handshake(wsi->ssl); +#else +#if defined(LWS_USE_MBEDTLS) +#else + n = SSL_accept(wsi->ssl); +#endif +#endif + lws_latency(context, wsi, + "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1); + + if (n == 1) + goto accepted; + + m = lws_ssl_get_error(wsi, n); + lwsl_debug("SSL_accept failed %d / %s\n", + m, ERR_error_string(m, NULL)); +go_again: + if (m == SSL_ERROR_WANT_READ) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_err("%s: WANT_READ change_pollfd failed\n", __func__); + goto fail; + } + + lwsl_info("SSL_ERROR_WANT_READ\n"); + break; + } + if (m == SSL_ERROR_WANT_WRITE) { + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_err("%s: WANT_WRITE change_pollfd failed\n", __func__); + goto fail; + } + + break; + } + lwsl_err("SSL_accept failed skt %u: %s\n", + wsi->sock, ERR_error_string(m, NULL)); + + lws_ssl_elaborate_error(); + goto fail; + +accepted: + /* OK, we are accepted... give him some time to negotiate */ + lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + + wsi->mode = LWSCM_HTTP_SERVING; + + lws_http2_configure_if_upgraded(wsi); + + lwsl_debug("accepted new SSL conn\n"); + break; + } + + return 0; + +fail: + return 1; +} + +void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) +{ + if (vhost->ssl_ctx) { +#if defined(LWS_USE_POLARSSL) + lws_free(vhost->ssl_ctx); +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_CTX_free(vhost->ssl_ctx); +#endif +#endif + } + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) { +#if defined(LWS_USE_POLARSSL) + lws_free(vhost->ssl_client_ctx); +#else +#if defined(LWS_USE_MBEDTLS) +#else + SSL_CTX_free(vhost->ssl_client_ctx); +#endif +#endif + } +} + +void +lws_ssl_context_destroy(struct lws_context *context) +{ +#if defined(LWS_USE_POLARSSL) +#else +#if defined(LWS_USE_MBEDTLS) +#else +#if (OPENSSL_VERSION_NUMBER < 0x10100006L) +#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if (OPENSSL_VERSION_NUMBER >= 0x10100005L) && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +#endif +} diff --git a/feeds/wlan-ap/opensync/Makefile b/feeds/wlan-ap/opensync/Makefile index 490a2d3f..b8e2b624 100644 --- a/feeds/wlan-ap/opensync/Makefile +++ b/feeds/wlan-ap/opensync/Makefile @@ -30,7 +30,7 @@ endef define Package/opensync CATEGORY:=Network TITLE:=cloud network management system - DEPENDS:=+libev +jansson +protobuf +libprotobuf-c +libmosquitto +libopenssl +openvswitch +libpcap +libuci +libcurl +libnl-tiny +libubus +libblobmsg-json +tcpdump +curl +lldpd +wlan-ap-keys +libradiusclient + DEPENDS:=+libev +jansson +protobuf +libprotobuf-c +libmosquitto +libopenssl +openvswitch +libpcap +libuci +libcurl +libnl-tiny +libubus +libblobmsg-json +tcpdump +curl +lldpd +wlan-ap-keys +libradiusclient +libwebsocket endef define Package/opensync/description diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/command.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/command.h index 2605ceea..046dd045 100644 --- a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/command.h +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/command.h @@ -49,5 +49,7 @@ extern void node_config_init(void); extern void webserver_init(void); extern void crashlog_init(void); extern pid_t cmd_handler_crashlog(struct task *task); +extern pid_t cmd_handler_port_forwarding(struct task *task); +extern int port_forwarding(char *ipAddress, char *port); #endif diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/libwebsockets.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/libwebsockets.c new file mode 100755 index 00000000..c72ffa5c --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/libwebsockets.c @@ -0,0 +1,2687 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "websocket/private-libwebsockets.h" +#include "websocket/libwcf_debug.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +#if defined(WIN32) || defined(_WIN32) +#else +#include +#endif + +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr; + +static const char * const log_level_names[] = { + "ERR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "PARSER", + "HEADER", + "EXTENSION", + "CLIENT", + "LATENCY", +}; + +void a2w_logfr_and_reboot( char * Buf ) +{ + char buf[200]; + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf)-1, "websocket requested reboot (%s)", Buf); + wc_put_logline( (WRITE_TO_FR | APP_LOG_WEBSOCK | DEBUG_LEVEL_1), "%s", buf ); + sleep(1); + system( "/splashbin/reboot" ); +} + +void +lws_free_wsi(struct lws *wsi) +{ + if (!wsi) + return; + + /* Protocol user data may be allocated either internally by lws + * or by specified the user. + * We should only free what we allocated. */ + if (wsi->protocol && wsi->protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) + lws_free(wsi->user_space); + + lws_free_set_NULL(wsi->rxflow_buffer); + lws_free_set_NULL(wsi->trunc_alloc); + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("ah det due to close\n"); + lws_header_table_detach(wsi, 0); + + wsi->context->count_wsi_allocated--; + lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, + wsi->context->count_wsi_allocated); + + lws_free(wsi); +} + +static void +lws_remove_from_timeout_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!wsi->timeout_list_prev) /* ie, not part of the list */ + return; + + lws_pt_lock(pt); + /* if we have a next guy, set his prev to our prev */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev; + /* set our prev guy to our next guy instead of us */ + *wsi->timeout_list_prev = wsi->timeout_list; + + /* we're out of the list, we should not point anywhere any more */ + wsi->timeout_list_prev = NULL; + wsi->timeout_list = NULL; + lws_pt_unlock(pt); +} + +LWS_VISIBLE void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + time_t now; + + lws_pt_lock(pt); + + time(&now); + + if (reason && !wsi->timeout_list_prev) { + /* our next guy is current first guy */ + wsi->timeout_list = pt->timeout_list; + /* if there is a next guy, set his prev ptr to our next ptr */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = &wsi->timeout_list; + /* our prev ptr is first ptr */ + wsi->timeout_list_prev = &pt->timeout_list; + /* set the first guy to be us */ + *wsi->timeout_list_prev = wsi; + } + + lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs); + wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout = reason; + + lws_pt_unlock(pt); + + if (!reason) + lws_remove_from_timeout_list(wsi); +} + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) +{ + struct lws_context_per_thread *pt; + struct lws **pwsi, *wsi1, *wsi2; + struct lws_context *context; + struct lws_tokens eff_buf; + int n, m, ret; + + if (!wsi) + return; + + lws_access_log(wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->premature_rx) + lws_free(wsi->premature_rx); + + if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) { + lwsl_notice("delaying close\n"); + wsi->close_is_pending_send_completion = 1; + return; + } +#endif + + if (wsi->u.hdr.ah) + /* we're closing, losing some rx is OK */ + wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + //lwsl_notice("%s: closing %p: close child %p\n", + // __func__, wsi, wsi2); + wsi1 = wsi2->sibling_list; + //lwsl_notice("%s: closing %p: next sibling %p\n", + // __func__, wsi2, wsi1); + wsi2->parent = NULL; + /* stop it doing shutdown processing */ + wsi2->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi2, reason); + wsi2 = wsi1; + } + wsi->child_list = NULL; + } + +#ifdef LWS_WITH_CGI + if (wsi->mode == LWSCM_CGI) { + /* we are not a network connection, but a handler for CGI io */ + if (wsi->parent && wsi->parent->cgi) + /* end the binding between us and master */ + wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; + wsi->socket_is_permanently_unusable = 1; + + lwsl_debug("------ %s: detected cgi fdhandler wsi %p\n", __func__, wsi); + goto just_kill_connection; + } + + if (wsi->cgi) { + struct lws_cgi **pcgi = &pt->cgi_list; + /* remove us from the cgi list */ + lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->cgi); + while (*pcgi) { + if (*pcgi == wsi->cgi) { + /* drop us from the pt cgi list */ + *pcgi = (*pcgi)->cgi_list; + break; + } + pcgi = &(*pcgi)->cgi_list; + } + /* we have a cgi going, we must kill it */ + wsi->cgi->being_closed = 1; + lws_cgi_kill(wsi); + } +#endif + + if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED && + wsi->u.http.fd != LWS_INVALID_FILE) { + lws_plat_file_close(wsi, wsi->u.http.fd); + wsi->u.http.fd = LWS_INVALID_FILE; + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->socket_is_permanently_unusable || + reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || + wsi->state == LWSS_SHUTDOWN) + goto just_kill_connection; + + wsi->state_pre_close = wsi->state; + + switch (wsi->state_pre_close) { + case LWSS_DEAD_SOCKET: + return; + + /* we tried the polite way... */ + case LWSS_AWAITING_CLOSE_ACK: + goto just_kill_connection; + + case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE: + if (wsi->trunc_len) { + lws_callback_on_writable(wsi); + return; + } + lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + goto just_kill_connection; + default: + if (wsi->trunc_len) { + lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); + return; + } + break; + } + + if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) + goto just_kill_connection; + + if (wsi->mode == LWSCM_HTTP_SERVING) { + if (wsi->user_space) + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) { + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + /* + * are his extensions okay with him closing? Eg he might be a mux + * parent and just his ch1 aspect is closing? + */ + + if (lws_ext_cb_active(wsi, + LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { + lwsl_ext("extension vetoed close\n"); + return; + } + + /* + * flush any tx pending from extensions, since we may send close packet + * if there are problems with send, just nuke the connection + */ + + do { + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* show every extension the new incoming data */ + + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0); + if (m < 0) { + lwsl_ext("Extension reports fatal error\n"); + goto just_kill_connection; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) + if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len) != + eff_buf.token_len) { + lwsl_debug("close: ext spill failed\n"); + goto just_kill_connection; + } + } while (ret); + + /* + * signal we are closing, lws_write will + * add any necessary version-specific stuff. If the write fails, + * no worries we are closing anyway. If we didn't initiate this + * close, then our state has been changed to + * LWSS_RETURNED_CLOSE_ALREADY and we will skip this. + * + * Likewise if it's a second call to close this connection after we + * sent the close indication to the peer already, we are in state + * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time. + */ + + if (wsi->state_pre_close == LWSS_ESTABLISHED && + (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */ + (reason != LWS_CLOSE_STATUS_NOSTATUS && + (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) { + lwsl_debug("sending close indication...\n"); + + /* if no prepared close reason, use 1000 and no aux data */ + if (!wsi->u.ws.close_in_ping_buffer_len) { + wsi->u.ws.close_in_ping_buffer_len = 2; + wsi->u.ws.ping_payload_buf[LWS_PRE] = + (reason >> 16) & 0xff; + wsi->u.ws.ping_payload_buf[LWS_PRE + 1] = + reason & 0xff; + } + +#if defined (LWS_WITH_ESP8266) + wsi->close_is_pending_send_completion = 1; +#endif + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.close_in_ping_buffer_len, + LWS_WRITE_CLOSE); + if (n >= 0) { + /* + * we have sent a nice protocol level indication we + * now wish to close, we should not send anything more + */ + wsi->state = LWSS_AWAITING_CLOSE_ACK; + + /* + * ...and we should wait for a reply for a bit + * out of politeness + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1); + lwsl_debug("sent close indication, awaiting ack\n"); + + return; + } + + lwsl_info("close: sending close packet failed, hanging up\n"); + + /* else, the send failed and we should just hang up */ + } + +just_kill_connection: + + if (wsi->parent) { + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + //lwsl_notice("%s: detach %p from parent %p\n", + // __func__, wsi, wsi->parent); + *pwsi = wsi->sibling_list; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (*pwsi) + lwsl_err("%s: failed to detach from parent\n", + __func__); + } + +#if LWS_POSIX + /* + * Testing with ab shows that we have to stage the socket close when + * the system is under stress... shutdown any further TX, change the + * state to one that won't emit anything more, and wait with a timeout + * for the POLLIN to show a zero-size rx before coming back and doing + * the actual close. + */ + if (wsi->state != LWSS_SHUTDOWN && + wsi->state != LWSS_CLIENT_UNCONNECTED && + reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && + !wsi->socket_is_permanently_unusable) { + lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, wsi->sock, wsi->state); + n = shutdown(wsi->sock, SHUT_WR); + if (n) + lwsl_debug("closing: shutdown (state %d) ret %d\n", wsi->state, LWS_ERRNO); + +// This causes problems with disconnection when the events are half closing connection +// FD_READ | FD_CLOSE (33) +#ifndef _WIN32_WCE + /* libuv: no event available to guarantee completion */ + if (!LWS_LIBUV_ENABLED(context)) { + + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + wsi->state = LWSS_SHUTDOWN; + lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, + context->timeout_secs); + return; + } +#endif + } +#endif + + lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, + wsi, wsi->sock); + +#ifdef LWS_WITH_HTTP_PROXY + if (wsi->rw) { + lws_rewrite_destroy(wsi->rw); + wsi->rw = NULL; + } +#endif + /* + * we won't be servicing or receiving anything further from this guy + * delete socket from the internal poll list if still present + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + + lws_remove_from_timeout_list(wsi); + + /* checking return redundant since we anyway close */ + if (wsi->sock != LWS_SOCK_INVALID) + remove_wsi_socket_from_fds(wsi); + +#if defined(LWS_WITH_ESP8266) + espconn_disconnect(wsi->sock); +#endif + + wsi->state = LWSS_DEAD_SOCKET; + + lws_free_set_NULL(wsi->rxflow_buffer); + + if (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->mode == LWSCM_WS_SERVING || + wsi->mode == LWSCM_WS_CLIENT) { + + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + } + + if (wsi->u.ws.tx_draining_ext) { + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + } + lws_free_set_NULL(wsi->u.ws.rx_ubuf); + + if (wsi->trunc_alloc) + /* not going to be completed... nuke it */ + lws_free_set_NULL(wsi->trunc_alloc); + + wsi->u.ws.ping_payload_len = 0; + wsi->u.ws.ping_pending_flag = 0; + } + + /* tell the user it's all over for this guy */ + + if (wsi->protocol && wsi->protocol->callback && + ((wsi->state_pre_close == LWSS_ESTABLISHED) || + (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) || + (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) || + (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) || + (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) || + (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) { + + if (wsi->user_space) { + lwsl_debug("%s: doing LWS_CALLBACK_HTTP_DROP_PROTOCOL for %p prot %s", __func__, wsi, wsi->protocol->name); + wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + } + lwsl_debug("calling back CLOSED\n"); + wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED, + wsi->user_space, NULL, 0); + } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_debug("calling back CLOSED_HTTP\n"); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0 ); + } else if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) && + !wsi->already_did_cce) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, NULL, 0); + } else + lwsl_debug("not calling back closed mode=%d state=%d\n", + wsi->mode, wsi->state_pre_close); + + /* deallocate any active extension contexts */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) + lwsl_warn("extension destruction failed\n"); + /* + * inform all extensions in case they tracked this guy out of band + * even though not active on him specifically + */ + if (lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0) + lwsl_warn("ext destroy wsi failed\n"); + + wsi->socket_is_permanently_unusable = 1; + +#ifdef LWS_USE_LIBUV + if (LWS_LIBUV_ENABLED(context)) { + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + /* libuv has to do his own close handle processing asynchronously */ + lws_libuv_closehandle(wsi); + + return; + } +#endif + + lws_close_free_wsi_final(wsi); +} + +void +lws_close_free_wsi_final(struct lws *wsi) +{ + int n; + + if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) { +#if LWS_POSIX + //lwsl_err("*** closing sockfd %d\n", wsi->sock); + n = compatible_close(wsi->sock); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); + +#else + compatible_close(wsi->sock); + (void)n; +#endif + wsi->sock = LWS_SOCK_INVALID; + } + + /* outermost destroy notification for wsi (user_space still intact) */ + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->user_space, NULL, 0); + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + for (n = 0; n < 6; n++) + if (wsi->cgi->pipe_fds[n / 2][n & 1] >= 0) + close(wsi->cgi->pipe_fds[n / 2][n & 1]); + + lws_free(wsi->cgi); + } +#endif + + lws_free_wsi(wsi); +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, sl = strlen(name); + + while (lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { + + if (!strncmp(buf, name, sl)) + return buf + sl; + + n++; + } + + return NULL; +} + +#if LWS_POSIX +LWS_VISIBLE int +interface_to_sa(struct lws_vhost *vh, const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ + int ipv6 = 0; +#ifdef LWS_USE_IPV6 + ipv6 = LWS_IPV6_ENABLED(vh); +#endif + (void)vh; + + return lws_interface_to_sa(ipv6, ifname, addr, addrlen); +} +#endif + +#if LWS_POSIX +static int +lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + struct addrinfo ai, *res; + struct sockaddr_in addr4; + + if (rip) + rip[0] = '\0'; + name[0] = '\0'; + addr4.sin_family = AF_UNSPEC; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(vh)) { + if (!lws_plat_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ads)->sin6_addr, rip, rip_len)) { + lwsl_err("inet_ntop", strerror(LWS_ERRNO)); + return -1; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in6), name, + name_len, NULL, 0, 0); + + return 0; + } else +#endif + { + struct addrinfo *result; + + memset(&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_CANONNAME; + + if (getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in), + name, name_len, NULL, 0, 0)) + return -1; + + if (!rip) + return 0; + + if (getaddrinfo(name, NULL, &ai, &result)) + return -1; + + res = result; + while (addr4.sin_family == AF_UNSPEC && res) { + switch (res->ai_family) { + case AF_INET: + addr4.sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; + addr4.sin_family = AF_INET; + break; + } + + res = res->ai_next; + } + freeaddrinfo(result); + } + + if (addr4.sin_family == AF_UNSPEC) + return -1; + + if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) + return -1; + + return 0; +#else + (void)vh; + (void)ads; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + + return -1; +#endif +} +#endif + +LWS_VISIBLE const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ +#if LWS_POSIX + socklen_t len, olen; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + int af = AF_INET; + void *p, *q; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + af = AF_INET6; + q = &sin6.sin6_addr; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + q = &sin4.sin_addr; + } + + olen = len; + if (getpeername(wsi->sock, p, &len) < 0 || len > olen) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + return NULL; + } + + return lws_plat_inet_ntop(af, q, name, namelen); +#else +#if defined(LWS_WITH_ESP8266) + return lws_plat_get_peer_simple(wsi, name, namelen); +#else + return NULL; +#endif +#endif +} + +LWS_VISIBLE int +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + socklen_t len; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + struct lws_context *context = wsi->context; + int ret = -1; + void *p; + + rip[0] = '\0'; + name[0] = '\0'; + + lws_latency_pre(context, wsi); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); + +bail: + lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); + return ret; +#else + (void)wsi; + (void)fd; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; +#endif +} + +LWS_EXTERN void * +lws_context_user(struct lws_context *context) +{ + return context->user_space; +} + +LWS_VISIBLE struct lws_vhost * +lws_vhost_get(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context *context = vh->context; + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost == vh && wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) +{ + int n; + + for (n = 0; n < wsi->vhost->count_protocols; n++) + if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) + return 1; + + return 0; +} + +/** + * lws_now_secs() - seconds since 1970-1-1 + * + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + + +#if LWS_POSIX + +LWS_VISIBLE int +lws_get_socket_fd(struct lws *wsi) +{ + return wsi->sock; +} + +#endif + +#ifdef LWS_LATENCY +void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completed) +{ + unsigned long long u; + char buf[256]; + + u = time_in_microseconds(); + + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; + } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); + + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif + +LWS_VISIBLE int +lws_rx_flow_control(struct lws *wsi, int enable) +{ + if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) + return 0; + + lwsl_info("%s: (0x%p, %d)\n", __func__, wsi, enable); + wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable; + + return 0; +} + +LWS_VISIBLE void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + const struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + unsigned int n, m = context->count_threads; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); + } + pt++; + } +} + +LWS_VISIBLE extern const char * +lws_canonical_hostname(struct lws_context *context) +{ + return (const char *)context->canonical_hostname; +} + +int user_callback_handle_rxflow(lws_callback_function callback_function, + struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + + n = callback_function(wsi, reason, user, in, len); + if (!n) + n = _lws_rx_flow_control(wsi); + + return n; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +LWS_VISIBLE int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +{ +#if !defined(LWS_WITH_ESP8266) + char *p; + char authstring[96]; + + if (!proxy) + return -1; + + p = strchr(proxy, '@'); + if (p) { /* auth is around */ + + if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + strncpy(authstring, proxy, p - proxy); + // null termination not needed on input + if (lws_b64_encode_string(authstring, (p - proxy), + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) + goto auth_too_long; + + lwsl_info(" Proxy auth in use\n"); + + proxy = p + 1; + } else + vhost->proxy_basic_auth_token[0] = '\0'; + + strncpy(vhost->http_proxy_address, proxy, + sizeof(vhost->http_proxy_address) - 1); + vhost->http_proxy_address[ + sizeof(vhost->http_proxy_address) - 1] = '\0'; + + p = strchr(vhost->http_proxy_address, ':'); + if (!p && !vhost->http_proxy_port) { + lwsl_err("http_proxy needs to be ads:port\n"); + + return -1; + } else { + if (p) { + *p = '\0'; + vhost->http_proxy_port = atoi(p + 1); + } + } + + lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address, + vhost->http_proxy_port); + + return 0; + +auth_too_long: + lwsl_err("proxy auth too long\n"); +#endif + return -1; +} + +LWS_VISIBLE const struct lws_protocols * +lws_get_protocol(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_is_final_fragment(struct lws *wsi) +{ + lwsl_info("%s: final %d, rx pk length %d, draining %d", __func__, + wsi->u.ws.final, wsi->u.ws.rx_packet_length, + wsi->u.ws.rx_draining_ext); + return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext; +} + +LWS_VISIBLE unsigned char +lws_get_reserved_bits(struct lws *wsi) +{ + return wsi->u.ws.rsv; +} + +int +lws_ensure_user_space(struct lws *wsi) +{ + lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol); + if (!wsi->protocol) + return 1; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size); + if (wsi->user_space == NULL) { + lwsl_err("Out of memory for conn user space\n"); + return 1; + } + } else + lwsl_info("%s: %p protocol pss %u, user_space=%d\n", + __func__, wsi, wsi->protocol->per_session_data_size, + wsi->user_space); + return 0; +} + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) +{ + time_t o_now = time(NULL); + unsigned long long now; + struct tm *ptm = NULL; +#ifndef WIN32 + struct tm tm; +#endif + int n; + +#ifndef _WIN32_WCE +#ifdef WIN32 + ptm = localtime(&o_now); +#else + if (localtime_r(&o_now, &tm)) + ptm = &tm; +#endif +#endif + p[0] = '\0'; + for (n = 0; n < LLL_COUNT; n++) { + if (level != (1 << n)) + continue; + now = time_in_microseconds() / 100; + if (ptm) + n = lws_snprintf(p, len, + "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + (int)(now % 10000), log_level_names[n]); + else + n = lws_snprintf(p, len, "[%llu:%04d] %s: ", + (unsigned long long) now / 10000, + (int)(now % 10000), log_level_names[n]); + return n; + } + + return 0; +} + +LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) +{ +#if !defined(LWS_WITH_ESP8266) + char buf[50]; + + lwsl_timestamp(level, buf, sizeof(buf)); +#if 0 + fprintf(stderr, "%s%s", buf, line); +#else + wc_put_logline( (APP_LOG_WEBSOCK | DEBUG_LEVEL_1), "%s%s", buf, line); +#endif +#endif +} + +LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) +{ +#if defined(LWS_WITH_ESP8266) + char buf[128]; +#else + char buf[256]; +#endif + unsigned int n; + + if (!(log_level & filter)) + return; + + n = vsnprintf(buf, sizeof(buf) - 1, format, vl); + (void)n; +#if defined(LWS_WITH_ESP8266) + buf[sizeof(buf) - 1] = '\0'; +#else + /* vnsprintf returns what it would have written, even if truncated */ + if (n > sizeof(buf) - 1) + n = sizeof(buf) - 1; + if (n > 0) + buf[n] = '\0'; +#endif + + lwsl_emit(filter, buf); +} + +LWS_VISIBLE void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _lws_logv(filter, format, ap); + va_end(ap); +} + +LWS_VISIBLE void lws_set_log_level(int level, + void (*func)(int level, const char *line)) +{ + log_level = level; + if (func) + lwsl_emit = func; +} + +LWS_VISIBLE int +lws_is_ssl(struct lws *wsi) +{ +#ifdef LWS_OPENSSL_SUPPORT + return wsi->use_ssl; +#else + (void)wsi; + return 0; +#endif +} + +LWS_VISIBLE int +lws_partial_buffered(struct lws *wsi) +{ + return !!wsi->trunc_len; +} + +void lws_set_protocol_write_pending(struct lws *wsi, + enum lws_pending_protocol_send pend) +{ + lwsl_info("setting pps %d\n", pend); + + if (wsi->pps) + lwsl_err("pps overwrite\n"); + wsi->pps = pend; + lws_rx_flow_control(wsi, 0); + lws_callback_on_writable(wsi); +} + +LWS_VISIBLE size_t +lws_get_peer_write_allowance(struct lws *wsi) +{ +#ifdef LWS_USE_HTTP2 + /* only if we are using HTTP2 on this connection */ + if (wsi->mode != LWSCM_HTTP2_SERVING) + return -1; + /* user is only interested in how much he can send, or that he can't */ + if (wsi->u.http2.tx_credit <= 0) + return 0; + + return wsi->u.http2.tx_credit; +#else + (void)wsi; + return -1; +#endif +} + +LWS_VISIBLE void +lws_union_transition(struct lws *wsi, enum connection_mode mode) +{ + lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode); + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->mode = mode; +} + +LWS_VISIBLE struct lws_plat_file_ops * +lws_get_fops(struct lws_context *context) +{ + return &context->fops; +} + +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_get_context(const struct lws *wsi) +{ + return wsi->context; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_count_threads(struct lws_context *context) +{ + return context->count_threads; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len) +{ + unsigned char *p, *start; + int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE; + + assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT); + + start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE]; + + *p++ = (((int)status) >> 8) & 0xff; + *p++ = ((int)status) & 0xff; + + if (buf) + while (len-- && p < start + budget) + *p++ = *buf++; + + wsi->u.ws.close_in_ping_buffer_len = p - start; +} + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi) +{ + /* there is no pending change */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) { + lwsl_debug("%s: no pending change\n", __func__); + return 0; + } + + /* stuff is still buffered, not ready to really accept new input */ + if (wsi->rxflow_buffer) { + /* get ourselves called back to deal with stashed buffer */ + lws_callback_on_writable(wsi); + return 0; + } + + /* pending is cleared, we can change rxflow state */ + + wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + +LWS_EXTERN int +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) +{ + static const unsigned char e0f4[] = { + 0xa0 | ((2 - 1) << 2) | 1, /* e0 */ + 0x80 | ((4 - 1) << 2) | 1, /* e1 */ + 0x80 | ((4 - 1) << 2) | 1, /* e2 */ + 0x80 | ((4 - 1) << 2) | 1, /* e3 */ + 0x80 | ((4 - 1) << 2) | 1, /* e4 */ + 0x80 | ((4 - 1) << 2) | 1, /* e5 */ + 0x80 | ((4 - 1) << 2) | 1, /* e6 */ + 0x80 | ((4 - 1) << 2) | 1, /* e7 */ + 0x80 | ((4 - 1) << 2) | 1, /* e8 */ + 0x80 | ((4 - 1) << 2) | 1, /* e9 */ + 0x80 | ((4 - 1) << 2) | 1, /* ea */ + 0x80 | ((4 - 1) << 2) | 1, /* eb */ + 0x80 | ((4 - 1) << 2) | 1, /* ec */ + 0x80 | ((2 - 1) << 2) | 1, /* ed */ + 0x80 | ((4 - 1) << 2) | 1, /* ee */ + 0x80 | ((4 - 1) << 2) | 1, /* ef */ + 0x90 | ((3 - 1) << 2) | 2, /* f0 */ + 0x80 | ((4 - 1) << 2) | 2, /* f1 */ + 0x80 | ((4 - 1) << 2) | 2, /* f2 */ + 0x80 | ((4 - 1) << 2) | 2, /* f3 */ + 0x80 | ((1 - 1) << 2) | 2, /* f4 */ + + 0, /* s0 */ + 0x80 | ((4 - 1) << 2) | 0, /* s2 */ + 0x80 | ((4 - 1) << 2) | 1, /* s3 */ + }; + unsigned char s = *state; + + while (len--) { + unsigned char c = *buf++; + + if (!s) { + if (c >= 0x80) { + if (c < 0xc2 || c > 0xf4) + return 1; + if (c < 0xe0) + s = 0x80 | ((4 - 1) << 2); + else + s = e0f4[c - 0xe0]; + } + } else { + if (c < (s & 0xf0) || + c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) + return 1; + s = e0f4[21 + (s & 3)]; + } + } + + *state = s; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path) +{ + const char *end; + static const char *slash = "/"; + + /* cut up the location into address, port and path */ + *prot = p; + while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) + p++; + if (!*p) { + end = p; + p = (char *)*prot; + *prot = end; + } else { + *p = '\0'; + p += 3; + } + *ads = p; + if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) + *port = 80; + else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) + *port = 443; + + while (*p && *p != ':' && *p != '/') + p++; + if (*p == ':') { + *p++ = '\0'; + *port = atoi(p); + while (*p && *p != '/') + p++; + } + *path = slash; + if (*p) { + *p++ = '\0'; + if (*p) + *path = p; + } + + return 0; +} + +#ifdef LWS_NO_EXTENSIONS + +/* we need to provide dummy callbacks for internal exts + * so user code runs when faced with a lib compiled with + * extensions disabled. + */ + +int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + (void)context; + (void)ext; + (void)wsi; + (void)reason; + (void)user; + (void)in; + (void)len; + + return 0; +} +#endif + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface) +{ +#if LWS_POSIX +#ifdef LWS_USE_UNIX_SOCK + struct sockaddr_un serv_unix; +#endif +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; + socklen_t len = sizeof(struct sockaddr); + int n; + struct sockaddr_in sin; + struct sockaddr *v; + +#ifdef LWS_USE_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_unix; + n = sizeof(struct sockaddr_un); + bzero((char *) &serv_unix, sizeof(serv_unix)); + serv_unix.sun_family = AF_UNIX; + if (sizeof(serv_unix.sun_path) <= strlen(iface)) { + lwsl_err("\"%s\" too long for UNIX domain socket\n", + iface); + return -1; + } + strcpy(serv_unix.sun_path, iface); + if (serv_unix.sun_path[0] == '@') + serv_unix.sun_path[0] = '\0'; + + } else +#endif +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } + + serv_addr4.sin_port = htons(port); + } /* ipv4 */ + + n = bind(sockfd, v, n); +#ifdef LWS_USE_UNIX_SOCK + if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { + lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", + sockfd, iface, n, LWS_ERRNO); + return -1; + } else +#endif + if (n < 0) { + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); + return -1; + } + + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else + port = ntohs(sin.sin_port); +#endif + + return port; +} + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi) +{ + if (!wsi->context->ws_ping_pong_interval) + return; + if (wsi->state != LWSS_ESTABLISHED) + return; + + wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() + + wsi->context->ws_ping_pong_interval; +} + +static const char *hex = "0123456789ABCDEF"; + +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 2) { + if (*p == '\'') { + *q++ = '\''; + *q++ = '\''; + len --; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + if (!p) { + escaped[0] = '\0'; + return escaped; + } + + while (*p && len-- > 6) { + if (*p == '\"' || *p == '\\' || *p < 0x20) { + *q++ = '\\'; + *q++ = 'u'; + *q++ = '0'; + *q++ = '0'; + *q++ = hex[((*p) >> 4) & 15]; + *q++ = hex[(*p) & 15]; + len -= 5; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 3) { + if (*p == ' ') { + *q++ = '+'; + p++; + continue; + } + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z')) { + *q++ = *p++; + continue; + } + *q++ = '%'; + *q++ = hex[(*p >> 4) & 0xf]; + *q++ = hex[*p & 0xf]; + + len -= 2; + p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len) +{ + int state = 0, n; + char sum = 0; + + while (*escaped && len) { + switch (state) { + case 0: + if (*escaped == '%') { + state++; + escaped++; + continue; + } + if (*escaped == '+') { + escaped++; + *string++ = ' '; + len--; + continue; + } + *string++ = *escaped++; + len--; + break; + case 1: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + sum = n << 4; + state++; + break; + + case 2: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + *string++ = sum | n; + len--; + state = 0; + break; + } + + } + *string = '\0'; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context) +{ + struct lws_context_creation_info info; + + info.uid = context->uid; + info.gid = context->gid; + + if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(&info); + + return 0; +} + +int +lws_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + unsigned int n; + + if (!size) + return 0; + + va_start(ap, format); + n = vsnprintf(str, size, format, ap); + va_end(ap); + + if (n >= size) + return size; + + return n; +} + + +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi) { +#ifdef LWS_WITH_CGI + return !!wsi->cgi; +#else + return 0; +#endif +} + +#ifdef LWS_WITH_CGI + +static int +urlencode(const char *in, int inlen, char *out, int outlen) +{ + char *start = out, *end = out + outlen; + + while (inlen-- && out < end - 4) { + if ((*in >= 'A' && *in <= 'Z') || + (*in >= 'a' && *in <= 'z') || + (*in >= '0' && *in <= '9') || + *in == '-' || + *in == '_' || + *in == '.' || + *in == '~') { + *out++ = *in++; + continue; + } + if (*in == ' ') { + *out++ = '+'; + in++; + continue; + } + *out++ = '%'; + *out++ = hex[(*in) >> 4]; + *out++ = hex[(*in++) & 15]; + } + *out = '\0'; + + if (out >= end - 4) + return -1; + + return out - start; +} + +static struct lws * +lws_create_basic_wsi(struct lws_context *context, int tsi) +{ + struct lws *new_wsi; + + if ((unsigned int)context->pt[tsi].fds_count == + context->fd_limit_per_thread - 1) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws)); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = tsi; + new_wsi->context = context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_CGI; + new_wsi->mode = LWSCM_CGI; + new_wsi->hdr_parsing_completed = 0; + new_wsi->position_in_fds_table = -1; + + /* + * these can only be set once the protocol is known + * we set an unestablished connection's protocol pointer + * to the start of the defauly vhost supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = context->vhost_list->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->sock = LWS_SOCK_INVALID; + context->count_wsi_allocated++; + + return new_wsi; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len, + int timeout_secs, const struct lws_protocol_vhost_options *mp_cgienv) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *env_array[30], cgi_path[400], e[1024], *p = e, + *end = p + sizeof(e) - 1, tok[256], *t; + struct lws_cgi *cgi; + int n, m, i, uritok = -1; + + /* + * give the master wsi a cgi struct + */ + + wsi->cgi = lws_zalloc(sizeof(*wsi->cgi)); + if (!wsi->cgi) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + cgi = wsi->cgi; + cgi->wsi = wsi; /* set cgi's owning wsi */ + + /* create pipes for [stdin|stdout] and [stderr] */ + + for (n = 0; n < 3; n++) + if (pipe(cgi->pipe_fds[n]) == -1) + goto bail1; + + /* create cgi wsis for each stdin/out/err fd */ + + for (n = 0; n < 3; n++) { + cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi); + if (!cgi->stdwsi[n]) + goto bail2; + cgi->stdwsi[n]->cgi_channel = n; + cgi->stdwsi[n]->vhost = wsi->vhost; + +// lwsl_err("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, wsi, n, +// cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]); + + /* read side is 0, stdin we want the write side, others read */ + cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)]; + if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) { + lwsl_err("%s: setting NONBLOCK failed\n", __func__); + goto bail2; + } + } + + for (n = 0; n < 3; n++) { + lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->sock); + if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n])) + goto bail3; + cgi->stdwsi[n]->parent = wsi; + cgi->stdwsi[n]->sibling_list = wsi->child_list; + wsi->child_list = cgi->stdwsi[n]; + } + + lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT); + lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN); + lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN); + + lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__, + cgi->stdwsi[LWS_STDIN]->sock, cgi->stdwsi[LWS_STDOUT]->sock, + cgi->stdwsi[LWS_STDERR]->sock); + + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs); + + /* the cgi stdout is always sending us http1.x header data first */ + wsi->hdr_state = LCHS_HEADER; + + /* add us to the pt list of active cgis */ + lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->cgi); + cgi->cgi_list = pt->cgi_list; + pt->cgi_list = cgi; + + /* prepare his CGI env */ + + n = 0; + + if (lws_is_ssl(wsi)) + env_array[n++] = "HTTPS=ON"; + if (wsi->u.hdr.ah) { + static const unsigned char meths[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + }; + static const char * const meth_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", + }; + + for (m = 0; m < ARRAY_SIZE(meths); m++) + if (lws_hdr_total_length(wsi, meths[m]) >= + script_uri_path_len) { + uritok = meths[m]; + break; + } + + if (uritok < 0) + goto bail3; + + lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s", + lws_hdr_simple_ptr(wsi, uritok)); + cgi_path[sizeof(cgi_path) - 1] = '\0'; + env_array[n++] = cgi_path; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s", + meth_names[m]); + p++; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "QUERY_STRING="); + /* dump the individual URI Arg parameters */ + m = 0; + while (1) { + i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok), + WSI_TOKEN_HTTP_URI_ARGS, m); + if (i < 0) + break; + t = tok; + while (*t && *t != '=' && p < end - 4) + *p++ = *t++; + if (*t == '=') + *p++ = *t++; + i = urlencode(t, i- (t - tok), p, end - p); + if (i > 0) { + p += i; + *p++ = '&'; + } + m++; + } + if (m) + p--; + *p++ = '\0'; + + env_array[n++] = p; + p += lws_snprintf(p, end - p, "PATH_INFO=%s", + lws_hdr_simple_ptr(wsi, uritok) + + script_uri_path_len); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_REFERER=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_HOST=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "HTTP_COOKIE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "USER_AGENT=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT)); + p++; + } + if (uritok == WSI_TOKEN_POST_URI) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)); + p++; + } + } + env_array[n++] = p; + p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1; + + while (mp_cgienv) { + env_array[n++] = p; + p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name, + mp_cgienv->value); + lwsl_debug(" Applying mount-specific cgi env '%s'\n", + env_array[n - 1]); + p++; + mp_cgienv = mp_cgienv->next; + } + + env_array[n++] = "SERVER_SOFTWARE=libwebsockets"; + env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin"; + env_array[n] = NULL; + +#if 0 + for (m = 0; m < n; m++) + lwsl_err(" %s\n", env_array[m]); +#endif + + /* + * Actually having made the env, as a cgi we don't need the ah + * any more + */ + if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen) + lws_header_table_detach(wsi, 0); + + /* we are ready with the redirection pipes... run the thing */ +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + cgi->pid = fork(); +#else + cgi->pid = vfork(); +#endif + if (cgi->pid < 0) { + lwsl_err("fork failed, errno %d", errno); + goto bail3; + } + +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + setpgrp(); /* stops on-daemonized main processess getting SIGINT from TTY */ + + if (cgi->pid) { + /* we are the parent process */ + wsi->context->count_cgi_spawned++; + lwsl_debug("%s: cgi %p spawned PID %d\n", __func__, cgi, cgi->pid); + return 0; + } + + /* somewhere we can at least read things and enter it */ + if (chdir("/tmp")) + lwsl_notice("%s: Failed to chdir\n", __func__); + + /* We are the forked process, redirect and kill inherited things. + * + * Because of vfork(), we cannot do anything that changes pages in + * the parent environment. Stuff that changes kernel state for the + * process is OK. Stuff that happens after the execvpe() is OK. + */ + + for (n = 0; n < 3; n++) + if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) { + lwsl_err("%s: stdin dup2 failed\n", __func__); + goto bail3; + } + +#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) + for (m = 0; m < n; m++) { + p = strchr(env_array[m], '='); + *p++ = '\0'; + setenv(env_array[m], p, 1); + } + execvp(exec_array[0], (char * const *)&exec_array[0]); +#else + execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]); +#endif + + exit(1); + +bail3: + /* drop us from the pt cgi list */ + pt->cgi_list = cgi->cgi_list; + + while (--n >= 0) + remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]); +bail2: + for (n = 0; n < 3; n++) + if (wsi->cgi->stdwsi[n]) + lws_free_wsi(cgi->stdwsi[n]); + +bail1: + for (n = 0; n < 3; n++) { + if (cgi->pipe_fds[n][0]) + close(cgi->pipe_fds[n][0]); + if (cgi->pipe_fds[n][1]) + close(cgi->pipe_fds[n][1]); + } + + lws_free_set_NULL(wsi->cgi); + + lwsl_err("%s: failed\n", __func__); + + return -1; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi) +{ + int n, m, match = 0, lp = 0; + static const char * const content_length = "content-length: "; + char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, + *end = &buf[sizeof(buf) - 1 - LWS_PRE], c, l[12]; + + if (!wsi->cgi) + return -1; + + while (wsi->hdr_state != LHCS_PAYLOAD) { + /* we have to separate header / finalize and + * payload chunks, since they need to be + * handled separately + */ + n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1); + if (n < 0) { + if (errno != EAGAIN) { + lwsl_debug("%s: read says %d\n", __func__, n); + return -1; + } + else + n = 0; + } + if (n) { + lwsl_debug("-- 0x%02X %c\n", (unsigned char)c, c); + switch (wsi->hdr_state) { + case LCHS_HEADER: + if (!content_length[match] && + (c >= '0' && c <= '9') && + lp < sizeof(l) - 1) { + l[lp++] = c; + l[lp] = '\0'; + wsi->cgi->content_length = atol(l); + } + if (tolower(c) == content_length[match]) + match++; + else + match = 0; + + /* some cgi only send us \x0a for EOL */ + if (c == '\x0a') { + wsi->hdr_state = LCHS_SINGLE_0A; + *p++ = '\x0d'; + } + *p++ = c; + if (c == '\x0d') { + wsi->hdr_state = LCHS_LF1; + break; + } + + break; + case LCHS_LF1: + *p++ = c; + if (c == '\x0a') { + wsi->hdr_state = LCHS_CR2; + break; + } + /* we got \r[^\n]... it's unreasonable */ + lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, (unsigned char)c); + return -1; + + case LCHS_CR2: + if (c == '\x0d') { + /* drop the \x0d */ + wsi->hdr_state = LCHS_LF2; + break; + } + wsi->hdr_state = LCHS_HEADER; + match = 0; + *p++ = c; + break; + case LCHS_LF2: + case LCHS_SINGLE_0A: + m = wsi->hdr_state; + if (c == '\x0a') { + lwsl_debug("Content-Length: %ld\n", wsi->cgi->content_length); + wsi->hdr_state = LHCS_PAYLOAD; + /* drop the \0xa ... finalize will add it if needed */ + if (lws_finalize_http_header(wsi, + (unsigned char **)&p, + (unsigned char *)end)) + return -1; + break; + } + if (m == LCHS_LF2) + /* we got \r\n\r[^\n]... it's unreasonable */ + return -1; + /* we got \x0anext header, it's reasonable */ + *p++ = c; + wsi->hdr_state = LCHS_HEADER; + break; + case LHCS_PAYLOAD: + break; + } + } + + /* ran out of input, ended the headers, or filled up the headers buf */ + if (!n || wsi->hdr_state == LHCS_PAYLOAD || (p + 4) == end) { + + m = lws_write(wsi, (unsigned char *)start, + p - start, LWS_WRITE_HTTP_HEADERS); + if (m < 0) { + lwsl_debug("%s: write says %d\n", __func__, m); + return -1; + } + /* writeability becomes uncertain now we wrote + * something, we must return to the event loop + */ + + return 0; + } + } + + n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), + start, sizeof(buf) - LWS_PRE); + + if (n < 0 && errno != EAGAIN) { + lwsl_debug("%s: stdout read says %d\n", __func__, n); + return -1; + } + if (n > 0) { + m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP); + //lwsl_notice("write %d\n", m); + if (m < 0) { + lwsl_debug("%s: stdout write says %d\n", __func__, m); + return -1; + } + wsi->cgi->content_length_seen += m; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi) +{ + struct lws_cgi_args args; + int status, n; + + lwsl_debug("%s: %p\n", __func__, wsi); + + if (!wsi->cgi) + return 0; + + if (wsi->cgi->pid > 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) { + lwsl_debug("%s: PID %d reaped\n", __func__, + wsi->cgi->pid); + goto handled; + } + /* kill the process group */ + n = kill(-wsi->cgi->pid, SIGTERM); + lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n", __func__, + wsi->cgi->pid, n, errno); + if (n < 0) { + /* + * hum seen errno=3 when process is listed in ps, + * it seems we don't always retain process grouping + * + * Direct these fallback attempt to the exact child + */ + n = kill(wsi->cgi->pid, SIGTERM); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGPIPE); + if (n < 0) { + n = kill(wsi->cgi->pid, SIGKILL); + if (n < 0) + lwsl_err("%s: SIGKILL PID %d failed errno %d (maybe zombie)\n", + __func__, wsi->cgi->pid, errno); + } + } + } + /* He could be unkillable because he's a zombie */ + n = 1; + while (n > 0) { + n = waitpid(-wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", __func__, n); + if (n <= 0) { + n = waitpid(wsi->cgi->pid, &status, WNOHANG); + if (n > 0) + lwsl_debug("%s: reaped PID %d\n", __func__, n); + } + } + } + +handled: + args.stdwsi = &wsi->cgi->stdwsi[0]; + + if (wsi->cgi->pid != -1 && user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_CGI_TERMINATED, + wsi->user_space, + (void *)&args, 0)) { + wsi->cgi->pid = -1; + if (!wsi->cgi->being_closed) + lws_close_free_wsi(wsi, 0); + } + + return 0; +} + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt) +{ + struct lws_cgi **pcgi, *cgi = NULL; + int status, n = 1; + + while (n > 0) { + /* find finished guys but don't reap yet */ + n = waitpid(-1, &status, WNOHANG | WNOWAIT); + if (n <= 0) + continue; + lwsl_debug("%s: observed PID %d terminated\n", __func__, n); + + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) { + lwsl_debug("%s: wsi %p: expected content length seen: %ld\n", + __func__, cgi->wsi, cgi->content_length_seen); + } + + /* reap it */ + waitpid(n, &status, WNOHANG); + /* + * he's already terminated so no need for kill() + * but we should do the terminated cgi callback + * and close him if he's not already closing + */ + if (n == cgi->pid) { + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, n); + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + cgi = NULL; + } + /* if not found on the cgi list, as he's one of ours, reap */ + if (!cgi) { + lwsl_debug("%s: reading PID %d although no cgi match\n", + __func__, n); + waitpid(n, &status, WNOHANG); + } + } + +/* disable this to confirm timeout cgi cleanup flow */ +#if 1 + pcgi = &pt->cgi_list; + + /* check all the subprocesses on the cgi list */ + while (*pcgi) { + /* get the next one first as list may change */ + cgi = *pcgi; + pcgi = &(*pcgi)->cgi_list; + + if (cgi->pid <= 0) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (cgi->content_length) { + lwsl_debug("%s: wsi %p: expected content length seen: %ld\n", + __func__, cgi->wsi, cgi->content_length_seen); + } + + /* reap it */ + if (waitpid(cgi->pid, &status, WNOHANG) > 0) { + + lwsl_debug("%s: found PID %d on cgi list\n", + __func__, cgi->pid); + /* defeat kill() */ + cgi->pid = 0; + lws_cgi_kill(cgi->wsi); + + break; + } + } +#endif + + /* general anti zombie defence */ + n = waitpid(-1, &status, WNOHANG); + //if (n > 0) + // lwsl_notice("%s: anti-zombie wait says %d\n", __func__, n); + + return 0; +} +#endif + +#ifdef LWS_NO_EXTENSIONS +LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + return -1; +} +#endif + +#ifdef LWS_WITH_ACCESS_LOG +int +lws_access_log(struct lws *wsi) +{ + char *p = wsi->access_log.user_agent, ass[512]; + int l; + + if (!wsi->access_log_pending) + return 0; + + if (!wsi->access_log.header_log) + return 0; + + if (!p) + p = ""; + + l = lws_snprintf(ass, sizeof(ass) - 1, "%s %d %lu %s\n", + wsi->access_log.header_log, + wsi->access_log.response, wsi->access_log.sent, p); + + if (wsi->vhost->log_fd != (int)LWS_INVALID_FILE) { + if (write(wsi->vhost->log_fd, ass, l) != l) + lwsl_err("Failed to write log\n"); + } else + lwsl_err("%s", ass); + + if (wsi->access_log.header_log) { + lws_free(wsi->access_log.header_log); + wsi->access_log.header_log = NULL; + } + if (wsi->access_log.user_agent) { + lws_free(wsi->access_log.user_agent); + wsi->access_log.user_agent = NULL; + } + wsi->access_log_pending = 0; + + return 0; +} +#endif + +#ifdef LWS_WITH_SERVER_STATUS + +LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) +{ + static const char * const prots[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" + }; + char *orig = buf, *end = buf + len - 1, first = 1; + int n = 0; + + if (len < 100) + return 0; + + buf += lws_snprintf(buf, end - buf, + "{\n \"name\":\"%s\",\n" + " \"port\":\"%d\",\n" + " \"use_ssl\":\"%d\",\n" + " \"sts\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"conn\":\"%lu\",\n" + " \"trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"http2_upg\":\"%lu\"" + , + vh->name, vh->listen_port, +#ifdef LWS_OPENSSL_SUPPORT + vh->use_ssl, +#else + 0, +#endif + !!(vh->options & LWS_SERVER_OPTION_STS), + vh->rx, vh->tx, vh->conn, vh->trans, vh->ws_upgrades, + vh->http2_upgrades + ); + + if (vh->mount_list) { + const struct lws_http_mount *m = vh->mount_list; + + buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); + while (m) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"mountpoint\":\"%s\",\n" + " \"origin\":\"%s%s\",\n" + " \"cache_max_age\":\"%d\",\n" + " \"cache_reuse\":\"%d\",\n" + " \"cache_revalidate\":\"%d\",\n" + " \"cache_intermediaries\":\"%d\"\n" + , + m->mountpoint, + prots[m->origin_protocol], + m->origin, + m->cache_max_age, + m->cache_reusable, + m->cache_revalidate, + m->cache_intermediaries); + if (m->def) + buf += lws_snprintf(buf, end - buf, + ",\n \"default\":\"%s\"", + m->def); + buf += lws_snprintf(buf, end - buf, "\n }"); + first = 0; + m = m->mount_next; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + if (vh->protocols) { + n = 0; + first = 1; + + buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); + while (n < vh->count_protocols) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"%s\":{\n" + " \"status\":\"ok\"\n }\n }" + , + vh->protocols[n].name); + first = 0; + n++; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + buf += lws_snprintf(buf, end - buf, "\n}"); + + return buf - orig; +} + + +LWS_EXTERN LWS_VISIBLE int +lws_json_dump_context(const struct lws_context *context, char *buf, int len) +{ + char *orig = buf, *end = buf + len - 1, first = 1; + const struct lws_vhost *vh = context->vhost_list; + +#ifdef LWS_WITH_CGI + struct lws_cgi * const *pcgi; +#endif + const struct lws_context_per_thread *pt; + time_t t = time(NULL); + int listening = 0, cgi_count = 0, n; + + buf += lws_snprintf(buf, end - buf, "{ " + "\"version\":\"%s\",\n" + "\"uptime\":\"%ld\",\n" + "\"cgi_spawned\":\"%d\",\n" + "\"pt_fd_max\":\"%d\",\n" + "\"ah_pool_max\":\"%d\",\n" + "\"wsi_alive\":\"%d\",\n", + lws_get_library_version(), + (unsigned long)(t - context->time_up), + context->count_cgi_spawned, + context->fd_limit_per_thread, + context->max_http_header_pool, + context->count_wsi_allocated); +#ifdef LWS_HAVE_GETLOADAVG + { + double d[3]; + int m; + + m = getloadavg(d, 3); + for (n = 0; n < m; n++) { + buf += lws_snprintf(buf, end - buf, + "\"l%d\":\"%.2f\",\n", + n + 1, d[n]); + } + } +#endif + + buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + if (n) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n" + " \"fds_count\":\"%d\",\n" + " \"ah_pool_inuse\":\"%d\",\n" + " \"ah_wait_list\":\"%d\"\n" + " }", + pt->fds_count, + pt->ah_count_in_use, + pt->ah_wait_list_length); + } + + buf += lws_snprintf(buf, end - buf, "], \"vhosts\":[\n "); + + while (vh) { + if (!first) + if(buf != end) + *buf++ = ','; + buf += lws_json_dump_vhost(vh, buf, end - buf); + first = 0; + if (vh->lserv_wsi) + listening++; + vh = vh->vhost_next; + } + + buf += lws_snprintf(buf, end - buf, "],\n\"listen_wsi\":\"%d\"", + listening); + +#ifdef LWS_WITH_CGI + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + pcgi = &pt->cgi_list; + + while (*pcgi) { + pcgi = &(*pcgi)->cgi_list; + + cgi_count++; + } + } +#endif + buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", + cgi_count); + + buf += lws_snprintf(buf, end - buf, "}\n "); + + return buf - orig; +} + +#endif diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirclient.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirclient.h new file mode 100755 index 00000000..dcdabcfc --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirclient.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _REDIRCLIENT_H_ +#define _REDIRCLIENT_H_ + +#define MAX_REDIR_CLIENTS 5 + +#define WS_PING_INTERVAL 20 // in seconds +#define WS_RESET_LINK_INTERVAL (3*WS_PING_INTERVAL) +#define WS_DEFAULT_PORT 7681 +#define REDIR_RCV_ARRAY_MAX 10 + +typedef struct _redirConfig +{ + int use_mirror; + int use_ssl; + int port; + int loglevel; + unsigned int size; + unsigned int pingsize; + int flood; + int clients; + int wsLinkIsReady; + unsigned int write_options; + unsigned int pingIntervalSec; + unsigned int wsResetIntervalSec; + int ietf_version; + int cfgDataLen; + char *pCfgData; + char address[30]; + char protocol_name[256]; + char urlname[30]; + char ssl_certdir[1024]; +} redirConfig; + +typedef struct _redirStatus +{ + int wsIsReady; + unsigned long lastSentSec; // in seconds + unsigned long lastRecvSec; // in seconds +} redirStatus; + + +extern redirConfig redircfg; +extern redirStatus redirst; + +extern void* redirClientTask(void *handler); +extern int redirConfigInit( void ); +extern int redirNotifyTxMessageIsReady(struct lws_context *context, unsigned long lastSentSec); +extern int redirWsLinkIsNotReady(); +extern void redirDisableMsgSending(); + +#endif /* _REDIRCLIENT_H_ */ + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirdebug.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirdebug.h new file mode 100755 index 00000000..f88d39e2 --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirdebug.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _REDIR_DEBUG_H_ +#define _REDIR_DEBUG_H_ + +#define D_NORMAL 1 +#ifdef BCM_AP_ENTERPRISE + +#if 1 + +#include + +//#define wlog(...) wc_put_logline( (APP_LOG_REDIR | DEBUG_LEVEL_1), __VA_ARGS__ ) +//#define wdbg( level, ... ) wc_put_logline( level, __VA_ARGS__ ) + +#else + +//#//define wlog(...) printf(__VA_ARGS__) +//#define wdbg(level, ...) +// if (level) printf(__VA_ARGS__) + + +#endif + +#else +//#define wlog(...) printf(__VA_ARGS__) +//#define wdbg(level, ...) + // if (level) printf(__VA_ARGS__) + +#endif + +//#define linetest() wlog("%s %d \n", __func__, __LINE__) + +#endif + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirintercomm.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirintercomm.h new file mode 100755 index 00000000..041f0267 --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirintercomm.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _REDIRINTERCOMM_H_ +#define _REDIRINTERCOMM_H_ + +typedef struct _redirNotify { + unsigned int seqnum; +} redirNotify_t; + +extern int redirInitInterComm(); +extern int redirGetReceiveFd(); +extern int redirGetWriteFd(); +extern int redirAddPOnePollFd(int fd, int events); +extern int redirRemoveOnePollFd(int fd); +extern int redirChangeModePollFd(int fd, int events); +extern int redirInitializePollFd(); +extern int redirWaitforFds(struct lws_context *context, int timeout); + +#endif /* _REDIRINTERCOMM_H_ */ + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmessagedef.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmessagedef.h new file mode 100755 index 00000000..7e2e6a21 --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmessagedef.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __REDIR_MSG_DEF_H__ +#define __REDIR_MSG_DEF_H__ + +#include "redirdebug.h" + +#define GCC_PACKED __attribute__((packed)) + +typedef struct +{ + unsigned char msgId; + unsigned char seqNum; + unsigned short msgLen; // asn.1 message length + unsigned char buf[0]; +} GCC_PACKED redirGenMsgHeader_t; + +#endif // __REDIR_MSG_DEF_H__ + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmsgdispatch.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmsgdispatch.h new file mode 100755 index 00000000..7747813f --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirmsgdispatch.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _REDIRMSGDISPATCH_H_ +#define _REDIRMSGDISPATCH_H_ + +#define REDIR_MSG_MAX_SIZE 8192 + +typedef struct _msgDispData +{ + int needToPing; + int txMsgIsReady; + unsigned char newCfgSeqCnt; + unsigned char expectNewCfgSeq; + + // state machine parameters. + int absTimeToWait; // absolute time in second. + int waitForMsg; + int backOffTime; // in seconds. + int waitTimedOut; // Timed out for the wait. + int dropAllMsgBackOff; // Drop all the frames during back off time. + + int wsIsReady; // web socket is ready. + int applyFailedNum; + + int applyNewCfgFailedNum; + unsigned char smSeqCnt; + unsigned char expectSeq; //expected seqNum from Response message. + +} msgDispData_t; + + +extern msgDispData_t gmsgdd; + +extern int redirOpenRedirSocket( unsigned short Port ); +extern void redirCloseRedirSocket( void ); +extern void redirMessageRedirect( unsigned char * Msg, size_t Len ); +extern void* redirMsgDispatchTaskFunc(void *x_void_ptr); +extern int redirNotifyWebSocketIsReady(); + + +#endif /* _REDIRMSGDISPATCH_H_ */ diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirtask.h b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirtask.h new file mode 100755 index 00000000..b02df5bf --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/inc/redirtask.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef _REDIRTASK_H_ +#define _REDIRTASK_H_ + +#define REDIR_MAX_TASKS 50 +#define REDIR_TASK_ID_BASE 0x10000000 + +typedef unsigned long Address; + +typedef struct _redirTask +{ + struct _redirTask *nextFreeTask; // pointer to the next redirTask entry + int entryId; // Unique ID for this entry. We could add type to it if needed. + int allocated; // Whether this entry is used or not. + char name[16]; // Name of the pthread. + unsigned priority; + pthread_t threadSelf; // The pthread that is associated with this entry. + pthread_attr_t threadAttr; + void (*taskFunc)(void); // The start routine what runs in the pthread. +} redirTask; + +extern redirTask* allocTaskFromPool(); +extern void releaseTaskBackToPool(redirTask *pTask); +extern int createOneTask(Address taskFunc, char *Name, int *taskId); +extern int redirInitTasks(); +extern void waitForOtherTaskesToFinish(); + + +#endif /* _REDIRTASK_H_ */ + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/cmd_portforwarding.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/cmd_portforwarding.c new file mode 100644 index 00000000..9dde7d2f --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/cmd_portforwarding.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include + +#include "command.h" + +#include +#include +#include + +static void cmd_handler_port_forwarding_cb(struct ev_loop *loop, ev_child *child, int revents) +{ + struct task *task = container_of(child, struct task, child); + + ev_child_stop(loop, child); + task_status(task, TASK_COMPLETE, NULL); +} + +pid_t cmd_handler_port_forwarding(struct task *task) +{ + char *gateway = NULL; + char *port = NULL; + pid_t pid; + + gateway = SCHEMA_KEY_VAL(task->conf.payload, "gateway_hostname"); + port = SCHEMA_KEY_VAL(task->conf.payload, "gateway_port"); + + LOG(ERR, "gateway : %s , port: %s ", gateway, port); + pid = fork(); + if (pid == 0) { + port_forwarding(gateway, port); + exit(1); + } + + if (pid < 0) { + LOG(ERR, "portforwarding, failed to fork"); + task_status(task, TASK_FAILED, "forking portforwarding failed"); + return -1; + } + + ev_child_init(&task->child, cmd_handler_port_forwarding_cb, pid, 0); + ev_child_start(EV_DEFAULT, &task->child); + + LOGN("portforwarding: started"); + return pid; +} diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/libwcf_devif.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/libwcf_devif.c new file mode 100755 index 00000000..890284ed --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/libwcf_devif.c @@ -0,0 +1,1216 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "websocket/libwcf.h" +#include "websocket/libwcf_debug.h" +#include "websocket/libwcf_private.h" + +#define BR_NAME "br_c" + +int br_socket_fd = -1; +unsigned int AppLogMask = 0xFFFFFFFF; +time_t MaskUpdated = 0; + +static FILE *fpopen(const char *dir, const char *name) +{ + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, "%s/%s", dir, name); + return fopen(path, "r"); +} + +static void fetch_id(const char *dev, const char *name, struct bridge_id *id) +{ + FILE *f = fpopen(dev, name); + + fscanf(f, "%2hhx%2hhx.%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &id->prio[0], &id->prio[1], + &id->addr[0], &id->addr[1], &id->addr[2], + &id->addr[3], &id->addr[4], &id->addr[5]); + fclose(f); +} + +/* Fetch an integer attribute out of sysfs. */ +static int fetch_int(const char *dev, const char *name) +{ + FILE *f = fpopen(dev, name); + int value = -1; + + if (!f) + fprintf(stderr, "%s: %s\n", dev, strerror(errno)); + else { + fscanf(f, "%i", &value); + fclose(f); + } + return value; +} + +/* Get a time value out of sysfs */ +static void fetch_tv(const char *dev, const char *name, + struct timeval *tv) +{ + __jiffies_to_tv(tv, fetch_int(dev, name)); +} + +/* + * Convert device name to an index in the list of ports in bridge. + * + * Old API does bridge operations as if ports were an array + * inside bridge structure. + */ +static int get_portno(const char *brname, const char *ifname) +{ + int i; + int ifindex = if_nametoindex(ifname); + int ifindices[MAX_PORTS]; + unsigned long args[4] = { BRCTL_GET_PORT_LIST, + (unsigned long)ifindices, MAX_PORTS, 0 }; + struct ifreq ifr; + + if (ifindex <= 0) + goto error; + + if( br_socket_fd < 0 ) { + return -1; + } + + memset(ifindices, 0, sizeof(ifindices)); + strncpy(ifr.ifr_name, brname, IFNAMSIZ-1); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("get_portno: get ports of %s failed: %s\n", + brname, strerror(errno)); + goto error; + } + + for (i = 0; i < MAX_PORTS; i++) { + if (ifindices[i] == ifindex) + return i; + } + + dprintf("%s is not a in bridge %s\n", ifname, brname); + error: + return -1; +} + +/* get information via ioctl */ +/*static int old_get_bridge_info(const char *bridge, struct bridge_info *info) +{ + struct ifreq ifr; + struct __bridge_info i; + unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO, + (unsigned long) &i, 0, 0 }; + + if( br_socket_fd < 0 ) { + return -1; + } + + memset(info, 0, sizeof(*info)); + strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("%s: can't get info %s\n", + bridge, strerror(errno)); + return errno; + } + + memcpy(&info->designated_root, &i.designated_root, 8); + memcpy(&info->bridge_id, &i.bridge_id, 8); + info->root_path_cost = i.root_path_cost; + info->root_port = i.root_port; + info->topology_change = i.topology_change; + info->topology_change_detected = i.topology_change_detected; + info->stp_enabled = i.stp_enabled; + __jiffies_to_tv(&info->max_age, i.max_age); + __jiffies_to_tv(&info->hello_time, i.hello_time); + __jiffies_to_tv(&info->forward_delay, i.forward_delay); + __jiffies_to_tv(&info->bridge_max_age, i.bridge_max_age); + __jiffies_to_tv(&info->bridge_hello_time, i.bridge_hello_time); + __jiffies_to_tv(&info->bridge_forward_delay, i.bridge_forward_delay); + __jiffies_to_tv(&info->ageing_time, i.ageing_time); + __jiffies_to_tv(&info->hello_timer_value, i.hello_timer_value); + __jiffies_to_tv(&info->tcn_timer_value, i.tcn_timer_value); + __jiffies_to_tv(&info->topology_change_timer_value, + i.topology_change_timer_value); + __jiffies_to_tv(&info->gc_timer_value, i.gc_timer_value); + + return 0; +} +*/ +#if 0 +/* + * Get bridge parameters using either sysfs or old + * ioctl. + */ +int br_get_bridge_info(const char *bridge, struct bridge_info *info) +{ + DIR *dir; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/bridge", bridge); + dir = opendir(path); + if (dir == NULL) { + dprintf("path '%s' is not a directory\n", path); + goto fallback; + } + + memset(info, 0, sizeof(*info)); + fetch_id(path, "root_id", &info->designated_root); + fetch_id(path, "bridge_id", &info->bridge_id); + info->root_path_cost = fetch_int(path, "root_path_cost"); + fetch_tv(path, "max_age", &info->max_age); + fetch_tv(path, "hello_time", &info->hello_time); + fetch_tv(path, "forward_delay", &info->forward_delay); + fetch_tv(path, "max_age", &info->bridge_max_age); + fetch_tv(path, "hello_time", &info->bridge_hello_time); + fetch_tv(path, "forward_delay", &info->bridge_forward_delay); + fetch_tv(path, "ageing_time", &info->ageing_time); + fetch_tv(path, "hello_timer", &info->hello_timer_value); + fetch_tv(path, "tcn_timer", &info->tcn_timer_value); + fetch_tv(path, "topology_change_timer", + &info->topology_change_timer_value);; + fetch_tv(path, "gc_timer", &info->gc_timer_value); + + info->root_port = fetch_int(path, "root_port"); + info->stp_enabled = fetch_int(path, "stp_state"); + info->topology_change = fetch_int(path, "topology_change"); + info->topology_change_detected = fetch_int(path, "topology_change_detected"); + + closedir(dir); + return 0; + +fallback: + return old_get_bridge_info(bridge, info); +} +#endif + +static int old_get_port_info(const char *brname, const char *port, + struct port_info *info) +{ + struct __port_info i; + int index; + + memset(info, 0, sizeof(*info)); + + index = get_portno(brname, port); + if (index < 0) + return errno; + + else { + struct ifreq ifr; + unsigned long args[4] = { BRCTL_GET_PORT_INFO, + (unsigned long) &i, index, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy(ifr.ifr_name, brname, IFNAMSIZ-1); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("old can't get port %s(%d) info %s\n", + brname, index, strerror(errno)); + return errno; + } + } + + info->port_no = index; + memcpy(&info->designated_root, &i.designated_root, 8); + memcpy(&info->designated_bridge, &i.designated_bridge, 8); + info->port_id = i.port_id; + info->designated_port = i.designated_port; + info->path_cost = i.path_cost; + info->designated_cost = i.designated_cost; + info->state = i.state; + info->top_change_ack = i.top_change_ack; + info->config_pending = i.config_pending; + __jiffies_to_tv(&info->message_age_timer_value, + i.message_age_timer_value); + __jiffies_to_tv(&info->forward_delay_timer_value, + i.forward_delay_timer_value); + __jiffies_to_tv(&info->hold_timer_value, i.hold_timer_value); + return 0; +} + +/* + * Get information about port on bridge. + */ +int br_get_port_info(const char *brname, const char *port, + struct port_info *info) +{ + DIR *d; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brport", port); + d = opendir(path); + if (!d) + goto fallback; + + memset(info, 0, sizeof(*info)); + + fetch_id(path, "designated_root", &info->designated_root); + fetch_id(path, "designated_bridge", &info->designated_bridge); + info->port_no = fetch_int(path, "port_no"); + info->port_id = fetch_int(path, "port_id"); + info->designated_port = fetch_int(path, "designated_port"); + info->path_cost = fetch_int(path, "path_cost"); + info->designated_cost = fetch_int(path, "designated_cost"); + info->state = fetch_int(path, "state"); + info->top_change_ack = fetch_int(path, "change_ack"); + info->config_pending = fetch_int(path, "config_pending"); + fetch_tv(path, "message_age_timer", &info->message_age_timer_value); + fetch_tv(path, "forward_delay_timer", &info->forward_delay_timer_value); + fetch_tv(path, "hold_timer", &info->hold_timer_value); + closedir(d); + + return 0; +fallback: + return old_get_port_info(brname, port, info); +} + + +static int br_set(const char *bridge, const char *name, + unsigned long value, unsigned long oldcode) +{ + int ret; + char path[SYSFS_PATH_MAX]; + FILE *f; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/bridge/%s", bridge, name); + f = fopen(path, "w"); + if (f) { + ret = fprintf(f, "%ld\n", value); + fclose(f); + } else { + /* fallback to old ioctl */ + struct ifreq ifr; + unsigned long args[4] = { oldcode, value, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); + ifr.ifr_data = (char *) &args; + ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + + return ret < 0 ? errno : 0; +} + +#if 0 +int br_set_bridge_forward_delay(const char *br, struct timeval *tv) +{ + return br_set(br, "forward_delay", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_FORWARD_DELAY); +} + +int br_set_bridge_hello_time(const char *br, struct timeval *tv) +{ + return br_set(br, "hello_time", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_HELLO_TIME); +} + +int br_set_bridge_max_age(const char *br, struct timeval *tv) +{ + return br_set(br, "max_age", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_MAX_AGE); +} +#endif + +int br_set_ageing_time(const char *br, struct timeval *tv) +{ + return br_set(br, "ageing_time", __tv_to_jiffies(tv), + BRCTL_SET_AGEING_TIME); +} + +#if 0 +int br_set_stp_state(const char *br, int stp_state) +{ + return br_set(br, "stp_state", stp_state, BRCTL_SET_BRIDGE_STP_STATE); +} + +int br_set_bridge_priority(const char *br, int bridge_priority) +{ + return br_set(br, "priority", bridge_priority, + BRCTL_SET_BRIDGE_PRIORITY); +} +#endif + +/*static int port_set(const char *bridge, const char *ifname, + const char *name, unsigned long value, + unsigned long oldcode) +{ + int ret; + char path[SYSFS_PATH_MAX]; + FILE *f; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brport/%s", ifname, name); + f = fopen(path, "w"); + if (f) { + ret = fprintf(f, "%ld\n", value); + fclose(f); + } else { + int index = get_portno(bridge, ifname); + + if (index < 0) + ret = index; + else { + struct ifreq ifr; + unsigned long args[4] = { oldcode, index, value, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); + ifr.ifr_data = (char *) &args; + ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + } + + return ret < 0 ? errno : 0; +}*/ + + +int wc_set_socket( void ) +{ + if( br_socket_fd == -1 ) + br_socket_fd = socket( AF_LOCAL, SOCK_STREAM, 0 ); + + if( br_socket_fd < 0 ) + return -1; + + return( 0 ); +} + + +int wc_add_tunnel( struct add_tun_parms * TunParms ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1001, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct add_tun_parms ); + args[3] = (unsigned long)TunParms; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_del_tunnel( struct del_tun_parms * TunParms ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1002, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct del_tun_parms ); + args[3] = (unsigned long)TunParms; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_logline( struct wc_log_line * LogLine ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1003, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_log_line ); + args[3] = (unsigned long)LogLine; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_fr( struct fr_record * LogLine ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1007, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct fr_record ); + args[3] = (unsigned long)LogLine; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_put_logline( unsigned int Mask, const char * format, ... ) +{ + int Log = 1; + + if( Mask != 0xFFFFFFFF ) + { + time_t TempT = time( NULL ); + + if( TempT != MaskUpdated ) + { + FILE * MaskFd; + int Mask; + //unsigned int MaskHex; + + MaskFd = fopen( "/proc/sys/kernel/wcf/app_logmask", "r" ); + if( MaskFd ) + { + fscanf( MaskFd, "%d", &Mask ); + AppLogMask = (unsigned int)Mask; + fclose( MaskFd ); + MaskUpdated = TempT; + } + } + if( !(Mask & 0x40000000) && ((AppLogMask & Mask) != Mask) ) + Log = 0; + } + if( Log ) + { + struct ifreq ifr; + unsigned long args[4] = { 1111, 1004, 0, 0 }; + struct wc_log_line LogLine; + va_list args_log; + char logBuf[200]; + + va_start( args_log, format ); + vsnprintf( logBuf, sizeof( logBuf ), format, args_log ); + logBuf[199] = 0; + va_end( args_log ); + + LogLine.Num = Mask; + memcpy( &(LogLine.Message[0]), logBuf, 200 ); + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_log_line ); + args[3] = (unsigned long)&(LogLine); + ifr.ifr_data = (char *) &args; + if( br_socket_fd == -1 ) + br_socket_fd = socket( AF_LOCAL, SOCK_STREAM, 0 ); + + if( br_socket_fd < 0 ) + return -1; + + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); + } + else + return( 0 ); +} + + +int wc_get_tunstats( struct wc_tunnel_stats * TunStats ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1005, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_tunnel_stats ); + args[3] = (unsigned long)TunStats; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_switch_wireless( int Dir ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1006, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)&Dir; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_report_radius( unsigned int Code, unsigned int Addr, unsigned int Time ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1008, 0, 0 }; + unsigned int Parms[3]; + + if( br_socket_fd < 0 ) + return -1; + Parms[0] = Code; + Parms[1] = Addr; + Parms[2] = Time; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( Parms ); + args[3] = (unsigned long)Parms; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_radius_stats( struct radius_report_stats * Stats ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1009, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)Stats; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_local_mac( struct local_mac_set * Parms ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1010, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct local_mac_set ); + args[3] = (unsigned long)Parms; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_capture( struct wc_capt_buf * Capture ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1011, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)Capture; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_perf_stats( struct wc_perf_stats * PerfStats ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1012, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)PerfStats; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_clt_info( struct clt_hash_info * CltInfo ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1013, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = 12; /* Should include Num and EntryPtr */ + args[3] = (unsigned long)CltInfo; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_mac_info( struct mac_hash_info * MacInfo ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1014, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = 12; /* Should include Num and EntryPtr */ + args[3] = (unsigned long)MacInfo; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_ngbrCapture( struct wc_ngbr_capt_buf * NgbrCapture ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1015, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)NgbrCapture; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_register_iac( struct wc_iac_register * IacRegister ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1016, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_iac_register ); + args[3] = (unsigned long)IacRegister; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_send_iac( struct wc_iac_send * IacSend ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1017, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + //args[2] = sizeof( struct wc_iac_send ); + args[2] = IacSend->MessLen + 3 * sizeof( unsigned int ); + args[3] = (unsigned long)IacSend; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_apc( struct wc_apc_spec * ApcSpec ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1018, 0, 0 }; + int iii, Lcopy = 3 * sizeof( unsigned int ); + + if( br_socket_fd < 0 ) + return -1; + + for( iii=0; iiiNeighbors[iii].Ip == 0 ) + break; + } + + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + //args[2] = sizeof( struct wc_apc_spec ); + args[2] = Lcopy; + args[3] = (unsigned long)ApcSpec; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_apc( struct wc_apc_spec * ApcSpec ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1019, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)ApcSpec; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_mac_ident( struct wc_mac_ident * MacIdent ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1020, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_mac_ident ); + args[3] = (unsigned long)MacIdent; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_mac_ident( struct wc_mac_ident * MacIdent ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1021, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_mac_ident ); + args[3] = (unsigned long)MacIdent; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_subnet_filter( struct wc_subnet_filter * Filter, int Mode ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1022, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_subnet_filter ); + args[3] = (unsigned long)Filter; + if( !Mode ) + args[1] = 1023; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_send_dhcp_release( struct wc_dhcp_release * Release ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1024, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_dhcp_release ); + args[3] = (unsigned long)Release; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_eth_link_state( int * State ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1025, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)State; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_add_cp_whitelist( struct wc_cp_whitelist * Entry ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1027, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_cp_whitelist ); + args[3] = (unsigned long)Entry; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_update_cp_whitelist( int * Flag ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1028, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Flag; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_change_log_control( int * enable ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1030, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)enable; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_bss_upstream_stats( struct bss_upstream_stats * Stats ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1032, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)Stats; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_send_arp( struct wc_arp_sweep * Arp ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1033, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_arp_sweep ); + args[3] = (unsigned long)Arp; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_arp_cache( struct wc_arp_entry * Arp ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1034, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct wc_arp_entry ); + args[3] = (unsigned long)Arp; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_cp_mac_whitelist( struct local_mac_set * Parms ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1035, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct local_mac_set ); + args[3] = (unsigned long)Parms; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_cp_mac_whitelist_erase( void ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1036, 0, 0 }; + int Tmp = 0; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)&Tmp; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_add_bj_forw_rule( struct bonjour_forw_rule * Entry ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1037, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct bonjour_forw_rule ); + args[3] = (unsigned long)Entry; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_bj_rules_erase( void ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1040, 0, 0 }; + int Tmp = 0; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)&Tmp; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_rogue_ap_macs( struct rogue_ap_macs * Entry ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1041, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = 200 * sizeof( struct rogue_ap_macs ); + args[3] = (unsigned long)Entry; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_raw_packet_throttle( struct raw_pkt_throttle * Throttle ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1042, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct raw_pkt_throttle ); + args[3] = (unsigned long)Throttle; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_ethports_stats( struct wc_ethports_stats * PortStats ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1044, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)PortStats; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_upd_uaTimeout( struct ua_timeout_upd * upd ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1045, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct ua_timeout_upd ); + args[3] = (unsigned long)upd; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_delete_client( struct delClient * client ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1046, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct delClient ); + args[3] = (unsigned long)client; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_mesh_mode( int * Mode ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1047, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Mode; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_get_mesh_mode( int * Mode ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1048, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Mode; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_mesh_eth_protection( int * Mode ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1049, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Mode; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_set_eth_blocking( int * Block ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1050, 0, 0 }; + + /* Block=1 means block ethernet, zer0 - unblock */ + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Block; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_video_server_get( struct stream_srvr * Srvr ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1052, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); /* Dummy length for GET only */ + args[3] = (unsigned long)Srvr; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_video_servers_clear( unsigned int * Type ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1053, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( int ); + args[3] = (unsigned long)Type; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +int wc_video_server_set( struct stream_srvr * Srvr ) +{ + struct ifreq ifr; + unsigned long args[4] = { 1111, 1054, 0, 0 }; + + if( br_socket_fd < 0 ) + return -1; + strncpy( ifr.ifr_name, BR_NAME, IFNAMSIZ-1 ); + args[2] = sizeof( struct stream_srvr ); + args[3] = (unsigned long)Srvr; + ifr.ifr_data = (char *) &args; + return( ioctl( br_socket_fd, SIOCDEVPRIVATE, &ifr ) ); +} + + +#if 0 +int br_set_port_priority(const char *bridge, const char *port, int priority) +{ + return port_set(bridge, port, "priority", priority, BRCTL_SET_PORT_PRIORITY); +} + +int br_set_path_cost(const char *bridge, const char *port, int cost) +{ + return port_set(bridge, port, "path_cost", cost, BRCTL_SET_PATH_COST); +} +#endif + +static inline void __copy_fdb(struct fdb_entry *ent, + const struct __fdb_entry *f) +{ + memcpy(ent->mac_addr, f->mac_addr, 6); + ent->port_no = f->port_no; + ent->is_local = f->is_local; + __jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value); +} + +#if 0 +int br_read_fdb(const char *bridge, struct fdb_entry *fdbs, + unsigned long offset, int num) +{ + FILE *f; + int i, n; + struct __fdb_entry fe[num]; + char path[SYSFS_PATH_MAX]; + + /* open /sys/class/net/brXXX/brforward */ + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brforward", bridge); + f = fopen(path, "r"); + if (f) { + fseek(f, offset*sizeof(struct __fdb_entry), SEEK_SET); + n = fread(fe, sizeof(struct __fdb_entry), num, f); + fclose(f); + } else { + /* old kernel, use ioctl */ + unsigned long args[4] = { BRCTL_GET_FDB_ENTRIES, + (unsigned long) fe, + num, offset }; + struct ifreq ifr; + int retries = 0; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); + ifr.ifr_data = (char *) args; + + retry: + n = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + + /* table can change during ioctl processing */ + if (n < 0 && errno == EAGAIN && ++retries < 10) { + sleep(0); + goto retry; + } + } + + for (i = 0; i < n; i++) + __copy_fdb(fdbs+i, fe+i); + + return n; +} +#endif diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirclient.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirclient.c new file mode 100755 index 00000000..75cee279 --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirclient.c @@ -0,0 +1,747 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CMAKE_BUILD +#include +#endif +#include +#include + +#include "redirclient.h" +#include "redirintercomm.h" +#include "redirmessagedef.h" +#include "redirmsgdispatch.h" + +#define REDIR_BUFF_MAX 200 + + +typedef struct redirBufDes_s +{ + int isValid; + int size; + unsigned char * pBuf; +}redirBufDes_t; + +typedef struct redirBufferPool_s +{ + pthread_mutex_t txBufLock; + pthread_mutex_t rxBufLock; + int numBuf; + redirBufDes_t bufTxDes[REDIR_BUFF_MAX]; + redirBufDes_t bufRxDes[REDIR_BUFF_MAX]; +} redirBufferPool_t; + + +redirBufferPool_t commBuf; + +struct list_head +{ + struct list_head *next, *prev; +}; + + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + + +static LIST_HEAD( redirMsgHd ); + +struct redirTxMsg +{ + struct list_head pend; + int size; + unsigned char * pBuf; +}; + +static inline void __list_add( struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); }) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + + +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + + +// global configuration +redirConfig redircfg; +redirStatus redirst; + +int RedirWsTxErr = 0; + +/* + * this is specified in the 04 standard, control frames can only have small + * payload length styles + */ +#define MAX_PING_PAYLOAD 125 +#define MAX_REDIR_PAYLOAD 8192 + +#define PING_RINGBUFFER_SIZE 256 + +struct lws * redir_wsi[MAX_REDIR_CLIENTS]; +static unsigned char pingbuf[LWS_PRE + MAX_REDIR_PAYLOAD]; +static char peer_name[128]; +static unsigned long started; + +static unsigned long rtt_min = 100000000; +static unsigned long rtt_max; +static unsigned long rtt_avg; +static unsigned long global_rx_count; +static unsigned long global_tx_count; + +struct ping { + unsigned long issue_timestamp; + unsigned long index; + unsigned int seen; +}; + +struct per_session_data__ping { + unsigned long ping_index; + + struct ping ringbuffer[PING_RINGBUFFER_SIZE]; + int ringbuffer_head; + int ringbuffer_tail; + + unsigned long rx_count; +}; + + +int redirAddOneTxBuffToPool( unsigned char * Buffer, unsigned int len ) +{ + int i; + int found = 0; + + pthread_mutex_lock( &commBuf.txBufLock ); + for( i=0; irx_count++; + + shift = 56; + p = in; + l = 0; + + while (shift >= 0) { + l |= (*p++) << shift; + shift -= 8; + } + + /* find it in the ringbuffer, look backwards from head */ + n = psd->ringbuffer_head; + while (!match) { + + if (psd->ringbuffer[n].index == l) { + psd->ringbuffer[n].seen++; + match = 1; + continue; + } + + if (n == psd->ringbuffer_tail) { + match = -1; + continue; + } + + if (n == 0) + n = PING_RINGBUFFER_SIZE - 1; + else + n--; + } + + if (match < 1) { + + return 0; + } + + if (psd->ringbuffer[n].seen > 1) + //wlog("DUP! "); + + if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min) + rtt_min = iv - psd->ringbuffer[n].issue_timestamp; + + if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max) + rtt_max = iv - psd->ringbuffer[n].issue_timestamp; + + rtt_avg += iv - psd->ringbuffer[n].issue_timestamp; + global_rx_count++; + + return 0; +} + +static int redirLinkClientMsgReceiveHandler( struct lws * wsi, struct per_session_data__ping * psd, void * in, + size_t len ) +{ + int tlen; + unsigned char * CharBuf; + + psd->rx_count++; + + tlen = len; + if( tlen > REDIR_MSG_MAX_SIZE ) + tlen = REDIR_MSG_MAX_SIZE; + + CharBuf = (unsigned char *)in; + + if( strstr( (char *)CharBuf, "connect_to_CE_port" ) ) + { + redirOpenRedirSocket( 22 ); + return( 0 ); + } + if( strstr( (char *)CharBuf, "disconnect_from_CE_port" ) ) + { + redirCloseRedirSocket( ); + return( 0 ); + } + if( (*(CharBuf+0) == 0) && (*(CharBuf+1) == 0) ) + { + redirMessageRedirect( CharBuf+4, tlen-4 ); + return( 0 ); + } + return( 0 ); +} + + +void redirInitLastReceivedTime() +{ + struct timespec tv; + + clock_gettime( CLOCK_MONOTONIC, &tv ); + redirst.lastRecvSec = tv.tv_sec; +} + + +void redirUpdateLastReceivedTime() +{ + struct timespec tv; + static int cnt = 0; + + cnt++; +#if 1 + clock_gettime( CLOCK_MONOTONIC, &tv ); + redirst.lastRecvSec = tv.tv_sec; +#else + if (cnt >= 7 && cnt <= 17) { + ; + } else { + gettimeofday(&tv, NULL); + redirst.lastRecvSec = tv.tv_sec; + } +#endif + +} + + +static int callback_redir_link(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n; + struct per_session_data__ping *psd = user; + struct lws_pollargs *pa = (struct lws_pollargs *)in; + //struct timeval tv; + + switch (reason) { + case LWS_CALLBACK_CLOSED: + /* remove closed guy */ + for (n = 0; n < redircfg.clients; n++) + if (redir_wsi[n] == wsi) { + redircfg.clients--; + while (n < redircfg.clients) { + redir_wsi[n] = redir_wsi[n + 1]; + n++; + } + } + + break; + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + psd->rx_count = 0; + psd->ping_index = 1; + psd->ringbuffer_head = 0; + psd->ringbuffer_tail = 0; + + /* + * start the ball rolling, + * LWS_CALLBACK_CLIENT_WRITEABLE will come next service + */ + + lws_callback_on_writable( wsi); + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + case LWS_CALLBACK_CLIENT_RECEIVE_PONG: + redirUpdateLastReceivedTime(); + if (reason == LWS_CALLBACK_CLIENT_RECEIVE) { + redirLinkClientMsgReceiveHandler( wsi, psd, in, len); + } else { + redirLinkClientPongReceiveHandler( wsi, psd, in, len); + } + + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + if( lws_partial_buffered( wsi ) ) + { + //wlog( "******************************** writable, wait for nexttime \n" ); + lws_callback_on_writable( wsi ); + break; + } + if( gmsgdd.txMsgIsReady ) + { + int done, i; + redirBufDes_t tmpBTD[REDIR_BUFF_MAX]; + + pthread_mutex_lock( &(commBuf.txBufLock) ); + memcpy( tmpBTD, commBuf.bufTxDes, sizeof( commBuf.bufTxDes ) ); + memset( commBuf.bufTxDes, '\0', sizeof( commBuf.bufTxDes ) ); + pthread_mutex_unlock( &(commBuf.txBufLock) ); + + // construct the linked list + for( i = 0; i < REDIR_BUFF_MAX; i++ ) + { + if( tmpBTD[i].isValid ) + { + struct redirTxMsg * ctm = calloc( 1, sizeof( struct redirTxMsg ) ); + ctm->pBuf = tmpBTD[i].pBuf; + ctm->size = tmpBTD[i].size; + list_add_tail(&ctm->pend, &redirMsgHd); + //wlog( "===www adding to list %d", ctm->size ); + } + } + + // now to send the list + done = 1; + { + struct list_head *p, *n; + struct redirTxMsg *ctm; + + list_for_each_safe(p, n, &redirMsgHd) + { + ctm = list_entry(p, struct redirTxMsg, pend); + + //wlog("*** send client msg cnt %d pbuf %p\n", cnt, ctm->pBuf); + //wlog( "===www processing from list %d", ctm->size ); + redirLinkClientMsgWritableHandler( wsi, psd, ctm->pBuf, ctm->size ); + //wlog("--- send client msg cnt %d done\n", cnt); + + list_del(&ctm->pend); + free(ctm->pBuf); + free(ctm); + + if (lws_partial_buffered(wsi)) + { + //wlog("***** skip rest messages for this time\n"); + lws_callback_on_writable( wsi); + done = 0; + break; + } + } + } + if( done ) + gmsgdd.txMsgIsReady = 0; + } + break; +#if 0 + case LWS_CALLBACK_CLIENT_WRITEABLE: + gettimeofday(&tv, NULL); + redirst.lastSentSec = tv.tv_sec; + //wlog("send ws frame, lastSentUs updated %ld sec \n", tv.tv_sec); + if (gmsgdd.needToPing) { + gmsgdd.needToPing = 0; + redirLinkClientPingWritableHandler(this, wsi, psd); + } + if (gmsgdd.txMsgIsReady) { + int i; + int idx = 1; + redirBufDes_t tmpBTD[REDIR_BUFF_MAX]; + redirGetAndClearAllTxBufDescritors(redircfg.rcvArray[idx].pBP, tmpBTD); + for (i=0; ifd, pa->events); + break; + + case LWS_CALLBACK_DEL_POLL_FD: + redirRemoveOnePollFd(pa->fd); + break; + + case LWS_CALLBACK_CHANGE_MODE_POLL_FD: + redirChangeModePollFd(pa->fd, pa->events); + break; + + default: + break; + } + + return 0; +} + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + { + "redir-link-protocol", callback_redir_link, + sizeof(struct per_session_data__ping), 0, 0, NULL + }, + { + NULL, NULL, 0, 0, 0, NULL /* end of list */ + } +}; + + +int redirNotifyTxMessageIsReady(struct lws_context *context, unsigned long lastSentSec) +{ + int n; + struct timespec tv; + + if( lastSentSec == 0 ) + { + clock_gettime( CLOCK_MONOTONIC, &tv ); + lastSentSec = tv.tv_sec; + } + + for( n = 0; n < redircfg.clients; n++ ) + lws_callback_on_writable( redir_wsi[n] ); + redirst.lastSentSec = lastSentSec; + + return( 1 ); +} + +static const struct lws_extension exts[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate; client_no_context_takeover; client_max_window_bits" + }, + { + "deflate-frame", + lws_extension_callback_pm_deflate, + "deflate_frame" + }, + { NULL, NULL, NULL /* terminator */ } +}; + + +static int redirStartWebSocketConnection( struct lws_context_creation_info * pInfo ) +{ + struct lws_context * context; + char ip[30]; + struct timespec tv; + int n; + unsigned long l; + //int rc = 0; + int ret = 0; + struct lws_client_connect_info i; + + + redircfg.clients = 1; + + context = lws_create_context( pInfo ); + if( context == NULL ) + { + return( -1 ); + } + + memset(&i, 0, sizeof(i)); + i.context = context; + i.address = redircfg.address; + i.port = redircfg.port; + i.ssl_connection = redircfg.use_ssl; + i.path = redircfg.urlname; + i.origin = "origin"; + i.protocol = protocols[0].name; + i.client_exts = exts; + i.host = i.address; + i.ietf_version_or_minus_one = redircfg.ietf_version; + + do + { + redir_wsi[0] = lws_client_connect_via_info(&i); + if (redir_wsi[0] == NULL) + { + printf( "cmClient failed to connect\n" ); + sleep( 5 ); + // return NULL; + } + } while( redir_wsi[0] == NULL ); + + + memset( peer_name, '\0', sizeof( peer_name ) ); + + if( redir_wsi[0] ) + { + lws_get_peer_addresses( redir_wsi[0], lws_get_socket_fd(redir_wsi[0]), peer_name, + sizeof( peer_name ), ip, sizeof( ip ) ); + } + + clock_gettime( CLOCK_MONOTONIC, &tv ); + started = (tv.tv_sec * 1000000) + tv.tv_nsec/1000; + + redircfg.wsLinkIsReady = 1; + redirNotifyWebSocketIsReady( ); + redirInitLastReceivedTime( ); + /* service loop */ + n = 0; + + while( n >= 0 ) + { + clock_gettime( CLOCK_MONOTONIC, &tv ); + l = tv.tv_sec; + + /* servers can hang up on us */ + if (redircfg.clients == 0) { + n = -1; + continue; + } + + if( RedirWsTxErr ) + { + n = -4; + continue; + } + + if (l- redirst.lastRecvSec > redircfg.wsResetIntervalSec) { + n = -3; + redirDisableMsgSending(); + continue; + } + if ((l - redirst.lastSentSec) > redircfg.pingIntervalSec) { + gmsgdd.needToPing = 1; + redirNotifyTxMessageIsReady( context, l ); + } + + /* + * this represents an existing server's single poll action + * which also includes libwebsocket sockets + */ + redirWaitforFds( context, 2000 ); + } + + /* stats */ + redircfg.wsLinkIsReady = 0; + lws_context_destroy( context ); + ret = n; + return( ret ); +} + + +void * redirClientTask( void * handler ) +{ + struct lws_context_creation_info info; + char ca_filepath[1032]; + char cert_filepath[1035]; + char key_filepath[1039]; + FILE *fptr; + + memset( &info, 0, sizeof info ); + memset( &commBuf, 0, sizeof( commBuf ) ); + pthread_mutex_init( &(commBuf.txBufLock), NULL ); + pthread_mutex_init( &(commBuf.rxBufLock), NULL ); + commBuf.numBuf = REDIR_BUFF_MAX; + + redirInitializePollFd( ); + + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = protocols; + info.extensions = exts; + if( redircfg.use_ssl ) + { + fprintf( stderr, "use_ssl %d (%s) \n", redircfg.use_ssl, redircfg.ssl_certdir ); + { + snprintf( ca_filepath, sizeof( ca_filepath ), "%s/%s", redircfg.ssl_certdir, "ca.pem" ); //ca.crt + snprintf( cert_filepath, sizeof( cert_filepath ), "%s/%s", redircfg.ssl_certdir, "client.pem" ); //client.crt + snprintf( key_filepath, sizeof( key_filepath ), "%s/%s", redircfg.ssl_certdir, "client_dec.key" ); //client.key + fprintf( stderr, "use pathhhhh %s \n", ca_filepath ); + } + info.ssl_ca_filepath = ca_filepath; + info.ssl_cert_filepath = cert_filepath; + info.ssl_private_key_filepath = key_filepath; + } + info.gid = -1; + info.uid = -1; + + if( redircfg.use_ssl ) + { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + fptr = fopen( ca_filepath, "r" ); + if( fptr != NULL ) + { + fclose( fptr ); + + do + { + if( redirStartWebSocketConnection( &info ) < 0 ) + break; + } while( 1 ); + } + else + printf("No certificate files present \n"); + } + else + { + do + { + if( redirStartWebSocketConnection( &info ) < 0 ) + break; + } while( 1 ); + } + printf("exiting websockets\n"); + return( NULL ); +} + +int redirConfigInit( void ) +{ + memset( &redircfg, '\0', sizeof( redircfg ) ); + + redircfg.port = WS_DEFAULT_PORT; + redircfg.pingIntervalSec = WS_PING_INTERVAL; + redircfg.wsResetIntervalSec = WS_RESET_LINK_INTERVAL; + redircfg.size = 64; + redircfg.pingsize = MAX_PING_PAYLOAD; + redircfg.flood = 0; + redircfg.wsLinkIsReady = 0; + redircfg.clients = 1; + redircfg.ietf_version = -1; + strcpy( redircfg.urlname, "/" ); + + memset( &redirst, '\0', sizeof( redirst ) ); + + return( 0 ); +} + + +int redirWsLinkIsNotReady() +{ + return( !redircfg.wsLinkIsReady ); +} + + +void redirDisableMsgSending() +{ + redircfg.wsLinkIsReady = 0; +} + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirintercomm.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirintercomm.c new file mode 100755 index 00000000..80fb0fad --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirintercomm.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CMAKE_BUILD +#include +#endif + +#include +#include "redirtask.h" +#include "redirclient.h" +#include "redirmessagedef.h" +#include "redirintercomm.h" +#include "redirmsgdispatch.h" + +int max_poll_elements; +struct pollfd *pollfds; +int *fd_lookup; +int count_pollfds; + +static int fd[2]; + +int redirInitInterComm() +{ + pipe(fd); + max_poll_elements = getdtablesize(); + pollfds = malloc(max_poll_elements * sizeof(struct pollfd)); + fd_lookup = malloc(max_poll_elements * sizeof(int)); + if (pollfds == NULL || fd_lookup == NULL) { + return -1; + } + return 0; +} + +int redirGetReceiveFd() +{ + return fd[0]; +} + +int redirGetWriteFd() +{ + return fd[1]; +} + +static unsigned int cnSendSeqNum = 0; + +int redirSendNotification( redirNotify_t * cNotify ) +{ + ssize_t ret; + int wfd = -1; + + cNotify->seqnum = cnSendSeqNum++; + wfd = redirGetWriteFd(); + ret = write( wfd, cNotify, sizeof( redirNotify_t ) ); + if( ret < 0 ) + return -1; + return 0; +} + +int redirReceiveNotificationFromOtherEngines(struct lws_context *context, int rfd) +{ + char readbuffer[200]; + + read(rfd, readbuffer, sizeof(readbuffer)); + redirNotifyTxMessageIsReady(context, 0); + + return 0; +} + +int redirAddPOnePollFd(int fd, int events) +{ + if (count_pollfds >= max_poll_elements) { + return -1; + } + + fd_lookup[fd] = count_pollfds; + pollfds[count_pollfds].fd = fd; + pollfds[count_pollfds].events = events; + pollfds[count_pollfds++].revents = 0; + return 0; +} + +int redirRemoveOnePollFd(int fd) +{ + int temp; + + if (--count_pollfds <= 0) { + return -1; + } + + temp = fd_lookup[fd]; + /* have the last guy take up the vacant slot */ + pollfds[temp] = pollfds[count_pollfds]; + fd_lookup[pollfds[count_pollfds].fd] = temp; + return 0; +} + +int redirChangeModePollFd(int fd, int events) +{ + pollfds[fd_lookup[fd]].events = events; + return 0; +} + +int redirInitializePollFd() +{ + int rfd = -1; + + rfd = redirGetReceiveFd(); + fd_lookup[rfd] = count_pollfds; + pollfds[count_pollfds].fd = rfd; + pollfds[count_pollfds].events = POLLIN; + pollfds[count_pollfds++].revents = 0; + return 0; +} + +int redirWaitforFds(struct lws_context *context, int timeout) +{ + int n = 0; + + // wait for 5 seconds. + n = poll(pollfds, count_pollfds, timeout); + if (n < 0) + return -1; + + if (n) { + if (pollfds[0].revents & POLLIN) { + redirReceiveNotificationFromOtherEngines(context, pollfds[0].fd); + } + } + + for (n = 1; n < count_pollfds; n++) { + if (pollfds[n].revents) { + /* + * returns immediately if the fd does not + * match anything under libwebsockets + * control + */ + if (lws_service_fd(context, &pollfds[n]) < 0) { + + } + } + } + + return 0; +} + + + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmain.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmain.c new file mode 100755 index 00000000..5915a0ae --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmain.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "command.h" +#include "redirdebug.h" +#include "redirtask.h" +#include "redirclient.h" +#include "redirintercomm.h" +#include "redirmsgdispatch.h" +#include "websocket/libwcf.h" + + +static int redirInit( void ) +{ + if( redirInitInterComm( ) < 0 ) + return( 0 ); + + if( redirInitTasks( ) < 0 ) + { + return( -1 ); + } + + return( 0 ); +} + +int port_forwarding(char *ipAddress, char *port) +{ + struct addrinfo hints; + struct addrinfo *result; + int s; + int waitForAddrInfo = 1; + int waitForCnt = 0; + int taskIds[REDIR_MAX_TASKS]; + + memset( taskIds, 0, sizeof( taskIds ) ); + wc_set_socket( ); + redirConfigInit( ); + + strcpy( redircfg.address, ipAddress); + redircfg.port = atoi(port); + redircfg.use_ssl = 1; + strncpy(redircfg.ssl_certdir, "/usr/opensync/certs/", sizeof( redircfg.ssl_certdir )-1); /* copy the root directory of the certificate */ + redircfg.ssl_certdir[sizeof( redircfg.ssl_certdir )-1] = '\0'; + + + optind++; + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = 0; /* Datagram socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + do + { + s = getaddrinfo( redircfg.address, NULL, &hints, &result ); + if( s != 0 ) + { + sleep( 2 ); + waitForCnt++; + } + else + { + waitForAddrInfo = 0; + } + if( waitForCnt > 1000 ) + { + LOG(ERR, "could not resolve address"); + return( -1 ); + } + if( !result ){ + freeaddrinfo( result ); + } + } while( waitForAddrInfo ); + + // Some general initialization. + redirInit( ); + createOneTask( (Address)redirClientTask, "redirClient", &taskIds[0] ); + createOneTask( (Address)redirMsgDispatchTaskFunc, "redirMsgDispatcher", &taskIds[1] ); + /* wait for the other tasks to finish */ + waitForOtherTaskesToFinish( ); + return 0 ; +} + + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmsgdispatch.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmsgdispatch.c new file mode 100755 index 00000000..abe0a585 --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirmsgdispatch.c @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "redirmessagedef.h" +#include "redirclient.h" +#include "redirintercomm.h" +#include "redirmsgdispatch.h" + +msgDispData_t gmsgdd; + +pthread_mutex_t state_machine_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t state_machine_cv = PTHREAD_COND_INITIALIZER; + +int hasCloudMsg = 0; +pthread_mutex_t waitForMessageResp_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t waitForMessageResp_cv = PTHREAD_COND_INITIALIZER; + +extern struct lws * redir_wsi[MAX_REDIR_CLIENTS]; +extern int RedirWsTxErr; + +extern int redirAddOneTxBuffToPool( unsigned char * Buffer, unsigned int len ); +extern int redirSendNotification( redirNotify_t * cNotify ); + +int RedirSockFd = -1; + + +int redirOpenRedirSocket( unsigned short Port ) +{ + struct sockaddr_in serv_addr; + int nn; + char RedirBuf[50]; + + RedirSockFd = socket( AF_INET, SOCK_STREAM, 0 ); + if( RedirSockFd < 0 ) + { + RedirSockFd = -1; + return( -1 ); + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons( Port ); + serv_addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + if( connect( RedirSockFd, (struct sockaddr *)&serv_addr, sizeof( serv_addr ) ) < 0 ) + { + RedirSockFd = -1; + return( -1 ); + } + sprintf( RedirBuf, "connected_to_CE_port:%d", 22 ); + nn = lws_write( redir_wsi[0], (unsigned char *)RedirBuf, strlen( RedirBuf ), redircfg.write_options ); + if( nn < 0 ) + RedirWsTxErr = 1; + return( 0 ); +} + + +void redirCloseRedirSocket( void ) +{ + if( RedirSockFd != -1 ) + { + close( RedirSockFd ); + RedirSockFd = -1; + } +} + + +void redirMessageRedirect( unsigned char * Msg, size_t Len ) +{ + if( RedirSockFd == -1 ) + return; + write( RedirSockFd, Msg, Len ); +} + + +static int redirWaitForWebSocketToEstablish( ) +{ + pthread_mutex_lock( &state_machine_mutex ); + if( !redirst.wsIsReady ) + pthread_cond_wait( &state_machine_cv, &state_machine_mutex ); + + pthread_mutex_unlock( &state_machine_mutex ); + + return( 0 ); +} + +int redirNotifyWebSocketIsReady( ) +{ + pthread_mutex_lock( &state_machine_mutex ); + redirst.wsIsReady = 1; + + pthread_cond_signal( &state_machine_cv ); + pthread_mutex_unlock( &state_machine_mutex ); + + return( 0 ); +} + +void redirResetStateMachine() +{ + gmsgdd.backOffTime = 5; + gmsgdd.dropAllMsgBackOff = 1; + gmsgdd.applyFailedNum = 0; +} + + +unsigned char RecBuf[REDIR_MSG_MAX_SIZE]; +unsigned char RedirBuf[REDIR_MSG_MAX_SIZE]; + +void * redirMsgDispatchTaskFunc( void * x_void_ptr ) +{ + int n; + + // initialize some global configuration. + memset( &gmsgdd, '\0', sizeof gmsgdd ); + + // wait for the websocket link to establish first. + redirWaitForWebSocketToEstablish( ); + + while( 1 ) + { + while( redirWsLinkIsNotReady( ) ) + { + sleep( 3 ); + } + if( RedirSockFd != -1 ) + { + n = recv( RedirSockFd, (void *)RecBuf, sizeof( RecBuf ) - 100, 0 ); + if( n > 0 ) + { + unsigned char * SendBuf = calloc( 1, n+4 ); + + if( SendBuf ) + { + redirNotify_t cn; + + *(SendBuf+3) = 22; + memcpy( SendBuf+4, RecBuf, n ); + redirAddOneTxBuffToPool( SendBuf, n+4 ); + redirSendNotification( &cn ); + } + } + } + } + + return( NULL ); +} + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirtask.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirtask.c new file mode 100755 index 00000000..b76e389e --- /dev/null +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/redirtask.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "redirmessagedef.h" +#include "redirtask.h" + +static int numTasks = 0; +static redirTask *tasks = NULL; +static redirTask *freeTasks = NULL; +static pthread_mutex_t tasksMutex; +static int numAllocatedTasks = 0; + +redirTask* allocTaskFromPool() +{ + redirTask *pTask = NULL; + + pthread_mutex_lock(&tasksMutex); + if (freeTasks) { + pTask = freeTasks; + pTask->allocated = 1; + freeTasks = freeTasks->nextFreeTask; + ++numAllocatedTasks; + } + pthread_mutex_unlock(&tasksMutex); + return pTask; +} + +void releaseTaskBackToPool(redirTask *pTask) +{ + int rc; + + if (pTask->allocated) { + pthread_mutex_lock(&tasksMutex); + if ((rc = pthread_attr_destroy(&pTask->threadAttr)) != 0) { + } + pTask->allocated = 0; + pTask->threadSelf = 0; + pTask->nextFreeTask = freeTasks; + freeTasks = pTask; + numAllocatedTasks--; + pthread_mutex_unlock(&tasksMutex); + } +} + +int createOneTask(Address taskFunc, char *Name, int *entryId) +{ + redirTask *pTask = NULL; + + pTask = allocTaskFromPool(); + + if (pTask) { + int rc; + + *entryId = pTask->entryId; + strncpy(&pTask->name[0], Name, 16); + pTask->name[15] = '\0'; + pTask->taskFunc = (void (*)(void))taskFunc; + + if ((rc = pthread_attr_init(&pTask->threadAttr)) != 0) { + } + + if ((rc = pthread_create(&pTask->threadSelf, &pTask->threadAttr, (void *)taskFunc, pTask)) != 0) { + releaseTaskBackToPool(pTask); + return -1; + } + } + + return 0; +} + +int redirInitTasks() +{ + int i, rc; + redirTask *pTask; + pthread_mutexattr_t mAttr; + + numTasks = REDIR_MAX_TASKS; + tasks = calloc(numTasks, sizeof(redirTask)); + if (!tasks) { + return -1; + } + + pTask = tasks; + for (i = 0; i < numTasks; i++) { + pTask->entryId = REDIR_TASK_ID_BASE | i; + pTask->allocated = 0; + pTask->nextFreeTask = pTask + 1; + pTask++; + } + + tasks[numTasks - 1].nextFreeTask = NULL; + freeTasks = tasks; + + pthread_mutexattr_init(&mAttr); + + if ((rc = pthread_mutex_init(&tasksMutex, &mAttr)) != 0) { + free(tasks); + return -1; + } + + // setup main pTask up in the table. + pTask = allocTaskFromPool(); + + strncpy(&pTask->name[0], "main", 16); + pTask->taskFunc = NULL; + pTask->threadSelf = pthread_self(); + + return 0; +} + +void waitForOtherTaskesToFinish() +{ + redirTask *pTask = tasks; + void *ret = NULL; + int i; + + pTask++; + for (i = 1; i < REDIR_MAX_TASKS; i++) { + if (pTask->allocated) { + printf("TASK %d\n", i); + if (pthread_join(pTask->threadSelf, &ret)) { + printf("returning\n"); + return; + } + } + pTask++; + pthread_kill(pTask->threadSelf,0); + return; + } +} + diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/task.c b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/task.c index a5bb040a..a82ffd8d 100644 --- a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/task.c +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/src/task.c @@ -23,6 +23,9 @@ static struct cmd_handler { }, { .cmd = "crashlog", .cb = cmd_handler_crashlog, + }, { + .cmd = "startPortForwardingSession", + .cb = cmd_handler_port_forwarding, }, }; diff --git a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/unit.mk b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/unit.mk index 33b212b2..1e0db658 100644 --- a/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/unit.mk +++ b/feeds/wlan-ap/opensync/src/platform/openwrt/src/command/unit.mk @@ -42,12 +42,19 @@ UNIT_SRC += src/cmd_tcpdump.c UNIT_SRC += src/webserver.c UNIT_SRC += src/crashlog.c UNIT_SRC += src/cmd_crashlog.c +UNIT_SRC += src/redirclient.c +UNIT_SRC += src/redirintercomm.c +UNIT_SRC += src/redirmain.c +UNIT_SRC += src/redirmsgdispatch.c +UNIT_SRC += src/redirtask.c +UNIT_SRC += src/libwcf_devif.c +UNIT_SRC += src/cmd_portforwarding.c UNIT_CFLAGS := -I$(UNIT_PATH)/inc UNIT_CFLAGS += -Isrc/lib/common/inc/ UNIT_CFLAGS += -Isrc/lib/version/inc/ -UNIT_LDFLAGS += -lev -lubox -luci -lubus +UNIT_LDFLAGS += -lev -lubox -luci -lubus -lwebsocket UNIT_LDFLAGS += -lrt UNIT_EXPORT_CFLAGS := $(UNIT_CFLAGS)