opensync: Remote ssh session feature

The feature provides ability to create a remote ssh session.

libwebsocket is a modified version of library hence included as a package.

Signed-off-by: Ammad Rehmat <ammad.rehmat@connectus.ai>
This commit is contained in:
Ammad Rehmat
2020-10-14 17:55:10 -04:00
committed by John Crispin
parent 0cb7073abb
commit bd2412f02e
51 changed files with 29661 additions and 2 deletions

View File

@@ -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))

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <stdio.h>
#include <string.h>
#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

View File

@@ -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, "<!DOCTYPE %.*s %s ",
(int) token->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, "</%.*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++) {
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, "<!-- %.*s -->\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);
}

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}

View File

@@ -0,0 +1,238 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#if !LWS_HAVE_GETIFADDRS
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "websocket/private-libwebsockets.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#ifdef LWS_HAVE_NETINET_IN6_VAR_H
#include <netinet/in6_var.h>
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#include "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

View File

@@ -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;
}

View File

@@ -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, "<html><body><h1>%u</h1>%s</body></html>",
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;
}

View File

@@ -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 <sys/types.h>
#include <ifaddrs.h>
#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

View File

@@ -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 */
};

View File

@@ -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 */

View File

@@ -0,0 +1,470 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef _LIBBRIDGE_H
#define _LIBBRIDGE_H
#include <sys/socket.h>
#include <net/if.h>
#include <sys/time.h>
//#include <linux/if_bridge.h>
/* 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

View File

@@ -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, ... );

View File

@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef _LIBBRIDGE_PRIVATE_H
#define _LIBBRIDGE_PRIVATE_H
#include <linux/sockios.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/if_bridge.h>
#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

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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 <dlfcn.h> header file. */
#define LWS_HAVE_DLFCN_H
/* Define to 1 if you have the <fcntl.h> 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 <in6addr.h> header file. */
/* #undef LWS_HAVE_IN6ADDR_H */
/* Define to 1 if you have the <inttypes.h> 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 <memory.h> 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 <netinet/in.h> 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 <stdint.h> header file. */
#define LWS_HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */
#define LWS_HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#define LWS_HAVE_STRING_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
#define LWS_HAVE_SYS_PRCTL_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#define LWS_HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
/* #undef LWS_HAVE_SYS_SOCKIO_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define LWS_HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#define LWS_HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> 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 <vfork.h> 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 <zlib.h> 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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,730 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "websocket/private-libwebsockets.h"
#include <pwd.h>
#include <grp.h>
#ifdef LWS_WITH_PLUGINS
#include <dlfcn.h>
#endif
#include <dirent.h>
/*
* 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;
}

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "websocket/private-libwebsockets.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
struct sha1_ctxt {
union {
unsigned char b8[20];
unsigned int b32[5];
} h;
union {
unsigned char b8[8];
u_int64_t b64[1];
} c;
union {
unsigned char b8[64];
unsigned int b32[16];
} m;
unsigned char count;
};
/* sanity check */
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
# define unsupported 1
#elif BYTE_ORDER != BIG_ENDIAN
# if BYTE_ORDER != LITTLE_ENDIAN
# define unsupported 1
# endif
#endif
#ifndef unsupported
/* constant table */
static const unsigned int _K[] =
{ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
#define K(t) _K[(t) / 20]
#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
#define F1(b, c, d) (((b) ^ (c)) ^ (d))
#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
#define F3(b, c, d) (((b) ^ (c)) ^ (d))
#define S(n, x) (((x) << (n)) | ((x) >> (32 - n)))
#define H(n) (ctxt->h.b32[(n)])
#define COUNT (ctxt->count)
#define BCOUNT (ctxt->c.b64[0] / 8)
#define W(n) (ctxt->m.b32[(n)])
#define PUTBYTE(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
ctxt->c.b64[0] += 8; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
#define PUTPAD(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
static void
sha1_step(struct sha1_ctxt *ctxt)
{
unsigned int a, b, c, d, e, tmp;
size_t t, s;
#if BYTE_ORDER == LITTLE_ENDIAN
struct sha1_ctxt tctxt;
memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64);
ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2];
ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0];
ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6];
ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4];
ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10];
ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8];
ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14];
ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12];
ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18];
ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16];
ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22];
ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20];
ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26];
ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24];
ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30];
ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28];
ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34];
ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32];
ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38];
ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36];
ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42];
ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40];
ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46];
ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44];
ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50];
ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48];
ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54];
ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52];
ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58];
ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56];
ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62];
ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60];
#endif
a = H(0); b = H(1); c = H(2); d = H(3); e = H(4);
for (t = 0; t < 20; t++) {
s = t & 0x0f;
if (t >= 16)
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 20; t < 40; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 40; t < 60; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 60; t < 80; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
H(0) = H(0) + a;
H(1) = H(1) + b;
H(2) = H(2) + c;
H(3) = H(3) + d;
H(4) = H(4) + e;
bzero(&ctxt->m.b8[0], 64);
}
/*------------------------------------------------------------*/
static void
_sha1_init(struct sha1_ctxt *ctxt)
{
bzero(ctxt, sizeof(struct sha1_ctxt));
H(0) = 0x67452301;
H(1) = 0xefcdab89;
H(2) = 0x98badcfe;
H(3) = 0x10325476;
H(4) = 0xc3d2e1f0;
}
void
sha1_pad(struct sha1_ctxt *ctxt)
{
size_t padlen; /*pad length in bytes*/
size_t padstart;
PUTPAD(0x80);
padstart = COUNT % 64;
padlen = 64 - padstart;
if (padlen < 8) {
bzero(&ctxt->m.b8[padstart], padlen);
COUNT += (unsigned char)padlen;
COUNT %= 64;
sha1_step(ctxt);
padstart = COUNT % 64; /* should be 0 */
padlen = 64 - padstart; /* should be 64 */
}
bzero(&ctxt->m.b8[padstart], padlen - 8);
COUNT += ((unsigned char)padlen - 8);
COUNT %= 64;
#if BYTE_ORDER == BIG_ENDIAN
PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]);
PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]);
PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]);
#else
PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]);
PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]);
PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]);
PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]);
#endif
}
void
sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len)
{
size_t gaplen;
size_t gapstart;
size_t off;
size_t copysiz;
off = 0;
while (off < len) {
gapstart = COUNT % 64;
gaplen = 64 - gapstart;
copysiz = (gaplen < len - off) ? gaplen : len - off;
memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz);
COUNT += (unsigned char)copysiz;
COUNT %= 64;
ctxt->c.b64[0] += copysiz * 8;
if (COUNT % 64 == 0)
sha1_step(ctxt);
off += copysiz;
}
}
void
sha1_result(struct sha1_ctxt *ctxt, void *digest0)
{
unsigned char *digest;
digest = (unsigned char *)digest0;
sha1_pad(ctxt);
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, &ctxt->h.b8[0], 20);
#else
digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2];
digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0];
digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6];
digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4];
digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10];
digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8];
digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14];
digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12];
digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18];
digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16];
#endif
}
/*
* This should look and work like the libcrypto implementation
*/
LWS_VISIBLE unsigned char *
lws_SHA1(const unsigned char *d, size_t n, unsigned char *md)
{
struct sha1_ctxt ctx;
_sha1_init(&ctx);
sha1_loop(&ctx, d, n);
sha1_result(&ctx, (void *)md);
return md;
}
#endif /*unsupported*/

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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_ */

View File

@@ -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 <libwcf_debug.h>
//#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

View File

@@ -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_ */

View File

@@ -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__

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <libubox/list.h>
#include <evsched.h>
#include <net/if.h>
#include "command.h"
#include <fcntl.h>
#include <unistd.h>
#include <curl/curl.h>
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,747 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#ifdef CMAKE_BUILD
#include <websocket/lws_config.h>
#endif
#include <websocket/libwebsockets.h>
#include <libwebsockets.c>
#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; i<commBuf.numBuf; i++ )
{
if( commBuf.bufTxDes[i].isValid == 0 )
{
commBuf.bufTxDes[i].pBuf = Buffer;
commBuf.bufTxDes[i].isValid = 1;
commBuf.bufTxDes[i].size = len;
found = 1;
break;
}
}
if( found )
gmsgdd.txMsgIsReady = 1;
else
{
free( Buffer );
}
pthread_mutex_unlock( &commBuf.txBufLock );
return 0;
}
static int redirLinkClientMsgWritableHandler( struct lws *wsi, struct per_session_data__ping *psd, unsigned char *buff, int len)
{
int n;
unsigned char * p;
p = &pingbuf[LWS_PRE];
memcpy(p, buff, len);
global_tx_count++;
n = lws_write(wsi, &pingbuf[LWS_PRE],
len, redircfg.write_options | LWS_WRITE_BINARY);
if( n < 0 )
{
RedirWsTxErr = 1;
return -1;
}
if (n < len) {
lwsl_err("Partial write\n");
return -1;
}
return 0;
}
static int redirLinkClientPongReceiveHandler(struct lws *wsi, struct per_session_data__ping *psd, void *in,
size_t len)
{
struct timespec tv;
unsigned char *p;
int shift, n;
unsigned long l;
int match = 0;
unsigned long iv;
clock_gettime( CLOCK_MONOTONIC, &tv );
iv = (tv.tv_sec * 1000000) + tv.tv_nsec/1000;
psd->rx_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; i<REDIR_BUFF_MAX; i++ ) {
if (tmpBTD[i].isValid) {
redirLinkClientMsgWritableHandler(this, wsi, psd, tmpBTD[i].pBuf);
free(tmpBTD[i].pBuf);
} else{
break;
}
}
gmsgdd.txMsgIsReady = 0;
}
break;
#endif
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
//wlog("LWS_CALLBACK_CLIENT_CONNECTION_ERROR **********************\n");
break;
case LWS_CALLBACK_ADD_POLL_FD:
redirAddPOnePollFd(pa->fd, 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;
}

View File

@@ -0,0 +1,160 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#include <pthread.h>
#ifdef CMAKE_BUILD
#include <lws_config.h>
#endif
#include <websocket/libwebsockets.h>
#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;
}

View File

@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <websocket/libwebsockets.h>
#include <net/if.h>
#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 ;
}

View File

@@ -0,0 +1,159 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#include <pthread.h>
#include <websocket/libwebsockets.h>
#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 );
}

View File

@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <unistd.h>
#include <pthread.h>
#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;
}
}

View File

@@ -23,6 +23,9 @@ static struct cmd_handler {
}, {
.cmd = "crashlog",
.cb = cmd_handler_crashlog,
}, {
.cmd = "startPortForwardingSession",
.cb = cmd_handler_port_forwarding,
},
};

View File

@@ -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)